Skinning Issues

Started by
10 comments, last by AhmedOmar 12 years, 7 months ago
Hello GameDev community,

I have run into an issue while animating my skinned meshes which is frustrating the socks off of me. The mesh renders perfectly fine in the bind pose position but once I try to animate it it gets distorted. I export the mesh from a Collada file to my own custom format through a custom exporter I built (which I double checked and works perfectly), than I bring the mesh into the engine and do the following for skinning (with no success):

First, I create the matrix palette by looping through all joints and calculating the skinning matrix as such:
For the root joint, I just multiply it's inverse bind pose matrix by the appropriate key frame transform matrix (no distortion in root bone).
For the remaining joints, I multiply it's transform matrix by it's parent's world matrix (which is the product of the transform matrices form the root joint to the parent joint), then multiply their product matrix by the joint's inverse bind pose (tons of distortions here).

Next, I loop through all vertices and calculate the new vertex position by taking the summation of the product of the vertex position and the appropriate skinning matrix multiplied by the joint weight.

Then I render.

Keep in mind that I'm not interpolating between key frames just yet, I'm just replacing the transform matrices exported from the <library_animations> node in Collada. I'm using C++ and DirectX by the way.

I hope that anybody can help me solve the problem or at least identify where it is.

Thanks in advance.
Advertisement
You description seems correct, so maybe you have done a typo in matrix multiplication.
Btw, what exactly do you mean by bone bind pose matrix? Is it relative to parent or relative to skin?
I usually use the following notation:
  • B2P - bone "rest" orientation, relative to parent
  • B2S - bone "rest" orientation, relative to object/skin - it is the product of the chain of matrices from root to bone (B2S = B2Sparent * B2P)
  • S2P the inverse of B2S
  • AB2P - animated orientation of bone - relative to parent
  • AB2S - animated orientation of bone - relative to object/skin (AB2S = AB2Sparent * AB2P)
  • The matrix you will need (the one transforming skin vertices) is:
  • AS2S = AB2S * S2B
Lauris Kaplinski

First technology demo of my game Shinya is out: http://lauris.kaplinski.com/shinya
Khayyam 3D - a freeware poser and scene builder application: http://khayyam.kaplinski.com/
<br />You description seems correct, so maybe you have done a typo in matrix multiplication.<br />Btw, what exactly do you mean by bone bind pose matrix? Is it relative to parent or relative to skin?<br />I usually use the following notation:<br /><ul class='bbc'><li>B2P - bone &quot;rest&quot; orientation, relative to parent</li><li>B2S - bone &quot;rest&quot; orientation, relative to object/skin - it is the product of the chain of matrices from root to bone (B2S = B2Sparent * B2P)</li><li>S2P the inverse of B2S</li><li>AB2P - animated orientation of bone - relative to parent</li><li>AB2S - animated orientation of bone - relative to object/skin (AB2S = AB2Sparent * AB2P)</li><li>The matrix you will need (the one transforming skin vertices) is:</li><li>AS2S = AB2S * S2B</li></ul><br />


I'm kind of a newbie here so bare with me.

I think that collada stores it's matrices relative to skin although I'm not 100% sure.

I'll review my code with your notations and see what happens. Thanks a lot.
Wow, this shouldn't be this hard. :(

Here is my code abbreviated:


m_skinningMatrices[0] = m_transformMatrices[0][m_transformIndex] * m_skeleton.m_joints[0].m_invBindPose;
m_skeleton.m_joints[0].m_worldMatrix = m_transformMatrices[0][m_transformIndex];

for (UINT iJoint = 1; iJoint < m_skeleton.m_jointCount; iJoint++) {
UINT parent = m_skeleton.m_joints[iJoint].m_parent;
m_skeleton.m_joints[iJoint].m_worldMatrix = m_skeleton.m_joints[parent].m_worldMatrix * m_transformMatrices[iJoint][m_transformIndex];
m_skinningMatrices[iJoint] = m_skeleton.m_joints[iJoint].m_worldMatrix * m_skeleton.m_joints[iJoint].m_invBindPose;
}


m_invBindPose is taken from <library_controllers> in Collada file.
m_transformMatrices are taken from <library_animations> in Collada file.

Hope this helps.
What you wrote seems correct, the description.
A simple idea, just to double check, but did you try changing your multiplication from: worldMatrix * inverseBindPoseMatrix into inverseBindPoseMatrix * worldMatrix ?

And do you pass them correctly to the shader? Row vs column major.(in case you do it on the gpu)

