Bunch of Questions on Skeletal Animation

Started by
2 comments, last by DrGUI 19 years, 1 month ago
Alrighty, I've got a bunch of questions on skeletal animation. If I may refer you to the SimpleAnimation example in the Dec 2004 Sdk, and the SimpleAnimation.fx file. I was having a read through this .fx file, and it seems to be exactly what I'm looking for, (ie a way of doing multiple bone skeletal animation in hlsl), however, after fiddling with it and the source code for the sample I'm a tad puzzled on how to actually use the thing. First of all, the sample code immediately confuzzled me as I can find no place in it in which it actually uses this effect. It does in fact, load in this effect file in OnCreateDevice, and successfully compiles it as purposefully screwing up the .fx file results in a nice error being tossed at me. Then, in OnFrameRender, it sets the direction of the light and the projection matrix into the effect. But, then where does it use this effect? I've tried purposefully breaking it (ie by making it set the position of all vertices to the origin) but it always renders just as normal. I've also added in a pixel shader to render it completely black, but once again, its just plain not called. So, is there something I'm missing here, or is it just plain stupidly loading in this effect and never using it? Secondly, by looking at the derived Frame, MeshContainer and AllocHierarchy classes that this sample uses, it seems to be doing the multiple bone skeletal animation with the fixed vertex pipeline. However, what is the BoneCombinations array and what is it used for? Thirdly, if this is indeed fixed vertex pipeline skeletal animation going on, is it possible to create a pixel shader that does not use a vertex shader at all? If so, what sorts of input parameters would it be able to use? Ultimately, I want my characters to use normal mapping and pixel perfect lighting, I just need the skeletal animation part to work. Fourthly, over in the skin.vsh file in the MultiAnimation example, it states the following: // The palette size is 26 by default. This is sufficiently small for most // vs_1_1 shaders. Shaders targeted at vs_2_0 and above can set this higher // to accommondate more bones in a call. For example, tiny_anim.x has 35 // bones, and so can be rendered in a single call if MATRIX_PALETTE_SIZE is // set to 35 or more. I've only got a vs_1_1 capable card (Radeon 9100), so this 26 palette size limit directly affects me. Is this an absolute max on the number of bones that can be used in a single mesh, or can it be tweaked up slightly? At a rough estimate, I predict that I'll only need about 20 bones per character, as I don't require any for things like individual fingers or facial animation. This a realistic number or am I under-estimating? (Estimate is: 1 for the head, 1 for the neck, 4 or so for the main body, 3 per arm, 2 per leg, 1 per foot = 18 total) Now, directing your attention away from this sample to 2 more general questions: 1. Is there a better sample of how to do this somewhere on the net? 2. How exactly does one use the SkinInformation.ConvertToIndexedBlendedMesh function and what exactly does it do? Btw, I do have a system set up already using the fixed vertex pipeline that loads in meshes with a single bone per vertex and that supports multiple animations. So, this is the last step that I need to do. If I load in a mesh with more than 1 bone per vertex, none of the animation works and the mesh just sits there in its rest pose doing nothing.
Advertisement
Some of your questions.

Quote:Original post by andor
I can find no place in it in which it actually uses this effect.

With the sample framework things might be getting done in a way that you cannot see on the surface. I don't use the framework myslef, but maybe sombody else here does.
Quote:
is it possible to create a pixel shader that does not use a vertex shader at all? If so, what sorts of input parameters would it be able to use?

in your fx fie if you set the PixelShader or the VertexShader (or both) to null th FF is used for that portion of the pipeline. The vertex shader's semantics map back into the FF as you might expect POSITION0 is required, TEXCOORD[0-7] are picked up, COLOR0 is vertex color and COLOR1, I believe, is specular color.
Quote:
I've only got a vs_1_1 capable card (Radeon 9100), so this 26 palette size limit directly affects me. Is this an absolute max on the number of bones that can be used in a single mesh, or can it be tweaked up slightly?

it probably can be tweaked depending on the number of other constants you need. I have heard of using 2 vectors per bone, 1 for rotation and one for position, instead of using mats(which use 3 or 4 vector registers) and building your mats in the shader to save constant space, don't know how effective it is though.
Quote:
2. How exactly does one use the SkinInformation.ConvertToIndexedBlendedMesh function and what exactly does it do?

It uses bone combination information to add blend variables to each vert. It should add 4 weight variables and an index variable (4 byte indices) to every vert.

Hope this helps.
Quote:Original post by turnpast
in your fx fie if you set the PixelShader or the VertexShader (or both) to null th FF is used for that portion of the pipeline. The vertex shader's semantics map back into the FF as you might expect POSITION0 is required, TEXCOORD[0-7] are picked up, COLOR0 is vertex color and COLOR1, I believe, is specular color.


Interesting, I'll have to give that a try. For most of my stuff, I don't particularly care about the vertex shader's capabilities, though I would like to grab normal information out of it.

Quote:Original post by turnpast
it probably can be tweaked depending on the number of other constants you need. I have heard of using 2 vectors per bone, 1 for rotation and one for position, instead of using mats(which use 3 or 4 vector registers) and building your mats in the shader to save constant space, don't know how effective it is though.


