是否值得通过设置一个附加的每个顶点属性作为对象索引来最小化绘制调用的次数?

Is it worthy to minimize the count of draw call by setting up an addition per-vertex attribute used as object index?

我正在尝试为具有许多 material 和网格的场景资产组织绘图调用。我想的攻略:

但是,有这么多不同的网格体具有不同的dynamic transformations,因此它们无法组织到一个实例绘制调用中。相反,每个具有不同动态变换的网格都需要一个绘制调用,从而导致多个绘制调用。

如果我使用一个存储缓冲区来保存每个网格的变换并使用网格的唯一object index索引到存储缓冲区中,得到相应的变换。但是对象索引不能被实例化,因为具有不同动态变换的网格不能被实例化。因此,每个顶点都需要一个额外的顶点属性来实现该方法。同时,由于一个网格的图元索引相同,因此会占用一些内存用于冗余图元索引数据。

这个方法值得实施吗?或者有没有更实用的方法来减少绘制调用的次数?

因此您有许多要绘制的对象。其中一些对象使用来自不同对象的不同网格,但有时它们共享相同的底层网格。但无论如何,一个特定的对象集合都共享相同的顶点格式和源缓冲区 (VAO),以及相同的着色器逻辑和着色器资源。

但这些对象确实有不同的 per-object 信息。这就是区分此类集合中每个对象的原因。您引用了变换信息,但实际上,它可能是各种 per-object 状态(纹理等)。

您的目标是通过一次绘制调用绘制集合中的所有对象。所以你需要一种方法来区分着色器中的不同对象,以及一种使用这些不同对象的方法。

您最终需要的是为每个对象提供一个索引,并向着色器提供一个数组,着色器将使用该数组来获取该索引并获取其 per-object 信息。特定对象的所有顶点需要获得相同的索引,如果两个不同的对象从同一个网格绘制,则这两个对象的着色器需要不同的索引。

这就是 multi-draw 和 gl_DrawID 的用途。 gl_DrawID 是 GL 4.6 中的核心,但在被纳入核心之前,它作为扩展名 ARB_shader_draw_parameters 存在了一段时间。这是 pretty widely available.

Multi-draw rendering commands 允许您通过单个函数调用发出多个绘图调用。这些绘制调用都必须使用相同的基元类型,但您之前已经有了这个限制,所以这没问题。绘图命令数组中的每一个绘图都是按从前到后的顺序执行的。

因此,每次抽奖都有一个索引,从 0 到抽奖次数。您可以通过 gl_DrawID predefined input 在顶点着色器中访问此索引。因此,您可以使用此索引从您的 per-object SSBO 数组中获取任何您想要的 per-object 信息。

Plus,gl_DrawID,以及它的任何后续派生,总是一个 dynamically uniform expression. As such, if you have an array of SSBOs (not an array within an SSBO; an array of SSBOs), array of sampler uniforms, or an array of bindless textures,你可以使用 gl_DrawID 或一些基于它的(动态统一的)表达式来访问这样的数组.所以你可以用这个索引切换纹理。

那么在这个过程中实例化会发生什么?虽然 multi-draw indirect 实际上允许每次绘制都有自己的实例计数和基础实例,但实例化使访问 per-object 信息变得更加复杂。每个绘制索引原本只使用一条per-object信息,所以每个绘制命令都可以直接使用自己的索引。但是如果一个绘制命令可以消耗多条 per-object 数据,那么每个绘制命令都必须知道之前的绘制命令消耗了多少实例。

实际上这并不难,shader_draw_parameters 包含解决方案:gl_BaseInstance。 multi-draw 间接命令具有实例计数和基本实例值。实例计数直接映射到 gl_InstanceID 字段,但基本实例值不会添加到此。相反,您可以直接使用 gl_BaseInstance.

访问它

你可以利用这个来计算一个特定的指数。 multi-draw 命令中每次绘制的基本实例值需要是先前绘制命令(包括仅绘制 1 个实例的命令)的所有实例计数的总和。因此,您的 per-object 索引不再是 gl_DrawID;它是 gl_BaseInstance + gl_InstanceID.

但是,如果你这样做,你就失去了gl_DrawID动态统一的优势。 gl_InstanceIDgl_BaseInstance 不需要动态统一,因此您无法使用它们来索引 SSBO 数组或采样器甚至无绑定纹理。所以如果你想根据绘制调用切换纹理,你只能使用数组纹理。