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

Posted

技术标签:

【中文标题】OpenGL 顶点数组对象是存储顶点缓冲区名称和索引,还是只存储索引?【英文标题】:Do OpenGL Vertex Array Objects store vertex buffer names and indices, or only indices? 【发布时间】:2014-11-05 21:59:22 【问题描述】:

创建时,VAO 是否仅跟踪 VBO 索引(通过glBindVertexBuffer),或者哪些 VBO 名称绑定到这些索引?如果我在创建 VAO 期间使用glVertexAttribBinding 指定绑定索引 0,我可以在绘制调用之前将不同的 VBO 绑定到索引 0 并让它使用该 VBO 的内容,还是总是使用任何VBO 在创建 VAO 时绑定到索引 0?

我之所以问,是因为我发现很多示例在调用 glVertexAttribFormat 和 glVertexAttribBinding 之前调用 glBindVertexBuffer,如果 VAO 仅跟踪索引,则不需要这样做(因为绑定索引在 @ 中给出987654326@).

【问题讨论】:

【参考方案1】:

您可能知道,这是 OpenGL 4.3 中引入的相当新的功能。按照我阅读规范的方式,属性和缓冲区之间的映射现在具有一定程度的间接性:

    每个属性都有一个属性,用于指定它使用的绑定索引。 缓冲区绑定到绑定索引。

您可以将其视为两张表,一张定义从属性索引到绑定索引的映射,另一张定义从绑定索引到缓冲区名称的映射。这两个表都是 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 的内容。

我发现很多示例在调用 glVertexAttribFormatglVertexAttribBinding 之前调用 glBindVertexBuffer,如果 VAO 仅跟踪索引,则不需要这样做

VAO 跟踪所有这些调用设置的状态,因此将所有这些调用用作设置 VAO 的一部分确实有意义。跟踪 VAO 中的整个顶点设置状态是首先拥有 VAO 的主要目的。它允许您在初始化期间设置一次状态,然后您只需调用一次glBindVertexArray() 即可在绘制调用之前再次设置整个状态。

【讨论】:

如果 VAO 跟踪分配给索引的 VBO 名称,则它不需要跟踪索引。在创建 VAO 之后,任何引用该缓冲区索引的顶点属性现在都可以忘记缓冲区索引值并只记住缓冲区索引名称(实际上,在调用 glVertexAttribBinding 之后它可以忘记索引值)。无论哪种方式,我的困惑都来自“官方”opengl 文档的这一部分:opengl.org/wiki/Vertex_Specification#Multibind_and_separation。从此:“......在多个缓冲区之间快速切换时保持相同的格式......” 我不知道如何更清楚地解释它。您是说“它不需要跟踪索引”。好吧,你可能认为它没有必要,但它就是这样指定的,所以它就是这样工作的。它启用了您所描述的功能。间接允许您将相同的缓冲区索引分配给多个属性,然后通过将不同缓冲区绑定到此索引的单个调用来更改所有这些属性使用的缓冲区。 我猜术语不清楚,加上一些教程/文档似乎有冲突。从您的回复中:“缓冲区绑定到绑定索引。” - 这里的“绑定”和“绑定”这两个词是多余的吗?如果不是,除了 VBO 绑定到它们之外,是什么使“绑定索引”绑定? “缓冲区绑定到索引”这句话是否等同于您要说的? 来自我之前的评论:“作为开发人员,在保持相同格式同时在多个缓冲区之间快速切换以从中提取数据时通常很有用。要实现这一点......”。这意味着您可以在创建 VAO 后将不同的缓冲区绑定到索引,并且 VAO 将在绘制调用期间识别它。但是,来自arcsynthesis.org/gltut/Positioning/Tutorial%2005.html:“当您调用 glVertexAttribPointer 时,OpenGL 会获取 在此调用时绑定到 GL_ARRAY_BUFFER 的任何缓冲区,并将其与给定的顶点属性相关联。”。这两个似乎是矛盾的。 “绑定索引”是规范和手册页中用于这些索引的术语。我尽力在回答中遵循官方术语。稍后我可能会尝试添加另一种方法来解释这一点,即使我写的内容对我来说似乎很清楚。【参考方案2】:

答案是它们不存储顶点名称,只存储索引。如果您将不同的 VBO 绑定到不同 VAO 中的同一索引,则必须在使用第一个 VAO 的任何 glDraw 调用之前再次重新绑定第一个 VBO。

至少在我的 Macbook Pro 上:

渲染器:NVidia GeForce GT 650M OpenGL 引擎

版本:4.1 NVIDIA-10.0.43 310.41.05f01

【讨论】:

您的问题是关于仅在 OpenGL 4.3 及更高版本中可用的功能。您是如何在仅支持 OpenGL 4.1 的配置上对其进行测试的? 正确,我没有在 4.3+ 上进行测试,因此无法将顶点缓冲区绑定到 0 以外的索引。但是,我假设如果 VAO 在 4.1 中不跟踪 VBO 名称,他们赢了也不要在 4.3+ 中跟踪它们。但这就是我包含系统规格的原因; YMMV。

以上是关于OpenGL 顶点数组对象是存储顶点缓冲区名称和索引,还是只存储索引?的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL顶点数组对象不记录VBO和IBO绑定

OpenGL 顶点数组/缓冲区对象

在OpenGL中,除非我再次重新指定顶点属性,否则为啥我的缓冲区对象不会绘制?

我的OpenGL学习进阶之旅介绍 顶点数组对象VAO并实战一下

我的OpenGL学习进阶之旅介绍顶点缓冲区对象VBO和元素数组缓冲区对象EBO,并对比使用VBO和不使用VBO绘制三角形的效果

OpenGL复制顶点缓冲区对象