glVertexAttribPointer澄清

时间:2012-01-02 19:58:06

标签: opengl

只是想确保我理解正确(我会问SO聊天,但它已经死了!):

我们有一个顶点阵列,我们通过绑定它来制作“当前” 然后我们有一个缓冲区,我们绑定到目标
然后我们通过glBufferData填充该目标 它基本上填充了与该目标绑定的任何内容,即我们的缓冲区
然后我们调用glVertexAttribPointer来描述数据是如何布局的 - 数据是绑定到GL_ARRAY_BUFFER的任何数据 并且此描述符将保存到我们原始的顶点数组

(1)我的理解是否正确?
关于一切如何相关,documentation有点稀疏。

(2)是否存在某种默认的顶点阵列?因为我忘了/省略glGenVertexArraysglBindVertexArray,我的程序在没有它的情况下工作正常。


修改:我错过了一步... glEnableVertexAttribArray

(3)在调用glVertexAttribPointer时,Vertex Attrib是否与顶点数组相关联,然后我们可以随时通过glEnableVertexAttribArray启用/禁用该属性,无论哪个顶点数组目前有约束力?

或(3b)在调用glEnableVertexAttribArray时,顶点属性是否与顶点数组相关联,因此我们可以通过在不同时间调用glEnableVertexAttribArray将相同的顶点属性添加到多个顶点数组,当绑定不同的顶点数组时?

2 个答案:

答案 0 :(得分:207)

有些术语有点过时了:

  • Vertex Array只是一个包含顶点数据的数组(通常是float[])。它不需要绑定任何东西。不要与Vertex Array Object或VAO混淆,我将在稍后讨论
  • Buffer Object,通常在存储顶点时称为Vertex Buffer Object,或简称为VBO,就是您所谓的Buffer
  • 没有任何东西被保存回顶点数组,glVertexAttribPointer的工作方式与glVertexPointerglTexCoordPointer完全相同,只需要提供一个指定属性的数字而不是命名属性。您将此值传递为index。您下次致电glVertexAttribPointerglDrawArrays时,所有glDrawElements来电都会排队等候。如果您有VAO绑定,VAO将存储您所有属性的设置。

这里的主要问题是您将顶点属性与VAO混淆。顶点属性只是为绘图定义顶点,texcoords,法线等的新方法。 VAO存储状态。我首先要解释绘图如何与顶点属性一起使用,然后解释如何使用VAO减少方法调用的数量:

  1. 必须先启用属性,然后才能在着色器中使用它。例如,如果要将顶点发送到着色器,则很可能将其作为第一个属性0发送。因此,在渲染之前,需要使用glEnableVertexAttribArray(0);启用它。
  2. 现在已启用某个属性,您需要定义它将要使用的数据。为此,您需要绑定您的VBO - glBindBuffer(GL_ARRAY_BUFFER, myBuffer);
  3. 现在我们可以定义属性 - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);。按参数顺序:0是你定义的属性,3是每个顶点的大小,GL_FLOAT是类型,GL_FALSE表示不规范化每个顶点,最后2个零意味着有顶点上没有任何步幅或偏移。
  4. 用它绘制一些东西 - glDrawArrays(GL_TRIANGLES, 0, 6);
  5. 你绘制的下一件事可能不会使用属性0(实际上它会,但这是一个例子),所以我们可以禁用它 - glDisableVertexAttribArray(0);
  6. glUseProgram()次调用中包裹它,并且您有一个可以正确处理着色器的渲染系统。但是,假设您有5种不同的属性,顶点,texcoords,法线,颜色和光照贴图坐标。首先,您将为每个属性进行一次glVertexAttribPointer调用,并且您必须事先启用所有属性。假设您定义属性0-4,因为我已将它们列出。你可以这样启用所有这些:

    for (int i = 0; i < 5; i++)
        glEnableVertexAttribArray(i);
    

    然后你必须为每个属性绑定不同的VBO(除非你将它们全部存储在一个VBO中并使用偏移/步幅),然后你需要从{{1}进行5次不同的glVertexAttribPointer调用} glVertexAttribPointer(0,...);分别为顶点到光照贴图坐标。

    希望只有这个系统才有意义。现在,我将转到VAO,解释如何使用它们来减少进行此类渲染时的方法调用次数。请注意,不需要使用VAO。

    glVertexAttribPointer(4,...);或VAO用于存储所有Vertex Array Object次调用的状态以及每次glVertexAttribPointer次调用时定位的VBO。

    通过调用glVertexAttribPointer生成一个。要在VAO中存储您需要的所有内容,请将其与glGenVertexArrays 绑定,然后执行完整的绘制调用。所有 draw 绑定调用都被VAO拦截并存储。您可以使用glBindVertexArray

    取消绑定VAO

    现在,当您想要绘制对象时,您不需要重新调用所有VBO绑定或glBindVertexArray(0);调用,只需要将VAO与glVertexAttribPointer绑定,然后调用{ {1}}或glBindVertexArray并且您将绘制完全相同的内容,就好像您正在进行所有这些方法调用一样。你可能也想在之后解开VAO。

    一旦解除了VAO的绑定,所有状态将返回到绑定VAO之前的状态。我不确定在VAO绑定时是否进行了任何更改,但是可以通过测试程序轻松找出。我想你可以认为glDrawArrays绑定到“默认”VAO ......


    更新:有人提请我注意实际绘制电话的需要。事实证明,在设置VAO时,实际上并不需要进行全部绘制调用,只需要所有绑定内容。不知道为什么我认为这是必要的,但现在已经修好了。

答案 1 :(得分:2)

要调用的API的术语和顺序确实令人困惑。更令人困惑的是各个方面 - 缓冲区,通用顶点属性和着色器属性变量如何相关联。请参阅OpenGL-Terminology以获得相当不错的解释。

此外,链接OpenGL-VBO,shader,VAO显示了一个包含必要API调用的简单示例。对于那些从立即模式转换到可编程管道的人来说,这是特别好的。

希望它有所帮助。

编辑:从下面的评论中可以看出,人们可以做出假设并得出结论。现实情况是,这对于初学者来说非常困惑。