问题描述
我已经学习 OpenGL 三天了,我可以完成一些事情,但我感觉就像在不知道自己在做什么的情况下复制粘贴.我认真地认为我缺乏关于什么时候完全什么(VBO,属性,...)绑定到顶点数组对象(VAO)的基本理解,并且没有找到任何资源来阐明这些方面细节.
I've been learning OpenGL for three days and I can get stuff done but I feel like copy pasting without knowing what I'm doing. I seriously think I lack basic understanding about when is exactly what (VBO, attributes, ...) bound to a Vertex Array Object (VAO), and haven't found any resources that clarify these aspects in detail.
特别是,这些是我的一些问题.如果我创建一个 VAO:
In particular, these are some of my issues. If I create a VAO:
GLuint vao;
glGenVertexArrays(1, &vao);
在绑定 VAO 之前可以绑定任何东西吗?(如果我现在创建一个 VBO,它是否绑定到 VAO?)
can anything get bound to it before I bind the VAO? (if I create a VBO now, is it bound to the VAO?)
glBindVertexArray(vao);
绑定VAO后,如果我创建一个VBO:
After binding the VAO, if I create a VBO:
GLuint vbo;
glGenBuffers(1, &vbo);
它是否绑定到 VAO?还是我绑定的时候发生的?
is it bound to the VAO? Or does it happen when I bind it?
glBindVertexArray(vbo);
或者当我向它复制一些东西时?
Or maybe when I copy something to it?
如果我得到一个属性位置:
If I get an attribute location:
att = glGetAttribLocation(program_id, "name");
它是否绑定到 VAO?还是启用后发生的:
is it bound to the VAO? Or does it happen after enabling it:
glEnableVertexAttribArray(att);
... 或设置后:
glVertexAttribPointer(att, ...);
?
我猜 EBO 的行为就像 VBO 一样,所以我希望同样的规则"适用.
I guess EBOs behave just like VBOs, so I hope the same "rules" apply.
制服的行为应该像全局变量,所以它们根本不应该受到 VAO 的影响.
Uniforms should behave like globals, so they shouldn't be affected by VAOs at all.
现在,关于解除绑定:
如果我将 VBO绑定"到 VAO,然后解除绑定 VBO,它是否会与 VAO 分离?
If I "bind" a VBO to a VAO, and then unbind the VBO, does it get detached from a VAO?
如果我有一个绑定到多个 VAO 的 VBO,当我取消绑定该 VBO 时会发生什么?
If I have a VBO that is bound to multiple VAOs, what happens when I unbind that VBO?
关于释放资源:
当我删除一个 VBO 时会发生什么?它是否会从所有 VAO 中删除?或者他们仍然对那个 VBO 有悬空引用"?
What happens when I delete an VBO? Does it get deleted from all the VAOs ? Or do they still have "dangling references" to that VBO?
关于程序:
IIUC 我可以在程序之间重用 VBO.但是,如果 VAO 绑定属性和 VBO,并且属性带有程序参数,我是否可以在程序之间重用 VAO?为什么属性完全采用程序参数?
IIUC I can reuse VBOs between programs. However, if VAOs bind attributes and VBOs, and attributes take a program parameter, can I reuse VAOs between programs? Why do attributes take a program parameter at all?
关于调试:
有没有办法漂亮地打印 OpenGL 状态机?我想要一种方法来了解已链接的程序,使用哪些着色器,存在哪些 VAO,哪些 VBO 绑定到哪些 VAO,哪些属性绑定到哪些 VAO 和 VBO,它们是否已设置?他们启用了吗?那里有哪些制服...
Is there a way to pretty print the OpenGL state machine? I would like a way to know the programs that have been linked, with which shaders, which VAOs are there, which VBOs are bound to which VAOs, which attributes are bound to which VAOs and VBOs, have they been set? are they enabled? which uniforms are there...
关于绘图调用:
假设有人给了我一个 VAO,我必须绘制它.有没有办法知道我应该调用 glDrawArrays 还是 glDrawElements?我可以从 VAO 以某种方式查询这些信息吗?也许连同我存储在那里的 VBO 的大小?
Suppose someone gives me a VAO, and I have to draw it. Is there a way to know if I should be calling glDrawArrays or glDrawElements? Can I query somehow this information from a VAO? Maybe along with the sizes of my VBOs stored in there?
推荐答案
这是很多子问题.但由于这是一个经常让新 OpenGL 爱好者感到困惑的领域,让我尝试提供一些内容,希望能帮助更多人.我会故意略读一些细节,比如不是来自缓冲区的顶点属性,以避免在这里写一本书.
That's a lot of sub-questions. But since this an area that is often confusing newer OpenGL enthusiasts, let me try and provide some content that will hopefully help more people. I will intentionally skim over some details, like vertex attributes that are not sourced from a buffer, to avoid writing a book here.
要理解的关键是VAO是状态的集合.它不拥有任何数据.VBO 拥有顶点数据.另一方面,VAO 包含用于描述绘制调用从何处获取其顶点属性的所有状态.这包括,对于每个属性:
The key thing to understand is that a VAO is a collection of state. It does not own any data. It's VBOs that own vertex data. A VAO, on the other hand, contains all the state used to describe where a draw call gets its vertex attributes from. This includes, for each attribute:
- 如果已启用.
- 属性存储在哪个缓冲区中.
- 数据在缓冲区中的哪个偏移量开始.
- 后续属性之间的间距(也就是步幅).
- 数据的类型.
- 组件的数量.
另外,仅一次:
- 绑定了哪个元素数组缓冲区.
将此映射到 API 调用,以下调用会更改当前绑定的 VAO 跟踪的状态:
Mapping this to API calls, the following calls change state tracked by the currently bound VAO:
glEnableVertexAttribArray(...)
glDisableVertexAttribArray(...)
glVertexAttribPointer(...)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ...)
请注意,这不包括GL_ARRAY_BUFFER
的当前绑定.根据为特定属性调用 glVertexAttribPointer()
时绑定的缓冲区,间接跟踪用于每个属性的缓冲区.
Note that this does not include the current binding of GL_ARRAY_BUFFER
. The buffer used for each attribute is tracked indirectly, based on which buffer was bound when glVertexAttribPointer()
is called for the specific attribute.
这应该为特定的子问题奠定基础:
This should set the basis for the specific sub-questions:
如果我创建了一个 VAO,在绑定 VAO 之前可以绑定任何东西吗?
没有.需要先绑定 VAO,然后才能修改其中存储的任何状态.
No. The VAO needs to be bound before you can modify any state stored in it.
(如果我现在创建一个 VBO,它是否绑定到 VAO?)
没有.您可以在绑定 VAO 之前绑定 VBO,并使用 glBufferData()
用数据填充 VBO.VBO 本质上只是一个愚蠢的数据容器.但是在VAO中跟踪的任何一种顶点属性设置都只能在绑定VAO之后进行.
No. You can bind a VBO before you bind a VAO, and fill the VBO with data using glBufferData()
. A VBO is essentially just a dumb data container. But any kind of vertex attribute setup tracked in the VAO can only be done after the VAO is bound.
如果我得到一个属性位置,它是否绑定到 VAO?
不,glGetAttribLocation()
只是顾名思义,即获取属性位置.它不会改变任何状态.您将使用属性位置进行诸如 glEnableVertexAttribArray()
和 glVertexAttribPointer()
之类的调用.
No, glGetAttribLocation()
only does what the name suggests, which is get an attribute location. It does not change any state. You will use the attribute locations for calls like glEnableVertexAttribArray()
and glVertexAttribPointer()
.
或者是在启用之后发生...还是在设置之后发生
当您在给定 VBO 绑定时调用 glVertexAttribPointer()
时,属性与给定 VBO 的关联就建立起来了.
The association of an attribute with a given VBO is established when you call glVertexAttribPointer()
while the given VBO is bound.
我猜 EBO 的行为就像 VBO 一样,所以我希望同样的规则"适用.
大部分,但不完全.GL_ELEMENT_ARRAY_BUFFER
绑定是存储在 VAO 中的状态的一部分.这是有道理的,因为只有一个元素数组缓冲区用于绘制调用(而顶点属性可能来自多个不同的数组缓冲区),并且没有单独的调用指定使用来自当前绑定元素数组缓冲区的索引数据",如 glVertexAttribPointer()
指定使用当前绑定数组缓冲区中的顶点数据".而是在您调用 glDrawElements()
时隐式发生.所以在draw call的时候需要绑定元素数组buffer,这个绑定是VAO状态的一部分.
Mostly, but not entirely. The GL_ELEMENT_ARRAY_BUFFER
binding is part of the state stored in the VAO. This makes sense because there is only one element array buffer used for a draw call (while the vertex attributes could come from multiple different array buffers), and there is no separate call that specifies "use the index data from the currently bound element array buffer", like glVertexAttribPointer()
specifies "use the vertex data from the currently bound array buffer". Instead that happens implicitly when you call glDrawElements()
. Therefore, the element array buffer needs to be bound at the time of the draw call, and this binding is part of the VAO state.
制服的行为应该像全局变量,所以它们根本不应该受到 VAO 的影响.
正确.制服与着色器程序相关,而不是 VAO.
Correct. Uniforms are associated with shader programs, not VAOs.
如果我将 VBO绑定"到 VAO,然后解除绑定 VBO,它是否会与 VAO 分离?
没有.我相信上面的解释已经涵盖了这一点.
No. I believe this is already covered by the explanations above.
如果我有一个绑定到多个 VAO 的 VBO,当我取消绑定该 VBO 时会发生什么?
因为一个 VAO 没有任何反应,多个 VAO 仍然没有反应.
Since nothing happens with one VAO, still nothing with multiple VAOs.
当我删除一个 VBO 时会发生什么?它是否会从所有 VAO 中删除?或者他们仍然对那个 VBO 有悬空引用"?
这是 OpenGL 较暗的角落之一.如果你能背诵出所有对象类型的确切删除规则(它们并不都一样),你就达到了高级……在这种情况下,VBO自动从当前绑定的VAO解除绑定>,但不是来自当前未绑定的其他 VAO.如果其他 VAO 引用了 VBO,则 VBO 将保持活动状态,直到所有这些绑定被破坏或 VAO 被删除.
This is one of the darker corners of OpenGL. If you can recite the exact deletion rules for all object types (they are not all the same), you have reached the advanced level... In this case, the VBO is automatically unbound from the currently bound VAO, but not from other VAOs that are not currently bound. If other VAOs have references to the VBO, the VBO will stay alive until all those bindings are broken, or the VAOs deleted.
但是,如果 VAO 绑定属性和 VBO,并且属性带有程序参数,我是否可以在程序之间重用 VAO?
是的,您可以将一个 VAO 用于多个程序.程序状态和VAO状态是独立的.程序中的顶点属性位置指定哪个顶点属性用于为顶点着色器中的每个 attribute
/in
变量提供值.
Yes, you can use a VAO for multiple programs. Program state and VAO state are independent. The vertex attribute location in the program specifies which vertex attribute is used to source the values for each attribute
/in
variable in the vertex shader.
只要多个程序对相同的属性使用相同的位置,就可以使用相同的 VAO.为了使这成为可能,您可能希望通过使用顶点着色器的 layout (location=...)
指令或通过调用 glBindAttribLocation()
指令来指定每个程序的属性位置代码>在链接程序之前.
As long as multiple programs use the same locations for the same attributes, you can use the same VAO. To make this possible, you may want to specify the attribute location for each program by using the layout (location=...)
directive the vertex shader, or by calling glBindAttribLocation()
before linking the program.
有没有办法漂亮地打印 OpenGL 状态机?
glGet*()
调用可让您检索几乎所有当前的 OpenGL 状态.不方便,但都可以.许多平台/供应商还提供开发者工具,让您可以查看程序执行中给定点的 OpenGL 状态.
There are glGet*()
calls that let you retrieve pretty much all of the current OpenGL state. Not convenient, but it's all available. Many platforms/vendors also provide developer tools that allow you to look at OpenGL state at a given point in your program execution.
假设有人给了我一个 VAO,我必须绘制它.有没有办法知道我应该调用 glDrawArrays 还是 glDrawElements?
这是一个不寻常的场景.大多数情况下,您创建 VAO,因此您知道如何绘制它.或者,如果其他人创建了它,您会要求他们绘制它.但是如果你真的需要它,你可以用 glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ...)
获取当前绑定的元素数组缓冲区.
This is an unusual scenario. Most of the time, you create the VAO, so you know how to draw it. Or if somebody else created it, you would ask them to draw it. But if you really need it, you can get the currently bound element array buffer with glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ...)
.
这篇关于什么时候绑定到 VAO?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!