What you wrote seems correct, the description.
A simple idea, just to double check, but did you try changing your multiplication from: worldMatrix * inverseBindPoseMatrix into inverseBindPoseMatrix * worldMatrix ?

And do you pass them correctly to the shader? Row vs column major.(in case you do it on the gpu)


I've tried changing the order but no luck. And also I'm doing it all on the cpu, I plan to write a skinning shader once I get it right.

[quote name='Buckshag' timestamp='1317038417' post='4866025']
What you wrote seems correct, the description.
A simple idea, just to double check, but did you try changing your multiplication from: worldMatrix * inverseBindPoseMatrix into inverseBindPoseMatrix * worldMatrix ?

And do you pass them correctly to the shader? Row vs column major.(in case you do it on the gpu)


I've tried changing the order but no luck. And also I'm doing it all on the cpu, I plan to write a skinning shader once I get it right.
[/quote]

Did you verify if the Collada matrices are compatible with yours somehow?
I have no experience with Collada, but your math seems correct. Unless Collada stores things in a different space.
Did you try setting some simple position/rotation on some bones on a simple test model, like a cylinder with 2 bones in it.
And then check if the values in the matrices of Collada are what you expect them to be.

[quote name='AOmar' timestamp='1317038954' post='4866028']
[quote name='Buckshag' timestamp='1317038417' post='4866025']
What you wrote seems correct, the description.
A simple idea, just to double check, but did you try changing your multiplication from: worldMatrix * inverseBindPoseMatrix into inverseBindPoseMatrix * worldMatrix ?

And do you pass them correctly to the shader? Row vs column major.(in case you do it on the gpu)


I've tried changing the order but no luck. And also I'm doing it all on the cpu, I plan to write a skinning shader once I get it right.
[/quote]

Did you verify if the Collada matrices are compatible with yours somehow?
I have no experience with Collada, but your math seems correct. Unless Collada stores things in a different space.
Did you try setting some simple position/rotation on some bones on a simple test model, like a cylinder with 2 bones in it.
And then check if the values in the matrices of Collada are what you expect them to be.
[/quote]

I'm pretty sure that the collada matrices are correct since they work perfectly fine on the root bone, and I have been working this whole time with a cylinder test model with 2 bones trying to pinpoint the problem. Here's a screenshot of the distorted mesh in the engine (left) and the correct mesh in maya (right):

cylindero.png

I'm pulling my hair out of frustration. Somebody please help save my keyboard before I smash it into a rcok.
m_skeleton.m_joints[parent].m_worldMatrix * m_transformMatrices[iJoint][m_transformIndex];

Did you try switching these two as well? So like

m_transformMatrices[iJoint][m_transformIndex] * m_skeleton.m_joints[parent].m_worldMatrix

Can you draw your skeleton in lines? You first need to be sure your bone transformations in world space are correct.
Then we can see if its the skinning algorithm or that already.

If you really can't figure it out maybe I can give it a debugging try if you can send the code.



m_skeleton.m_joints[parent].m_worldMatrix * m_transformMatrices[iJoint][m_transformIndex];

Did you try switching these two as well? So like

m_transformMatrices[iJoint][m_transformIndex] * m_skeleton.m_joints[parent].m_worldMatrix

Can you draw your skeleton in lines? You first need to be sure your bone transformations in world space are correct.
Then we can see if its the skinning algorithm or that already.

If you really can't figure it out maybe I can give it a debugging try if you can send the code.





I've tried every possible order there is and nothing:

As for the code here is the exact code I use for calculating the matrix palette:

VOID SkinnedMesh::CalculateSkinningMatrices(void) {
m_skinningMatrices[0] = m_transformMatrices[0][m_transformIndex] * m_skeleton.m_joints[0].m_invBindPose;
m_skeleton.m_joints[0].m_worldMatrix = m_transformMatrices[0][m_transformIndex];

for (UINT iJoint = 1; iJoint < m_skeleton.m_jointCount; iJoint++) {
UINT parent = m_skeleton.m_joints[iJoint].m_parent;
m_skeleton.m_joints[iJoint].m_worldMatrix = m_skeleton.m_joints[parent].m_worldMatrix *
m_transformMatrices[iJoint][m_transformIndex];
m_skinningMatrices[iJoint] = m_skeleton.m_joints[iJoint].m_worldMatrix * m_skeleton.m_joints[iJoint].m_invBindPose;
}
}


All I need to know is is the above code for calculating the matrix palette correct, if so then I can be 100% sure that the problem lies within the collada matrices themselves (although they seem correct), if not then what exactly is wrong with the math above?

Thanks guys and I really appreciate all the help.

This topic is closed to new replies.

Advertisement