glBindVertexArrays与glBindBuffer的作用是什么?它们的关系是什么?

时间:2014-02-08 22:25:42

标签: c++ opengl

我是OpenGL和图形编程的新手。到目前为止,我一直在读一本非常详尽且写得很好的教科书。但是,我在代码中找到了一点,我不太了解,我想在我之前理解这些线条。继续前进。

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中的无符号整数标识符),第三行创建对象/使其处于活动状态。

本书解释了第4到第7行创建缓冲区对象来存储我们的数据,第5行给我们一个未使用的标识符(类似于顶点数组对象的第2行?),创建缓冲区的第6行,第7行在CPU上分配足够的内存并创建指向GL_STATIC_DRAW的数据(点)的指针。

对象活跃是什么意思?您何时会使用abuffer?顶点数组捆绑关联数据是什么意思,以及与此顶点数组对象关联的数据是什么时候?

我对abufferbuffer之间的关系感到困惑。我对顶点数组与缓冲区对象的关系以及该关系形成的关系感到困惑。我不确定它们是否实际上是相关的,但它们是在教科书中一个接一个地出现的。

任何帮助将不胜感激。感谢。

5 个答案:

答案 0 :(得分:126)

从低级别的角度来看,您可以将数组视为包含两个部分:

  • 有关数组大小,形状和类型的信息(例如,32位浮点数,包含每行包含四个元素的向量行)。

  • 数组数据,只是一大块字节。

尽管低级概念基本保持不变,但多年来指定数组的方式已多次改变。

OpenGL 3.0 / ARB_vertex_array_object

这就是可能今天应该做的事情。很难找到那些无法运行OpenGL 3.x且仍然有钱花在你的软件上的人。

OpenGL中的缓冲区对象是一大堆。将“活动”缓冲区视为全局变量,并且有许多函数使用活动缓冲区而不是使用参数。这些全局状态变量是OpenGL的丑陋一面(之前 直接国家访问,如下所述。

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(缓冲区)解码为顶点。这是阵列的工作。同样,有一个“活动”数组,您可以将其视为一个全局变量:

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 1.5,您实际上可以使用缓冲区,但是您使用单独的函数来绑定每种数据。例如,glVertexPointer()用于顶点数据,glNormalPointer()用于普通数据。在OpenGL 1.5之前,没有缓冲区,但您可以使用指针进入应用程序内存。

OpenGL 4.3 / ARB_vertex_attrib_binding

在4.3中,或者如果您有ARB_vertex_attrib_binding扩展名,则可以分别指定属性格式和属性数据。这很好,因为它可以让你轻松地在不同的缓冲区之间切换一个顶点数组。

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()来设置...你直接指定数组和缓冲区。您只需要在末尾绑定数组即可绘制它。

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()同时执行这两者。

福尔康

Vulkan走得更远,你编写的代码就像我上面写的伪代码一样。所以你会看到类似的东西:

// 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]

答案 1 :(得分:15)

您对本书的解释并不完全正确。顶点数组对象不存储任何数据。它们是一类称为容器的对象,如Framebuffer Objects。您可以将其他对象附加/关联,但它们从不存储数据。因此,它们不是可共享上下文的资源。

基本上,顶点数组对象在OpenGL 3.0中封装了顶点数组状态。从OpenGL 3.1(代替GL_ARB_compatibility)和OpenGL 3.2+核心配置文件开始,您必须始终为glVertexAttribPointer (...)glDrawArrays (...)等命令设置非零VAO才能运行。绑定的VAO形成这些命令的必要上下文,并持久存储状态。

在旧版本的GL(兼容性)中,VAO存储的状态是全局状态机的一部分。

值得一提的是,GL_ARRAY_BUFFER的“当前”绑定是 VAO跟踪的状态之一。虽然此绑定由glVertexAttribPointer (...)等命令使用,但VAO不存储它们仅存储指针的绑定(与GL 4.3一起引入的GL_ARB_vertex_attrib_binding扩展使这一点复杂化,所以让我们为简单起见忽略它)

VAO 执行 记住绑定到GL_ELEMENT_ARRAY_BUFFER的内容,以便像glDrawElements (...)这样的索引绘图命令按预期运行( 例如 VAO重复使用最后一个元素数组缓冲区绑定。

答案 2 :(得分:6)

调用glVertexAttribPointer时会创建关系。

Overview of VertexArrays

GL_VERTEX_ARRAY_BINDINGGL_ARRAY_BUFFER_BINDING是常量,但它们可以指向绑定的全局状态。我指的是图像中的状态而不是常数(橙色)。使用glGet查找不同的全局状态。

VertexArray是关于顶点或许多平行顶点的信息分组(包括数组缓冲区)。

GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDINGglGetVertexAttrib一起使用以查找设置了哪个属性数组缓冲区。

glBindBuffer(GL_ARRAY_BUFFER设置全局状态GL_ARRAY_BUFFER_BINDING

glBindVertexArray设置全局状态GL_VERTEX_ARRAY_BINDING

答案 3 :(得分:5)

OpenGL是一个有状态的接口。它很糟糕,过时且丑陋,但这对你来说是遗留下来的。

顶点数组对象是缓冲区绑定的集合,驱动程序可以使用它来获取绘制调用的数据,大多数教程只使用一个,并且从不解释如何使用多个VAO。

缓冲区绑定告诉opengl将该缓冲区用于相关方法,尤其是glVertexAttribPointer方法。

答案 4 :(得分:-2)

VertexArray与VBO之间没有任何关系。

顶点数组在RAM中分配内存并发送指向API的指针。 VBO在显卡中分配内存 - 系统内存没有地址。如果您需要从系统访问vbo数据,则需要先将其从vbo复制到系统。

此外,在较新版本的OpenGL中,顶点数组被完全删除(在3.0中删除,在3.1中删除)