Скелетная анимация на GPU для Android OpenGL ES - PullRequest
5 голосов
/ 01 апреля 2011

Я реализую скелетную анимацию в своем телефоне Android, и вот как я это делаю:

  1. Рассчитать все матрицы преобразования кости на стороне процессора

  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. В вершинном шейдере выберите матрицу из этой текстуры и примените ее к позиции вершины

Этот подход прекрасно работает в моем Motorola Milestone XT-701 с использованием графического процессора POWERVR. Но когда я запускаю его на оснастке Qualcomm (SE Xperia X10i и Google Nexus one), многие треугольники исчезли (выглядит случайным образом), поэтому кажется, что модель мерцает.

Я также попытался уменьшить сложность сцены, отобразив только одну анимированную модель, и мерцание стало меньше, но все еще существует. Кто-нибудь знает, что я могу делать не так? Это какая-то проблема синхронизации?

Здесь вы можете увидеть снимки (первые два изображения правильные, а два последних неправильные). APK-файл моей программы можно скачать здесь . (не требует никакого разрешения, поэтому не беспокойтесь)

Вот вершинный шейдер, который я использовал:

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 Ответ

5 голосов
/ 01 апреля 2011
  1. Повторное создание текстуры каждого кадра неэффективно.Вы можете использовать один и тот же texID и просто вызывать glTexImage* каждый кадр.

  2. Почему вы не используете 1D текстуру?Это снимет бремя преобразования текстовых координат в 2D.

  3. Ваша матрица суставов имеет размер 4x4, но вы сохраняете ее как 4 вектора GL_RGBA.Этот внутренний формат допускает только диапазон [0,1], который не подходит для вашей задачи.Попробуйте вместо этого использовать GL_RGBA_16f в качестве внутреннего формата.

...