How to using GPU skeletal animation correctly? Topic is solved

Problems building or running the engine, queries about how to use features etc.
yubinyyy
Gnoblar
Posts: 2
Joined: Thu Oct 31, 2024 2:26 pm

How to using GPU skeletal animation correctly?

Post by yubinyyy »

Ogre Version:1.8 :?:
Operating System:windows :?:
Render System:DXorOpenGL :?:
Hello everyone, I would like to ask some questions about GPU skeletal animation. The Ogre version is 1.8:
1.In a submesh, is there a situation where vertex A is bound to 2 bones and vertex B is bound to 3 bones? Do we need to execute different shader scripts?
2.Is there a submesh1 vertex bound to 1 bone, and a submesh2 vertex bound to 2 bones? Do I need to execute different shader scripts?
3.How to know how many bones are bound to the submesh or mesh vertices?
4.After we determine how many bones are bound, can we pass the bound number into the shader to make “boneNum” in it work? Or do we need to use different shaders according to the bound number?

Code: Select all

for (i = 0; i < boneNum; ++i)
{
	blendPos += float4(mul(worldMatrix3x4Array[blendIdx[i]], position).xyz, 1.0) * blendWgt[i];
}

5.I noticed that there is no case where the number of bones is 3 in the shader examples, but is it actually possible?
(we have hardwareSkinningOneWeight_vp hardwareSkinningTwoWeights_vp hardwareSkinningFourWeights_vp)

rpgplayerrobin
Orc Shaman
Posts: 711
Joined: Wed Mar 18, 2009 3:03 am
x 391

Re: How to using GPU skeletal animation correctly?

Post by rpgplayerrobin »

1.In a submesh, is there a situation where vertex A is bound to 2 bones and vertex B is bound to 3 bones? Do we need to execute different shader scripts?

Yes, this is possible, but the same shader can be used since the other empty bones should have no effect.
But the system is a bit weird, in Mesh::compileBoneAssignments it shows that it only writes the data when needed, so if a mesh has 3 bones per vertex, it will only write three of them even if the shader uses a uint4+float4 (for the indices+weights), which means that you will still need to use a vertex shader that then only handles 3 bones, instead of a vertex shader that always handles 4 bones.
It is rather annoying for sure, but that is how the system works currently even in the latest version as well.
I wish it would instead write all 4 variables even if only 1 bone is being used, since in that case only a single shader could be used. But right now when trying to use a shader of 4 bones on a mesh that only has 3 bones, it will look very strange and simply not work.

2.Is there a submesh1 vertex bound to 1 bone, and a submesh2 vertex bound to 2 bones? Do I need to execute different shader scripts?

I am not sure about this, but since every submesh is its own thing it might work like that unless the mesh is using shared vertex data (as seen in Mesh::_compileBoneAssignments).

3.How to know how many bones are bound to the submesh or mesh vertices?

That is tricky as well. In my game I just try the 4 bones shader first, and if something is not right I go down to the 3 bones shader (none so far has used 2 or 1 bones luckily).
It seems you can also use this code on the mesh or submesh:

Code: Select all

unsigned short maxBones = mesh->_rationaliseBoneAssignments(mesh->sharedVertexData->vertexCount, mesh->getBoneAssignments());

