0

Suppose I want to render many different models, each with a different transformation matrix I want to be applied to their vertices. As far as I understand, the naive approach is to specify a matrix uniform in the vertex shader, the value of which is updated for each mesh during rendering.

It's obvious to me that this is a bad idea, due to the expense of many uniform updates and draw calls. So, what is the most efficient way to achieve this in modern OpenGL?

I've genuinely tried to find a straight, clear answer to this question. Most answers I find vaguely mention UBOs, or instance drawing (which afaik won't work unless you are drawing instances of the same mesh many times, which is not my goal).

ben
  • 133
  • 13
  • 1
    *"what is the most efficient way to achieve this in modern OpenGL"* - is usually opinion-based and depends on your particular situation. Are all meshes static or are they animated and change their position from frame to frame? Do you always want to draw all the meshes or only those that are in the viewing frustum ([Frustum Culling](https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling))? Do you need to do depth sorting for transparent objects? – Rabbid76 Jan 28 '23 at 21:10
  • @Rabbid76 Let's say the mesh transforms are changing frame-to-frame. All meshes are drawn all the time. – ben Jan 28 '23 at 21:15
  • From my experience, the best performance gain is to draw as little as possible and reduce the number of meshes to be drawn. – Rabbid76 Jan 28 '23 at 21:19
  • Anyway, you can use [Multi Draw](https://www.khronos.org/opengl/wiki/Vertex_Rendering#Multi-Draw) and use [`gl_DrawID`](https://www.khronos.org/opengl/wiki/Vertex_Shader#Other_inputs) to index an array with the model matrices. – Rabbid76 Jan 28 '23 at 21:33
  • @Rabbid76 Aren't arrays constant size? Why wouldn't I be able to use a buffer to store matrices? – ben Jan 28 '23 at 21:36
  • *"Aren't arrays constant size"* - Not if you use (the last element) of a [Shader Storage Buffer Object](https://www.khronos.org/opengl/wiki/Shader_Storage_Buffer_Object) – Rabbid76 Jan 28 '23 at 21:42

1 Answers1

2

With OpenGL 4.6 or with ARB_shader_draw_parameters, each draw in a multi-draw rendering command (functions of the form glMultiDraw*) is assigned a draw index from 0 to the number of draw calls specified by that function. This index is provided to the Vertex Shader via the gl_DrawID input value. You can then use this index to fetch a matrix from any number of constructs: UBOs, SSBOs, buffer textures, etc.

This works for multi-draw indirect rendering as well. So in theory, you can have a compute shader operation generate a bunch of rendering commands, then render your entire scene with a single draw call (assuming that all of your objects live in the same vertex buffers and can use the same shader and other state). Or at the very least, a large portion of the scene.

Furthermore, this index is considered dynamically uniform, so you can also use it (or values derived from it and other dynamically uniform values) to index into arrays of textures, fetch a texture from an array of bindless textures, or the like.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982