glVertexAttribPointer 澄清 [英] glVertexAttribPointer clarification
问题描述
只是想确保我正确理解这一点(我会在 SO Chat 上问,但它已经死在那里了!):
Just want to make sure I understand this correctly (I'd ask on SO Chat, but it's dead in there!):
我们有一个顶点数组,我们通过绑定它使其成为当前"
然后我们有一个缓冲区,我们将其绑定到目标
然后我们通过 glBufferData
填充那个 Target它基本上填充了绑定到该目标的任何内容,即我们的 Buffer
然后我们调用 glVertexAttribPointer
,它描述了数据的布局方式——数据是绑定到 GL_ARRAY_BUFFER
的任何东西并且这个描述符被保存到我们原来的 Vertex Array
We've got a Vertex Array, which we make "current" by binding it
then we've got a Buffer, which we bind to a Target
then we fill that Target via glBufferData
which essentially populates whatever was bound to that target, i.e. our Buffer
and then we call glVertexAttribPointer
which describes how the data is laid out -- the data being whatever is bound to GL_ARRAY_BUFFER
and this descriptor is saved to our original Vertex Array
(1) 我的理解正确吗?
文档 对所有内容之间的相关性有点稀疏.
(1) Is my understanding correct?
The documentation is a little sparse about how everything correlates.
(2) 是否有某种默认的顶点数组?因为我忘记/省略了 glGenVertexArrays
和 glBindVertexArray
并且我的程序在没有它的情况下运行良好.
(2) Is there some kind of default Vertex Array? Because I forgot/omitted glGenVertexArrays
and glBindVertexArray
and my program worked fine without it.
我错过了一个步骤...glEnableVertexAttribArray
.
I missed a step... glEnableVertexAttribArray
.
(3) 调用glVertexAttribPointer
时是否与Vertex Array绑定了Vertex Attrib,然后我们可以随时通过glEnableVertexAttribArray
启用/禁用那个attrib,不管当前绑定的是哪个顶点数组?
(3) Is the Vertex Attrib tied to the Vertex Array at the time glVertexAttribPointer
is called, and then we can enable/disable that attrib via glEnableVertexAttribArray
at any time, regardless of which Vertex Array is currently bound?
或(3b) 调用glEnableVertexAttribArray
时是否与Vertex Array绑定的Vertex Attrib,因此我们可以通过调用glEnableVertexAttribArray<将同一个Vertex Attrib添加到多个Vertex Array中/code> 在不同的时间,当不同的顶点数组被绑定时?
Or (3b) Is the Vertex Attrib tied to the Vertex Array at the time glEnableVertexAttribArray
is called, and thus we can add the same Vertex Attrib to multiple Vertex Arrays by calling glEnableVertexAttribArray
at different times, when different Vertex Arrays are bound?
推荐答案
有些术语有点不对:
- A
Vertex Array
只是一个包含顶点数据的数组(通常是一个float[]
).它不需要绑定到任何东西.不要与Vertex Array Object
或 VAO 混淆,我稍后会讲到 - 一个
Buffer Object
,在存储顶点时通常被称为Vertex Buffer Object
,或者简称VBO,就是你所谓的Buffer
. - 没有任何东西被保存回顶点数组,
glVertexAttribPointer
的工作方式与glVertexPointer
或glTexCoordPointer
的工作方式完全一样,只是你得到的不是命名属性提供一个数字来指定您自己的属性.您将此值作为index
传递.您所有的glVertexAttribPointer
调用都会排队等待您下次调用glDrawArrays
或glDrawElements
.如果您绑定了 VAO,VAO 将存储您所有属性的设置.
- A
Vertex Array
is just an array (typically afloat[]
) that contains vertex data. It doesn't need to be bound to anything. Not to be confused with aVertex Array Object
or VAO, which I will go over later - A
Buffer Object
, commonly referred to as aVertex Buffer Object
when storing vertices, or VBO for short, is what you're calling just aBuffer
. - Nothing gets saved back to the vertex array,
glVertexAttribPointer
works exactly likeglVertexPointer
orglTexCoordPointer
work, just instead of named attributes, you get to provide a number that specifies your own attribute. You pass this value asindex
. All yourglVertexAttribPointer
calls get queued up for the next time you callglDrawArrays
orglDrawElements
. If you have a VAO bound, the VAO will store the settings for all your attributes.
这里的主要问题是您将顶点属性与 VAO 混淆了.顶点属性只是为绘图定义顶点、texcoords、法线等的新方法.VAO 存储状态.我将首先解释如何使用顶点属性进行绘图,然后解释如何使用 VAO 减少方法调用的数量:
The main issue here is that you're confusing vertex attributes with VAOs. Vertex attributes are just the new way of defining vertices, texcoords, normals, etc. for drawing. VAOs store state. I'm first going to explain how drawing works with vertex attributes, then explain how you can cut down the number of method calls with VAOs:
- 您必须先启用一个属性,然后才能在着色器中使用它.例如,如果您想将顶点发送到着色器,您很可能会将其作为第一个属性 0 发送.因此,在渲染之前,您需要使用
glEnableVertexAttribArray(0);
. - 既然启用了一个属性,您需要定义它要使用的数据.为此,您需要绑定您的 VBO -
glBindBuffer(GL_ARRAY_BUFFER, myBuffer);
. - 现在我们可以定义属性 -
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
.参数顺序:0是你定义的属性,3是每个顶点的大小,GL_FLOAT
是类型,GL_FALSE
表示不对每个顶点进行归一化,最后 2 个零表示顶点上没有步幅或偏移. - 用它画一些东西 -
glDrawArrays(GL_TRIANGLES, 0, 6);
- 接下来你绘制的东西可能不会使用属性 0(实际上它会,但这是一个例子),所以我们可以禁用它 -
glDisableVertexAttribArray(0);
- You must enable an attribute before you can use it in a shader. For example, if you want to send vertices over to a shader, you're most likely going to send it as the first attribute, 0. So before you render, you need to enable it with
glEnableVertexAttribArray(0);
. - Now that an attribute is enabled, you need to define the data it's going to use. In order to do so you need to bind your VBO -
glBindBuffer(GL_ARRAY_BUFFER, myBuffer);
. - And now we can define the attribute -
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
. In order of parameter: 0 is the attribute you're defining, 3 is the size of each vertex,GL_FLOAT
is the type,GL_FALSE
means to not normalize each vertex, the last 2 zeros mean that there's no stride or offset on the vertices. - Draw something with it -
glDrawArrays(GL_TRIANGLES, 0, 6);
- The next thing you draw may not use attribute 0 (realistically it will, but this is an example), so we can disable it -
glDisableVertexAttribArray(0);
将其包裹在 glUseProgram()
调用中,您就拥有了一个可以正确使用着色器的渲染系统.但是假设您有 5 个不同的属性,顶点、texcoords、法线、颜色和光照贴图坐标.首先,您将对这些属性中的每一个进行单个 glVertexAttribPointer
调用,并且您必须事先启用所有属性.假设您按照我列出的方式定义属性 0-4.您可以像这样启用所有这些:
Wrap that in glUseProgram()
calls and you have a rendering system that works with shaders properly. But let's say you have 5 different attributes, vertices, texcoords, normals, color, and lightmap coordinates. First of all, you would be making a single glVertexAttribPointer
call for each of these attributes, and you'd have to enable all the attributes beforehand. Let's say you define the attributes 0-4 as I have them listed. You would enable all of them like so:
for (int i = 0; i < 5; i++)
glEnableVertexAttribArray(i);
然后您必须为每个属性绑定不同的 VBO(除非您将它们全部存储在一个 VBO 中并使用偏移量/步幅),然后您需要进行 5 个不同的 glVertexAttribPointer
调用,来自 glVertexAttribPointer(0,...);
to glVertexAttribPointer(4,...);
分别用于顶点到光照贴图坐标.
And then you would have to bind different VBOs for each attribute (unless you store them all in one VBO and use offsets/stride), then you need to make 5 different glVertexAttribPointer
calls, from glVertexAttribPointer(0,...);
to glVertexAttribPointer(4,...);
for vertices to lightmap coordinates respectively.
希望这个系统本身就有意义.现在我将转到 VAO 来解释如何使用它们来减少执行此类渲染时的方法调用次数.请注意,没有必要使用 VAO.
Hopefully that system alone makes sense. Now I'm going to move on to VAOs to explain how to use them to cut down on the number of method calls when doing this type of rendering. Note that using a VAO is not necessary.
Vertex Array Object
或 VAO 用于存储所有 glVertexAttribPointer
调用的状态和每个 glVertexAttribPointer
的目标 VBO代码>进行了调用.
A Vertex Array Object
or VAO is used to store the state of all the glVertexAttribPointer
calls and the VBOs that were targeted when each of the glVertexAttribPointer
calls were made.
您通过调用glGenVertexArrays
生成一个.要将您需要的所有内容存储在 VAO 中,请使用 glBindVertexArray
将其绑定,然后执行完整的绘制调用.所有 draw 绑定调用都会被 VAO 拦截并存储.您可以使用 glBindVertexArray(0);
You generate one with a call to glGenVertexArrays
. To store everything you need in a VAO, bind it with glBindVertexArray
, then do a full draw call. All the draw bind calls get intercepted and stored by the VAO. You can unbind the VAO with glBindVertexArray(0);
现在当你想绘制对象时,你不需要重新调用所有的 VBO 绑定或 glVertexAttribPointer
调用,你只需要用 glBindVertexArray<绑定 VAO/code> 然后调用
glDrawArrays
或 glDrawElements
并且您将绘制完全相同的东西,就像您进行所有这些方法调用一样.之后您可能也想解除绑定 VAO.
Now when you want to draw the object, you don't need to re-call all the VBO binds or the glVertexAttribPointer
calls, you just need to bind the VAO with glBindVertexArray
then call glDrawArrays
or glDrawElements
and you'll be drawing the exact same thing as though you were making all those method calls. You probably want to unbind the VAO afterwards too.
解除绑定 VAO 后,所有状态都会恢复到绑定 VAO 之前的状态.我不确定您在绑定 VAO 时所做的任何更改是否保留,但这可以通过测试程序轻松解决.我想您可以将 glBindVertexArray(0);
视为绑定到默认"VAO...
Once you unbind the VAO, all the state returns to how it was before you bound the VAO. I'm not sure if any changes you make while the VAO is bound is kept, but that can easily be figured out with a test program. I guess you can think of glBindVertexArray(0);
as binding to the "default" VAO...
更新:有人让我注意到实际绘制调用的必要性.事实证明,在设置 VAO 时,您实际上并不需要进行完整的绘制调用,只需进行所有绑定即可.不知道为什么我之前认为有必要,但现在已经修复了.
Update: Someone brought to my attention the need for the actual draw call. As it turns out, you don't actually need to do a FULL draw call when setting up the VAO, just all the binding stuff. Don't know why I thought it was necessary earlier, but it's fixed now.
这篇关于glVertexAttribPointer 澄清的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!