Skinning Animation: ... offset matrix ?!

Started by
4 comments, last by _Flame_ 5 years ago

hi, i´ve tried for a long time to get a skinning example code to work, without much success. does anyone know a website/tutorial where it is described step-by-step what happens to the vertices during key-frame animations? i want to point put that there isnt a more confusing way than wrapping essential pieces of code into class methods, as it is done here:

http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html

so this one is just not comprehendable, at least not to me ...

i´d like to reiterate what i already know: vertices are defined in "mesh space". to transform that mesh to its proper position in the world, several chained transformations have to be applied to those vertices ("world space"). first, a model is (or can be) a hierarchical organized scene, for example, to put the hand of a human to its correct place, the ellbow-transform and shoulder-transform have to be applied after the models transform itself (that is, where the model in the world is located).

absolute_hand_transform = model_transform x shoulder x ellbow x hand

... each defined relative to its parent, of course. this is all static, no skinning there ... thats what i´ve already implemented.

if you load a model using ASSIMP (for example), bones contain a "offset matrix", that transform the vertices of a mesh to "local space of the bone":

Quote

A mesh may have a set of bones in the form of aiBone structures.. Bones are a means to deform a mesh according to the movement of a skeleton. Each bone has a name and a set of vertices on which it has influence. Its offset matrix declares the transformation needed to transform from mesh space to the local space of this bone.

... then, an animation produces for each bone a relative transformation (relative to its parent, i guess) that i can use (draw) after all the previous or "parent" transforms are applied.

IS IT THEN CORRECT (?) that to draw a skinned mesh, the transformations i have to calculate and send to the vertexshader are calculated as following:


MeshSpace-To-WorldSpace =

ModelToWorld-Transform x

MeshSpace-To-BoneSpace_0 x AnimatedBone_0 x

MeshSpace-To-BoneSpace_1 x AnimatedBone_1 x

MeshSpace-To-BoneSpace_2 x AnimatedBone_2 x

MeshSpace-To-BoneSpace_3 x AnimatedBone_3

where all those "MeshSpace-To-BoneSpace" transforms are these "bone offset matrices" AND each of those "AnimatedBone" are directly computed by interpolating the animation keys ... correct ???

(i assume its not because often i read about having to use some "inverse bone offset transform" in some way i dont get ... ?)

 

thanks in advance for any answers/suggestions/clarifications !!

Advertisement

I recommend Frank Luna's whitepaper on character skinning and animation (https://www.gamedevs.org/uploads/skinned-mesh-and-character-animation-with-directx9.pdf).

On pages 12 and 13 you see a formula, F(i) = M(i) * C(i), where i = ith bone of the character's skeleton

and F(i) = final transform, which combines a bone's offset transform (M(i)) with its combined transform (C(i)).

The combined transform is the hierarchical scene graph part you mentioned along with the keyframe animation.
Note that this transform moves the character directly to a pose and keyframe j is not generally relative to keyframe j-1. The bone's offset transform is an inverse transform that moves the mesh into the space of the bone, which is necessary now that it has been transformed away from bind position by the combined transform.
 

I also had a lot of questions about skeletal animation, until I found a good working example: https://github.com/vovan4ik123/assimp-Cpp-OpenGL-skeletal-animation

Good luck

Another resource to consider are the ThinMatrix animation tutorials. It's a video tutorial with a very nice explanation of all the concepts involved in skeletal animation.

I just finished skinning animation with opengl, assimp, and glm. It's not easy as it seems. I want to share all my pain that i went through so it maybe helpful to someone who feels same level of frustration like i did.

First of all I implemented skinning animation in past with directx and it was much easier!

In my case issue was not lack of skinning theory but many bugs. My knowledge of opengl, assimp and glm is not very good and I did a lot of debugging and tests.

The advice is to go step by step.

1. First - try to visualize skeleton(bones) without any animation. Is sounds easy but i spent some time to make it work. My problem was incorrect drawing procedure. Obviously all you need is to update world transformations and draw lines from one bone position to child ones but i did it incorrectly(wrong calculation where child lines should be in a vertex buffer). Final transformations are not needed for bone visualization, only global/world ones.

2. Second - try to animate any bone(that was easy for me).  All you need is to multiply local bone transformation on some transformation(probably rotation) and calculate world transformations.

3. Fun begins. Try to visualize mesh by applying bones in initial pose(No animation). It's where you will need offset matrices, final transformations and to use bone indices with weights in a vertex shader. Offset matrix is basically inverse matrix of world bone transformation. For some reason assimp offset matrices work better in my case than my inverse matrices(I will research why). My main problem here was that i used glVertexAttribFormat to declare bone indices instead of glVertexAttribIFormat! Be careful glVertexAttribFormat converts integer values to floats and indexing is absolutely wrong with it. I spent couple days until i realized what is wrong.

4. Actual animation. glm has functionality to converts quaternions to matrices but what i overlooked initially is that glm::quaternion constructor is quat(w, x, y, z). I used to pass x, y, z, w and of course it didn't work.

Also pay attention assimp matrices are row-major but glm mat4 is column-major. So matrix transposition is required!

Also it's very important to be sure that you are really sending matrices to a vertex shader. I had a code that didn't raise errors when it failed to send them and i spent some time to fixing that too. It's better to throw an exception if for example uniform location cannot be found.

Thanks for reading, i hope it will help!

This topic is closed to new replies.

Advertisement