适用于Android OpenGL ES的GPU上的骨骼动画

时间:2011-04-01 06:42:50

标签: android opengl-es gpu

我正在我的Android手机中实现骨骼动画,这就是我的工作方式:

  1. 计算CPU端的所有骨骼转换矩阵

  2. 创建一个浮动纹理来存储这些矩阵(所以这是在每个帧的开头完成的)。代码如下所示:

    if(texID) {
        glDeleteTextures(1, &texID);
        texID = 0;
    }
    
    glGenTextures(1, &texID);
    glBindTexture(GL_TEXTURE_2D, texID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, p);
    
  3. 在顶点着色器中,从此纹理中获取矩阵并应用于顶点位置

  4. 这种方法在使用POWERVR GPU的Motorola Milestone XT-701中运行良好。但是当我在Qualcomm snapdragon(SE Xperia X10i和Google Nexus one)上运行它时,有很多三角形消失了(看起来是随机的),所以看起来模型是闪烁的。

    我还尝试通过仅渲染一个动画模型来降低场景复杂度,并且闪烁变小但仍然存在。有谁知道我可能做错了什么?这是某种同步问题吗?

    你可以在这里看到snapshots(前两张图片是正确的,后两张图片是错误的)。我的grogram的APK文件可以下载here。 (它不需要任何许可,所以不要担心)

    这是我使用的顶点着色器:

    struct light {
        lowp vec4    position;  // light position for a point/spot light or
                          // normalized dir. for a directional light
        lowp vec4    ambient_color;
        lowp vec4    diffuse_color;
        lowp vec4    specular_color;
        lowp vec3    spot_direction;
        lowp vec3    attenuation_factors;
        lowp float   spot_exponent;
        lowp float   spot_cutoff_angle;
        bool         compute_distance_attenuation;
    };
    
    struct material {
        lowp vec4    ambient_color;
        lowp vec4    diffuse_color;
        lowp vec4    specular_color;
        lowp vec4    emissive_color;
        lowp float   specular_exponent;
    };
    
    // uniforms used by the vertex shader
    // uniform vec4 u_color;
    uniform highp mat4     u_mvMatrix;
    uniform highp mat4     u_projMatrix;
    uniform bool           u_enable_lighting;
    uniform light          u_light_state;
    uniform material       u_material_state;
    uniform bool           u_enable_texture;
    uniform highp sampler2D   s_jointTex; // use highp for float texture
    
    // attributes input to the vertex shader
    // attribute lowp vec4 a_color;
    attribute highp vec3    a_position;
    attribute lowp vec3        a_normal;
    attribute mediump vec2    a_texCoord;
    attribute highp float    a_jointID;
    
    // varying variables – input to the fragment shader
    varying lowp vec4        v_front_color;
    varying mediump vec2    v_texCoord;
    
    vec2 mapTo2D(float idx)
    {
        vec2 st = vec2(idx + 0.5, 0.5);
        return st / 256.0;
    }
    
    void main()
    {
        mat4 joint = mat4(1.0);
    
        if(a_jointID >= 0.0)
        {
            float idx = a_jointID * 4.0;
            joint = mat4(    texture2D(s_jointTex, mapTo2D(idx)), 
                            texture2D(s_jointTex, mapTo2D(idx+1.0)), 
                            texture2D(s_jointTex, mapTo2D(idx+2.0)), 
                            texture2D(s_jointTex, mapTo2D(idx+3.0)) );
            gl_Position = (u_projMatrix * u_mvMatrix) * joint * vec4(a_position, 1.0); // hint compiler to extract uniform calculation
            // v_front_color = vec4(1.0, 0.0, 0.0, 1.0);
        }
        else
        {
            gl_Position = (u_projMatrix * u_mvMatrix) * vec4(a_position, 1.0); // hint compiler to extract uniform calculation
            // v_front_color = vec4(0.0, 1.0, 0.0, 1.0);
        }
    
        if(u_enable_lighting)
        {
            lowp vec4 computed_color = vec4(0.0);
            vec3 normal = normalize( vec3(u_mvMatrix * joint * vec4(a_normal, 0.0) ) );
            vec3 lightDir = normalize( vec3(u_mvMatrix * u_light_state.position) );
            float NdotL = max(dot(normal, lightDir), 0.0);
    
            computed_color += u_light_state.ambient_color * u_material_state.ambient_color + NdotL * u_light_state.diffuse_color * u_material_state.diffuse_color;
    
            if(NdotL > 0.0) {
                vec3 half_vec = normalize(lightDir + vec3(0.0, 0.0, 1.0)); // why?
                float NdotHV = dot(normal, half_vec);
                if(NdotHV > 0.0)
                    computed_color += u_light_state.specular_color * u_material_state.specular_color * pow(NdotHV, u_material_state.specular_exponent);
            }
            v_front_color = computed_color;
        }
        else
            v_front_color = vec4(1.0, 1.0, 1.0, 1.0); // u_material_state.ambient_color; // TODO?
    
        v_texCoord = a_texCoord;
    }
    

1 个答案:

答案 0 :(得分:5)

  1. 每帧重新创建纹理效率不高。您可以使用相同的texID,并且每帧只调用glTexImage*

  2. 为什么不使用1D纹理?这将消除tex-coord转换为2D的负担。

  3. 您的关节矩阵是4x4,但您将其存储为4个GL_RGBA向量。此内部格式仅允许[0,1]范围,这不适合您的任务。请尝试使用GL_RGBA_16f作为内部格式。