Archived

This topic is now archived and is closed to further replies.

Optimizing skeletal animation

This topic is 5042 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Ok, so I have this big array of vertices. Every 3 consequtive vertices represent a triangle. Right now I have something like this every frame:
for every joint
{
   for every vertex belonging to it
   {
      vertex = vertex * bone's translation matrix;
      vertex = vertex * bone's rotation matrix;
      normal = normal * bone's rotation matrix;
   }
}

set pointer, enable arrays.. glDrawArrays()
  
Now I was thinking about using vertex shaders + VBOs - is that possible? I imagined it like this: *Form groups of polys to put in VBOs (every group belongs to a joint) - I guess about 100-200 polys each. Do this at startup. *Each frame draw every group with a vertex shader for matrix multiplication which I do on the CPU in the example above Would this be possible? If yes, would it be any faster - talking about Ti4200+ hardware. If not - why not? any other good ideas? PS: I'm quite new to shaders and VBOs so bare with me edit: What about using it the same way as I already do, except that I would use dynamic VBOs - would that increase performancy any? [edited by - klajntib on February 20, 2004 7:42:29 PM]

Share this post


Link to post
Share on other sites
I''ve currently got shader and VBO support in a SA system I am prototyping. My system is organized so that each vertex has an index into an array of bones which transforms it (currently, no weighting from multiple bones).

The way it works is I loop through each of the bones and calculate the transformation matrix for that bone. All of the bone matrices are fed to the vertex program (I use ARB_vertex_program right now, and no Cg/GLSL etc... Using a high-level shader language would probably be a better idea, since I have no clue as to optimizing for pipeline architecture) as local parameters using glProgramLocalParameter4fARB(). Typically, I send only the first three rows of each transformation matrix, to save space and allow more bones. Given a minimum of 96 local parameter vectors, and allowing for some in-program usage, I can do up to 27 bones, easily enough to handle even my most complicated models. Your mileage may vary; if you need more bones than that (with 96 parameter units and 3 vectors per bone matrix, that''s a max of 32 bones assuming no other bindings are used for other parameters or temporaries), you''ll have to send them in batches and structure your render code accordingly.

I pass each vertex bone index as a generic attribute to the shader, which is used to reference the bone table, acquire the correct transformation, and apply it.

My vertex data is organized as indexed triangles and drawn using glDrawElements(). All vertices are pre-transformed by the inverse of the skeleton''s rest pose matrix when loaded, to avoid having to redundantly perform this transformation step every frame.

As for the VBO''s, that''s simple. Since you are already using arrays, it is a simple matter to generate vertex buffers and bind the vertex/normal/bone index/UV array(s) to the buffers. Once you pre-transform the model vertices by the rest post and put them in the VBO, you don''t need to access or modify them again, since all transformations thereafter are done in the shader. Just bind the shader and the vertex buffer object(s), and call your glDrawElements() (or glDrawArrays(), whichever you use) as normal, though skipping, of course, the phase where you iterate through the vertices and transform them by hand.

I currently have VBOs disabled in my code, since I get worse performance with them than without. (36-byte vertex structure, I heard somewhere that a 32-byte alignment is preferred; I could be wrong. Plus, it''s a GeForce 2; what can you really expect? )


Golem
Blender--The Gimp--Python--Lua--SDL
Nethack--Crawl--ADOM--Angband--Dungeondweller

Share this post


Link to post
Share on other sites
ok, so basicly, you use vertex shaders to avoid using a new buffer for vertex data and computing vertex*matrix transformations for each vertex on the CPU, right?

Cool I''m going to try this out. I guess I''ll first add the vertex program and then add VBOS..

Share this post


Link to post
Share on other sites
I use a very similar approach to VertexNormal, with a fallback to software skinning if hardware is not available, or if the number of bones exceeds 30. Even though the sw skinning route uses SSE instructions to do vector ops, the drop in frame rate is pretty significant (probably because I''m having to update the VBO each frame). As a real-world example, to render 100 flocking 121 vertex birds, each skinned individually, gives me a frame rate of 51 fps with software skinning. With hardware skinning enabled, I get 129 fps. There''s almost no overhead with the hardware skinning route.

BTW, This page gives a very good overview with an example implementation in Cg.

Share this post


Link to post
Share on other sites
Oh my god.. i always wondered what skinning was! Now I know, LOL
I'm going to go look for some info on skinning using vertex weights and so on.. right now I don't have any weights.. what's the advantage of using weights and such? I really have no idea

Edit:

Ok, i've been doing some reading - weighted skinning is basicly used to prevent artifacts at joints points (ie: knees, shoulders..) right?

[edited by - klajntib on February 21, 2004 10:04:58 AM]

Share this post


Link to post
Share on other sites
another thing - Do I gain much if i use glDrawElements and draw from and indexed array instead of glDrawArrays? Except a few KB of memory?

Logic tells me that glDrawElements is just a kind of a for loop which loops through all indices and sends appropriate vertices, normals and texcoords to the GPU right?

Which is what I do beforehand (due to problems with different normals for same vertices - shared data in ms3d format) and then just use glDrawArrays.

Share this post


Link to post
Share on other sites
quote:
Original post by klajntib

Ok, i''ve been doing some reading - weighted skinning is basicly used to prevent artifacts at joints points (ie: knees, shoulders..) right?

[edited by - klajntib on February 21, 2004 10:04:58 AM]


Pretty much, yes. Especially in the case where a joint flexes sharply, single-matrix skinning causes serious artifacts. Elbow and knees flatten off to sharp angles and so on. Weighted skinning is a little more complicated than single bone skinning, due to the necessity of having to combine multiple matrices. I haven''t implemented this yet, but there are plenty of generic vertex attributes available for sending multiple bone indices and weighting values, and it could still all be performed in the shader.

quote:

another thing - Do I gain much if i use glDrawElements and draw from and indexed array instead of glDrawArrays? Except a few KB of memory?

Logic tells me that glDrawElements is just a kind of a for loop which loops through all indices and sends appropriate vertices, normals and texcoords to the GPU right?

Which is what I do beforehand (due to problems with different normals for same vertices - shared data in ms3d format) and then just use glDrawArrays.



I think the biggest difference is memory usage. With glDrawElements(), you supply a list of indices which are used to pick and choose vertex data from locations within the vertex data arrays just as you guessed. glDrawArrays() starts at the beginning and works through to the end.

With glDrawArrays(), you store one set of vertex data for every face vertex in the mesh, regardless of whether that vertex data is exactly duplicated somewhere else in the mesh. With indexed arrays, a single vertex data set can be reused for multiple faces, without physical duplication of the data. Of course, not all vertices with the same (x,y,z) location are the same vertex--normal or UV information may be different.

Performance-wise, I personally have never seen much difference between the two methods, not enough to bother about. You are still drawing the same number of vertices, it''s just that they are chosen from different places. I prefer, however, to use indexed arrays and save a little space--especially since I went through all the work and trouble of writing a Blender Python script to crunch the mesh and merge duplicate vertices before exporting to file.


Golem
Blender--The Gimp--Python--Lua--SDL
Nethack--Crawl--ADOM--Angband--Dungeondweller

Share this post


Link to post
Share on other sites