如何有效地在openGL中渲染大量对象?

qwe*_*ewq 4 opengl

这个问题是广泛的,所以我将尽我所能使其更加精确。

我编写了程序,仅加载和渲染具有多个纹理的模型。由于我使用单一模型,因此能够在负责绘制的循环之前一次形成所有缓冲区,启用所有顶点属性数组,绑定所有顶点数组以及绑定并设置活动纹理。

因此,我的绘图循环每次迭代的程序仅执行单个line-glDrawArrays。在有大量对象的情况下可以这样做吗?或者我确实需要形成所有缓冲区,启用attrib数组,绑定和设置活动纹理,设置着色器程序等等,每次绘制循环都需要迭代(这意味着向视频发送大量数据)卡应该慢)?

der*_*ass 7

目前还不清楚您到底在寻找什么-我们正在谈论多少对象。

因此,我的绘图循环每次迭代的程序仅执行单个line-glDrawArrays。在有大量对象的情况下可以这样做吗?或者我确实需要形成所有缓冲区,启用attrib数组,绑定和设置活动纹理,设置着色器程序等等,每次绘制循环都需要迭代(这意味着向视频发送大量数据)卡应该慢)?

是的,那太慢了。缓冲区对象允许您以GL可访问的方式存储数据。在理想情况下,GL可以决定将数据直接存储在VRAM中(尽管您永远无法完全控制OpenGL的数据)。因此,如果您有静态的,不变的网格数据,则只需上传一次即可。在单个缓冲区中合并许多小对象的数据可能也很有用。

您可以使用存储顶点属性指针并启用的顶点数组对象(VAO),因此在绘制时,您可以绑定VAO并发出绘制调用。因此,渲染多个对象的基本方法是

// ... 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*(...)
Run Code Online (Sandbox Code Playgroud)

如果每帧仅绘制数百个对象,则使用此方法可能不会遇到性能问题。(不要误会我的意思,这里我只是在谈论每个对象的一次绘制调用的开销,而不是实际对象的渲染成本-如果您的对象具有数百万个顶点,或者您正在生成很多片段,您仍然可以通过很少或单个对象来降低性能。在这种情况下,您需要一种更聪明的策略来有效地呈现对象本身,例如,通过应用“细节级别”方法)

通常,有两种主要方法可改善此类渲染循环的性能:

  1. 减少状态更改次数。

    切换不同的GL状态意味着性能下降。一种标准方法是按着色器程序,纹理对对象进行分组,以便您可以绘制多个对象,而无需在它们之间进行昂贵的状态切换。

    看看这个相关问题的答案,了解不同状态变化的相对成本。

  2. 增加通过单个绘制调用绘制的对象的数量。从某种意义上说,这也意味着方法1,因为您不能在draw调用期间切换OpenGL状态。但是,使用现代GL,例如,您可以使用阵列纹理,并将不同对象的纹理放入单个纹理对象中(也可以通过使用纹理图集在不使用纹理阵列的情况下进行此操作,并且可以将两者组合在一起)。

    在这方面,其他非常有趣的功能是

    • 实例化渲染(渲染同一对象的许多实例,每个实例的某些属性不同,例如模型矩阵)
    • 间接渲染(将多个draw调用的参数放入另一个缓冲区中,并通过一个glMultiDrawElementsIndirect()调用执行。由于源数据来自缓冲区对象,因此您可以将其用作生成对draw调用的参数的范围。直接在GPU上飞行,例如通过计算着色器。

近年来,已经提出了一些策略以有效地渲染许多对象,这些策略以标题“ 接近零驱动器开销(AZDO)”而闻名