Polygon batching and world matrices

Started by
13 comments, last by CountOfMonteChristo 21 years, 4 months ago
Trying to implement polygon batching, I stumbled upon some things that are not entirely clear to me. If I understand the concept correctly, the whole idea of polygon batching is placing the vertices of objects that use a similar texture or the same render states into one big vertex buffer. That way, you just set the texture once per frame (preventing redundant texture changes), set the necessary render states once per frame (preventing redundant setrenderstates), and then draw the vertex buffer, rendering all objects in one (or multiple, depending on the size of the VB) drawprimitive call. But when you only use one drawprimitive call to render all these polygons, what do you do with the world matrix? I mean, you can''t just copy all vertex data into the large buffer, because then all objects would still be in object space. If you just rendered the entire buffer with one call, all objects would be in exactly the same place, because the world matrix you set is applied to all vertices. You could change the World matrix for each object, but then you''d still end up calling DrawPrimitive multiple times. So how do you place all objects in their own spot? The only way I can think of to make this work, is if you made the vertices go through a world matrix before placing them into the big matrix. For instance, if I had a couple of trees, I''d take the vertex data of tree number one, translate and rotate it, and then place it into the big buffer. Then, I''d take the vertices of tree two (or tree one again, if it''s the same shape), translate and rotate them, and copy them into the vertex buffer, etcetera. Then, one call to DrawPrimitive, and voila, trees in different positions and with different orientations. Only problem is, how do you apply these matrices to the vertex data before copying it to the big vertex buffer? How should the vertex data be stored, anyway? In another, smaller vertex buffer? In an array of CUSTOMVERTEXes? Also, what do you do with the world matrix set in m_pD3DDevice->SetTransform()? Set it to an identity matrix? After all, the vertices in the big buffer are already in world space. So... what do you do? I''ve searched Google, GameDev and GamaSutra for some information on polygon batching, but I haven''t found any information on how to implement it. Am I on the right track here, or am I missing something completely? How did you solve it? Any pointers, articles or tutorials you know of?
Advertisement
I''m also interested in this. This is something that has confused me a lot, because I really don''t like the thought of changing every single verticies position in the vertex buffer whenever I want to move an object in it.



¬_¬
The way I see it, there are two good methods.

The first, is the big buffer. On the plus side, you can render everything in just one call(one call per texture per renderstate, that is), however, you need to lock/unlock the buffer when an object in the buffer moves. It isn''t as bad as it seems though, cause you only need to lock/unlock when something moves. So, for many static objects, using this method should work best.

A second yet far more complex method is to store which vertices belong to which object (first vertex and the number would be best). It does mean you need to use many DrawPrimitive() calls and just as many SetWorldMat() calls, but it does allow you to modify the objects'' position easily. This method would work well for large, many-faced yet few objects.

All in all, unless you''ve got a very special situation I''d go for the first (large buffer) method, since it should be faster. It''s also easier to implement.

Of course, I''m pretty new here -- so what do I know? .
Sirob Yes.» - status: Work-O-Rama.
For static objects you can transform them once before copying them to the _static_ VB. Even if they''ll move once or twice during the game it''s fine to lock that buffer. It''s not as slow as the d3d doc wants you to believe. Also, you can combine a static VB with a dynamic IB, so you can choose which parts of the VB you want to render. This can be useful when you need to render only parts of your world thanks to visibility tests, or for instance when an object is destroyed.

For dynamic objects, you should transform the object''s polygon''s when its world transform changes. What I do is keep a pointer to the mesh its using, and a transformed copy of the vertices in the object''s instance itself. Whenever I call SetTransform on the object, it recomputes the vertex positions. Then you copy this systemmem copy to the dynamic VB every frame.

For both methods, don''t ever copy each individual vertex or index into its locked buffer. It''s hella slow for whatever reason. Although for static VB''s this is less of a problem at loadtime, it''s an even bigger problem than dynamic VB''s if you ever wanna change them during runtime. Always work in system memory and copy each batch of vertices/indices to the locked buffer. Play around to see what batch size works best.
I''ve only skimmed the other replies, but remember that you should batch *as much as it makes sense to*. This doesn''t mean that you should jump through unnecessary hoops for the sake of batching.

If you have lots of moving objects, use separate world matrices for each object. Locking a buffer and doing your own transform basically kills off any hardware advantages. The hardware is *very good* at transforming vertices. Don''t fight it.

In my apps, I almost never lock the vertex buffer after initial set up.

There are some people that call DrawPrimitive for each separate triangle. This isn''t good, but calling DrawPrimitive for every character, vehicle, etc. is fine as long as you batch according to textures, shaders, etc.

Author, "Real Time Rendering Tricks and Techniques in DirectX", "Focus on Curves and Surfaces"
Author, "Real Time Rendering Tricks and Techniques in DirectX", "Focus on Curves and Surfaces", A third book on advanced lighting and materials
Hmmm... Interesting stuff. So, I guess the large vertex buffer idea is out for everything except non-moving stuff like scenery, right?

