Як я можу надійно впровадити графічний графічний графічний процес в Android?


11

Я намагаюся змусити скин-героїв працювати на Android.

Ідея досить ванільна: я маю свої матриці для зняття шкіри, і разом з кожною вершиною я надсилаю до чотирьох матричних індексів і чотирьох відповідних ваг. Я сумую їх у вершинній шейдері та застосовую їх до кожної вершини.

Це те, що я роблю у вершинній шейдері у версії своєї гри для iOS (не заперечуйте нормалів):

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;
}

І це працює досить добре. Однак, з тим самим кодом в Android, на деяких пристроях (зокрема, Nexus 7 2013) ви не можете отримати доступ до uniforms з постійними індексами. Іншими словами, ви не можете цього зробити:

bones[int(in_bone_index.w)]

бо bones[some_non_constant]завжди оцінюється як bones[0], що зовсім не забавно. Найгірше те, що компілятор шейдерів із задоволенням складає це.

У цього хлопця, здавалося, була точно така ж проблема. Він вирішив це, дійшовши до уніформи як вектори, а не матриці. Я робив те саме, і насправді це працювало!

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;
}

Але я думаю, що це спрацювало як випадковість. uniforms призначені не для випадкового доступу, тому я побоююся, що ця "техніка" працюватиме не на кожному пристрої.

Цей хлопець передає свої матриці як текстури, що є досить крутою ідеєю. Я зробив текстуру 4x32 OES_texture_float, де кожен тексель є матричним рядком, а кожен текстурний ряд - цілою матрицею. Я отримую доступ до нього так:

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;
}

Насправді, це спрацювало дуже добре ... Доки я не спробував це на своєму Galaxy Note 2. Цього разу компілятор поскаржився, що я не можу використовувати texture2Dвершинний шейдер!

Тож те, що я роблю, - це перевірити, чи підтримує GPU доступ до текстури на вершинній шейдері та чи підтримує він OES_texture_float. Якщо це так, я використовую текстурний підхід. Якщо цього не відбувається, я використовую векторний підхід.

Однак текстурний підхід доступний не на всіх платформах, а векторний підхід начебто працює випадково. Мені хотілося б знати, чи є спосіб передати мої матриці зняття на вершинний шейдер, який надійно працює на всіх пристроях.

У мене можуть бути мінімальні розумні вимоги до ОС, як-от Android 4.1+, але я хотів би мати рішення, яке працює на всіх пристроях, які відповідають цим вимогам.


Ну, я не можу придумати альтернативи, TBH. Я вважаю, що найкраще скористатися обома методами, залежно від того, який із них доступний, якщо жодна з них не доступна, не використовуйте графічний графік, просто відступ до впровадження процесора для роботи з процесором (напевно, з менш деталізованими моделями ?).
concept3d

@ concept3d: Я не знаю, можливо, якесь розширення, яке гарантовано існує на Android 4.1+, яке призначене для вирішення цієї проблеми, або робити щось концептуально інше з тими ж результатами. Я вважаю себе досить досвідченим у загальних концепціях багатьох тем, але мої знання про платформу Android дуже обмежені, і я думав, що може бути чисто технічне вирішення цієї проблеми.
Піжама Panda

Тож, можливо, саме тому я не зміг змусити себе працювати над своїм Nexus 7.: / Спасибі, ваше запитання відкрило мені очі!
асинхрон

Відповіді:


4

Це невідповідна поведінка Nexus 7 (Adreno GPU). Ви кажете, що "уніформа не призначена для випадкового доступу", але згідно з додатком А до специфікації :

Уніформа (крім пробників)

У вершинній шейдері передбачена підтримка всіх форм індексації масивів. У фрагменті шейдера підтримка індексації призначена лише для виразів з постійним індексом.

Це звучить з обговорення тут , що ця помилка стосується лише рівномірним матричних масивів, тому робота навколо з допомогою векторів, швидше за все , працювати надійно і бути стерпним на інші графічні процесори (я знаю , що випадкова рівномірну індексація працює по крайней мере , на Малі і PowerVR графічних процесорах).


Хм, я думаю, це здається правильним. Я думаю, що я читав цю тему раніше, але Qualcomm не підтвердив цю проблему.
Піжама Panda
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.