Interesting idea, that would work quite nicely I believe. Since the translation component should be constant for all of the bones, it really only needs to be stored once. Building the transformation matrix in the shader would consume some extra gpu cycles so I'll use that as a last resort if I need to.

Solves some of my questions, so thanks :)
Bizzarly, the SimpleAnimation sample did not seem to use its effect when I last looked at it. I highly recommend going through the C++ SkinnedMesh sample with the docs to understand how it works. That definately does use the shaders and uses several different methods; ff indexed skinning, ff non-indexed skinning, asm skinning and hlsl indexed skinning.

I have got HLDL indexed skinning working, you can have the skinning functions:
Skinning.inc:
// If you want a palette size other than the default (26),// #define MATRIX_PALETTE_SIZE_DEFAULT before your #include Skinning.inc#ifndef SKINNING#define SKINNING//----------------------------------------------------------------------------// Global parameters //----------------------------------------------------------------------------// Declare the 4x3 matrix palette.  This is the array of bone matrices used in// skinning vertices.// The palette size is 26 by default.  This is sufficiently small for most // vs_1_1 shaders.  Shaders targeted at vs_2_0 and above can set this higher// to accommondate more bones in a call.  For example, tiny_anim.x has 35// bones, and so can be rendered in a single call if MATRIX_PALETTE_SIZE is// set to 35 or more.// An HLSL shader can set MATRIX_PALETTE_SIZE_DEFAULT to a different value.// The calling app can also set it in the D3DXMACRO structure when compiling// the shader.  The calling app can query the actual palette size by examining// MATRIX_PALETTE_SIZE (but changing it after compilation will not change the// palette size in the compiled shader, of course).#ifndef MATRIX_PALETTE_SIZE_DEFAULT#define MATRIX_PALETTE_SIZE_DEFAULT 26#endifconst int MATRIX_PALETTE_SIZE = MATRIX_PALETTE_SIZE_DEFAULT;float4x3 amPalette[MATRIX_PALETTE_SIZE_DEFAULT] : WORLDMATRICES;// call this function to skin VB position and normalvoid SkinIndexed(const in float4 position,                 const in float3 blendWeights,                 const in float4 blendIndices,                 const in float3 normal,                 uniform int numBones,                 out float3 skinnedPosition,                 out float3 skinnedNormal){    float lastWeight = 1.0;    float afBlendWeights[3] = (float[3])blendWeights;    int aiIndices[4] = (int[4])D3DCOLORtoUBYTE4(blendIndices);        //First bone    if (numBones == 1) {        skinnedPosition.xyz = mul(position,         amPalette[aiIndices[0]]);        skinnedNormal       = mul(normal, (float3x3)amPalette[aiIndices[0]]);    }    else {        float fWeight = afBlendWeights[0];        lastWeight -= fWeight;        skinnedPosition.xyz = mul(position,         amPalette[aiIndices[0]]) * fWeight;        skinnedNormal      = mul(normal, (float3x3)amPalette[aiIndices[0]]) * fWeight;    }    //Second to third bones    for( int iBone = 1; (iBone < 3) && (iBone < numBones - 1); ++iBone )    {        float fWeight = afBlendWeights[iBone];        lastWeight -= fWeight;        skinnedPosition.xyz += mul(position,         amPalette[aiIndices[iBone]]) * fWeight;        skinnedNormal       += mul(normal, (float3x3)amPalette[aiIndices[iBone]]) * fWeight;    }    //Last bone if not first bone    if (numBones > 1) {        skinnedPosition.xyz += mul(position,         amPalette[aiIndices[numBones - 1]]) * lastWeight;        skinnedNormal       += mul(normal, (float3x3)amPalette[aiIndices[numBones - 1]]) * lastWeight;    }}void SkinIndexedPosOnly(const in float4 position,                        const in float3 blendWeights,                        const in float4 blendIndices,                        uniform int numBones,                        out float3 skinnedPosition){    float lastWeight = 1.0;    float afBlendWeights[3] = (float[3])blendWeights;    int aiIndices[4] = (int[4])D3DCOLORtoUBYTE4(blendIndices);        //First bone    if (numBones == 1) {        skinnedPosition.xyz = mul(position, amPalette[aiIndices[0]]);    }    else {        float fWeight = afBlendWeights[0];        lastWeight -= fWeight;        skinnedPosition.xyz = mul(position, amPalette[aiIndices[0]]) * fWeight;    }    //Second to third bones    for( int iBone = 1; (iBone < 3) && (iBone < numBones - 1); ++iBone )    {        float fWeight = afBlendWeights[iBone];        lastWeight -= fWeight;        skinnedPosition.xyz += mul(position, amPalette[aiIndices[iBone]]) * fWeight;    }    //Last bone if not first bone    if (numBones > 1) {        skinnedPosition.xyz += mul(position, amPalette[aiIndices[numBones - 1]]) * lastWeight;    }}#endif // #ifndef SKINNING


Hope that helps,
Andrew

This topic is closed to new replies.

Advertisement