0
votes

In OpenGL with shaders, I want to render two objects which I have loaded as two meshes. Each object is represented by a set of vertex positions, a set of vertex colours, and a set of vertex indices for the triangles.

There are three ways I can think of to draw the two objects. Which is the best practice?

1) I concatenate the vertex positions of the two objects into one long array of vertices, and similar for the vertex colours and the vertex indices. I then create one vertex position buffer, one vertex colour buffer, and one index buffer. When rendering, I then make one call to glBindBuffer(...) and glDrawElements(...).

2) I concatenate the vertex positions of the two objects into one long array of vertices, and similar for the vertex colours. I then create one vertex position buffer, and one vertex colour buffer. When rendering, I then make two calls to glBindBuffer(...) and glDrawElements(...), one for each object.

3) I create two vertex position buffers, two vertex colour buffers, and two index buffers. When rendering, I then make two calls to glBindBuffer(...) and glDrawElements(...), one for each object.

Thanks!

2
Is it really only two objects you're rendering? Or are you just using that number to simplify the explanation?Reto Koradi
Yes, it is two CAD objects. Both are meshes that I have been given. I want to render the two objects in the same viewport.Karnivaurus

2 Answers

1
votes

The general rule for OpenGL optimization is to minimize the number of state changes. And binding buffers is a state change.

So, all other things being equal, #1 would probably be the fastest.

However, it is not always particularly useful. Often times, different objects have different transforms relative to one another. So usually, you'll need to change some state between rendering the objects, so #1 is not an option.

Even so, you need not resort to option 2. You could use the same buffer setup as option 1, but just issue two draw calls. Each draw would render with part of the arrays.

Equally importantly, with vertex arrays, the issue is not so much binding buffers (though they aren't cheap). It's changing the vertex format that you use, the relative arrangements of attributes. If you use the separate attribute format API, you can easily change buffer bindings without touching the format. And usually, you will only have maybe 5-6 vertex formats throughout your entire program.

The other issue is that you have not fully explored the array of possibilities here. For example, in all of your cases, the positions, colors, normals and other attributes each inhabit separate buffers. Well... why are they in separate buffers? Interleaving your vertex attributes generally gives better performance.

So really, the answer for best performance is "none of the above".

1
votes

With only 2 objects being rendered, none of these options will bring you even close to any kind of performance bottleneck. If you're targeting your frame rate to match the display refresh rate, which is typically around 60 fps, you only need to render 120 objects per second.

Say you need a good handful of state setup calls per object, that gets you in the range of 1000 state setting calls per second. While the performance characteristics are of course highly platform/vendor specific, a halfway decent driver will be able to handle a few million simple state setting calls (like binding buffers, setting up vertex attributes, etc.) per second. So you're at least about 3 or 4 orders of magnitude below a level where I would start to be worried about throughput.

Now, if you had thousands of objects, things start to look a little different. There are two things I would recommend for sure:

  • Use interleaved attributes. This means that you store the position and color of one vertex sequentially, followed by the attributes for the next vertex. Say you have 3 components (xk, yk, pk) for the position and 4 components (rk, gk, bk, ak) for the color of vertex k, the memory layout of the buffer is:

    x0 y0 z0 r0 g0 b0 a0 x1 y1 z1 r1 g1 b1 a1 x2 y2 z2 r2 g2 b2 a2 ...
    
  • Use VAOs (Vertex Array Objects) to set up the state. This will allow you to set the entire vertex attribute state for an object with a single glBindVertexArray() call.

Whether it's better to have separate VBOs for each object, or to share larger VBOs between objects, is hard to tell in general. Having a large number of small VBOs could certainly be harmful to performance, and sharing them may be beneficial if you can arrange that fairly easily. But I can also picture scenarios where having very large VBOs could have adverse effects. So you may have to try different options if you wanted to get the maximum performance in the case where you have a lot of objects. Unfortunately, the results might very well be platform dependent.