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

3
创建时,VAO 仅跟踪 VBO 索引(通过 glBindVertexBuffer),还是也跟踪绑定到这些索引的 VBO 名称?如果我在 VAO 创建期间使用 glVertexAttribBinding 指定绑定索引为 0,那么我能否在绘制调用之前将不同的 VBO 绑定到索引 0 并使用该 VBO 的内容,或者它总是使用在创建 VAO 时绑定到索引 0 的任何 VBO?我之所以问这个问题,是因为我发现很多示例在调用 glVertexAttribFormatglVertexAttribBinding 之前都会调用 glBindVertexBuffer,如果 VAO 仅跟踪索引,则这不应该是必要的(因为绑定索引已在 glVertexAttribBinding 中给出)。
2个回答

5
如您所知,这是OpenGL 4.3中引入的相对较新的功能。根据规范,属性和缓冲区之间的映射现在具有一定的间接性:
1. 每个属性都有一个属性,指定它使用哪个绑定索引。 2. 缓冲区绑定到绑定索引。
您可以将此视为两个表格:一个定义了从属性索引到绑定索引的映射,另一个定义了从绑定索引到缓冲区名称的映射。这两个表格都是VAO状态的一部分。
我认为这些可以完全独立地指定,并且可以按任何顺序进行指定。 glVertexAttribBinding()建立第一个关联,即属性索引和绑定索引之间的关联。 glBindVertexBuffer()建立了绑定索引和缓冲区名称之间的关联。
规范中的状态表确认了这一理解。 GL 4.4规范中的表23.4标题为“顶点数组对象状态”,列出了以下内容:
- VERTEX_ATTRIB_BINDING,可以使用glGetVertexAttribiv()查询,是给定属性索引的绑定索引的值。 - VERTEX_BINDING_BUFFER,可以使用glGetIntegeri_v()查询,是给定绑定索引的缓冲区名称的值。
基于此,回答您的具体问题:
创建时,VAO是否仅跟踪VBO索引(通过glBindVertexBuffer),还是跟踪绑定到这些索引的VBO名称?
它们都会跟踪。
我是否可以在绘制调用之前将不同的VBO绑定到索引0,并使用该VBO的内容?
是的,如果您将不同的VBO绑定到绑定索引0,则具有绑定索引0的所有属性都将使用该VBO的内容。
我发现很多示例在调用glVertexAttribFormat和glVertexAttribBinding之前先调用glBindVertexBuffer,如果VAO仅跟踪索引,则这不应该是必需的。
VAO跟踪所有这些调用设置的状态,因此使用它们作为设置VAO的一部分是有意义的。在VAO中跟踪整个顶点设置状态是拥有VAO的主要目的。它允许您在初始化期间仅设置一次状态,然后在绘制调用之前仅需要单个调用glBindVertexArray()即可再次设置整个状态。

如果VAO跟踪分配给索引的VBO名称,那么它就不需要跟踪索引。创建VAO后,引用该缓冲区索引的任何顶点属性现在都可以忘记缓冲区索引值,只记住缓冲区索引名称(实际上,在调用glVertexAttribBinding之后,它可以忘记索引值)。无论哪种方式,我的困惑都来自于OpenGL官方文档中的这一部分:https://www.opengl.org/wiki/Vertex_Specification#Multibind_and_separation。从中可以看到:“...在快速切换多个缓冲区的同时保持相同的格式...” - jorgander
我不确定如何更清楚地解释。你说“它不需要跟踪索引”。嗯,你可能认为这是不必要的,但这就是它的规定方式,所以它是这样工作的。它使你发现的功能得以实现。间接寻址允许您将相同的缓冲区索引分配给多个属性,然后通过绑定不同的缓冲区到该索引的单个调用来更改所有这些属性使用的缓冲区。 - Reto Koradi
我猜术语不太清晰,而且一些教程/文档似乎存在冲突。从你的回答中:“缓冲区绑定到绑定索引。” - 这里的“绑定”和“绑定”这些词是多余的吗?如果不是,除了VBO绑定到它们之外,什么使“绑定索引”绑定?短语“缓冲区绑定到索引”是否等同于您想要表达的内容? - jorgander
根据我之前的评论:“作为开发人员,快速在多个缓冲区之间切换以提取数据时保持相同的格式通常很有用。要实现这一点...”。这意味着您可以在VAO创建后将不同的缓冲区绑定到索引,并且VAO将在绘制调用期间识别它。然而,在http://www.arcsynthesis.org/gltut/Positioning/Tutorial%2005.html中指出:“当您调用glVertexAttribPointer时,OpenGL会获取*此调用时*绑定到GL_ARRAY_BUFFER的任何缓冲区,并将其与给定的顶点属性相关联。”这两者似乎矛盾。 - jorgander
“binding index”是规范和手册中用于这些索引的术语。在我的回答中,我尽力遵循官方术语。尽管我写的内容对我来说似乎非常清晰,但我可能会尝试添加另一种解释方法。 - Reto Koradi
glVertexAttribPointer()是定义属性的传统方式。我们在这里讨论的是OpenGL 4.3中引入的新的更灵活的方法。如果符合您的需求,您显然可以使用更简单的glVertexAttribPointer(),因为它仍然可用于最新版本的OpenGL。 - Reto Koradi

-1
答案是它们不存储顶点名称,只存储索引。如果您将不同的VBO绑定到不同的VAO中的相同索引,则必须在使用第一个VAO的任何glDraw调用之前重新绑定第一个VBO。
至少在我的Macbook Pro上:
渲染器:NVidia GeForce GT 650M OpenGL Engine 版本:4.1 NVIDIA-10.0.43 310.41.05f01

2
你的问题涉及到OpenGL 4.3及以上版本才支持的功能。你是如何在仅支持OpenGL 4.1的配置上进行测试的? - Reto Koradi
正确,我没有在4.3+上进行测试,因此无法将顶点缓冲绑定到除0以外的索引。但是,我认为如果VAO在4.1中不跟踪VBO名称,它们在4.3+中也不会跟踪它们。但这就是为什么我包括了我的系统规格;你的情况可能会有所不同。 - jorgander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接