Skeletal Animation Shader for an Assimp Model

Started by
4 comments, last by Chicktopus 9 years, 8 months ago
Hi guys
I'm having some trouble getting my skeletal animation working. I've imported the model with Assimp, created the bone hierarchy, calculated all the bone transforms, so on and so forth. I've debugged through the code and the bone information *looks* like it's updating as I'd expect. The issue I'm having seems to come from the shader. As soon as I start using the bone information to update the position, the model ceases to display. I'm not sure whether this is because the bone information hasn't been successfully passed to the shader, the multiplications are wrong, or for some other reason (such as the model is now positioned out-of-view of the camera). If required I can post the entire contents of the class so you can see the entire process. Any input would be ace.
Here's the vertex shader code:


    #version 330
    
    layout (location = 0) in vec3 inPosition;
    layout (location = 1) in vec2 inCoord;
    layout (location = 2) in vec3 inNormal;
    layout (location = 3) in ivec4 BoneIDs;
    layout (location = 4) in vec4 Weights;
    
    smooth out vec3 vNormal;
    smooth out vec2 vTexCoord;
    smooth out vec3 vWorldPos;
    smooth out vec4 vEyeSpacePos;
    
    uniform struct Matrices
    {
     mat4 projMatrix;
     mat4 modelMatrix;
     mat4 viewMatrix;                                                                           
     mat4 normalMatrix;
    } matrices;
    
    const int MAX_BONES = 100;
    uniform mat4 gBones[MAX_BONES];
    
    void main()
    {
      mat4 BoneTransform = gBones[BoneIDs[0]] * Weights[0];
      BoneTransform     += gBones[BoneIDs[1]] * Weights[1];
      BoneTransform     += gBones[BoneIDs[2]] * Weights[2];
      BoneTransform     += gBones[BoneIDs[3]] * Weights[3];
     
      mat4 m_MV = matrices.viewMatrix*matrices.modelMatrix;  
      mat4 m_MVP = matrices.projMatrix*matrices.viewMatrix*matrices.modelMatrix;
      vTexCoord = inCoord;
     
      vec4 m_transPos = BoneTransform * vec4(inPosition, 1.0);
      vEyeSpacePos = m_MV * m_transPos;
      gl_Position = m_MVP * m_transPos;
      
        vec4 m_normal = BoneTransform * vec4(inNormal, 1.0);
        vNormal      = (matrices.modelMatrix * m_normal).xyz;
        vWorldPos    = (matrices.modelMatrix * m_transPos).xyz;      
    }
Here's the Draw() function as it stands, in which the bone information and uniforms are passed to the shader:


    void Sprite::Draw()
    {
     glBindVertexArray(g_VAO);
     g_mainSP.UseProgram();
    
     g_mainSP.SetUniform("matrices.projMatrix", g_projMat);
     g_mainSP.SetUniform("matrices.viewMatrix", g_viewMat);
     g_mainSP.SetModelAndNormalMatrix("matrices.modelMatrix", "matrices.normalMatrix", g_modelMat);
     for (uint i = 0; i < g_transforms.size(); i++) {
     g_mainSP.SetBoneTransform(i, g_transforms[i]);
     }
    
     g_mainSP.SetUniform("gSampler", 0);
     g_mainSP.SetUniform("vColor", glm::vec4(1, 1, 1, 1));
     g_ambient.SetUniformData(&g_mainSP, "sunLight");
    
     if (!g_loaded)return;
     for (uint i = 0; i < g_subMeshes.size(); i++) 
     {
     const uint m_matIndex = g_subMeshes[i].materialIndex;
     g_textures[m_matIndex].BindTexture();
     aiMesh* m_mesh = g_scene->mMeshes[i];
     glDrawElementsBaseVertex(GL_TRIANGLES,
     g_subMeshes[i].numIndices,
     GL_UNSIGNED_INT,
     (void*)(sizeof(uint)* g_subMeshes[i].baseIndex),
     g_subMeshes[i].baseVertex);
     }
     glBindVertexArray(0);
    }
