Search code examples
androidc++opengl-esglsles

Android OpenGL ES 3.0 Skeletal Animations glitch with UBO


I've been spending the better part of the last 2 days hunting down an OpenGL ES bug I've encountered only on some devices. In detail:

I'm trying to implement skeletal animations, using the following GLSL code:

#ifndef NR_BONES_INC
#define NR_BONES_INC

#ifndef NR_MAX_BONES
#define NR_MAX_BONES 256
#endif

in ivec4 aBoneIds;
in vec4 aBoneWeights;

layout (std140) uniform boneOffsets { mat4 offsets[NR_MAX_BONES]; } bones;

mat4 bones_get_matrix() {
    mat4 mat = bones.offsets[aBoneIds.x] * aBoneWeights.x;
    mat += bones.offsets[aBoneIds.y] * aBoneWeights.y;
    mat += bones.offsets[aBoneIds.z] * aBoneWeights.z;
    mat += bones.offsets[aBoneIds.w] * aBoneWeights.w;

    return mat;
}

#endif

This is then included in the vertex shader and used as such:

vec4 viewPos = mv * bones_get_matrix() * vec4(aPos, 1.0);
gl_Position = projection * viewPos;

The desired output, achieved for example on the Android Emulator (armv8) running on my M1 MacBook Pro is this:

Normal looking T-Pose

I can't actually capture the output of the faulting device (Espon Moverio BT-350, Android on x86 Intel Atom) sadly, but it's basically the same picture without head, arms or legs.

The uniform buffer bound to boneOffsets, for testing, is created as a std::vector<glm::mat4> with size 256 and is created/bound as such:

GLint buffer = 0;
std::vector<glm::mat4> testData(256, glm::mat4(1.0));
glGenBuffers(1, &buffer);
glBindBuffer(GL_UNIFORM_BUFFER, buffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(glm::mat4) * testData.size(), &testData[0], GL_DYNAMIC_DRAW);

glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
glUniformBlockBinding(programId, glGetUniformBlockIndex(programId, "boneOffsets"), 0); 

Am I missing a step somewhere in my setup? Is this a GPU bug I'm encountering? Have I misunderstood the std140 layout?

P.S.: After every OpenGL call, I run glGetError(), but nothing shows up. Also nothing in the various info logs for Shaders and Programs.

EDIT

It's the next day, and I've tried skipping the UBO and using a plain uniform array (100 elements instead of 256, my model has 70-ish bones anyway). Same result.

I've also just tried with a "texture". It's a 4*256 GL_RGBAF teture, which is "sampled" as such:

uniform sampler2D bonesTxt;

mat4 read_bones_txt(int id) {
    return mat4(
        texelFetch(bonesTxt, ivec2(0, id), 0),
        texelFetch(bonesTxt, ivec2(1, id), 0),
        texelFetch(bonesTxt, ivec2(2, id), 0),
        texelFetch(bonesTxt, ivec2(3, id), 0));
}

Still no dice. As a comment suggested, I've checked my bone IDs and Weights. What I send to glBufferData() is ok, but I can't actually check what's on the GPU because I can't get RenderDoc (or anything else) to work on my device.


Solution

  • I finally figured it out.

    When binding my bone IDs I used glVertexAttribPointer() instead of glVertexAttribIPointer().

    I was sending the correct type (GL_INT) to glVertexAttribPointer(), but I didn't read this line in the docs:

    For glVertexAttribPointer() [...] values will be converted to floats [...]

    As usual, RTFM people.