Vertex skinning, blending matrices problem

Started by
2 comments, last by spek 14 years, 2 months ago
I'm doing something wrong here with the skeleton animations, using matrices. Most vertices have only 1 bone, but where 2 bones are used it goes wrong. If I use them seperately the result is correct. So, either I initialize the skeleton wrong, or the blending goes wrong. In case 2 bones are used on a vertex, they both have a weight of 0.5 so far. The sum of the weights is always one.

// initialize all vertices via the INVERSE MATRIX of the base pose
for each vertex
	m4x4 bone1Matrix	= skeleton.BasePose.InverseMatrix[ vertex.boneIndex1 ];
	m4x4 bone2Matrix	= skeleton.BasePose.InverseMatrix[ vertex.boneIndex2 ];
	
	// Multiply matrices with the basepose world vertices
	v1	= bone1Matrix * skeleton.Mesh.vertex
	v2	= bone2Matrix * skeleton.Mesh.vertex
	
	// result (weights are both 0.5)
	// Store into a initial skinned vertexlist
	vRes	= v1 * weight1  +  v2 * weight2;
	skeleton.BasePose.skinnedVertex = vRes;
Next step would be animating with the base data:

// Transform all vertices
for each vertex
	m4x4 bone1Matrix	= skeleton.currentFrame.Matrix[ vertex.boneIndex1 ];
	m4x4 bone2Matrix	= skeleton.currentFrame.Matrix[ vertex.boneIndex2 ];
	
	// Multiply current matrices with the skinned vertices
	// from the initial stage.
	v1	= bone1Matrix * skeleton.BasePose.skinnedVertex
	v2	= bone2Matrix * skeleton.BasePose.skinnedVertex
	
	// result (weights are both 0.5)
	// Store into result
	vRes	= v1 * weight1  +  v2 * weight2;
	skeleton.skinnedResult.vertex = vRes;
I think it goes wrong because the initial vertex was made by two matrices. The two seperate transformations in the second step go wrong. I could fix this by storing TWO sets of initial skinned vertices, but maybe it can be fixed alot more easy. I mean, skinning shaders only have 1 set of vertices either so... Rick
Advertisement
You could use 2 arrays of pre-calculated vertices, I suppose. But, for large meshes, those arrays could be pretty big.

Perhaps the most straightforward way would be to just use the inverse matrix to calculate the final vertex positions.

// Transform all verticesfor each vertex	m4x4 bone1Matrix = skeleton.currentFrame.Matrix[ vertex.boneIndex1 ];	m4x4 bone2Matrix = skeleton.currentFrame.Matrix[ vertex.boneIndex2 ];		// Multiply current matrices with the inverse matrix and vert	v1 = bone1Matrix * skeleton.BasePose.InverseMatrix[vertex.boneIndex1] * skeleton.Mesh.vertex;	v2 = bone2Matrix * skeleton.BasePose.InverseMatrix[vertex.boneIndex2] * skeleton.Mesh.vertex;		// result (weights are both 0.5)	// Store into result	vRes	= v1 * weight1  +  v2 * weight2;	skeleton.skinnedResult.vertex = vRes;

Does that work?

Because several vertices may be influenced by the same bone, you can also calculate an array of
// finalMatrix = array of size(numBones)finalMatrix[boneIndex] = boneMatrix[boneIndex]*inverseMatrix[boneIndex]...Then, in your loop, calculate:v1 = finalMatrix[boneIndex1]*meshVert;v2 = finalMatrix[boneIndex2]*meshVert;

Quote:skinning shaders only have 1 set of vertices either

Not sure what you mean by this. Shaders can be written to use multiple bone indices and matrices. Is that your concern?

A common algorithm to use with a shader is to calculate an array of finalMatrices = boneMatrix * inverseMatrix for each bone, as mentioned above.
...
The vertex position is calculated in the shader, given an array of those finalMatrices:
pos = vector4( 0, 0, 0, 1);v = input_mesh_vertex;numWeights = number_of_weights_for_this_vertex;weight[numWeights] = array_of_weights, usually passed in shader input structure per-vertexboneIndex = array_of_bone_indices, usually passed in shader input per-vertexsumWeights = 0;for( int n=0; n < numWeights-1; n++){   pos += finalMatrix[ boneIndex[n] ] * v * weight[n];   sumWeights += weight[n];}pos += finalMatrix[ boneIndex[numWeights-1]] * v * ( 1 - sumWeights );

This shader code, by the way, may have to have the order of multiplications reversed. I.e., pos += weight[n] * v * finalMatrix[boneIndex[n]], etc.

[Edited by - Buckeye on February 19, 2010 8:39:26 AM]

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.

Thanks for all the info!

What I meant to say was that shaders only make use of 1 set of vertices, and 1 set of matrices. The problem is that I make an initial list of vertices that are multiplied with their inverse bone matrix. But it seems to go wrong when I create an initial vertex that is assigned to 2 bones.

You showed how to make "final matrices", sounds what I need. If I understand it properly, instead of making this "initial mesh" I'll apply the base pose at runtime on the resulting matrices. So I can use the original set of vertices.

I gave it a quick try with just 1 weight. A few times I got "The Thing" as a result, but now he's walking like a charm again :). I'll try it with multiple bones now.

Thanks!
Rick
And with two bones it finally works as well :) Thanks again

This topic is closed to new replies.

Advertisement