I'm still not clear on a lot of things though... For instance, how do I transform an object's vertices before copying them to the large buffer? Preferably, with hardware support?

Suppose I have three trees to render, one at (-1,-1,-1), one at (1,-1,1) and one at (0,-1,3)... They all have the same texture and render states.

In my tree object, I have the vertices that make up a tree - in object space. (What's the best way to story information like this, by the way? A VB? an array?) I also have a large vertex buffer in which I would like to place the trees so that I render all three of them with one call to DrawPrimitive. The trees don't move, so if I manage to get them into the large VB at the start, I'm fine for the duration of the app, right?

Now... If I just copy the vertices to the big VB, they'd still be in object space. I'd get three trees, all in the same position at the origin. So I'll need to put them in their spot before (or during?) copying them to the large VB. Then I'd only need to set the world matrix to an identity matrix, and render the big VB in one call... which results in three trees at their desired position. With three trees, you could say big deal, but what if it were 50 trees? That would be a lot more efficient than 50 individual DrawPrimitive calls.

quote:Original post by CrazedGenius
I've only skimmed the other replies, but remember that you should batch *as much as it makes sense to*. This doesn't mean that you should jump through unnecessary hoops for the sake of batching.


Now, how do I do that, with hardware advantage? Do you multiply each vertex by the desired world matrix and then copy the result to the big vb? If anyone could help out with some detailed implementation information or a code example, that'd be great.

I see your point, CrazedGenius... Yet, with 50 similar, non-moving trees, to me it makes sense to batch. The problem is... how do I place them at their individual positions in the big vertex buffer in the initial setup?

[edited by - Bas Paap on December 2, 2002 7:32:52 PM]
quote:Original post by Bas Paap
I see your point, CrazedGenius... Yet, with 50 similar, non-moving trees, to me it makes sense to batch. The problem is... how do I place them at their individual positions in the big vertex buffer in the initial setup?


hello,

you mentally construct/position your objects in your VB and set an identity matrix for this object''s world transformation when you''re about to render it,





http://www.dualforcesolutions.comProfessional website designs and development, customized business systems, etc.,
quote:Hmmm... Interesting stuff. So, I guess the large vertex buffer idea is out for everything except non-moving stuff like scenery, right?


Wrong. You can store 15 object models in a large VB, having 10 instances of every object, and render them in 150 calls to DrawPrimitive, hanging the world matrix on ever call.

Changing the world matrix is CHEAP. Cheaper than a vertex buffer switch, cheaper than uploading the data.


Regards

Thomas Tomiczek
THONA Consulting Ltd.
(Microsoft MVP C#/.NET)
RegardsThomas TomiczekTHONA Consulting Ltd.(Microsoft MVP C#/.NET)
quote:Original post by mickey
you mentally construct/position your objects in your VB and set an identity matrix for this object''s world transformation when you''re about to render it,


Well, that would be okay if I had a few cubes to worry about... What if I had a dozen complex objects consisting of a huge number of vertices? I''d spend months calculating the transformed vertices for those. Can''t this be done during initialization, in code?


quote:Original post by thona
Wrong. You can store 15 object models in a large VB, having 10 instances of every object, and render them in 150 calls to DrawPrimitive, hanging the world matrix on ever call.

Changing the world matrix is CHEAP. Cheaper than a vertex buffer switch, cheaper than uploading the data.


It''s not the changing of the world matrix I worry about. It''s the 150 calls to DrawPrimitive that I fear would be expensive. After all, the SDK suggests calling DrawPrimitive fewer times to optimize code.
quote:It''s not the changing of the world matrix I worry about. It''s the 150 calls to DrawPrimitive that I fear would be expensive. After all, the SDK suggests calling DrawPrimitive fewer times to optimize code.


I would say that "common sense" is pretty good here.

Possibilities:
(a) use the vertex blending mechanism to basically have multiple world matrices. Have the different object vertex buffers - use different matrices. This can be done with the FFP (4 more bytes per vertex) or with a shader. This way you can - draw a lot of objects with ene call. This, though, is relevant more for simle objects.
(b) for more complex objects - you will draw more than a couple of vertices anyway :-)

IMHO calls to DrawIndexedPrimitive of more than 500 vertices are ok :-)

Dont forget what you trade this with: a lot of uploads.

And go into vertex shaders with this - there is wonderful stuff you can do with them. I am just playing around with a "rocket engine" particle engine, and i handle the complete movement AND deterioration of the particles in a vertex shader :-)


Regards

Thomas Tomiczek
THONA Consulting Ltd.
(Microsoft MVP C#/.NET)
RegardsThomas TomiczekTHONA Consulting Ltd.(Microsoft MVP C#/.NET)

This topic is closed to new replies.

Advertisement