Selecting multiple models in OpenGL with a single draw call

I built a 2D graphics engine and I created a batch system for it, so if I have 1000 sprites with the same texture, I can draw them with a single openGl call.

This is achieved by placing all vertices of all sprites with the same texture in the same vbo verbal array.

Instead of "printing these peaks, printing these peaks, printing these peaks," I "put all the peaks in what to print," just to be extremely clear. Easy enough, but now I'm trying to achieve the same thing in 3D, and I have a big problem.

The problem is that I use the Project View Model matrix to place and render my models, which is a common approach to rendering a model in 3D space.

For each model on the screen, I need to pass the MVP matrix to the shader so that I can use it to convert each vertex to the desired position.

If I made the conversion outside the shader, it would be executed by the processor, which I do not understand very well for obvious reasons.

But the problem is there. I need to pass the matrix to the shader, but the matrix is ​​different for each model.

Therefore, I can not do the same with 2d sprites, because changing the shader shape requires drawing every time.

I hope I understand, maybe you have a good idea that I didn’t have, or you already had the same problem. I know there is a solution somewhere, because in an engine like Unity, you can use the same shader for several models and get away with one draw call

+5
source share
3 answers

There is a function just like what you are looking for, and it is called instancing . With instancing, you save n matrices (or something else you need) in Uniform Buffer and call glDrawElementsInstanced to draw n copies. In the shader, you get additional input gl_InstanceID , with which you index in Uniform Buffer to get the matrix you need for this particular instance.

Read more about instincts here: https://www.opengl.org/wiki/Vertex_Rendering#Instancing

+7
source

The answer depends on whether the vertex data for each element is identical or not. If so, you can use instancing, as in @orost's answer, using glDrawElementsInstanced and gl_InstanceID in the vertex shader, and this method should be preferred.

However, if each vertex model requires different vertex data (which is often the case), you can do it with a single draw call. To do this, you would add another stream to your vertex data using glVertexAttribPointer (and glEnableVertexAttribArray ). This additional stream will contain the index of the matrix within the uniform buffer that the vertex should use when rendering - therefore, each cell in the VBO will have the same index in the additional stream. A single buffer contains the same data as in the instancing setting.

Note that this method may require additional CPU processing if you need to re-execute batch processing β€” for example, the object in the batch should no longer be displayed. If this process is often required, it is necessary to determine whether the dosage elements are really useful or not.

+1
source

Besides including and adding another vertex attribute as some object identifier, I would also like to mention one more strategy (which requires modern OpenGL):

The ARB_multi_draw_indirect extension (in the kernel since GL 4.3) adds indirect drawing commands. These commands set their parameters (number of vertices, starting index, etc.) directly from another buffer object. Using these functions, you can create many different objects with a single draw call.

However, since you still need some state for each object, such as transformation matrices, this function is not enough. But in combination with ARB_shader_draw_parameters (and not basically GL yet) you get the parameter gl_DrawID , which will be increased by one for each individual object in one indirect call with multiple attractiveness. This way you can index some UBOs, or TBOs, or SSBOs (or something else), where you store data for each object.

0
source

All Articles