Game Development Community

Rendering particles with glDrawArrays

by Alex Scarborough · in Torque Game Engine · 09/03/2005 (8:30 pm) · 7 replies

Read this.

Today I made some more progress. Namely, I fixed animating textures (instead of batching particles by datablock I rewrote it to batch particles by texture, and moved the logic for animating textures into updateSingleParticle). Sometime tomorrow I will most likely fix the issue that comes up with batching by texture, namely the erratic behavior of useInvAlpha. Next on the list is using multitexturing to render two or more sets together.

Anyhow, I was wondering if someone more experienced with OpenGL could look over the code for me and correct any stupid noob mistakes I may have made. I'm mainly doing this to learn OGL and have fun, but after running the profiler and noticing a very large performance boost, I think I should put this out as a community resource, and I think I'll rewrite the precipitation code to use (a better implementation of) glDrawArrays. If anyone wants my current code let me know and I'd be more than happy to send it to you.

As for a quick note: I read multiple times that glDrawElements was faster than glDrawArrays, but I have absolutely no clue how to properly set up a call to glDrawElements whereas it was very intuitive to setup a call to glDrawArrays. I would greatly appreciate it if anyone could point me to a decent tutorial on how to use glDrawElements

#1
09/04/2005 (8:42 pm)
GlDrawElements is pretty simple. Set everything up as if for glDrawArrays. Then build an array of indices (I usually use U16/GL_UNSIGNED_SHORT) and pass that to glDrawElements. The indices reference vertices from your client arrays. For instance, if you wanted to draw a triangle using the first 3 vertices, you'd have 0,1,2 as the indices. The nice thing here is that you need only provide 4 verts instead of 6, and then you can use a static index buffer that you pregenerate once and reuse, saving you a bunch of data processing.

I'd love to take a look at it, but I'm busy, so if you want a fast reply, I may not be the best choice. :P
#2
09/04/2005 (10:24 pm)
Thanks Ben! I got it up and running using glDrawElements now. I don't know how much advantage there is to using a static index buffer in a particle engine though... seems to me our indices are going to change quite a bit, but maybe that's just my OGL noobness speaking.

I also tried, and failed miserably, at setting up multitexturing to try and run fewer passes. Again, probably just me being new to OGL. I'll look into it more and try again some day.

Edit: Rewrote the batching logic to fix some nasty blending issues. We now render a new batch every time we switch textures or our glBlendFunc in an ordered list of particles. It certainly fixed the blending issues. I still need to figure out multitexturing though. That'll be a very nice addition to this.
#3
09/05/2005 (12:32 am)
Well, if you're only drawing a bunch of quads, it'll always be the some indices, just the vertex data changing.

Multitexturing is really only useful (without some Clever Stuff) for drawing the geometry with a fixed set of textures applied over the whole thing; I haven't seen it used much for increasing batch size. You might want to look into making a texture atlas of particle textures, though, as that could conceivably give you much better batching.

Then your only issue remains blend modes...
#4
09/05/2005 (12:46 am)
Couple of things: I fixed the blending issues, but only by adjusting the logic to generate the list of particles we're rendering this pass. We start with a list of all the particles in the emitter, then order them based on distance from the camera, then generate a list of particles that contain the same texture and blending settings. As soon as we hit a new texture or blending setting, we stop adding to our render list, render the particles, and then go back with the new texture/blending setting. Repeat until we've rendered all of the particles. This way we always render from farthest back to closest to us, which keeps everything blending nicely. After profiling this method (using glDrawElements) we actually manage to get better performance than the previous batching logic, and fix the blending issues.

I think multitexturing would be useful to cut down on how many times we have to do that. If (in an absolute worst case scenario) we have an emitter using two different textured particles, and every other particle uses a different texture, then we have a scenario where we may as well have used the original particle renderer. If we are multitexturing though, then we can just render them all together and not worry about it.

Either way, I'll most likely try to put in multitexturing just to learn how. This has been more of a "How much about rendering can I learn in a few days" then a "let's optimize the hell out of everything" project.

Thank you for helping me along with this project. It's been very fun learning my way around OGL. Incidentally, putting in glDrawElements did net a huge performance gain. Thanks for pointing out the (in retrospect) obvious way of setting that up.
#5
09/05/2005 (8:33 am)
This has been a really interesting post for me to read, because I've been trying to pick up some OpenGL, and I've learned a few things here. So thanks!

Using multitexturing to speed up rendering is an interesting idea. Having said that, I don't think there's a way to both use multitexturing and still use glDrawElements. The problem is that to set a coordinate for multitexturing, you call, for example,
glMultiTexCoord2fARB (GL_TEXTURE0_ARB, 0.0f, 1.0f);
Where the first argument is the texture level that you want to use. But to setup glDrawElements, you have to call glTexCoordPointer, which takes a list of numbers that all have the same texture level.

So you can either do multitexturing or glDrawElements, but not both. (Unless there's some strange way to finagle the two to work at once that I'm not aware of :p)
#6
09/05/2005 (10:46 am)
This is how to set up multitexturing (at least, the very basics of setting up multitexturing). You can use multiple textures on a single call to glDrawElements. It's just the rest of the setup that has been eluding me. But hey, that's what google and the local library are for.
#7
09/05/2005 (5:36 pm)
GlTexEnv does the setup on blend modes for the current texture unit.