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

Drawing massive numbers of billboards/sprites

Started by luke2 Apr 19, 2010 at 1:03 PM 6 replies 2.1k views
Original Post
luke2
luke2
I've used OpenGL before, but only in immediate mode. It was a fairly intuitive: glBegin(..) //Transformations.. //Draw textured quad glEnd() For each sprite. But this doesn't scale too well, especially with all the function overhead. Enter OpenGL 3.0 which apparently does away with immediate mode in favor of Vertex Buffer Objects. As I understand VBO's: You define an array of data, tell OpenGL how to interpret it (interweaved etc), fetch a VBO handle, bind that handle to the array/load it in. And it magically draws the geometry very quickly. But what I don't quite understand: How do I do multiple, separate quads in one VBO, which are all changing position/orientation often. And I'm talking 30k+ quads here. I would think modern hardware can handle 100k quads easily enough, I'm just not sure how to go about drawing them efficiently.
othello
othello
Hi,
All your quads can be in the same VBO. Just set your index array to point to the right vertices for your triangles.

For example:
array index | vertex0           | 0.0 0.01           | 1.0 0.02           | 1.0 1.03           | 0.0 1.04           | 10.0 10.05           | 11.0 10.06           | 11.0 11.07           | 10.0 11.0index array:0 1 20 2 34 5 64 6 7


This will draw two quads; one with the lower left at ( 0.0, 0.0 ) and the other with the lower left corner at ( 10.0, 10.0 ).
luke2
luke2
But if all my quads are moving constantly, won't this mean I'll be pumping/overwriting my VBO every frame or so?
othello
othello
Yeah but that's unavoidable. You would also do that if you're drawing an animated model or something. You can try and set GL_DYNAMIC_DRAW or GL_STREAM_DRAW to get the data in a better position in video memory closer to the CPU.

VBOs are really just a better way of sending data to the graphics card. You still have to send the data though.
Geometrian
Geometrian
Step 1:

Billboards are best done with point sprites: you don't have to bother with sending and rotating quads (the hardware geometry shader does that by itself), so you have to process/update fewer vertices. In effect, your code ends up running four times faster.

Step 2:

You only need update certain portions of the VBO as necessary--though, if you're updating the whole thing each frame, you're probably better off just using vertex arrays. Better solution: don't transform your vertices in hardware; that's what a vertex shader is for. Use a static VBO, but move the vertices according to a function or a texture in the shader.

-G
[size="1"]And a Unix user said rm -rf *.* and all was null and void...|There's no place like 127.0.0.1|The Application "Programmer" has unexpectedly quit. An error of type A.M. has occurred.
[size="2"]
luke2
luke2
Righto...

So, I:
1. Fill up buffer
2. Link it/Load it to GL
Per frame:
3. Modify the data as appropriate
4. Send changes to GL


My only other question is that this VBO tends to centralize data, would this mean that I should write my sprite code procedurally? I'm not sure how I could elegantly do it OOP (unless I made prodigious use of static members)
luke2
luke2
Quote:
Original post by Geometrian
Step 2:

Better solution: don't transform your vertices in hardware; that's what a vertex shader is for. Use a static VBO, but move the vertices according to a function or a texture in the shader.

-G


Could you explain what you mean by using a vertex shader to transform vertices? If these sprites are moving around, independently of one another, how can a texture define that?
kuroioranda
kuroioranda
Quote:
Original post by luke2
Quote:
Original post by Geometrian
Step 2:

Better solution: don't transform your vertices in hardware; that's what a vertex shader is for. Use a static VBO, but move the vertices according to a function or a texture in the shader.

-G


Could you explain what you mean by using a vertex shader to transform vertices? If these sprites are moving around, independently of one another, how can a texture define that?


I think he's referring to packing the sprites' x/y coordinates into a texture and then reading that in a vertex shader on the video card. A very common and similar trick is used when skinning 3D models as you only need to specify a small amount of data for the bone transforms each frame as a texture, upload the texture to the card, and then use that texture as inputs to transform the mesh geometry on the video card. The benefit is that you don't have to send the model's vertex or index data to the video every frame, nor do you have to do all of the math to transform the vertices on the CPU. So that's almost always a huge performance win, as a video card can do that sort of thing much faster than a CPU.

This may or may not help you in 2D, however. If your sprites' locations can be defined by some mathematical function as a function of time (e.g., falling snow), then you could make rendering all of your sprites very fast by storing a fixed amount of quads on the video card and then writing a shader that will derive their locations based on some time value (t-value). Then you just need to update the t-value before rendering the quadlist and the video card will do all of the work.

Unfortunately, most sprites don't work this way. Enemies, protagonists, smoke trails, most of these things move independently of each other and hence you're going to need to update them on the CPU each frame. In this case, packing the locations of each billboard into a texture doesn't save you nearly as much, as you'll still need to send an x/y coordinate pair for each vertex.

Long story short, I'd wait on doing anything that involved until you know you need it. I have personally transformed and rendered upwards of 60k camera oriented sprite billboards in about 5% of CPU time on my Athlon X2 4500+ (1.8GHz), single threaded, just stupidly rewriting the vertex buffer each frame.

The other thing to ask is, are these 30k all sprites on screen at the same time? The fastest code is the code that is never run, so if you only ever have 2k sprites on screen at a time, using some sort of partitioning algorithm like a quad-tree or a grid to quickly cull out the sprites that are offscreen that frame will very easily give you several orders of magnitude speedup with very little effort.

Topic Locked

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

Sign in to reply to this topic.