4.After we determine how many bones are bound, can we pass the bound number into the shader to make “boneNum” in it work? Or do we need to use different shaders according to the bound number?

  1. You can add it as a param_named in the shader material and then simply use that in the shader.
  2. Or, you can add it as a compile argument to be used as a define. The positive thing for this is that it is faster, since a loop with a param_named in it is often very unoptimized since it cannot unroll and optimize the loop when it compiles.
    For a compile argument, add this to the top of your vertex shader:

    Code: Select all

    #if !defined(MAX_BONES)
    	#define MAX_BONES 4
    #endif
    Then, when you make the shader material of it, do it like this to set the define to 3 instead if you want:

    Code: Select all

    vertex_program Skinning3Bones_VS hlsl
    {
    	source Skinning3Bones_VS.hlsl
    	entry_point main_vs
    	target vs_3_0
    	includes_skeletal_animation true
    	column_major_matrices false
    	preprocessor_defines MAX_BONES=3
    	...

5.I noticed that there is no case where the number of bones is 3 in the shader examples, but is it actually possible?
(we have hardwareSkinningOneWeight_vp hardwareSkinningTwoWeights_vp hardwareSkinningFourWeights_vp)

Yes. 1, 2, 3 and 4 is possible.

But overall, regarding this whole issue, if I were you I would first try to change the Ogre source code to always allow 4 bones to be used, which means that most (if not all) of your problems would go away, since then you can only use "4" instead of "numBones" and use the same shader for all meshes and submeshes without having to think about it.
I would simply try to change this code in Mesh::compileBoneAssignments:

Code: Select all

// write out the weights as floats
float* pWeight;
pWeightElem->baseVertexPointerToElement( pBase, &pWeight );
for ( int ii = 0; ii < numBlendWeightsPerVertex; ++ii )
{
	*pWeight++ = weights[ ii ];
}

To this (because for everything under 4 bones will have uninitialized memory otherwise):

Code: Select all

// write out the weights as floats
float* pWeight;
pWeightElem->baseVertexPointerToElement( pBase, &pWeight );
for ( int ii = 0; ii < 4; ++ii )
{
	*pWeight++ = weights[ ii ];
}

I should also try this for my game, since it would then make the handling of these issues so much easier, but I have too many other issues right now to fix so I cannot try this at the moment.

yubinyyy
Gnoblar
Posts: 2
Joined: Thu Oct 31, 2024 2:26 pm

Re: How to using GPU skeletal animation correctly?

Post by yubinyyy »

Very clear logic and practical solutions。Thank you rpgplayerrobin! :D

paroj
OGRE Team Member
OGRE Team Member
Posts: 2129
Joined: Sun Mar 30, 2014 2:51 pm
x 1141

Re: How to using GPU skeletal animation correctly?

Post by paroj »

rpgplayerrobin wrote: Thu Dec 19, 2024 9:21 pm

But overall, regarding this whole issue, if I were you I would first try to change the Ogre source code to always allow 4 bones to be used, which means that most (if not all) of your problems would go away, since then you can only use "4" instead of "numBones" and use the same shader for all meshes and submeshes without having to think about it.

This is not a good idea, as you would have to also modify the mesh vertex buffers to add padding. Weights do not have to be float, but can be also ushort2_norm or ubyte4_norm. So you would have to handle that.

Actually if you always pad your mesh data with 0 weights to 4 you get what you describe above - at the cost of wasting space and computation.

Also note that you are referring to weights with "bones" above. You certainly have more than 4 bones on your mesh, but each vertex can only have up to 4 weights (referring to bones) affecting it.

For reference this is the RTSS shader code that can do GPU skinning for you:

rpgplayerrobin
Orc Shaman
Posts: 711
Joined: Wed Mar 18, 2009 3:03 am
x 391

Re: How to using GPU skeletal animation correctly?

Post by rpgplayerrobin »

This is not a good idea, as you would have to also modify the mesh vertex buffers to add padding. Weights do not have to be float, but can be also ushort2_norm or ubyte4_norm. So you would have to handle that.

In that case it is good as it is.

at the cost of wasting space and computation.

It would take more performance for sure, but as it is in the vertex shader it will most likely not be a big difference of 3 vs 4 bones there. I would rather only have one type of vertex shader that works for any mesh, and I would most likely not see any difference in FPS since the vertex shader is so fast.

Also note that you are referring to weights with "bones" above. You certainly have more than 4 bones on your mesh, but each vertex can only have up to 4 weights (referring to bones) affecting it.

The original post mentioned "boneNum" as the max amount of bones per vertex, but of course the "worldMatrix3x4Array" variable that he uses will need to have at least the amount of bones on his biggest skeleton (50 bones = float3x4 worldMatrix3x4Array[50]).
I tried this in my game as well. A size of 5 in worldMatrix3x4Array compared to 50 has no impact on performance at all, so the same vertex shader can be used for all meshes.

rpgplayerrobin
Orc Shaman
Posts: 711
Joined: Wed Mar 18, 2009 3:03 am
x 391

Re: How to using GPU skeletal animation correctly?

Post by rpgplayerrobin »

I finally had some time to try this myself.

The only thing needed is to add this as the first line in the "Mesh::compileBoneAssignments" function:

Code: Select all

numBlendWeightsPerVertex = OGRE_MAX_BLEND_WEIGHTS;

I checked the performance before and after in scenes containing a lot of animated objects, and I could not see any difference in FPS.

Because of this change, a lot of complex user code and vertex shaders are no longer needed in my project, so I really recommend this change for non-RTSS users!