views:

915

answers:

3

Although there seem to be very few up to date references for OpenGL 3.x itself, the actual low level manipulation of OpenGL is relatively straight forward. However I am having serious trouble trying to even conceptualise how one would manipulate VBOs in order to render a dynamic world.

Obviously the immediate mode ways of old are non applicable, but from there where do I go? Do I write some kind of scene structure and then convert that to a set of vertices and stream that to the VBO, how would I store translation data? If so how would that look code wise?

Basically really unsure how to continue.

+1  A: 

Using VBOs doesn't mean you have to render your entire scene with only single draw call. You can still issue multiple draw calls, and set up different transformation matrices along the way.

For example, if you're using a scenegraph, each model in the scenegraph can correspond to a single draw call. In such a case, the easiest way to use VBOs is creating a separate VBO for each model.

As an optimization, you might be able to combine several models into a single VBO, then pass in non-zero offsets when making your draw calls; this plucks out the correct model from the VBO. It's also desirable to combine multiple draw calls into a single draw call, but that's not possible if they need to have independent transforms. (Actually it is possible in certain situations if you use instancing or vertex blending, but I suggest getting the basics out of the way first.)

prideout
+2  A: 

If your entire world is truly dynamic, you can use the GL_STREAM_DRAW_ARB usage flag and reset the data on each frame. Don't bother manipulating it, just try to stream as efficient as possible.

However, I assume that you have a scene that consists of multiple rigid objects that move relative to each other. In this case, use one VBO for each object and specify the GL_STATIC_DRAW_ARB usage flag. You can then set the modelview transform for each instance of an object and render them using one draw call per instance.

A rule of thumb (on the PC) is to issue not more than one draw call per MHz of your CPU. This is a crude estimate, but there's some truth to it. Don't worry about putting multiple independent objects into a single VBO or other performance tricks if you stay below this limit.

Malte Clasen
+1 for recognising batching as being the biggest problem with graphics coding these days.1 draw call per Mhz seems optimistic to me after some OpenGL ES iPhone coding though - I've found I'm limited to 30-40 batches to maintain a decent framerate!
tsalter
Ok, I should have added that this rule may not apply directly to mobile devices. I've seen it first on some GDC slides quite a few years ago, and it served me well on Windows PCs.
Malte Clasen
Btw, I just found the slides here: http://ati.amd.com/developer/gdc/D3DTutorial3_Pipeline_Performance.pdf (primarily D3D9, OpenGL on page 28)
Malte Clasen
+1  A: 

Short answer:

Use glMapBufferRange and only update the subrange that needs modification.

Long answer:

The trick is to map the already existing buffer with glMapBufferRange, and then only map the range you need. Given these assumptions:

  • Your geometry uses per-vertex animation morphing
  • The vertex count for models is constant during animation.

Then you can use glMapBufferRange to update only the changing parts, and leave the rest of the data alone. Full uploads using glBufferData are slow as a turtle, because they delete the old memory store and allocates a new one. That's in addition to uploading the new data. glMapBufferRange only lets you read/write existing data, it does no allocation or deallocation.

However, if you use skeleton animation, rather pass vertex transformations as 4x4 matrices per-vertex to the vertex shader, and do the calculations there. Per-vertex data is of course specified with glVertexAttribPointer.

Also, remember that you can read texture data in the vertex shader, and that OpenGL 3.1 introduced some new instance draw calls; glDrawArraysInstanced and glDrawElementsInstanced. Those combined can be used for instance-specific lookups. I.e you can do instance draw calls with the same geometry data bound, but send positions or whatever per-vertex data you need as textures or texture-arrays. This can save you from mixing and matching different vertex array data sets.

Imagine if you want to render 100 instances of the same model, but with different positions or color schemes. Or even texture maps.

Mads Elvheim