glVertexAttribDivisor 和 glVertexBindingDivisor 有什么区别?

What is the difference between glVertexAttribDivisor and glVertexBindingDivisor?

我一直在寻找将属性与任意顶点分组相关联的方法,起初实例化似乎是我实现此目的的唯一方法,但后来我偶然发现 this question and this answer 状态:

However what is possible with newer versions of OpenGL is setting the rate at which a certain vertex attribute's buffer offset advances. Effectively this means that the data for a given vertex array gets duplicated to n vertices before the buffer offset for a attribute advances. The function to set this divisor is glVertexBindingDivisor.

(强调我的)

这对我来说好像答案是声称我可以划分顶点数而不是实例数。但是,当我查看 glVertexBindingDivisor's documentation and compare it to glVertexAttribDivisor 时,它们似乎都指的是发生在 实例 上的划分,而不是 顶点 上发生的划分。例如在 glVertexBindingDivisor 的文档中它指出:

glVertexBindingDivisor and glVertexArrayBindingDivisor modify the rate at which generic vertex attributes advance when rendering multiple instances of primitives in a single draw command. If divisor is zero, the attributes using the buffer bound to bindingindex advance once per vertex. If divisor is non-zero, the attributes advance once per divisor instances of the set(s) of vertices being rendered. An attribute is referred to as instanced if the corresponding divisor value is non-zero.

(强调我的)

那么这两个函数之间的实际区别是什么?

So what is the actual difference between these two functions?

现代 OpenGL 有 两个 不同的 API 用于指定顶点属性数组及其特性。传统的glVertexAttribArray和朋友,其中glVertexAttribDivisor也是一部分。

随着 ARB_vertex_attrib_binding(从 GL 4.3 开始在核心中),引入了一个新的 API,它将顶点格式与指针分开。预计切换数据指针会很快,而切换顶点格式可能会更昂贵。新的 API 允许分别明确地控制两个方面,而旧的 API 总是同时设置这两个方面。

对于新的 API,引入了一个新层:缓冲区绑定点。 (有关更多详细信息,请参阅 OpenGL wiki。)glVertexBindingDivisor 指定此类绑定点的属性实例除数,因此它在概念上等同于新 [=28= 的 glVertexAttribDivisor 函数].

好的,先讲一点背景故事。

从 OpenGL 4.3/ARB_vertex_attrib_binding(又名:glVertexBindingDivisor 的来源,所以这是相关的),VAO 在概念上分为两部分:描述单个顶点格式的数组属性的数据价值,以及描述如何获取数据数组(缓冲区对象、偏移量、步幅和除数)的缓冲区绑定点数组。顶点格式指定其数据来自哪个缓冲区绑定点,这样多个属性就可以从同一个数组中获取数据(即:交错)。

当 VAO 分为这两部分时,旧的 APIs 根据新系统重新定义。因此,如果您使用属性索引调用 glVertexAttribPointer,此函数将为给定 index 处的格式设置顶点格式数据,并将设置缓冲区绑定状态(缓冲区对象、字节偏移量等) ) 同样 index。现在,这是两个独立的 VAO 状态数据数组(顶点格式和缓冲区绑定);此函数只是在两个数组中使用相同的索引。

但由于顶点格式和缓冲区绑定现在 分开 glVertexAttribPointer 也相当于说索引 index 处的顶点格式得到它的数据来自索引 index 处的缓冲区绑定。这很重要,因为那不是自动的; vertex_attrib_binding 的重点是一个索引处的顶点格式可以使用来自不同索引的缓冲区绑定。因此,当您使用旧的 API 时,它会通过将格式 index 链接到绑定 index.

来将自身重置为旧的行为

现在,这与除数有什么关系?好吧,因为我刚才说的那件事就是他们之间唯一的区别。

glVertexAttribDivisor 是用于设置除数的旧式 API。它采用属性索引,但它作用于作为缓冲区绑定点一部分的状态(实例化是每个数组构造,而不是现在每个属性构造)。这意味着该函数假定(在新系统中)index 处的属性从 index.

处的缓冲区绑定点获取其数据

我刚才说的有点假。它强制这个"assumption"通过直接设置顶点格式来使用那个缓冲区绑定点。也就是说,它执行与 glVertexAttribPointer 相同的最后一步。

glVertexBindingDivisor 是现代函数。它没有传递属性索引;它被传递了一个缓冲区绑定索引。因此,它不会更改属性的缓冲区绑定索引。

所以 glVertexAttribDivisor 完全等同于:

void glVertexAttribDivisor(GLuint index, GLuint divisor)
{
  glVertexBindingDivisor(index, divisor);
  glVertexAttribBinding(index, index);
}

显然,glVertexBindingDivisor 不会做最后一部分。