3
\$\begingroup\$

Sorry for asking such a basic question. I am reading books on OpenGL4 but in most examples they generally render only objects. So I understand how to deal with vertex buffers and vertex array buffers, etc. but my question is does it work when you have say hundred of objects to draw?

Should you have 1 vbo for example and replace the data in the buffer each time you need to draw a new object?

Should you have hundreds of vbos? By my understanding was that you were limited in the number of buffers you could create (using getBuffer()).

How would one typically do this?

Thank you.

\$\endgroup\$
3
  • \$\begingroup\$ Well I am not a pro on this but I think usually you can have multiple VBOs for different groups of things and then used index buffers to specify sub models/meshes within each of those. (Indexed rendering). Though i don't think there is any limit to the number of vertex buffers you can have. (Just memory) \$\endgroup\$ – CobaltHex Sep 13 '14 at 23:31
  • \$\begingroup\$ Thanks, that's helpful. Yes I see how this could work, but that'd mean all your objects would need to share the same shader? and that they would also all need to have the same vertex date? I need to think about this some more. But thx. \$\endgroup\$ – user18490 Sep 13 '14 at 23:32
  • \$\begingroup\$ you shouldnt have to. Just update the shader you are using and redraw with new indices. However, note that creating one giant vertex buffer may not be worth it. \$\endgroup\$ – CobaltHex Sep 13 '14 at 23:37
4
\$\begingroup\$

Even when you have hundreds of objects to draw, typically those are many copies of the same object. E.g., you might a stack of 10 crates, or two goblins, or a hundred trees. You only need to store this identical data once. You'd then have a structure similar to:

struct Material {
  Shader shader;
  vector<Texture> textures;
  UniformMap parameters;
};

struct Mesh {
  VBO vertices;
  IBO indices;
  InputLayout layout;
  Material mat;
  size_t startIndex;
  size_t numIndices;
};

struct MeshInstance {
  Mesh* mesh;
  Matrix4x4 transform;
};

You'd then be able to have many MeshInstance objects for each Mesh object.

Rendering is (in the naive, simple case with bad perf) something like:

for (auto& object : objects) {
  device.BindLayout(object->mesh.layout);
  device.BindMaterial(object->mesh.mat);
  device.BindVBO(object->mesh.vbo);
  device.BindIBO(object->mesh.ibo);
  device.SetUniform(object->transform);
  device.Draw(object->mesh.startIndex, object->mesh.numIndices);
}

The good version would sort objects by material/buffer and possibly using instanced drawing rather than setting a uniform and making a draw call for each individual object, but the gist is pretty much the same.

If you have a lot of unique objects, you may indeed need many different Mesh objects. Note in the above definition that there are both startIndex and numIndices. This is important: it allows you to put multiple models into a single VBO/IBO! For example, the goblin might use indices 0-10,000 and a tree might be at indices 10,001-12,400 and a crate could be 12,400-12,423.

This reduces the number of buffers you need but not the number of vertices you need, of course. Cutting down on buffers has speed advantages in that you no longer need to swap buffer bindings when drawing any of the objects that share that buffer, of course; do the same with texture atlasing (or array textures) and you can get very high draw-call rates.

If you have still more objects than can possibly fit in the limitations of your GPUs memory... have less objects. You can't expect to have more data than the machine can handle and expect a magic trick to make it work. A game for instance will carefully control how many assets are needed at any given time; you won't see one of every enemy on screen at once, for example. Many games need to implement sophisticated streaming technologies in order to allow large open worlds or exceptionally long/detailed linear levels and then still have to put constraints on designers to keep distinct objects and level features spread out and not over-concentrated in one area.

\$\endgroup\$
0
1
\$\begingroup\$

You probably would want to do it like this:
Have a VBO (+index buffer) for each mesh in the scene (a mesh is just the collection of vertices and indices). Have your objects separately hold a reference to their meshes and store their transformation. From there you can have multiple objects referencing the same mesh with different transformations which is memory efficient and you can sort your drawing to bind each mesh's VBO once while drawing.
A bit more advanced technique would be geometry instancing which could be done as the next step. This way you won't even have to do a draw call for each object separately.

\$\endgroup\$

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.