1

Consider a single VBO (or perhaps multiple VBOs) filled with multiple objects, each with arbitrary vertex counts. For demonstrative purposes, let's say there's an apple, with 500 vertices, and an orange, with 650 vertices stored within a VBO.

Typically if I wanted to draw the orange, I'd just call glDrawRangeElements and indicate that I wanted to draw elements 500-1150. If I wanted the orange drawn in a specific position in space, I'd use a model transformation.

However, what if I wanted to draw potentially thousands of objects? Furthermore, what if the VBO contained hundrends, if not thousands of objects as a sort of "object library"? I'd prefer not to have to transmit what I want to be drawn to my GPU using immediate mode (that is, calling glDrawRangeElements manually for each object). This would add a great deal of costly overhead in communicating with the GPU.

What I'm looking for is some way to upload some sort of array with OpenGL in one go. This array would contain some sort of identifier for each object I want drawn, and perhaps a transformation for each (position, rotation). Thus, if I wanted to draw a sea of different fruits at arbitrary locations, I'd just calculate an index once and upload it. The array can be interleved. For instance, the array might look like this: [ object id 1 ], [ position 1 ], [ rotation 1 ] ... [ object id n ].

genpfault
  • 51,148
  • 11
  • 85
  • 139
NickB
  • 13
  • 3

2 Answers2

3

Ultimately, what you're trying to do (draw lots of completely different objects) is not going to work with any for of "instancing."

Instancing is for objects that are the same. That is, repeated objects that are drawn in different locations. What you're doing is just drawing a lot of different objects. And for that, you're going to have to issue multiple draw calls. There's no avoiding that.

The best you can do is minimize your state changes. Use the same shader to render the objects. Use a texture atlas, possibly in array textures, so that you don't have to change textures between objects. Do not use glVertexAttribPointer (more than the initial setup) or change the element buffer.

The key to that last part is glDrawElementsBaseVertex. As long as all of the objects in the buffer object use the same vertex format, you can stick them all in the same buffer as a giant array. Then you use your index list to separate them. The base vertex part allows you to keep your indices relatively small.

Ultimately, what you want is to only change the model-space location of the object between objects. You have a number of options for handling that.

The simplest is to just use glUniform to change the model-to-camera matrix for each object, as normal. You should know how to do that one by now.

Slightly more complex is to use a uniform buffer to store all of your model-to-camera matrices for all of the objects you want to draw. Between objects, you use glBindBufferRange to bind the particular matrix for that object. There is no information on whether this will be faster or slower than the previous method. Note that implementations have an implementation-dependent alignment for ranges of uniform buffers.

Now technically, GL 4.x hardware allows you to create some data in a buffer object that represents a rendering command, then render with it. This is core in 4.0 and is called indirect rendering. The general idea for this was that OpenCL, or some specialized GL shader process, would write data to a buffer object, and the client code would only have to say, "render what he said", rather than having to read back data and manually issue a glDrawElements call.

That doesn't help you because each call has to be issued separately. Even with AMD's multi-indirect-drawing functionality, it still doesn't help you because you want to give each object a different transform. And multi-draw doesn't allow different transforms between objects.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Nicol, I appreciate the thorough reply and the supplemental advice. I had a suspicion that what I wanted to do isn't possible. Nevertheless, I'm going to look further into the indirect rendering that you mentioned. Thanks a lot! – NickB Jun 21 '12 at 18:53
1

Sounds like you want instancing.

Look into ARB_instanced_arrays/ARB_draw_instanced.

Here's a comparison of various techniques.

If you're using the fixed-function pipline you're SOL though :(

genpfault
  • 51,148
  • 11
  • 85
  • 139
  • Thank you for the prompt reply! I'm going to look those up right now. – NickB Jun 21 '12 at 16:25
  • I read all three links you provided. The third one was great and was very helpful. However, I get the impression that instancing only allows one to specify a _range_ of instances; I'd like to be able to select them arbitrarily. For example, if this is my set {pear, apple, orange, pizza}, and I just want apple and pizza, is there a way to pass in an index to specify only those and skip the other two? Thanks. – NickB Jun 21 '12 at 16:50