VAO 是不是同时记住 EBO/IBO(元素或索引)和 VBO?

Posted

技术标签:

【中文标题】VAO 是不是同时记住 EBO/IBO(元素或索引)和 VBO?【英文标题】:Does a VAO remember both a EBO/IBO (elements or indices) and a VBO?VAO 是否同时记住 EBO/IBO(元素或索引)和 VBO? 【发布时间】:2013-06-27 00:28:28 【问题描述】:

我的代码运行正常,但这可能是巧合,我不想稍后再纠缠于错误,所以我尽量保持它的整洁:

我执行以下操作来初始化网格:

    生成并绑定 VBO 和缓冲区数据 生成和绑定 IBO 和缓冲区数据 生成和绑定 VAO 绑定与之前相同的 VBO,在 1 中生成。 启用我需要的顶点属性数组并设置顶点属性指针 再次绑定 IBO(不知道为什么) BindVertexArray 回到 0,这样我就不会弄乱我刚刚创建的 VAO

据我了解,VAO 将存储我启用的顶点属性数组的状态。它还将存储我绑定的 VBO 和 IBO。由于我通过将顶点数组绑定回 0 “关闭”了 VAO 上的所有操作,因此我确保没有其他代码会弄乱我的 VAO。所以如果这没问题,我只需要渲染:

    绑定 VAO 绘制元素 解除绑定 VAO(绑定到 0)

这应该带来 AttribArray 状态以及存储的 VBO 和 IBO。我的问题是:

A. 设置 VertexAttribPointers 后是否需要绑定 IBO?如果有,为什么?

B. VAO 是否真的存储 VBO IBO?我听说它只存储绑定的 last 缓冲区,这意味着我必须像这样渲染:

    绑定 VAO 绑定 VBO 绘制元素 解除绑定 VAO

但这没有任何意义,为什么在不存储两个缓冲区对象的情况下使用 VAO?不就是绑定 VBO 和 IBO,然后绘制元素而不绑定 VAO 一样吗?

提前感谢您的帮助。

代码如下:

初始化

// generate VBO
glGenBuffers(1, &m_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, m_vertices.size()*sizeof(GLfloat), m_vertices.data(), GL_STATIC_DRAW);

// generate IBO
glGenBuffers(1, &m_ibo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size()*sizeof(unsigned short), m_indices.data(), GL_STATIC_DRAW);

// generate VAO
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);

// set the vertex attribute pointer
glEnableVertexAttribArray(0);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,vertexSize,reinterpret_cast<const GLvoid*>(0));         

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo);
glBindVertexArray(0);

绘图

glBindVertexArray(m_vao);
glDrawElements(GL_TRIANGLES,size,GL_UNSIGNED_SHORT,reinterpret_cast<const GLvoid*>(0));
glBindVertexArray(0);

另外,这样写是不是更干净:

    生成和绑定 VAO 生成并绑定 IBO 和 BufferData 生成并绑定 VBO 和 BufferData EnableVertexAttribArrays 我需要并设置 VertexAttribPointers 解除绑定 VAO(绑定到 0)

看起来更干净,但我不知道结果是否相同,特别是在第 4 步和第 5 步之间缺少 IBO 绑定。

【问题讨论】:

【参考方案1】:

解决您的问题:

A. 设置了 VertexAttribPointers 后是否需要绑定 IBO?如果有,为什么?

没有。您可以先绑定元素数组(术语中的 IBO),然后再执行顶点属性,但一般来说,它们是 VAO 中的单独绑定。例如,您可以绑定您的 IBO 以及多个 VBO,并使用 IBO 中的数据使用glDrawElements(和变体)进行渲染,或者使用仅使用 VBO 中的顺序顶点数据的glDrawArrays(和变体)进行渲染- 渲染命令决定是否使用 IBO。

B. VAO 是否真的同时存储了 VBO 和 IBO?

是的。一个 VAO 可以存储单个 IBO 和至少 16 个 VBO 的绑定信息。

我听说它只存储绑定的 last 缓冲区,这意味着我必须像这样渲染:

绑定VAO 绑定VBO 绘制元素 解除绑定 VAO

正如您在原始帖子中所推测的那样,此陈述是不正确的,并且您包含的 VBO 的绑定是不必要的。一个 VAO 可以存储与实现相关的最大(至少 16 个)VBO 数量,每个 VBO 都可以绑定到一个顶点属性。

另外,这样写是不是更干净:

    生成和绑定 VAO 生成并绑定 IBO 和 BufferData 生成并绑定 VBO 和 BufferData EnableVertexAttribArrays 我需要并设置 VertexAttribPointers 解除绑定 VAO(绑定到 0)

是的。正如您所指出的,这仅允许您在三个命令中进行绑定、渲染和清理。

真的,这就是 VAO 的全部意义,收集所有这些绑定和顶点属性关联,这样您就可以一次性完成所有的管道工作,然后一劳永逸。

【讨论】:

我遇到了一个问题,表明 VAO 没有记住绑定的缓冲区(这就是我发现这个的原因)。我有两种不同的形状,使用不同的 VAO、VBO 和 IBO 集。如果我只在渲染之前绑定 VAO,那么第一个初始化的形状会被第二个形状破坏。绑定 VBO 和 IBO 也解决了这个问题。这让我很困惑,因为我发现的所有文档和教程都表明我不需要这样做。有什么解释吗? VAO 绑定不记得 VBO 绑定(在某种意义上,调用 glBindBuffer(GL_ARRAY_BUFFER, VBO) 不会影响 VAO 的状态;并且更改绑定 VAO 不会影响哪个 VBO 是边界)。然而,当您调用 glVertexAttribPointer 时,VAO 会存储 VBO 的数据所在的位置(因此在使用 VAO 进行绘图时不需要绑定 VBO)。此外,VAO 确实记得 EBO/IBO 绑定。这提供了一个很好的概述:learnopengl.com/Getting-started/Hello-Triangle 另一个很好的描述:gamedev.stackexchange.com/a/99238/35574 glEnableVertexAttribArrays 只需要在创建 VAO 时调用一次。如果你把你的draw call包装在这些里面,你就错了。

以上是关于VAO 是不是同时记住 EBO/IBO(元素或索引)和 VBO?的主要内容,如果未能解决你的问题,请参考以下文章

Android OpenGL ES 学习 – 纹理

Android OpenGL ES 学习 – 纹理

Android OpenGL ES 学习 –矩阵变换

Android OpenGL ES 学习 –矩阵变换

Android OpenGL ES 学习 – 坐标系统和实现3D效果

Android OpenGL ES 学习 – 坐标系统和实现3D效果