排序子网格/VBO

Posted

技术标签:

【中文标题】排序子网格/VBO【英文标题】:Sorting submeshes/VBO 【发布时间】:2013-05-14 06:06:01 【问题描述】:

我有一个需要渲染的关卡。它被分成数百个子网格,其中包含有关哪个子网格可以看到哪个子网格的信息。每个级别都有一个纹理池,所有这些子网格都可以引用。所有子网格都有按纹理排序的顶点。这是一个例子。

子网 1

索引[1, 3, 4, 5, 6, 2, 7, 8, 10...] 纹理 1

索引[12, 15, 16, 12, 13, 19] 纹理 2

当可能有 1000 个子网格和 20 个纹理时,即使考虑到可见性,纹理交换的数量也会变得荒谬。

我在 VBO 中按顺序排列了所有子网格。我一直在尝试找出优化渲染和消除所有不必要的纹理交换的最佳方法。如果我只是对 VBO 进行排序,我会丢失子网格连接,并且可见性数据变得无用。或者有更好的方法吗?

或者我应该根据可见性每帧创建一个索引列表还是太慢了?

编辑:这是我当前设置的细分。

所有顶点的 VBO 按顺序从子网格 1 纹理 1,子网格 1 纹理 2,一直到子网格 n,纹理 n。

我有一个 IBO,它只是所有网格的所有索引,以相同的相对顺序。

当我渲染时,我会参考子网格并有一个起始索引和计数,它们是该子网格和纹理的 IBO 中的起始索引,然后是具有该纹理的数字的计数。

这本质上就是为什么我要进行如此多的交换,这确实会减慢速度。

【问题讨论】:

是否有可能将这些纹理中的一些合并到一个更大的纹理中? @Aleks 有可能。不过,我宁愿不使用纹理图集。 “纹理交换”是什么意思?您至少应该有 32 个纹理单元可供使用。 @Grimmy 每次在子网格中获得新纹理时,我都会使用 glBindTexture 绑定下一个纹理。 我会遍历可见性数据,生成要渲染的内容列表,然后通过渲染状态缩短该列表。最后,整个 vbo 只是一个按顺序访问的大数组(关于子网格),您不会在这里造成很多缓存未命中。 【参考方案1】:

所有顶点的 VBO 按顺序从子网格 1 纹理 1,子网格 1 纹理 2,一直到子网格 n,纹理 n。

你的问题。 “子网格”应该是单个渲染调用,而不是多个渲染调用。每个“子网格”都应该等同于“子网格 X,纹理 Y”对。特定的子网格使用一组特定的 glVertexAttribPointer 调用、glBindTexture 调用和单个 glDrawElements 调用来渲染。

此方案中的每个子网格都使用一个特定纹理。因此,您可以按照它们使用的纹理对绘制顺序进行排序。因此,所有使用纹理 1 的子网格都可以同时渲染,它们之间只需调用一个 glBindTexture

为了获得最佳性能,由于所有“子网格”都使用相同的缓冲区(因此,它们都使用相同的顶点格式是一个很好的改变),您还应该尝试use glDrawElementsBaseVertex 来渲染特定的“子网格” ",而不是为每个子网格单独调用glVertexAttribPointer

这里的想法是让你的 VBO 有效地成为一个长数组,每个子网格的所有顶点都在同一个数组中。每个子网格存储其索引数据的基本偏移量、要渲染的顶点计数和基本顶点偏移量,该偏移量表示子网格数据在大数组中从哪个索引开始。

在开始渲染所有子网格时,您进行一个glVertexAttribPointer 调用。然后就是 glBindTextureglDrawElementsBaseVertex 从那时起。您永远不会为每个子网格调用glVertexAttribPointer。您可以使用 BaseVertex 来定义每个子网格的数据在 VBO 中的来源。

【讨论】:

【参考方案2】:

修改后:

您需要一个中间步骤,它会缩短网格以根据纹理 ID 进行渲染。您需要针对每个子网格进行绘图调用 (glDrawElements)。 考虑到这一点,您可以执行以下操作:

基本上你做同样的事情,但不是渲染东西,而是将该网格存储到它需要的纹理的显示列表中。

创建一个名为显示列表的对象,其中包含要渲染的子网格(指针)数组。创建一个此类对象的数组,每个纹理 ID 1 个。

在你的算法中,在渲染之前,填充上面的结构并且不要渲染任何东西。

当您解决了可见和需要渲染的内容后,遍历显示列表数组 (0....num_textures) 并渲染该纹理的子网格。 提示:在此步骤中,您可以找到相邻的子网格,只需调用一次glDrawElements

优化了上面的思路。

这很容易实现,并将最大限度地减少纹理状态的变化。

【讨论】:

【参考方案3】:

当然你也会有一个索引缓冲区,你传入glDrawElements。我建议重新排列索引缓冲区中的内容。或者,如果您不想修改数据,请使用正确的偏移量和顶点数分别为每个子网格调用 glDrawElements,并按纹理对网格进行排序。

【讨论】:

除非他正在绘制他需要多次调用 glDrawElements 的所有内容。可能每个网格都有一个,或者找到连续的网格并只触发一个长度较大的 glDrawElements。 datenwolf,如果我要重新排列索引缓冲区中的内容,是否需要指向索引中正确位置的指针?我现在这样做的方式是我有我的大 VBO 和我的 IBO。每个网格都有一个 VBO 索引和许多与该纹理匹配的顶点。因此,如果我想在 submesh 8 中渲染所有 texture3,我会使用 glDrawElements 并从存储的索引开始。我怎样才能使它更灵活,以便当我对 IBO 进行排序时,我仍然可以拥有正确的位置?我是否应该更智能地构建我的 IBO,并在保持排序后的偏移量时对其进行排序?

以上是关于排序子网格/VBO的主要内容,如果未能解决你的问题,请参考以下文章

配置 VAO 以处理具有多个网格的 VBO

使用 VBO 的 OpenGL 贪心网格划分

多个网格、多个 VBO、多个 VAO、OpenGL 4.1

在 Vbo 中显示具有不同点大小的网格?

为啥绘制我的 OpenGL-ES VBO 网格会阻止其他三角形显示?

如何为大型网格实现 VBO 并获得流畅的动画?