Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Batching with world coordinates

Started by alaterale Apr 16, 2009 at 1:59 PM 2 replies 650+ views
Original Post
alaterale
alaterale
Hi, This is my first time posting here, normally I just spend a lot of time on Google or whatever to figure something out, but I'm having trouble understanding something that is pretty OpenGL related, and I figured this would be one of the better places to sign up and ask. I've tried searching to see if anyone else asked this, but I don't think anyone has. Okay, onto the question. I have a 2D sprite rendering engine that works fine as is, it uses vertex arrays to get better performance than immediate mode, though I still see there's a lot of time being spent in the draw code. Here's a rough view of what I'm doing for every sprite: glPushMatrix glMultMatrix(current sprite's transform matrix) CalculateVertexCoords for this sprite CalculateTextureCoords for this sprite glVertexPointer(load coords) glTexCoordPointer(load coords) glDrawArrays(5 points, using Triangle_Fan) glPopMatrix This all works fine, but with profiling and such I've noticed that DrawArrays was taking a long time, so I did research on batching and such, and lots of people have said (on other forum boards for sprite batching) that if you could compile all the coords into one set, and then make one call to DrawArrays, it would be faster. They also said to use screen or world coordinates for 2D since it's simpler that way. Here's the problem though, the vertex coordinates that are calculated are done from 0,0,0, so they're obviously not absolute coordinates. glDrawArrays gets a quad that is based on the origin, yet because of the local to world transform that happens behind the scenes, it puts the sprite in the right place. What I'd like to do is give glDrawArrays the absolute coordinates and remove the need for the Push/Pop matrix calls. Everything is just sprites so I don't think I really need to have multiple levels of matrices anyway. The transform matrix that is multiplied already stores position, up, view, and side vectors, and game specific code changes it (rotate, translate, etc gets concatenated into it). I'm not really sure what to call this problem, aside from, "How do I change that above repeated call to DrawArrays into a batch form, but keep the transformations". Maybe this is a math specific problem, but I figured OpenGL would be a place to start. If specific code is necessary, I can post it, but I figured this would be a general enough question. Maybe someone has already done this in their engine before? I appreciate any help I can get, thanks!
haegarr
haegarr
Quote:
Original post by alaterale
This is my first time posting here,...

Be welcome.

Quote:
Original post by alaterale
... normally I just spend a lot of time on Google or whatever to figure something out, but I'm having trouble understanding something that is pretty OpenGL related, and I figured this would be one of the better places to sign up and ask.

For sure it is ;)


The problem with batching several normally unrelated objects is that you must find a way to still transform each of them individually. As long as the objects are rendered separately, each of them can have its own set-up of the MODELVIEW matrix. Normally the VIEW portion is the same for all objects, but the MODEL portion is not. Since there is only one MODELVIEW matrix for a render call, you must find another way to apply the respective MODEL portion to each object.

One solution is, as you've said, to apply the respective MODEL matrix to the objects and yield in "new" objects, and render just these new objects instead of the original ones. This kind of transformation cannot be done by OpenGL but is to be done by yourself. You've said in the OP that the level of transformation depth is always 1, i.e. you have the respective MODEL transformation matrix for each object already at hand.

Hence, follow this layout:
* allocate buffer for vertices of all sprites at once
* foreach sprite
** foreach vertex of the sprite
*** compute new vertex as matrix * original vertex position
*** set buffer[n+i] to new vertex
* set-up MODELVIEW matrix to VIEW only
* render buffer

However, be aware that this works fine only if you don't need dozens of textures, since you also have to apply all needed textures at that single render call. Best is if you need only a single (perhaps big) texture. You may use an texture atlas for this purpose.
alaterale
alaterale
Quote:
Original post by haegarr

Hence, follow this layout:
* allocate buffer for vertices of all sprites at once
* foreach sprite
** foreach vertex of the sprite
*** compute new vertex as matrix * original vertex position
*** set buffer[n+i] to new vertex
* set-up MODELVIEW matrix to VIEW only
* render buffer

