Làm cách nào tôi có thể triển khai GPU skin trong Android một cách đáng tin cậy?


10

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 uniforms 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. uniforms 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 texture2Dtrê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 đó.


Chà, tôi không thể nghĩ ra giải pháp thay thế, TBH Tôi nghĩ rằng cách tốt nhất của bạn là sử dụng cả hai kỹ thuật tùy thuộc vào kỹ thuật nào có sẵn, nếu không có khả năng, đừng sử dụng GPU skinning chỉ để thực hiện giao diện CPU (có thể với các mô hình ít chi tiết hơn ?).
Concept3d

@ concept3d: Tôi không biết, có thể một số tiện ích mở rộng được đảm bảo tồn tại trên Android 4.1+ được thiết kế để giải quyết vấn đề này hoặc thực hiện một cái gì đó khác biệt về mặt khái niệm với cùng kết quả. Tôi cho rằng bản thân mình khá thành thạo các khái niệm chung về rất nhiều chủ đề, nhưng kiến ​​thức về nền tảng Android của tôi rất hạn chế và tôi đã nghĩ rằng có thể có một cách giải quyết kỹ thuật thuần túy cho vấn đề này.
Panda Pyjama

Vì vậy, có lẽ đây là lý do tại sao tôi không thể làm cho skin hoạt động trên Nexus 7 .: / Cảm ơn, câu hỏi của bạn đã mở mắt tôi!
async

Câu trả lời:


4

Đây là hành vi không tuân thủ của Nexus 7 (GPU Adreno). Bạn nói "đồng phục không có nghĩa là được truy cập ngẫu nhiên", nhưng theo Phụ lục A của thông số kỹ thuật :

Đồng phục (không bao gồm người lấy mẫu)

Trong shader đỉnh, hỗ trợ cho tất cả các dạng lập chỉ mục mảng là bắt buộc. Trong shader mảnh, hỗ trợ lập chỉ mục chỉ bắt buộc cho các biểu thức chỉ mục không đổi.

Có vẻ như từ cuộc thảo luận ở đây rằng lỗi này chỉ áp dụng cho các mảng ma trận thống nhất, do đó, việc sử dụng các vectơ có khả năng hoạt động đáng tin cậy và có thể di chuyển được với các GPU khác (Tôi biết lập chỉ mục thống nhất ngẫu nhiên hoạt động ít nhất trên GPU Mali và PowerVR).


Hmm, tôi đoán điều đó có vẻ đúng. Tôi nghĩ rằng tôi đã đọc chủ đề đó trước đây, nhưng không có sự thừa nhận nào từ Qualcomm xác nhận vấn đề này.
Panda Pyjama
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.