I have lots of 2D quads. I write their local vertex position info to a buffer (accompanied with a world transform matrix) which gets sent to the render thread.

I then pass the world transform matrix as a uniform into the vertex shader which applies itself to each vertex and works fine. I however have to make a call to glDrawElements() for each quad I want to render because each one has a different world transform.

What is the most efficient way for me to render all of these quads? Should I do what I'm already doing, or should I calculate the world position for each vertex on the CPU before I write them to the buffer, so I can attempt to batch more quads into a single glDrawElements() call? Or is there another approach?


You definitely want to use instancing.

Your quads are instances: constant data (e.g. 4 vertices, bound texture) but with a few different parameters (e.g. world space matrix). Your world space matrices will be the instance data: one for each instance. It's a quite elegant and standard solution.

You can have all sorts of data as instance data, in the above case this means applicable to all four vertices of the quad. Examples: size of the quad, texture index, colour tint, transparency values, etc.

Per-vertex data could be your unit quad { (0,0), (1,0), (0,1), (1,1) } with indices: { (0,1,2),(2,1,3)} for CCW ordering.

  • \$\begingroup\$ My vertex data isn't constant between instances. The quads will vary in size. I need to research further if this will work. \$\endgroup\$ – Game_Overture Nov 6 '14 at 17:00
  • \$\begingroup\$ It's still instancing, the size can be instance data just like the world transforms. Is this for a particle system? \$\endgroup\$ – Babis Nov 6 '14 at 17:01
  • \$\begingroup\$ It's not for particles. I have a designer tool that creates various sprite animations from an texture atlas, and each frame/quad could vary in size. \$\endgroup\$ – Game_Overture Nov 6 '14 at 17:05
  • \$\begingroup\$ updated the answer, let me know if it still doesn't cover you, and if not, why. \$\endgroup\$ – Babis Nov 6 '14 at 17:07
  • 1
    \$\begingroup\$ Not quite, you still need to have a quad before you can scale it. Typically I pass { (0,0), (1,0), (0,1), (1,1) } for vertex data, so you can then scale by width height. Then indices would be { (0,1,2),(2,1,3)} for CCW ordering. And you're done, really. The rest is instance data. \$\endgroup\$ – Babis Nov 6 '14 at 17:17

I second instancing.

For the sake of extra information: Converting quads to world-space on the CPU.

If you can support it, you could also offload that work to the GPU through a geometry-shader. A couple arrays, 1 for the vertices of each quad, 2 for the transform marix of each quad, and then compute the world coordinates based on that (or some similar approach.) Since the geometry shader comes before the vertex shader step, it would fit right in.

You could also use a compute-shader to offload cpu work, if it's to much.

You could also avoid local coordinates. Local coordinates are nice for many reasons. If you don't need those benefits, or use them rarely, you could just use world coordinates and there would be no conversion to worry about. Aside from the rare conversion to local. That's going to be very dependent on what you're doing though.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.