glBindVertexArrays 与 glBindBuffer 的作用是什么,它们的关系是什么?
我是 OpenGL 和图形编程的新手.到目前为止,我一直在阅读一本非常详尽且写得很好的教科书.但是,我在代码中遇到了一个我不太理解的地方,我想在我开始之前弄清楚这些行继续前进.
I'm new to OpenGL and Graphics Programming. I've been reading a textbook which has been really thorough and well-written so far.However, I've hit a point in the code that I'm not quite understanding and I'd like to make sense of these lines before I move on.
GLuint abuffer;
glGenVertexArrays(1, &abuffer);
glBindVertexArray(abuffer);
<小时>
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
这本书解释了前三行创建了一个顶点数组对象,用于将关联数据与顶点数组捆绑在一起.第二行找到一个未使用的名称(我猜是一个存储在 abuffer
中的无符号整数标识符),第三行创建对象/使其处于活动状态.
The book explains that the first three lines are creating a vertex-array object, which is used to bundle associated data with a vertex array. The second line finds an unused name (I'm guessing an unsigned integer identifier stored in abuffer
) and the third line creates the object / makes it active.
这本书解释了第 4-7 行创建了一个 缓冲对象 来存储我们的数据,第 5 行给了我们一个未使用的标识符(类似于第 2 行的顶点数组对象?),第 6 行创建缓冲区,第 7 行在 CPU 上分配足够的内存并为 GL_STATIC_DRAW
创建一个指向我们的数据(点)的指针.
The book explains that the 4th-7th lines creating a buffer object to store our data, with the 5th line giving us an unused identifier (similar to line 2 for the vertex array object?), the 6th line creating the buffer, and the 7th line allocating sufficient memory on the CPU and creating a pointer to our data (points) for GL_STATIC_DRAW
.
对象处于活动状态意味着什么?你以后什么时候使用abuffer
?一个顶点数组绑定关联数据是什么意思,这个顶点数组对象关联的数据是什么时候?
What does it mean for the object to be active? When would you subsequently use abuffer
? What does it mean for a vertex array to bundle associated data, and when was the data associated with this vertex-array object?
我对abuffer
和buffer
之间的关系感到困惑.我很困惑顶点数组与缓冲区对象的关系是什么,以及这种关系是在什么时候形成的.我不确定它们是否实际上相关,但它们在教科书中一个接一个地出现.
I'm confused about the relationship between abuffer
and buffer
. I'm confused about what the vertex array's relationship with the buffer object is, and at what point that relationship is formed. I'm not sure whether they are, in fact related, but they are presented in the textbook one immediately after the other.
任何帮助将不胜感激.谢谢.
Any help would be appreciated. Thanks.
推荐答案
从低级的角度来看,您可以将数组视为由两部分组成:
From a low-level perspective, you can think of an array as having two parts to it:
有关数组大小、形状和类型的信息(例如,32 位浮点数,包含每行具有四个元素的向量行).
Information about the size, shape, and type of the array (e.g., 32-bit floating point numbers, containing rows of vectors with four elements each).
数组数据,只不过是一大块字节.
The array data, which is little more than a big blob of bytes.
尽管低级概念基本保持不变,但多年来您指定数组的方式已经发生了多次变化.
Even though the low-level concept has mostly stayed the same, the way you specify arrays has changed several times over the years.
这就是你可能今天应该做的事情.很少有人无法运行 OpenGL 3.x,但仍然有钱花在您的软件上.
This is the way you probably should be doing things today. It is very rare to find people who can't run OpenGL 3.x and yet still have money to spend on your software.
OpenGL 中的缓冲区对象是一大堆位.将活动"缓冲区视为只是一个全局变量,并且有许多函数使用活动缓冲区而不是使用参数.这些全局状态变量是 OpenGL 丑陋的一面(在直接状态访问,下面介绍).
A buffer object in OpenGL is a big blob of bits. Think of the "active" buffer as just a global variable, and there are a bunch of functions which use the active buffer instead of using a parameter. These global state variables are the ugly side of OpenGL (prior to direct state access, which is covered below).
GLuint buffer;
// Generate a name for a new buffer.
// e.g. buffer = 2
glGenBuffers(1, &buffer);
// Make the new buffer active, creating it if necessary.
// Kind of like:
// if (opengl->buffers[buffer] == null)
// opengl->buffers[buffer] = new Buffer()
// opengl->current_array_buffer = opengl->buffers[buffer]
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Upload a bunch of data into the active array buffer
// Kind of like:
// opengl->current_array_buffer->data = new byte[sizeof(points)]
// memcpy(opengl->current_array_buffer->data, points, sizeof(points))
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
现在,典型的顶点着色器将顶点作为输入,而不是一大堆位.因此,您需要指定如何将位 blob(缓冲区)解码为顶点.这就是阵列的工作.同样,有一个活动"数组,您可以将其视为一个全局变量:
Now, your typical vertex shader takes vertexes as input, not a big blob of bits. So you need to specify how the blob of bits (the buffer) is decoded into vertexes. That is the job of the array. Likewise, there is an "active" array which you can think of as just a global variable:
GLuint array;
// Generate a name for a new array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);
// Make the buffer the active array buffer.
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Attach the active buffer to the active array,
// as an array of vectors with 4 floats each.
// Kind of like:
// opengl->current_vertex_array->attributes[attr] = {
// type = GL_FLOAT,
// size = 4,
// data = opengl->current_array_buffer
// }
glVertexAttribPointer(attr, 4, GL_FLOAT, GL_FALSE, 0, 0);
// Enable the vertex attribute
glEnableVertexAttribArray(attr);
OpenGL 2.0(旧方式)
在 OpenGL 2.x 中,没有顶点数组,数据只是全局的.您仍然必须调用 glVertexAttribPointer()
和 glEnableVertexAttribArray()
,但是您必须每次使用缓冲区时调用它们.在 OpenGL 3.x 中,您只需设置一次数组.
OpenGL 2.0 (the old way)
In OpenGL 2.x, there weren't vertex arrays and the data was just global. You still had to call glVertexAttribPointer()
and glEnableVertexAttribArray()
, but you had to call them every time that you used a buffer. In OpenGL 3.x, you just set up the array once.
回到 OpenGL 1.5,您实际上可以使用缓冲区,但是您使用了一个单独的函数来绑定每种数据.例如,glVertexPointer()
用于顶点数据,glNormalPointer()
用于普通数据.在 OpenGL 1.5 之前,没有缓冲区,但您可以使用指向应用程序内存的指针.
Going back to OpenGL 1.5, you could actually use buffers, but you used a separate function to bind each kind of data. For example, glVertexPointer()
was for vertex data, and glNormalPointer()
was for normal data. Prior to OpenGL 1.5, there weren't buffers, but you could use pointers into your application memory.
在4.3中,或者如果你有ARB_vertex_attrib_binding扩展,你可以分别指定属性格式和属性数据.这很好,因为它可以让您轻松地在不同缓冲区之间切换一个顶点数组.
In 4.3, or if you have the ARB_vertex_attrib_binding extension, you can specify the attribute format and the attribute data separately. This is nice because it lets you easily switch one vertex array between different buffers.
GLuint array;
// Generate a name for a new array array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);
// Enable my attributes
glEnableVertexAttribArray(loc_attrib);
glEnableVertexAttribArray(normal_attrib);
glEnableVertexAttribArray(texcoord_attrib);
// Set up the formats for my attributes
glVertexAttribFormat(loc_attrib, 3, GL_FLOAT, GL_FALSE, 0);
glVertexAttribFormat(normal_attrib, 3, GL_FLOAT, GL_FALSE, 12);
glVertexAttribFormat(texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexAttribBinding(loc_attrib, 0);
glVertexAttribBinding(normal_attrib, 0);
glVertexAttribBinding(texcoord_attrib, 0);
// Quickly bind all attributes to use "buffer"
// This replaces several calls to glVertexAttribPointer()
// Note: you don't need to bind the buffer first! Nice!
glBindVertexBuffer(0, buffer, 0, 32);
// Quickly bind all attributes to use "buffer2"
glBindVertexBuffer(0, buffer2, 0, 32);
OpenGL 4.5/ARB_direct_state_access
在 OpenGL 4.5 中,或者如果您有 ARB_direct_state_access 扩展,您不再需要调用 glBindBuffer()
或 glBindVertexArray()
来进行设置...您直接指定数组和缓冲区.只需在最后绑定数组即可绘制.
OpenGL 4.5 / ARB_direct_state_access
In OpenGL 4.5, or if you have the ARB_direct_state_access extension, you no longer need to call glBindBuffer()
or glBindVertexArray()
just to set things up... you specify the arrays and buffers directly. You only need to bind the array at the end to draw it.
GLuint array;
// Generate a name for the array and create it.
// Note that glGenVertexArrays() won't work here.
glCreateVertexArrays(1, &array);
// Instead of binding it, we pass it to the functions below.
// Enable my attributes
glEnableVertexArrayAttrib(array, loc_attrib);
glEnableVertexArrayAttrib(array, normal_attrib);
glEnableVertexArrayAttrib(array, texcoord_attrib);
// Set up the formats for my attributes
glVertexArrayAttribFormat(array, loc_attrib, 3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(array, normal_attrib, 3, GL_FLOAT, GL_FALSE, 12);
glVertexArrayAttribFormat(array, texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexArrayAttribBinding(array, loc_attrib, 0);
glVertexArrayAttribBinding(array, normal_attrib, 0);
glVertexArrayAttribBinding(array, texcoord_attrib, 0);
// Quickly bind all attributes to use "buffer"
glVertexArrayVertexBuffer(array, 0, buffer, 0, 32);
// Quickly bind all attributes to use "buffer2"
glVertexArrayVertexBuffer(array, 0, buffer2, 0, 32);
// You still have to bind the array to draw.
glBindVertexArray(array);
glDrawArrays(...);
ARB_direct_state_access 很好,原因有很多.您可以忘记绑定数组和缓冲区(除非您绘制),因此您不必考虑 OpenGL 正在为您跟踪的隐藏全局变量.您可以忘记为对象生成名称"和创建对象"之间的区别,因为 glCreateBuffer()
和 glCreateArray()
是同时进行的.
ARB_direct_state_access is nice for a lot of reasons. You can forget about binding arrays and buffers (except when you draw) so you don't have to think about hidden global variables that OpenGL is tracking for you. You can forget about the difference between "generating a name for an object" and "creating an object" because glCreateBuffer()
and glCreateArray()
do both at the same time.
Vulkan 走得更远,让您编写类似于我上面写的伪代码的代码.所以你会看到类似的东西:
Vulkan goes even farther and has you write code like the pseudocode I wrote above. So you'll see something like:
// This defines part of a "vertex array", sort of
VkVertexInputAttributeDescription attrib[3];
attrib[0].location = 0; // Feed data into shader input #0
attrib[0].binding = 0; // Get data from buffer bound to slot #0
attrib[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attrib[0].offset = 0;
// repeat for attrib[1], attrib[2]
相关文章