我已经学习OpenGL三天了,我可以完成任务,但我觉得在不知道我在做什么的情况下复制粘贴。我认真地认为我缺乏基本的理解,什么时候什么(VBO,属性,...)绑定到顶点数组对象(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,它是否会与VAO分离?
如果我有一个绑定到多个VAO的VBO,当我取消绑定VBO时会发生什么?
关于释放资源:
删除VBO后会发生什么?是否会从所有VAO中删除?或者他们是否仍然对该VBO“悬挂参考”?
关于节目:
IIUC我可以在程序之间重用VBO。但是,如果VAO绑定属性和VBO,并且属性采用程序参数,我可以在程序之间重用VAO吗?为什么属性会占用程序参数?
关于调试:
有没有办法漂亮打印OpenGL状态机?我想知道一种方法来了解已经链接的程序,哪些着色器,哪些VAO在哪里,哪些VBO绑定到哪些VAO,哪些属性被绑定到哪些VAO和VBO,它们是否被设置?他们启用了吗?有哪些制服......
关于绘制电话:
假设某人给了我一个VAO,我必须画它。有没有办法知道我是否应该调用glDrawArrays或glDrawElements?我能以某种方式从VAO查询这些信息吗?也许还有存储在那里的VBO的大小?
答案 0 :(得分:38)
这是很多子问题。但是,由于这个区域经常让新的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绑定时调用glVertexAttribPointer()
时,建立属性与给定VBO的关联。
我猜EBO的行为就像维多利亚时代一样,所以我希望同样的规则"规则"应用
主要是,但不完全。 GL_ELEMENT_ARRAY_BUFFER
绑定是存储在VAO中的状态的一部分。这是有道理的,因为只有一个元素数组缓冲区用于绘制调用(而顶点属性可能来自多个不同的数组缓冲区),并且没有单独的调用指定"使用当前绑定的索引数据元素数组缓冲区",如glVertexAttribPointer()
指定"使用来自当前绑定数组缓冲区的顶点数据"。相反,当您致电glDrawElements()
时,这种情况会发生。因此,元素数组缓冲区需要在绘制调用时绑定,并且此绑定是VAO状态的一部分。
制服应该像全局一样,所以它们不应该受到VAO的影响。
正确。制服与着色器程序相关联,而不是VAO。
如果我"绑定" VBO到VAO,然后取消绑定VBO,它是否与VAO分离?
没有。我相信上面的解释已经涵盖了这一点。
如果我有一个绑定到多个VAO的VBO,当我取消绑定VBO时会发生什么?
由于一个VAO没有任何反应,多个VAO仍然没有发生任何事情。
删除VBO后会发生什么?是否会从所有VAO中删除?或者他们仍然有"悬挂参考文献"那个VBO?
这是OpenGL的一个黑暗角落。如果您可以背诵所有对象类型的确切删除规则(它们并非完全相同),则您已达到高级级别...在这种情况下,VBO将自动从当前绑定的VAO ,但不来自当前未绑定的其他VAO。如果其他VAO引用了VBO,则VBO将保持活动状态,直到所有这些绑定被破坏或VAO被删除。
但是,如果VAO绑定属性和VBO,并且属性采用程序参数,我可以在程序之间重用VAO吗?
是的,您可以将VAO用于多个程序。程序状态和VAO状态是独立的。程序中的顶点属性位置指定使用哪个顶点属性为顶点着色器中的每个attribute
/ in
变量提供值。
只要多个程序对相同的属性使用相同的位置,就可以使用相同的VAO。为了实现这一点,您可能希望通过使用顶点着色器的layout (location=...)
指令或在链接程序之前调用glBindAttribLocation()
来为每个程序指定属性位置。
有没有办法漂亮地打印OpenGL状态机?
有glGet*()
个调用可以让您检索几乎所有当前的OpenGL状态。不方便,但它全部可用。许多平台/供应商还提供开发人员工具,允许您在程序执行的给定点查看OpenGL状态。
假设某人给了我一个VAO,我必须画它。有没有办法知道我是否应该调用glDrawArrays或glDrawElements?
这是一种不寻常的情况。大多数情况下,您创建VAO,因此您知道如何绘制它。或者,如果其他人创建了它,你会要求他们绘制它。但是如果你确实需要它,你可以使用glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ...)
来获取当前绑定的元素数组缓冲区。
答案 1 :(得分:13)
可以在State Tables section of the spec
中找到VAO表在伪代码中看起来像:
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];
};
值得注意的是程序状态(一个是绑定的,统一值等)。