【问题标题】:Render multiple models in OpenGL with a single draw call在 OpenGL 中使用单个绘图调用渲染多个模型
【发布时间】:2015-06-14 21:54:04
【问题描述】:

我构建了一个 2D 图形引擎,并为它创建了一个批处理系统,因此,如果我有 1000 个具有相同纹理的精灵,我可以通过一次调用 openGl 来绘制它们。

这是通过将具有相同纹理的所有精灵的所有顶点放入单个 vbo 顶点数组来实现的。

不是“打印这些顶点,打印这些顶点,打印这些顶点”,而是“将所有顶点放在一起,打印”,只是为了非常清楚。 很简单,但现在我正在尝试在 3D 中实现同样的效果,但我遇到了一个大问题。

问题是我使用模型视图投影矩阵来放置和渲染我的模型,这是在 3D 空间中渲染模型的常用方法。

对于屏幕上的每个模型,我需要将 MVP 矩阵传递给着色器,以便我可以使用它来将每个顶点转换到正确的位置。

如果我要在着色器之外进行转换,它将由 cpu 执行,我不是一个好主意,原因很明显。

但问题出在那儿。我需要将矩阵传递给着色器,但是对于每个模型,矩阵都是不同的。

所以我不能像使用 2d 精灵那样做,因为更改着色器制服每次都需要绘制。

我希望我已经很清楚了,也许你有一个我没有的好主意,或者你已经遇到了同样的问题。我知道某处有一个解决方案,因为在像 Unity 这样的引擎中,您可以为多个模型使用相同的着色器,并且只需一次绘制调用就可以逃脱

【问题讨论】:

    标签: opengl 3d shader


    【解决方案1】:

    存在与您正在寻找的功能完全一样的功能,称为实例化。通过实例化,您将 n 矩阵(或您需要的任何其他内容)存储在统一缓冲区中并调用 glDrawElementsInstanced 以绘制 n 副本。在着色器中,您会得到一个额外的输入 gl_InstanceID,您可以使用它来索引统一缓冲区以获取该特定实例所需的矩阵。

    您可以在此处阅读有关实例化的更多信息:https://www.opengl.org/wiki/Vertex_Rendering#Instancing

    【讨论】:

    • 很棒的答案!我很想自己实现类似的东西,但我真的不知道怎么做,这正是我想要的
    【解决方案2】:

    答案取决于每个项目的顶点数据是否相同。如果是,您可以像@orost 的回答那样使用实例化,在顶点着色器中使用glDrawElementsInstancedgl_InstanceID,并且应该首选该方法。

    但是,如果每个 3D 模型需要不同的顶点数据(这种情况经常发生),您仍然可以使用单个绘图调用来渲染它们。为此,您将使用glVertexAttribPointer(和glEnableVertexAttribArray)将另一个流添加到您的顶点数据中。这个额外的流将包含顶点在渲染时应该使用的统一缓冲区中的矩阵的索引 - 因此 VBO 中的每个网格在额外的流中将具有相同的索引。统一缓冲区包含与实例化设置中相同的数据。

    请注意,如果您需要重做批处理,此方法可能需要一些额外的 CPU 处理 - 例如,不应再渲染批处理中的对象。如果经常需要这个过程,应该确定批处理项目是否真的有益。

    【讨论】:

    • 迄今为止我得到的最佳答案,它真的帮助我理解了如何处理它......也许我会尝试编写一个自动选择实例化或这种方法的系统......我可以保存与网格的顶点数。然后,每次我向系统添加一个网格时,如果网格的数量与之前的网格相同,我保持一个 bool isInstance = true,但是一旦我得到不同数量的顶点,我将其设置为 false。在帧结束时,我使用一种或另一种方法进行渲染,具体取决于 bool 值
    • 你觉得合理吗?
    • @user3578051 重要的不仅是顶点的数量,还有实际的内容。实例化基本上只是多次绘制相同的网格。大概你会改变实例之间的统一数据。在这种方法中,顶点缓冲区数据的内容可以在实例之间完全不同。一个可以同时处理(并且更喜欢实例化)的系统可能是正确的方法,除非你的批次每帧都改变。
    • 嗯....等一下,我会告诉你我理解的内容,你告诉我我做错了什么。这个想法是将所有顶点放在一个 vbo 中(每个顶点包含位置、颜色、法线和其他任何内容)。假设我添加了一个立方体,然后添加了一个巨大的网格(总共 1000 个顶点)。我的 vbo 将有 36+1000 个顶点。然后我有一个包含 MVP 矩阵的统一缓冲区,在这种情况下是两个。我的理解是有一个长度为 1036 的缓冲区,其中前 36 个是指向第一个矩阵的指针,其他指向第二个矩阵的指针。对吗?
    • 如果这是正确的,我的想法是:1)我会给每个网格一个标识符(因为你说单独的顶点数量是不够的) 2)每次我添加到批次,我将模型与它自己的纹理一起放入批处理中 3)如果 id 不同,我将切换到非实例化 4)在帧结束时,我根据我需要的方法以两种方式渲染采用。可以吗?
    【解决方案3】:

    除了实例化和添加另一个顶点属性作为对象 ID 之外,我还想提一下另一种策略(不过需要现代 OpenGL):

    扩展 ARB_multi_draw_indirect(自 GL 4.3 起在核心中)添加了 indirect 绘图命令。这些命令确实直接从另一个缓冲区对象获取它们的参数(顶点数、起始索引等)。使用这些函数,可以通过一次绘制调用绘制许多不同的对象。

    但是,由于您仍然需要一些每个对象的状态,例如转换矩阵,因此该功能还不够。但是结合ARB_shader_draw_parameters(还没有在核心GL 中),你会得到gl_DrawID 参数,在一次mult draw 间接调用中,每个对象都会加一。这样,您可以索引到一些 UBO、TBO 或 SSBO(或其他)存储每个对象数据的位置。

    【讨论】:

      猜你喜欢
      • 2022-11-30
      • 1970-01-01
      • 1970-01-01
      • 2016-10-23
      • 1970-01-01
      • 2015-01-19
      • 2020-02-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多