何时应该调用glVertexAttribPointer?

42

从文档中并不清楚应该何时调用glVertexAttribPointer。看起来它是VBO初始化的一部分,但我注意到示例代码在渲染期间调用它。

glVertexAttribPointer(vertexAttributeId, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), reinterpret_cast<const GLvoid*>(offsetof(Vertex2D, m_x)));

在初始化GL_ARRAY_BUFFER时应该调用glVertexAttribPointer还是在渲染期间调用(在调用glBindBuffer之后)?


2
也许你应该问的问题是它是做什么的,而不是什么时候该被调用。如果你知道它是做什么的,那么什么时候调用它就显而易见了。如果你认为“它是VBO初始化的一部分”,那么你对这个函数的理解是非常错误的。 - Nicol Bolas
1
@NicolBolas 它存储了属性数据的偏移量(从当前绑定的缓冲区)?attributeId 是着色器中该属性位置的标识符(通过 glGetAttribLocation 检索或通过 glBindAttribLocation 设置)吗? - Mark Ingram
2
它存储属性数据的偏移量(从当前绑定的缓冲区)?它还存储用于该属性的缓冲对象(以及其格式信息和步幅数据)。非常不完整 - Nicol Bolas
顺便问一下,你最喜欢的OpenGL学习资源是什么? - Christian Rau
1
@ChristianRau 我主要阅读OpenGL网站上的文档。Nicol的网站也非常好。 - Mark Ingram
4个回答

63

函数glVertexAttribPointer指定了顶点属性的格式和源缓冲区(忽略了客户端数组的已弃用用法),该顶点属性在渲染时使用(即下一个glDraw...调用)。

现在有两种情况。您可以使用顶点数组对象(VAO),也可以不使用(尽管不使用VAO在现代OpenGL中已被弃用和不推荐/禁止使用)。如果您使用VAO,则通常会在渲染之前调用glVertexAttribPointer(以及相应的glEnableVertexAttribArray)来正确设置状态。然而,如果使用VAO,则实际上可以在VAO创建代码中调用它(以及启用函数)(通常是一些初始化或对象创建的一部分),因为它的设置存储在VAO内,而渲染时所需的全部操作只是绑定VAO并调用绘制函数。

但无论何时调用glVertexAttribPointer,都应该在其之前绑定相应的缓冲区(不管实际上是何时创建和填充的),因为glVertexAttribPointer函数将当前绑定的GL_ARRAY_BUFFER作为此属性的源缓冲区(并存储此设置,因此之后可以自由地绑定另一个VBO)。

因此,在现代OpenGL中使用VAOs(建议),通常类似于以下工作流程:

//initialization
glGenVertexArrays
glBindVertexArray

glGenBuffers
glBindBuffer
glBufferData

glVertexAttribPointer
glEnableVertexAttribArray

glBindVertexArray(0)

glDeleteBuffers //you can already delete it after the VAO is unbound, since the
                //VAO still references it, keeping it alive (see comments below).

...

//rendering
glBindVertexArray
glDrawWhatever
不使用VAO时,代码可能是这样的:
//initialization
glGenBuffers
glBindBuffer
glBufferData

...

//rendering
glBindBuffer
glVertexAttribPointer
glEnableVertexAttribArray
glDrawWhatever

1
glBindBuffer(GL_ARRAY_BUFFER, ...) 真正生效并且可以安全更改的时间点是一个常见的困惑点(这归因于VBOs被强行塞进了由向后兼容性驱动的现有客户端数组API中)。glVertexAttribBuffer函数会更有意义一些,虽然最近他们解决了这个问题(但t.niese回答中提到的那些新的函数也引入了其他混淆点)。 - Christian Rau
1
@MarkIngram 在glVertexAttribPointer之后您可以自由地解绑它(我必须搜索删除情况,但是解绑一定能够工作,其他任何情况都是驱动程序错误或者SO问题)。当然,对于GL_ELEMENT_ARRAY_BUFFER来说不同,因为该绑定直接存储在VAO中,如果要进行索引绘制,则应保持绑定状态。 - Christian Rau
1
@t.niese 谢谢,但这听起来至少应该在 glBindVertexArray(0) 之后完成。尽管如此,我不会将其添加到代码注释中(我已经讨厌它超过两行了;))。 - Christian Rau
@t.niese 嗯,这只是注释,我不觉得有必要清理它们。但如果你想的话,这是你的决定,只需尽量保持标准引用,因为我正在引用它(而且懒得将其添加到我的答案中 ;))。 - Christian Rau
2
@MarkIngram 根据评论的结论,您可以删除VBO,但是您需要先解绑VAO - Christian Rau
显示剩余16条评论

4

glVertexAttribPointer是一种不属于BufferProgram的东西,可以说它是它们之间的粘合剂。(该功能在Opengl 4.3中拆分为不同的函数VertexAttrib*FormatVertexAttribBindingBindVertexBuffer,可通过ARB_vertex_attrib_binding使用)

但如果你想说它是某个部分的一部分,我会说它是VAO的一部分,VAO存储绑定的Buffer对象、启用的属性以及如何将Buffer数据传递给Program的状态。

所以它属于设置VAO的部分。

编辑 说明顺序的简单设置:

  1. 创建/设置Buffer和创建程序
  2. 创建VAO,定义哪些属性被启用,在使用VAO时应绑定哪些缓冲区以及如何将数据传递给程序(glVertexAttribPointer

2
缓冲区、通用顶点属性和着色器属性变量之间的关联非常微妙。 glVertexAttribPointer 建立了这种关联。有关详细解释,请参见OpenGL-Terminology
此外,链接OpenGL-VBO,shader,VAO显示了一个具有必要的API调用序列的工作示例。

1

glVertexAttribPointer在大多数情况下需要调用,当适当的(即您想要使用的)VBO绑定时。然后它的最后一个参数是所述缓冲区中的偏移量。

参考手册中,最后一个参数被定义得非常好:

指定第一个通用顶点属性数组中第一个分量的偏移量,该数组位于目前绑定到GL_ARRAY_BUFFER目标的缓存的数据存储器中。初始值为0。

适当的顶点指针绑定和VBO源存储在VAO中,如果可能,请使用它们。

简短的示例(请原谅我的伪代码):

// Setup
CreateVAO(); BindVAO();
CreateVBO(); BindVBO();
VertexAttribPointer(/*id*/ 0, 3, GL_FLOAT, /*starting at offset*/ 0);

// We have specified Vertex attribute bound to location 0,
// with size of 3 floats, starting at offset 0 of VBO we've just created.        

//Draw
BindVAO();
Draw();

“在大多数情况下,必须在绑定适当的(即要使用的)VBO时调用glVertexAttribPointer。” - 嗯,实际上是相反的。 VBO的绑定应该由调用glVertexAttribPointer的时间来指导(这由VAO设置时间或在不使用VAO时的渲染时间来指导)。 - Christian Rau
1
好吧,glVertexAttribBuffer 函数会更加直观,但那是OpenGL。 - Christian Rau
@ChristianRau 我个人认为这差不多是一样的,但我不想争论语义问题;此外,社区似乎更倾向于你的答案。 - Bartek Banachewicz
@ChristianRau 我们只能希望4.4/5.0在这方面做得更好。 - Bartek Banachewicz

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