绑定到GL_ELEMENT_ARRAY_BUFFER,但未绑定VAO

Binding to GL_ELEMENT_ARRAY_BUFFER with no VAO bound

本文关键字:绑定 VAO ARRAY GL ELEMENT BUFFER      更新时间:2023-10-16

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

10.4顶点阵列对象

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

OpenGL wiki的缓冲区对象页面支持这一点:

GL_ELEMENT_ARRAY_BUFFER​

表单gl*Draw*Elements*​的所有呈现函数都将使用指针字段,作为从绑定到此的缓冲区对象开始的字节偏移量目标用于索引呈现的索引将取自缓冲区对象请注意,此绑定目标是"顶点阵列对象"状态的一部分,因此在此处绑定缓冲区之前必须绑定VAO

如果不是这样的话,那就太好了。这将使创建和管理索引缓冲区与任何特定的VAO分开变得容易。但是,如果在没有VAO绑定的情况下,只将缓冲区绑定到GL_ELEMENT_ARRAY_BUFFER是禁止的,那么唯一的选择是表示索引缓冲区的类在创建/更新它们时绑定伪VAO。

NicolBolas出色的OpenGL教程说,这种类型的使用实际上是有效的:

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER):在没有VAO绑定的情况下调用此操作不会失败。

这似乎与标准和opengl.org维基相矛盾。标准中是否有我遗漏的支持这一点的内容,或者这只是指不需要使用VAO的兼容性配置文件上下文?

如果您有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)要删除的对象。

但是,如果在没有VAO绑定的情况下,只将缓冲区绑定到GL_ELEMENT_ARRAY_BUFFER是禁止的,那么唯一的选择是表示索引缓冲区的类在创建/更新它们时绑定伪VAO。

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

例如,代替这个:

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,它的存在是为了提供一个临时目标,使缓冲区之间的复制更容易,但大多数其他目标也可以。)