However, be aware that this works fine only if you don't need dozens of textures, since you also have to apply all needed textures at that single render call. Best is if you need only a single (perhaps big) texture. You may use an texture atlas for this purpose.


Thanks for the reply and welcome! I had already figured that if I could find a way to generate absolute coordinates for the vertices of each sprite, I could deal with the texture problem by using a map or hash table where I would store the batched coordinates by texture, and then in the final part iterate through the textures that way. I just couldn't think of a way to give glDrawArrays absolute values and avoid the whole push/pop thing.

Also, I sort of already use a texture atlas, since the texture system I set up encourages multiple sprite animations in one texture, and I'm working on a texture manager to properly manage memory. But that's a different topic I guess.

I will give this a shot anyway, hopefully I can figure this out since I think with thousands of sprites it would be a big performance boost. By the way, by "original vertex position", do you mean the untransformed local-space value? So a sprite with one point on the origin and say another at 10,10,0 or something?

Also, I don't know what you mean by "set-up MODELVIEW matrix to VIEW only". Once I have a buffer (based on texture) with a lot of vertices and texturecoords, I was just thinking to call the opengl functions (vertexpointer,texcoordpointer,drawarrays) then, since it would then have absolute coordinates.
haegarr
haegarr
Quote:
Original post by alaterale
Thanks for the reply and welcome! I had already figured that if I could find a way to generate absolute coordinates for the vertices of each sprite, I could deal with the texture problem by using a map or hash table where I would store the batched coordinates by texture, and then in the final part iterate through the textures that way. I just couldn't think of a way to give glDrawArrays absolute values and avoid the whole push/pop thing.

Also, I sort of already use a texture atlas, since the texture system I set up encourages multiple sprite animations in one texture, and I'm working on a texture manager to properly manage memory. But that's a different topic I guess.

The texture co-ordinates doesn't change due to the vertex position transformation. In other words, also you're on the way to make the vertex position dynamic, you can use static texture co-ordinates. This furthur allows you to split the buffer into 2: One dynamic buffer for positions, and one static buffer for texture co-ordinates. However, since you are speaking of texture animation, that option is probably not available for you.

Quote:
Original post by alaterale
I will give this a shot anyway, hopefully I can figure this out since I think with thousands of sprites it would be a big performance boost. By the way, by "original vertex position", do you mean the untransformed local-space value? So a sprite with one point on the origin and say another at 10,10,0 or something?

Yes, I meant the local space values. The "original" is just the definition of the sprite shape. One can say that the algorithm makes a copy of the original and transforms the copy. So the original is left "as is" for the next frame, where another copy is made from it; and so on, and so on ...

Quote:
Original post by alaterale
Also, I don't know what you mean by "set-up MODELVIEW matrix to VIEW only". Once I have a buffer (based on texture) with a lot of vertices and texturecoords, I was just thinking to call the opengl functions (vertexpointer,texcoordpointer,drawarrays) then, since it would then have absolute coordinates.

Normally, you have to set-up 3 matrices for OpenGL: The projection matrix, the view matrix, and the model matrix. The projection matrix is normally set only once, in your case probably to an orthogonal projection. The view matrix is to be set once per frame; it is computed from the virtual camera. The model matrix is also computed once per frame but for each rendered object.

In OpenGL the view matrix and the model matrix are together managed on the MODELVIEW matrix stack. So, the typical OpenGL rendering loop clears the MODELVIEW matrix stack, pushes the VIEW portion of the MODELVIEW matrix onto the stack (e.g. using gluLookAt or the lower level glTranslate and glRotate functions with parameters from the camera), and finally multiplies the MODEL matrix for a particular object before rendring it.

The whole push/pop stuff is just there to guarantee that the next model will get the same VIEW matrix, and hence can multiply its own MODEL matrix without being disturbed by the former model. (This is a bit simplified, since there are situations where the model is _expected_ to be affected by the former model; this is the well-known forward kinematic.)

Now, in your case, maybe you have no camera yet, and hence don't set-up a VIEW matrix. In that case you automatically use the identity transformation what does, well, nothing.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.