Скелетная анимация на GPU для Android OpenGL ES

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

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

  2. Создайте текстуру float для хранения этих матриц (так что это делается в начале каждого кадра). Коды выглядят следующим образом:

    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. Но когда я запускаю его на Snapdragon Qualcomm (SE Xperia X10i и Google Nexus one), появляется много треугольников (выглядит случайным образом), поэтому кажется, что модель мерцает.

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

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

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

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

Solutions Collecting From Web of "Скелетная анимация на GPU для Android OpenGL ES"

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

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

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