We have a solid skeletal animation system in our engine. It allows to set up to three animations onto each bone and blend them seamlessly. The animation update (fetching animation matrix given current time) and blending (two slerps for the three matrices) is done on the CPU while the actuall skinning (vertex transforms) is done on the GPU. Each vertex in our engine can be affected by up to four bones.

I am now considering moving the aforementioned update and blend functionality onto the GPU. Our CPU is loaded much more than the GPU even though these two operations are already done in parallel. This is how it can be done:

  1. At compile time, all the animation data for each skeletal model for each timestep is baked into textures.
  2. The update and blend functionality is done on the GPU in a single Draw() call by instancing quads. One quad is required for each bone on the scene. Each quad will render 16 pixels - the resulting matrix. To compute this matrix, I will need to sample the animation texture from (1) 4*12 = 48 times (to get three animation matrices for the current frame) and perform 2 slerps. I think I will be doing it in the vertex shader.
  3. At the skinning phase, I will sample the texture, rendered at stage (2) 4*16=64 times to get four matrices for the four bones, that can affect current vertex. It will be a GPU performance hit, but that is the main purpose of this process: to free up CPU cycles.

So my question is, has anyone ever tried this? Is it even worth thinking about? This system will take quite some time to implement and if there is some serious flaw in it, I would like to know about it. From my perspective it sounds good. At the same time I have never heard of anyone using such a system, which alerts me. Everywhere I read, the animation data is passed into the vertex shader as a huge array of matrices, which implies standard CPU update/blend system.

I wonder how games like StarCraft 2 do the update and blending. They have situations when there are tones of skeletal creatures on the screen (200 zerglings is standard, each has 2000 polygons, idk how many bones though...)?

UPDATE: I am working with XNA 4.0 that is DirectX 9, that is 224 uniforms in the shader.


1 Answer 1


Why not just move the blending to the skinning phase?

You just need to add the base matrices and the interpolation qualifier to the uniforms (or a static texture for the matrices if you really want to); you have a lot of space for uniforms.

Also passing and slerping quaternions is cheaper than passing and slerping matrices. and applying a quaternion rotation to a vec4 is very straightforward.

  • \$\begingroup\$ I have not mentioned that I am working in XNA 4.0, that is DX9, that is 256 uniforms :). Not as much space :). I am starting to think that my question is obsolete though... About the quaternions, are you talking about using 2 quaternions instead of the full matrix for a single animation? I heard it produces artifacts am I worng? \$\endgroup\$
    – cubrman
    Commented Mar 27, 2015 at 9:21
  • \$\begingroup\$ if that is 4 components per uniform then that is still quite a bit. I mean passing 1 quat + vec3 per matrix. it halves the required space. and it's possible to recreate the original 4x4 from those 2 \$\endgroup\$ Commented Mar 27, 2015 at 9:31
  • \$\begingroup\$ I will check that approach again, but I did see a Radeon demo on quaternion animation, it had a clear case, where rotating a skeleton's arm in a specific position caused animation artifacts, while traditional matrix animation avoided them. \$\endgroup\$
    – cubrman
    Commented Mar 27, 2015 at 9:34
  • \$\begingroup\$ btw we are now using 224 uniforms (56*4), so we are almost out of uniforms. We need to pass additional data for lighting too. \$\endgroup\$
    – cubrman
    Commented Mar 27, 2015 at 9:36
  • \$\begingroup\$ I've just realized that shader model 3 has up to 224 and not 256 registers. I guess we are not having errors because our models don't have more than 40 bones (not 56) and shader compiler is smartly reducing the size of the array we declare in it (float4x4[56]). \$\endgroup\$
    – cubrman
    Commented Mar 27, 2015 at 9:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .