如何在Android中可靠地实现GPU外观?


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)中,您将无法访问uniform具有非恒定索引的。换句话说,您不能执行以下操作:

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

但是我认为这是偶然的。uniform并非随机访问s,因此我担心此“技术”将不适用于所有设备。

这个家伙将矩阵作为纹理传递,这是一个很酷的主意。我制作了一个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。如果是这样,我正在使用纹理方法。如果没有,我正在使用向量方法。

但是,纹理方法并非在所有平台上都可用,向量方法有点偶然。我想知道是否有一种方法可以将蒙皮矩阵传递给顶点着色器,该着色器可以在所有设备上可靠地工作。

我可以具有最低的合理OS要求,例如Android 4.1+,但我希望有一个适用于所有满足这些要求的设备的解决方案。


好吧,我想不出其他选择,TBH,我认为您最好的选择是使用两种技术,取决于哪一种可用,如果两种都不可用,请不要使用GPU外观,而只是回退到CPU外观实现(可能是采用较不详细的模型) ?)。
concept3d

@ concept3d:我不知道,也许可以确保在Android 4.1+上存在某些扩展名,该扩展名旨在解决此问题,或者在概念上做一些不同的事情而获得相同的结果。我认为自己非常精通许多主题的一般概念,但是我对Android平台的了解非常有限,而且我认为可能存在解决此问题的纯技术方法。
2014年

所以也许这就是为什么我无法在Nexus 7上使用皮肤的原因::/谢谢,您的问题让我大开眼界!
2014年

Answers:


4

Nexus 7(Adreno GPU)的行为不合格。您说“不应该统一访问制服”,而是根据规范的附录A :

制服(不包括采样器)

在顶点着色器中,必须支持所有形式的数组索引。在片段着色器中,仅对常量索引表达式强制要求支持索引。

这里的讨论中可以看出,此错误仅适用于统一矩阵阵列,因此使用向量的变通办法可能可靠地工作,并且可以移植到其他GPU(我知道随机统一索引至少在Mali和PowerVR GPU上有效)。


嗯,我想这似乎是对的。我想我以前读过该线程,但是高通公司没有确认这一问题。
2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.