Staram się, aby skórowanie postaci działało na Androidzie.
Pomysł jest dość waniliowy: mam swoje matryce do skórowania i wraz z każdym wierzchołkiem wysyłam do czterech wskaźników macierzy i czterech odpowiednich wag. Sumuję je w module cieniującym wierzchołki i stosuję je do każdego wierzchołka.
Oto, co robię w module cieniującym wierzchołki w mojej wersji na iOS (nie mówiąc o normalnych):
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;
}
I działa całkiem dobrze. Jednak z tym samym kodem w Androidzie, na niektórych urządzeniach (zwłaszcza Nexus 7 2013), nie możesz uzyskać dostępu do uniform
s przy niestałych indeksach. Innymi słowy, nie możesz tego zrobić:
bones[int(in_bone_index.w)]
ponieważ bones[some_non_constant]
zawsze jest oceniany jako bones[0]
, co wcale nie jest zabawne. Najgorsze jest to, że kompilator modułu cieniującego chętnie to kompiluje.
Ten facet miał dokładnie ten sam problem. Rozwiązał go, uzyskując dostęp do mundurów jako wektorów zamiast matryc. Zrobiłem to samo i faktycznie zadziałało!
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;
}
Ale myślę, że to działało przypadkowo. uniform
nie są przeznaczone do przypadkowego dostępu, więc obawiam się, że ta „technika” nie zadziała w każdym urządzeniu.
Ten facet przekazuje swoje matryce jako tekstury, co jest całkiem fajnym pomysłem. Zrobiłem teksturę 4x32 OES_texture_float, w której każdy texel jest rzędem matrycy, a każdy wiersz tekstury jest całą matrycą. Uzyskuję do tego dostęp w ten sposób:
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;
}
W rzeczywistości działało to całkiem nieźle ... Dopóki nie wypróbowałem tego na mojej Galaxy Note 2. Tym razem kompilator narzekał, że nie mogę używać texture2D
go w module cieniującym wierzchołków!
Więc sprawdzam, czy GPU obsługuje dostęp do tekstur w module cieniującym wierzchołki i czy obsługuje OES_texture_float. Jeśli tak, używam podejścia do tekstury. Jeśli nie, używam podejścia wektorowego.
Jednak podejście do tekstury nie jest dostępne na wszystkich platformach, a podejście wektorowe działa raczej przypadkowo. Chciałbym wiedzieć, czy istnieje sposób na przekazanie moich macierzy skórowania do modułu cieniującego wierzchołki, który niezawodnie działa na wszystkich urządzeniach.
Mogę mieć minimalne uzasadnione wymagania dotyczące systemu operacyjnego, takie jak Android 4.1+, ale chciałbym mieć rozwiązanie, które działa na wszystkich urządzeniach spełniających te wymagania.