Finally:

  void ShaderProgram::SetBoneTransform(glm::uint p_index, const glm::mat4& p_transform)
    {
     glUniformMatrix4fv(g_boneLoc[p_index], 1, GL_TRUE, glm::value_ptr<GLfloat>(p_transform));
    }
Advertisement

I'm not sure whether this is because the bone information hasn't been successfully passed to the shader, the multiplications are wrong, or for some other reason (such as the model is now positioned out-of-view of the camera)

What have you tried to determine those values? Staring at code isn't nearly as effective as looking at actual values. There are two pieces to debugging - the values have to be correct, and the code has to use them correctly.

Also, you're reserving 400 registers for just your bone matrices. Does your shader version/GPU support that? Do you seriously have models with 100 bones?

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Hi,

Thanks for the quick reply. This is the first time I've had to do skeletal animation, so I'm basing most things to do with the bones/animation side of things off the tutorial found here. Using the project included with that tutorial, I can debug both pieces of code at once to determine whether my values (bone location, indices, etc.) conform. Thus far the only difference I've been able to detect has been in the bone matrix transformations passed to glUniformMatrix4fv, but I'd expect that, considering the values are calculated from times and furthermore, the range of the matrix values seems correct.

You're quite right about the model not having 100 bones (it's around 30 I believe), however I'm hoping to use this shader for other models which may have more bones, in order to ensure that animations as a whole are working correctly. Once I'm certain they are, I'll be re-visiting the shader to only reserve the necessary number of bones for the model. Considering the project along with the tutorial works and that my computer has quite a reasonable spec, I don't see why similar code shouldn't work in my project.


Considering the project along with the tutorial works and that my computer has quite a reasonable spec, I don't see why similar code shouldn't work in my project.

But it doesn't work. ohmy.png

Skinned mesh animation is one of the most difficult subjects to get working in intermediate programming. If you just guess or assume things should work, it will likely take a long time to get your code running properly. You've asked for help and gotten a suggestion of something to look at which might take 2 minutes, but your response is to dismiss the oberservation, saying it shouldn't be the problem.

First: You have to want to find the problem. If you're program doesn't work properly, you're going to have to do something to the code to get it fixed.

Second: The suggestion wasn't frivolous. GPUs do have limitations. Why wouldn't you check that your shader version is compatible with your hardware?

Third: It might take you 2 minutes to change the buffer size (to, say, 40?), recompile and run the app. It's been over 12 hours that you've had the problem. Why wouldn't you take 2 minutes of that time for a quick test?

That being said, you can try using identity matrices for both the inverse bind pose and animation matrices. That should result in the vertices being rendered in bind pose without any change other than world-view-projection conversion to screen space. That will give you a good indication if the shader is working in that regard.

Please don't PM me with questions. Post them in the forums for everyone's benefit, and I can embarrass myself publicly.

You don't forget how to play when you grow old; you grow old when you forget how to play.

Sorry, at the time of that post I didn't have access to the code. I've just altered the MAX_BONES to 40, but sadly it hasn't made a difference. The shader version used above is the same as the other shaders in the project so I assumed this wasn't the issue. When I get a chance I'm going to try your suggestion and then have another thorough debug of both my project and the tutorial to see if I spot any discrepancies. Thanks for your help so far!

I've tried passing the identity matrix for the animation matrix / inverse bind pose doesn't make a difference either, the mesh still isn't rendering (even when passing the identity matrix directly to the shader). This being said, if in the shader itself I set the bone matrix to the identity, the mesh does indeed render in bind pose, so this suggests the bone information isn't being sent to the shader properly, although I can't pinpoint why as the VBO containing the bone information is in the same VAO as the VBO containing the vertex/texture/normal and IBO and the locations are correct.
One additional thing I noticed during my debug comparison is that the result of some matrix multiplications *are* different (as unlike the tutorial I'm using GLM as a maths library), so some issues could be from GLM's row-major vs. OpenGL's column major, but I'll deal with this once I have something displaying!
Thanks

This topic is closed to new replies.

Advertisement