Tôi đang cố gắng để nhân vật hoạt động trên Android.
Ý tưởng khá vani: Tôi có ma trận lột da của mình và cùng với mỗi đỉnh, tôi gửi tối đa bốn chỉ số ma trận và bốn trọng số tương ứng. Tôi tổng hợp chúng trong shader đỉnh và áp dụng chúng cho từng đỉnh.
Đây là những gì tôi đang làm trong trình tạo bóng đỉnh trong phiên bản iOS của trò chơi của tôi (đừng bận tâm đến các quy tắc thông thường):
attribute vec4 in_pos;
attribute vec4 in_normal;
attribute vec2 in_texture_coords;
attribute vec4 in_bone_index;
attribute vec4 in_bone_weight;
varying vec2 fs_texture_coords;
uniform mat4 world_view_projection;
uniform mat4 bones[@bind_matrix_count];
void main()
{
// Skinning
vec4 transformed_pos =
((in_pos * bones[int(in_bone_index.x)]) * in_bone_weight.x) +
((in_pos * bones[int(in_bone_index.y)]) * in_bone_weight.y) +
((in_pos * bones[int(in_bone_index.z)]) * in_bone_weight.z) +
((in_pos * bones[int(in_bone_index.w)]) * in_bone_weight.w);
gl_Position = world_view_projection * transformed_pos;
fs_texture_coords = in_texture_coords;
}
Và nó hoạt động khá tốt. Tuy nhiên, với cùng một mã trong Android, trong một số thiết bị (đáng chú ý là Nexus 7 2013), bạn không thể truy cập uniform
s với các chỉ số không liên tục. Nói cách khác, bạn không thể làm điều này:
bones[int(in_bone_index.w)]
bởi vì bones[some_non_constant]
luôn luôn được đánh giá là bones[0]
, điều này không gây cười chút nào. Điều tồi tệ nhất là trình biên dịch shader vui vẻ biên dịch cái này.
Anh chàng này dường như có cùng một vấn đề. Ông đã giải quyết nó bằng cách truy cập đồng phục dưới dạng vectơ thay vì ma trận. Tôi đã làm như vậy, và trên thực tế nó đã làm việc!
attribute vec4 in_pos;
attribute vec4 in_normal;
attribute vec2 in_texture_coords;
attribute vec4 in_bone_index;
attribute vec4 in_bone_weight;
varying vec2 fs_texture_coords;
uniform mat4 world_view_projection;
uniform vec4 bones[@bind_matrix_count * 4]; // four vec4's for each matrix
void main()
{
// Skinning
mat4 skin_0 = mat4(
bones[4 * int(in_bone_index.x) + 0],
bones[4 * int(in_bone_index.x) + 1],
bones[4 * int(in_bone_index.x) + 2],
bones[4 * int(in_bone_index.x) + 3]);
mat4 skin_1 = mat4(
bones[4 * int(in_bone_index.y) + 0],
bones[4 * int(in_bone_index.y) + 1],
bones[4 * int(in_bone_index.y) + 2],
bones[4 * int(in_bone_index.y) + 3]);
mat4 skin_2 = mat4(
bones[4 * int(in_bone_index.z) + 0],
bones[4 * int(in_bone_index.z) + 1],
bones[4 * int(in_bone_index.z) + 2],
bones[4 * int(in_bone_index.z) + 3]);
mat4 skin_3 = mat4(
bones[4 * int(in_bone_index.w) + 0],
bones[4 * int(in_bone_index.w) + 1],
bones[4 * int(in_bone_index.w) + 2],
bones[4 * int(in_bone_index.w) + 3]);
vec4 transformed_pos =
((in_pos * skin_0) * in_bone_weight.x) +
((in_pos * skin_1) * in_bone_weight.y) +
((in_pos * skin_2) * in_bone_weight.z) +
((in_pos * skin_3) * in_bone_weight.w);
gl_Position = world_view_projection * transformed_pos;
fs_texture_coords = in_texture_coords;
}
Nhưng tôi nghĩ rằng điều này làm việc như là cơ hội. uniform
s không có nghĩa là được truy cập ngẫu nhiên, vì vậy tôi sợ "kỹ thuật" này sẽ không hoạt động trong mọi thiết bị.
Anh chàng này đang vượt qua ma trận của mình như kết cấu, đó là một ý tưởng khá tuyệt vời. Tôi đã tạo một kết cấu 4x32 OES bảnure_float, trong đó mỗi texel là một hàng ma trận và mỗi hàng kết cấu là một toàn bộ ma trận. Tôi truy cập nó như thế này:
attribute vec4 in_pos;
attribute vec4 in_normal;
attribute vec2 in_texture_coords;
attribute vec4 in_bone_index;
attribute vec4 in_bone_weight;
varying vec2 fs_texture_coords;
uniform mat4 world_view_projection; // A texture!
uniform sampler2D bones;
void main()
{
// Skinning
mat4 bone0 = mat4(
texture2D(bones, vec2(0.00, in_bone_index.x / 32.0)),
texture2D(bones, vec2(0.25, in_bone_index.x / 32.0)),
texture2D(bones, vec2(0.50, in_bone_index.x / 32.0)),
texture2D(bones, vec2(0.75, in_bone_index.x / 32.0)));
mat4 bone1 = mat4(
texture2D(bones, vec2(0.00, in_bone_index.y / 32.0)),
texture2D(bones, vec2(0.25, in_bone_index.y / 32.0)),
texture2D(bones, vec2(0.50, in_bone_index.y / 32.0)),
texture2D(bones, vec2(0.75, in_bone_index.y / 32.0)));
mat4 bone2 = mat4(
texture2D(bones, vec2(0.00, in_bone_index.z / 32.0)),
texture2D(bones, vec2(0.25, in_bone_index.z / 32.0)),
texture2D(bones, vec2(0.50, in_bone_index.z / 32.0)),
texture2D(bones, vec2(0.75, in_bone_index.z / 32.0)));
mat4 bone3 = mat4(
texture2D(bones, vec2(0.00, in_bone_index.w / 32.0)),
texture2D(bones, vec2(0.25, in_bone_index.w / 32.0)),
texture2D(bones, vec2(0.50, in_bone_index.w / 32.0)),
texture2D(bones, vec2(0.75, in_bone_index.w / 32.0)));
vec4 transformed_pos =
((in_pos * bone0) * in_bone_weight.x) +
((in_pos * bone1) * in_bone_weight.y) +
((in_pos * bone2) * in_bone_weight.z) +
((in_pos * bone3) * in_bone_weight.w);
gl_Position = world_view_projection * transformed_pos;
fs_texture_coords = in_texture_coords;
}
Trên thực tế, điều này hoạt động khá tốt ... Cho đến khi tôi dùng thử trên Galaxy Note 2. Lần này trình biên dịch phàn nàn rằng tôi không thể sử dụng texture2D
trên trình tạo bóng đỉnh!
Vì vậy, những gì tôi đang làm là kiểm tra xem GPU có hỗ trợ truy cập kết cấu trên trình tạo bóng đỉnh hay không và liệu nó có hỗ trợ OES bảnure_float hay không. Nếu có, tôi đang sử dụng phương pháp kết cấu. Nếu không, tôi đang sử dụng phương pháp vectơ.
Tuy nhiên, cách tiếp cận kết cấu không có sẵn trên tất cả các nền tảng và cách tiếp cận véc tơ hoạt động khá tình cờ. Tôi muốn biết liệu có cách nào để chuyển ma trận lột da của tôi sang trình tạo bóng đỉnh, hoạt động đáng tin cậy trên tất cả các thiết bị.
Tôi có thể có các yêu cầu hệ điều hành hợp lý tối thiểu, như Android 4.1+, nhưng tôi muốn có một giải pháp hoạt động trên tất cả các thiết bị đáp ứng các yêu cầu đó.