![]() |
![]() |
![]() |
Skeletal Animation #2 Apr 05, 2006
This article builds on my first skeletal animation article. I recommend reading that one first if you haven't already. In this article I'll discuss a couple of techniques used to improve the results of deforming a mesh with skeletal animation.
Weighted Vertices
Multiple attachments are a standard technique to improve the look of the deformed mesh, for things like reducing bunching at joints or adding subtle control bones, like putting a bone in a character's stomach to allow visible breathing. Somewhere along the way someone decided that 4 was a good number of different bones to allow a vertex to be attached to, and allowing up to 4 attachments has been sufficient for me for many years. So for each vertex we store each bone it is attached to (pnBoneAttached), and the weight for that attachment (pfWeights). If you add up all the weights, they should add up to 1.0. The code for computing the vertex locations deformed by a weighted skeleton follows:
for (i = 0; i < numVerts; i++) { pRenderVertices[ i ].SetZero(); for (int x = 0; x < 4; x++) { int nBone = pnBoneAttached[ 4*i+x ]; if ( nBone == NO_BONE ) break; pRenderVertices[ i ] += (MBones[ nBone ] * pVerts[ i ]) * pfWeights[ 4*i+x ]; } }With multiple attachments not just the vertices need to be blended, the vertex normals do too, or in the case of object space normal mapping, the light vectors. This can be done using pretty much the same method used for blending the verts. The main difference is you'll just want to rotate the normal without any translating. What I do is use the line pNormal[i].RotByMatrix( MBones[ nBone ] ), where RotByMatrix uses three dot products to rotate pNormal by the matrix without translating. Blending Multiple Mesh Chunks
Another way to animate meshes is to blend between multiple chunks of a mesh to create the final mesh. This is used most often for facial animation, like blinking or smiling, although it can be used in plenty of other ways. (It is also possible to use skeletal animation for facial animation.) You might have one head mesh chunk with the eyes open, and on with the eyes closed, and blend between them to blink. You can allow stacking of multiple blend shapes, and sliders to adjust their influence.
The actual blending between the chunks can be simple, although what I do is more complicated than what I'll present here. For instance you might want to define vertices to be blended instead of an entire chunk, and there are specific implementation issues that vary between programs, so are not covered in this article. Let's say you have 1 chunk for the default position, and 2 additional blend chunks, each with a weight between 0 and 1. I'm assuming in this example that the blend chunks aren't offset from the default chunk, if they are you'll need to translate them first. The weights are scaled so that they all add up to one. If the weights of all the blend chunks total less than 1, the weight of the default chunk is assigned so that they'll total 1. int i; float fTotal = 0.0f; pfWeights[0] = 0.0f; // Chunk 0 is the default chunk // Set Weights for ( i = 1; i < nChunks; i++ ) { fTotal += pfWeights[i]; } if ( fTotal < 1.0f ) { pfWeights[0] = 1.0f - fTotal; fTotal = 1.0f; } for ( i = 1; i < nChunks; i++ ) { pfWeights[i] /= fTotal; } // Just blend vertex position linearly between all chunks for (i = 0; i < nVertsInChunk; i++) { pOutVerts[i].SetZero(); for ( int x = 0; x < nChunks; x++ ) { pOutVerts[i] += pInVerts[x][i] * pfWeights[x]; } }To use this with skeletal animation, you need to first run a pass to do the blending, and use the output as the input( pVerts ) in deforming the vertices by the attached bones. |