绑定到GL_ELEMENT_ARRAY_BUFFER而没有VAO绑定

时间:2013-12-05 05:09:13

标签: c++ opengl vao

当前绑定到OpenGL中GL_ELEMENT_ARRAY_BUFFER目标的缓冲区是顶点数组对象(此处为VAO)中包含的状态的一部分。根据OpenGL 4.4核心配置文件规范,似乎在没有绑定VAO的情况下尝试更改或访问GL_ELEMENT_ARRAY_BUFFER是一个错误:

  

10.4顶点数组对象

     

...任何命令都会生成INVALID_OPERATION错误   没有顶点数组时修改,绘制或查询顶点数组状态   受约束。这发生在初始GL状态,并且可能发生在a    BindVertexArray 的结果或 DeleteVertexArrays 的副作用。

这是由OpenGL wiki的Buffer Object页面支持的:

  

GL_ELEMENT_ARRAY_BUFFER​

     

gl*Draw*Elements*​形式的所有渲染函数都将使用指针   field作为从绑定到此的缓冲区对象的开头偏移的字节   目标。用于索引渲染的索引将从缓冲区中获取   宾语。 请注意,此绑定目标是Vertex Array Objects状态的一部分,   所以在绑定缓冲区之前必须绑定一个VAO

现在,如果不是这样的话会很好。这将使得与任何特定VAO分开创建和管理索引缓冲区变得容易。但是,如果仅在没有VAO绑定的情况下将缓冲区绑定到GL_ELEMENT_ARRAY_BUFFER,则唯一的替代方法是表示索引缓冲区的类在创建/更新/等时绑定虚拟VAO。

Nicol Bolas'出色的OpenGL tutorial说这种用法实际上是有效的:

  

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER):在没有VAO限制的情况下调用此方法不会失败。

这似乎与标准和opengl.org wiki相矛盾。是否有标准支持我错过了,或者这只是指兼容性配置文件上下文中不需要使用VAO?

2 个答案:

答案 0 :(得分:0)

如果你有AMD或NV GPU,你总是可以使用EXT_direct_state_access扩展来操作缓冲区对象而不绑定它(这纯粹是一个驱动程序功能,不需要任何特殊类别的硬件)。令人遗憾的是,英特尔,梅萨和苹果公司尽管已有5年的存在,但并未打算实施这一扩展 - 懒惰的懒虫。


查看以下功能,它们将使您所描述的更容易:

  • glNamedBufferDataEXT (...)
  • glNamedBufferSubDataEXT (...)
  • glMapNamedBufferEXT (...)
  • glUnmapNamedBufferEXT (...)

现在,由于DSA的采用很少,您可能不得不为不支持它的系统编写一些回退代码。您可以通过编写具有相同功能签名的函数来重现DSA的功能,这些函数签名使用虚拟VAO来绑定VBO和IBO,以便在不支持扩展的系统上进行数据操作。您必须在使用之前跟踪VAO的绑定,并在所述函数返回之前将其恢复以消除副作用。

在一个好的引擎中,你应该隐藏VAO绑定状态,而不必从GL查询它。也就是说,不是直接使用glBindVertexArray (...),而是实现一个包装该调用的系统,因此始终知道VAO绑定到特定上下文的内容。最后,这会模拟DSA功能,其中驱动程序支持不存在 很多 更高效。如果你尝试这样的事情,你需要知道glDelete (...)函数隐含地解绑(通过绑定 0 )被删除的对象,如果它被绑定在当前上下文中。

答案 1 :(得分:0)

<块引用>

但是,如果在没有 VAO 绑定时仅将缓冲区绑定到 GL_ELEMENT_ARRAY_BUFFER 是被禁止的,那么唯一的选择是让表示索引缓冲区的类在创建/更新/等时绑定虚拟 VAO。

对旧问题的新回复,但在不需要 VAO(或干扰当前绑定的 VAO)的情况下使用索引缓冲区的简单方法是将缓冲区绑定到 GL_ELEMENT_ARRAY_BUFFER 以外的目标。< /p>

例如,而不是这个:

glBindVertexArray(vaoID);                               // ...Do I even have a VAO yet?
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferID);   // <- Alters VAO state!
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, ...);

-- 可以这样写:

glBindBuffer(GL_COPY_WRITE_BUFFER, indexBufferID);
glBufferSubData(GL_COPY_WRITE_BUFFER, ...);

(这里我随意使用了 GL_COPY_WRITE_BUFFER,它的存在是为了提供一个临时目标,使缓冲区之间的复制更容易,但大多数其他目标也可以。)