Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to render numerous objects in openGL efficiently?

Tags:

opengl

The question is broad so I will try to make it as precise as I can.

I coded program that loads and renders just one model with several textures. Due to fact that I worked with single model I was able to form all buffers, enable all vertex attrib arrays, bind all vertex arrays and bind and set active textures just once prior to loop that was responsible for drawing.

Therefore my program on each iteration of drawing loop was executing just single line- glDrawArrays. Can the same be done in case of numerous objects or I do need to form all buffers, enable attrib arrays, bind and set active textures, set shader programs and so on on each iteration of drawing loop (that means sending tons of data to video card that should be slow)?

like image 627
qwertyu uytrewq Avatar asked May 05 '16 19:05

qwertyu uytrewq


People also ask

Does OpenGL render?

OpenGL (Open Graphics Library) is a cross-language, cross-platform application programming interface (API) for rendering 2D and 3D vector graphics. The API is typically used to interact with a graphics processing unit (GPU), to achieve hardware-accelerated rendering.


1 Answers

It is unclear what exactly you are looking for - and how many objects we are talking about.

Therefore my program on each iteration of drawing loop was executing just single line- glDrawArrays. Can the same be done in case of numerous objects or I do need to form all buffers, enable attrib arrays, bind and set active textures, set shader programs and so on on each iteration of drawing loop (that means sending tons of data to video card that should be slow)?

Yes, that would be slow. Buffer objects allow you to store data in a way that is accessible to the GL. In the ideal case, the GL can decide to store that data directly in the VRAM (although you never have total control over that with OpenGL). So if you have static, unchanging mesh data, uploading it once is the way to go. It might also be useful to coalesce the data of many small objects in a single buffer.

You can use Vertex Array Objects (VAOs) storing the vertex attribute pointers and enables, so at draw time, you can just bind the VAO and issue the draw call. Thus, the basic approach for rendering multiple objects would be

// ... at initinialization
for each object:
    create and upload VBO(s) and index buffers
    create and upload textures
    create and initialize VAO

// at draw time
for each object:
    bind VAO
    bind texture(s)
    set all other object-related OpenGL state
    (like switching programs, setting unforms for
     the model matrix, base colors, ...)
    glDraw*(...)

If you only draw a couple of hundred objects per frame, you will probably not run into performance problems using this method. (Don't get me wrong here, I'm only talking of the overhead of a single draw call per object here, not the rendering costs for the actual objects - if your objects have millions of vertices, or you are generating lots of fragments, you can still get the performance down with very few or a single object. In that case, you need a more clever strategy to efficiently render your objects itself, e.g. by applying Level-of-Detail methods)

In general, there are two main approaches to improve the performance of such a render loop:

  1. Reduce the number of state changes.

    Switching different GL states implies performance penalties. A standard approach is grouping the objects per shader program, texture and so that you can draw multiple objects without having expensive state switches inbetween.

    Have a look at this answer to a related question for the relative costs of different state changes.

  2. Increase the number of objects drawn by a single draw call. In a way, this also implies approach 1, as you can't switch OpenGL state during the draw call. However, with modern GL, you can for example use array textures and put the textures for a different objects into a single texture object (you can also do that without texture arrays by using texture atlasses, and you can combine both).

    Other very interesting features in this regard are

    • instanced rendering (rendering many instances of the same object, with some properties varying per instance, like the model matrix)
    • indirect rendering (putting the arguments for several draw calls into another buffer and execute it with a single glMultiDrawElementsIndirect() call. Since the source data comes from a buffer object, you can take this as far as generating the arguments for the draw calls on the fly directly on the GPU, for example via a compute shader.

In recent years, there have been proposed some strategies to efficiently render many objects, which are known under the title Approaching Zero Driver Overhead (AZDO).

like image 73
derhass Avatar answered Oct 20 '22 11:10

derhass