OpenGL 顶点缓冲区绑定点可以在不同的 VAO 之间重复使用吗?

Posted

技术标签:

【中文标题】OpenGL 顶点缓冲区绑定点可以在不同的 VAO 之间重复使用吗?【英文标题】:Can OpenGL vertex buffer binding points be reused across different VAOs? 【发布时间】:2015-03-23 21:06:55 【问题描述】:

假设我使用新的(从 OpenGL 4.3 开始)glBindVertexBuffer 机制设置了两个 VAO:

glGenVertexArrays(1, &vaoIndex0);
glGenVertexArrays(1, &vaoIndex1);

...

glBindVertexArray(vaoIndex0)
glBindVertexBuffer(bindingIndex0, ...)
glEnableVertexAttribArray(0)
glVertexAttribFormat(0, ...)
glVertexAttribBinding(0, bindingIndex0)
...
glBindVertexArray(0)

...

glBindVertexArray(vaoIndex1)
glBindVertexBuffer(bindingIndex1, ...)
glEnableVertexAttribArray(0)
glVertexAttribFormat(0, ...)
glVertexAttribBinding(0, bindingIndex1)
...
glBindVertexArray(0)

假设两者是独立的,除非它们存在于同一个 OpenGL 上下文中;它们绑定不同的缓冲区,将用于绘制不同的东西。

bindingIndex0 是否需要与 bindingIndex1 不同?两个指数的相等(或不相等)有什么意义吗?

...

编辑:

在收到答案后,我开始明白,对于真正知道什么是“顶点缓冲区绑定点”,特别是它的范围的人来说,我的问题似乎是在问一些与我的意图不同的问题。也许更好的措辞是“为了防止冲突,是否需要不遗余力地防止 OpenGL 顶点缓冲区绑定点 索引 被重用,甚至跨多个 VAO 重用?”但无论如何,现在似乎两个问题都得到了解答:不,您不能重用“绑定点”,不,您不需要以这种方式避免索引冲突。

【问题讨论】:

【参考方案1】:

所有这些调用都会修改 VAO 状态。所以不,您不能跨 VAO 重复使用这些设置。您当然可以在多个 VAO 中将它们设置为相同,但您必须在设置每个 VAO 时进行一次必要的状态设置调用。

您在代码片段中使用的bindingIndex0bindingIndex1 值没有任何特殊含义。它们只是在您使用glBindVertexBuffer() 绑定到绑定索引的缓冲区与您指定为使用该绑定索引的属性之间建立连接。

唯一的条件是绑定索引必须小于你可以查询为MAX_VERTEX_ATTRIB_BINDINGS的值,保证至少为16。由于这些调用修改每个VAO状态,你绝对可以使用相同的多个 VAO 的绑定索引。

查看这些较新的状态设置调用的一种方法是它们引入了以前不可用的间接级别:

如果没有这些调用,您可以在绑定所需缓冲区的同时调用glVertexAttribPointer(),在顶点属性和缓冲区之间建立直接连接。 通过这些较新的调用,顶点属性现在与绑定索引建立了连接,该绑定索引是通过glVertexAttribBinding() 建立的。然后将绑定索引连接到一个缓冲区,该缓冲区由glBindVertexBuffer() 建立。

换句话说,旧式的连接是:

attribute index --> buffer

这些 4.3+ 调用的新样式:

attribute index --> buffer index --> buffer

这种新灵活性的一个优点是您可以通过一次调用将新缓冲区绑定到多个属性。只要所有这些属性都具有相同的缓冲区索引,您只需调用一次glBindVertexBuffer() 即可为所有属性指定一个新缓冲区。

半正式定义

以下根本不是官方符号。我只是编的。但我认为通过写下一些伪数据结构来更正式地定义关系可能会很有用。

假设每个 VAO 包含两个数组来捕获上面解释的连接:

struct VAO 
    ...
    uint bufferIndexBindings[MAX_VERTEX_ATTRIB_BINDINGS];
    uint attribBufferIndices[MAX_VERTEX_ATTRIBS];

这里讨论的两个调用会像这样修改这个结构:

glBindVertexBuffer(uint bindingIndex, uint buffer, ...) 
    CurrentVAO.bufferIndexBindings[bindingIndex] = buffer;


glVertexAttribBinding(uint attribIndex, uint bindingIndex) 
    CurrentVAO.attribBufferIndices[attribIndex] = bindingIndex;

然后使用此状态获取索引为attribIndex 的给定属性的缓冲区:

CurrentVAO.bufferIndexBindings[CurrentVAO.attribBufferIndices[attribIndex]]

这也说明了我在上面解释的间接性,它在这里显示为对状态表的两个级别的查找。

【讨论】:

那么,由 bindingIndex0 索引的“绑定点”是否甚至不存在于其 VAO 之外?我基本上是在尝试询问绑定点索引的含义范围。 (我最初假设绑定点由 GL 上下文拥有,而不是由单个 VAO 拥有,所以我需要确保没有两个 VAO 尝试使用相同的绑定点索引。但后来我想到这可能不是真的,所以我在这里要求尝试确认。) @mjwach 我在答案中添加了另一部分,以不同的方式解释关系。绑定索引实际上只是每个 VAO 中的状态表的索引。它纯粹是 VAO 本地的。 好的。所以我的问题有点没有意义。您可以“重用”索引,因为它们只是数字,而且数字当然是全局的,但是您不能重用绑定点(询问是否可以),因为它们是 VAO-当地的。 “但我认为通过写下一些伪数据结构来更正式地定义关系可能很有用”这几乎是我所需要的......我希望每个 OpenGL 函数文档页面都能做到这一点。很难操作没有人向您展示定义的数据结构。

以上是关于OpenGL 顶点缓冲区绑定点可以在不同的 VAO 之间重复使用吗?的主要内容,如果未能解决你的问题,请参考以下文章

初识OpenGL (-)VAO顶点数组对象

初识OpenGL (-)VAO顶点数组对象

VAO 与 VBO 和 IBO 的手动绑定

OpenGL 顶点数组对象是存储顶点缓冲区名称和索引,还是只存储索引?

OpenGL ES之VBOEBO与VAO的说明和使用

快速修改 OpenGL VAO 缓冲区