Point Sprites vs Textured Quads in Open GLES 2.0

I'm wondering what would perform better, point sprites (GL_POINTS) or regular sprites (GL_TRIANGLES). Here's the scenario:

I am developing a simple particle system to allow me to produce various effects like fire (in a very simple way). Each particle will be rendered using a texture, but the rub is I want different textures depending on the "age" of the particle (imagine a fire particle transitioning from a "burning" style texture to a "smoke" style texture).

As I understand it if I use point sprites I would have to bind different textures during the lifetime of the particle set based on the age of the various particles still alive. That is, as many texture bind operations as there are "stages" for a particle (at most).

Conversely if I use regular sprites I can do a single bind of an atlas then just render away.

I'd also "like" to be able to rotate the particles, but that's probably not a deal breaker if the performance gain for point sprites is significant. I would definitely want to scale, but I'm assuming I can do that by changing the point size... not sure what the max is for that.

My sense is that a few additional texture bind operations is going to have less of a performance impact than drawing 4 times the number of vertices (4 verts per sprite vs 1 per point). Of course there'd also be an additional sort operation in the point sprite approach so I can batch up the particles with the same texture, but again that shouldn't be too heavy if there are only 3-4 states per particle.

I'm developing for mobile (Android/iOS) so chipset will vary (not sure how much impact that will have on the decision).

Now the "right" thing to do would be to implement both and test, but that sounds like way too much hard work ;)

Would I be correct in assuming point sprites are still the better approach?

With point sprites you have to obey the max point size supported by your implementation, which may be a lot smaller than you'd like - this will become particularly noticeable if particles ever need to get close to the viewpoint.

The attenuation formula used, while logical, may not give you good results either.

On the other hand there is the tradeoff of a much smaller vertex submission.

That's not the full story though, as those texture changes are going to impact performance quite badly. Not from the changes themselves, but from reducing your batch size. You'll find that the number of draw calls used will hugely increase, which is exactly the kind of thing no GPU likes.

So there is no perfect solution and either approach involves accepting some overhead thst doesnt exist in the other.

If it were me I'd do the textured quads and see how much of an impact those extra vertices really have. I suspect that they may be the more acceptable tradeoff.

• Yeah I came to the same conclusion. Actually on the hardware I'm using (PowerVR GPU on mobile) I did not notice any difference between point sprites and quads which makes me wonder if the GPU isn't just doing quads anyway. – Jason Mar 26 '13 at 16:06
• P.S. According to Apple the point size for PowerVR can range in size from 1.0 to 511.0 pixels. (developer.apple.com/library/ios/#documentation/3DDrawing/…) – Jason Mar 26 '13 at 16:11
• "I did not notice any difference between point sprites and quads which makes me wonder if the GPU isn't just doing quads anyway" - so long as it obeys the invariance rules there's no reason why a GPU can't just do that, but I have no special knowledge of this GPU so can't say any more. – Maximus Minimus Mar 26 '13 at 17:17

The downside with point sprites - I recently learned - is that they are viewport clipped by their centres. Yet I've been using them for fire particle effects etc for a long time and it I had never noticed! So how big a downside is up to you.

I've asked similar questions before and have since got a system going that I'm happy with. Here's my thoughts and recipe:

The problem with opengl-es - presumably a constrained environment - is the cost of uploading new data every frame. And quads are a lot more data. You may have gl_VertexID and glVertexAttribDivisor to mitigate this but I've never had an environment with them.

The key way to minimise data transfer is to do as much procedurally as possible. For example, I have a fire system that uses a random number generator so the state is actually passed in as just a handful of uniforms! I create a single buffer of random numbers, from one to max-particles. I reuse this same buffer for all particle systems. I then have a single random seed per individual particle system, and I combine this with the per-point random number to give each particle in each system its own state - from speed and size to position and direction and so on. I then just have to pass in the time as a float...

Now to talk about the textures:

When you draw the fragment itself in the fragment shader you get any per-vertex info your vertex shader has passed in a varying as well as the point's gl_PointCoord. So you can easily incorporate rotation of a texture into the fragment shader.

And you should put all the various animation frames or font glyths or icons or whatever into the same texture - a texture atlas - and simply pick the sub-part of the texture to draw in the fragment shader based on values passed from attribute via varying.

For example, imagine you want to write some text. So you have a big texture with the letters alphabet on. Then you have a vertex attribute of the position (vec2) on the screen, and the character to draw. If you arrange your font glyths in a 2D array, say 16*16 slots, it'd be trivial to pass the character to draw as a byte! If you have more code-points (unicode etc), or want to do tighter packing, and so on, it might be easier to actually pass in the sub-rect in the texture to use as a vec4. So you can write text on the screen using just a handful of bytes per character and point sprites.