我已经学习OpenGL三天了,我可以完成一些事情,但我感觉像复制粘贴,而不知道我在做什么。我真的认为我对什么时候是什么(VBO,attributes,。。。)绑定到顶点数组对象(VAO)缺乏基本的了解,也没有找到任何详细阐明这些方面的资源。
特别是这是我的一些问题。如果创建VAO:
GLuint vao;
glGenVertexArrays(1, &vao);
在我绑定VAO之前有什么东西可以绑定它吗?(如果我现在创建一个VBO,它是否绑定到VAO?)
glBindVertexArray(vao);
绑定VAO后,如果创建VBO:
GLuint vbo;
glGenBuffers(1, &vbo);
它和VAO绑定了吗?还是在我绑定它的时候发生的?
glBindVertexArray(vbo);
或者当我复制一些东西到它上的时候?
如果我得到一个属性位置:
att = glGetAttribLocation(program_id, "name");
它和VAO绑定了吗?还是在启用后发生:
glEnableVertexAttribArray(att);
。。。或设置后:
glVertexAttribPointer(att, ...);
?
我猜EBO的行为就像VBO一样,所以我希望同样的“规则”也适用。
制服应该表现得像全球一样,所以它们根本不应该受到VAO的影响。
现在,关于解除绑定:
如果我将VBO“绑定”到VAO,然后将VBO解除绑定,VBO是否与VAO分离?
如果我有一个VBO绑定到多个VAO,当我取消绑定该VBO时会发生什么?
以及释放资源:
当我删除VBO时会发生什么?会从所有VAO中删除吗?或者他们仍然有关于VBO的“悬而未决的参考”?
和关于程序:
IIUC I可以在程序之间重用VBO。但是,如果VAO绑定属性和VBO,并且属性取一个程序参数,我可以在程序之间重用VAO吗?为什么属性要带一个程序参数呢?
和关于调试:
有没有办法漂亮地打印OpenGL状态机?我想要一种方法来了解已经链接的程序,与哪些着色器,哪些VAO在那里,哪些VBO绑定到哪些VAO,哪些属性绑定到哪些VAO和VBO,它们已经设置好了吗?它们是否已启用?有哪些制服。。。
和绘制呼叫:
假设有人给我一个VAO,我得画出来。有没有办法知道我应该调用glDrawArrays还是GLDraWelements?我可以从VAO中查询这些信息吗?也许还有我存储在里面的VBO的大小?
这是一大堆分问题。但由于这是一个经常混淆新的OpenGL爱好者的领域,让我尝试并提供一些内容,希望能帮助更多的人。我会有意略过一些细节,比如不是来源于缓冲区的顶点属性,以避免在这里写成一本书。
要理解的关键是VAO是状态的集合。它不拥有任何数据。拥有顶点数据的是VBO。另一方面,VAO包含用于描述绘图调用从何处获取顶点属性的所有状态。对于每个属性,这包括:
另外,只有一次:
将此映射到API调用,以下调用将更改当前绑定的VAO跟踪的状态:
GlenableVertexAttriBarray(。。。)
GLDisableVertexAttriBarray(。。。)
GLVertexAttribPointer(。。。)
GLBindBuffer(GL_ELEMENT_ARRAY_BUFFER,。。。)
注意,这不包括GL_ARRAY_BUFFER
的当前绑定。每个属性使用的缓冲区都是间接跟踪的,根据为特定属性调用GLVertexAttribPointer()
时绑定的缓冲区。
这应为具体的子问题奠定基础:
如果我创建了一个VAO,在绑定VAO之前,任何东西都可以绑定到它吗?
不可以。在您可以修改存储在其中的任何状态之前,需要绑定VAO。
(如果我现在创建一个VBO,它是否绑定到VAO?)
不可以。您可以在绑定VAO之前绑定VBO,并使用GLBufferData()
用数据填充VBO。VBO本质上只是一个哑数据容器。但是在VAO中跟踪的任何类型的顶点属性设置只能在VAO绑定之后进行。
如果我得到一个属性位置,它是否绑定到VAO?
不,glgetAttribLocation()
只执行名称所暗示的功能,即获取属性位置。它不会改变任何状态。您将对诸如GlenableVertexAttribArray()
和GlVertexAttribPointer()
之类的调用使用属性位置。
还是在启用它之后。。。或者在设置它之后发生
属性与给定VBO的关联是在绑定给定VBO时调用GLVertexAttribPointer()
时建立的。
我猜EBO的行为就像VBO一样,所以我希望同样的“规则”也适用。
大部分,但不是全部。GL_ELEMENT_ARRAY_BUFFER
绑定是存储在VAO中的状态的一部分。这是有意义的,因为只有一个元素数组缓冲区用于绘制调用(而顶点属性可能来自多个不同的数组缓冲区),并且没有单独的调用指定“使用当前绑定的元素数组缓冲区中的索引数据”,如GLVertexAttribPointer()
指定“使用当前绑定的数组缓冲区中的顶点数据”。相反,当您调用GLDraWelements()
时,就会隐式地发生这种情况。因此,需要在draw调用时绑定元素数组缓冲区,这种绑定是VAO状态的一部分。
制服应该表现得像全球一样,所以它们根本不应该受到VAO的影响。
正确。制服与着色程序相关联,而不是与VAO相关联。
如果我将VBO“绑定”到VAO,然后将VBO解除绑定,VBO是否与VAO分离?
不是。我相信以上的解释已经涵盖了这一点。
如果我有一个VBO绑定到多个VAO,当我取消绑定该VBO时会发生什么?
因为一个VAO不会发生任何事情,所以多个VAO也不会发生任何事情。
当我删除VBO时会发生什么?会从所有VAO中删除吗?或者他们仍然有关于VBO的“悬而未决的参考”?
这是OpenGL比较黑暗的角落之一。如果你能背诵所有对象类型的确切删除规则(它们并不都一样),你就达到了高级……在这种情况下,VBO会自动与当前绑定的VAO解除绑定,但不会与当前未绑定的其他VAO解除绑定。如果其他VAO有对VBO的引用,则VBO将保持活动状态,直到所有这些绑定被破坏或VAO被删除。
但是,如果VAO绑定属性和VBO,并且属性取一个程序参数,我可以在程序之间重用VAO吗?
是的,您可以将一个VAO用于多个程序。程序状态和VAO状态是独立的。程序中的顶点属性位置指定顶点着色器中每个attribute
/in
变量的值的来源是哪个顶点属性。
只要多个程序对相同的属性使用相同的位置,您就可以使用相同的VAO。为此,您可能需要在链接程序之前使用布局(location=...)
指令(顶点着色器)指定每个程序的属性位置,或者调用GLBindatTribLocation()
。
有没有办法漂亮地打印OpenGL状态机?
有一些glget*()
调用可以让您检索几乎所有的当前OpenGL状态。不方便,但都有。许多平台/供应商还提供开发工具,允许您查看OpenGL在程序执行中的给定点的状态。
假设有人给我一个VAO,我得画出来。有没有办法知道我应该调用glDrawArrays还是GLDraWelements?
这是一个不寻常的场景。大多数时候,您创建了VAO,因此您知道如何绘制它。或者如果是别人创造的,你会让他们画出来。但是如果确实需要,可以使用GLGetIntegerV(GL_ELEMENT_ARRAY_BUFFER_BINDING,...)
获取当前绑定的元素数组缓冲区。
VAO表可在规范的State Tables部分找到
在psuedo代码中,它看起来像:
struct VAO{
GL_INT element_array_binding; //IBO used for glDrawElements and friends
char* label;//for debugging
struct{//per attribute values
bool enabled; //whether to use a VBO for it
//corresponding to the values passed into glVertexAttribPointer call
int size;
unsigned int stride;
GL_ENUM type;
bool normalized;
bool integer; //unconverted integers
bool long; //double precision
void* offset;
int bufferBinding;//GL_ARRAY_BUFFER bound at time of glVertexAttribPointer call
int attributeDiviser; //as used for instancing
} attributes[MAX_VERTEX_ATTRIBS];
};
值得注意的是缺少程序状态(哪一个是绑定的,统一值等等)