Skeletal animation math

Started by
11 comments, last by GameDev.net 19 years, 2 months ago
I’m in a dilemma I wrote everything and in the end I get this, It’s definitely the weights and the way I'm using them, given that I have no experience on the subject I could certainly say I did it wrong... So far I’ve just been stumbling in the dark. So I could use some illumination =D. anyway, I’m generating the weights for each bone by the distance from the child vertex to the parent pivot, using Weightbone += ( 50 / Magnitude( ParentVertex - Pivotbone ) )5 After adding all the weights together, I then make them so they all add to make 1 for any given vertice. Then I use the formula, NewVertex = Sum_of_all_bones_N ( ( Vertex*MatrixN ) * WeightN ) To do the vertex positions, I know something has to be wrong. It’s probably blatantly wrong to anyone how as experience with Skeletal Animation. If I’m not clear enough or I lacked some details I’d happy to post more information. Please help!
"I seek knowledge and to help those who also seek it"
Advertisement
When you initially skin the geometry, the vertices and bones are in the bind pose.
Each vertex has position "VertBindPos", each bone has transform "BoneBindMatrix"

To compute the world space position of a vertex attached to a single bone, you must first
put the vertex into the bind pose space of the bone, and then multiply the result by the
bones current world space matrix (you can pre-compute the bone inverse bind matrix offline and store it
ready for runtime use).

For skinning, you just perform the operation for multiple bones and take the weight into
account for each bone.

The math for computing the final skinned world space position of a vertex is then as follows:

vector3 VertSkinWorldPos( 0, 0, 0 );
for( int i = 0; i < nBones; i++)
{
vector3 VertBoneBindPos = VertBindPos * BoneInverseBindMatrix;
vector3 VertBoneWorldPos = VertBoneBindPos * BoneWorldMatrix;

VertSkinWorldPos += VertBoneWorldPos * VertBoneWeight;
}


I hope this helps.
Steve Broumley
Quote:Original post by sbroumley
vector3 VertBoneBindPos = VertBindPos * BoneInverseBindMatrix;
vector3 VertBoneWorldPos = VertBoneBindPos * BoneWorldMatrix;


Thanks for the response,
What exactly are the Bind matrix and the World Matrix? Could you explain how to make them?

Heres what I'm doing:
I only have 1 matrix at the time being, and I make it like this,

1. I subtract the initial pivot in object space
2. I then rotate the vertices around the pivot
3. Then I add the computed world space pivot to the final vertex after rotation

I then multiply the vertices by this matrix, and then add them up after each one is multiplied by its weight and that results in the above structure (see image)
"I seek knowledge and to help those who also seek it"
It is hard to tell from your figure what the problem is. How many bones are in the model? Just 2? The method for generating weights sounds fine. But looking at the image, if that is your yellow dot is the second bone and that bone should be pointing in the direction of the end, the weights are off.

As Steve said, it could be your deformation math not the weights. But if the bones are as I described, it looks like the deformation math works.

I would suggest a debug mode where you color the vertex by the dominant bone so you can visualize how your method works. Also verify the deformation math by assigning the weights rigidly to each vertex and make sure you get the expected result.

I wrote an article on automatic weighting of vertices for Game Developer several years back that I don't think I ever posted. I try to post that this week.
I saw your reply after I posted mine. I have posted a couple of articles that explain how the bind pose and weights work. You should read those to get familiar with the technique and terms.

Skinning in opengl with weight editing and a simple bone system.
http://www.darwin3d.com/gdm1998.htm#gdm0598

Better explaination of the deformation math and DirectX code.
http://www.darwin3d.com/gdm1999.htm#gdm1099
Quote:Original post by JeffLander
It is hard to tell from your figure what the problem is. How many bones are in the model? Just 2?

if that is your yellow dot is the second bone


to the first, right just 2, I thought I would simplify and add more polys and just work with a simple cylinder then a human model that I was trying eariler.

second, Thats the location of the joint between the 2 bones.

thanks for the links I'll read them right away

Added:
Heres my Bone weight distribution


now that I see this I can frankly see that its bit one sided...

[Edited by - Xero-X2 on February 12, 2005 1:32:38 PM]
"I seek knowledge and to help those who also seek it"
bah, just got back from work. So I can work on this again, I recreated the weighting code, for this much nicer even distribution with a kind of Averaging across the vertices based on the triangle connections.


how ever, the bone still shrinks where it bends far to much, but progress has been made!

So I'll work working and I would appreciate any more help that can be givin, perhapes suggestions. Many thanks to JeffLander and sbroumley so far. =D
"I seek knowledge and to help those who also seek it"
What you see is correct, your code is good now, you jus need to work on the weight distribution on the mesh itself now.
So that you can see it is correct look at the following ASCII art :) :

Green weight tries to do this:

|...|
|...|
|...|________
|...|
|...|________

Red:

|...|
|...|
___________
...........
___________

So it shrinks because the red weight makes the vertices go to the left and the green weight makes it go down.
To fix it make the weight more "sharp" on the place where it shrinks.
Quote:Original post by sbroumley
vector3 VertSkinWorldPos( 0, 0, 0 );
for( int i = 0; i < nBones; i++)
{
vector3 VertBoneBindPos = VertBindPos * BoneInverseBindMatrix;
vector3 VertBoneWorldPos = VertBoneBindPos * BoneWorldMatrix;

VertSkinWorldPos += VertBoneWorldPos * VertBoneWeight;
}


Is that correct? Shouldn't you be looping weights per vertex instead?
for(int i=0; i < nWeights; i++) {
}

Or maybe you mean bones per vertex?
Pinching at the joints as well as it collapsing when the bones twist is a known problem with this skinning method. There are lots of ways to fix it, use a set of small bones at the joint to soften the crease, add a bone at the joint that scales up and/or moves as the limb angle shrinks, etc. Most apps just sharped the falloff so in configs like yours it looks more like a fold.

To answer Raydog, the math ends up the same. You can even apply the weights to the matrices and then just multiply the matrix times the vertex once. That is how most vertex shader implementations do this technique.

Also, to optimize Steve's code a little:
Pre-multiply the BoneInverseBindMatrix * BoneWorldMatrix, then you only have one vertex * matrix. Also, do this once for all your bones each frame and store it in a "matrix palette" so you don't re-multiply the matrices for each vertex.

This topic is closed to new replies.

Advertisement