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

Das*_*mal 71 c++ opengl

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

GLuint abuffer;

glGenVertexArrays(1, &abuffer);
glBindVertexArray(abuffer);
Run Code Online (Sandbox Code Playgroud)
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
Run Code Online (Sandbox Code Playgroud)

该书解释说,前三行是创建一个顶点数组对象,用于将关联数据与顶点数组捆绑在一起.第二行找到一个未使用的名称(我猜测存储的无符号整数标识符abuffer),第三行创建对象/使其处于活动状态.

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

对象处于活动状态意味着什么?你abuffer什么时候会用?顶点数组捆绑关联数据是什么意思,以及与此顶点数组对象关联的数据何时?

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

任何帮助,将不胜感激.谢谢.

Die*_*Epp 136

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

  • 有关数组大小,形状和类型的信息(例如,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);
Run Code Online (Sandbox Code Playgroud)

现在,您的典型顶点着色器将顶点作为输入,而不是大块位.因此,您需要指定如何将位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);
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

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(...);
Run Code Online (Sandbox Code Playgroud)

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]
Run Code Online (Sandbox Code Playgroud)

  • 喜欢这里的历史背景.关于学习GL最神奇的令人沮丧的事情之一是API和最佳实践如何随着时间的推移而发生变化. (6认同)
  • **+ 1**用于描述GL的选择器/锁存机制(*例如*顶点指针适用的缓冲对象取自绑定对象而不是传递参数).这个API设计可以使用[`GL_EXT_direct_state_access`](http://www.opengl.org/registry/specs/EXT/direct_state_access.txt)解决,但这确实排除了在Mesa,Intel或Apple驱动程序上运行代码不幸的是: - \但是,我认为重要的是要指出顶点阵列自GL 1.1以来就已存在.GL 2.0中唯一改变的是引入通用顶点属性槽. (5认同)
  • 说到OpenGL 1.1,这个版本还引入了纹理***对象***.与顶点数组对象一样,在引入纹理对象之前GL中已经支持纹理,但数据/状态未持久存储.不是在OpenGL 1.0中更改绑定的纹理对象,而是编写大多数软件来执行显示列表中的纹理数据/状态设置.GL 3.0为顶点数组做了类似的事情,它引入了顶点数组***对象***. (4认同)

And*_*man 15

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

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

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

还值得一提的是,"当前"的结合GL_ARRAY_BUFFER不是该VAOs跟踪的国家之一.虽然这种绑定被命令使用,例如glVertexAttribPointer (...),VAO不存储绑定,但它们只存储指针(GL_ARB_vertex_attrib_binding与GL 4.3一起引入的扩展使这一点复杂化,所以让我们为了简单起见而忽略它).

然而,VAO 确实记住了绑定的内容GL_ELEMENT_ARRAY_BUFFER,以便索引绘制命令glDrawElements (...)(您期望的功能)(例如, VAO重新使用最后一个元素数组缓冲区绑定).

  • 感谢您指出**GL_ARRAY_BUFFER 的绑定不是 VAO 跟踪的状态之一**。 (3认同)

Jos*_*ssi 6

调用glVertexAttribPointer时会创建关系.

VertexArrays概述

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

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

GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDINGglGetVertexAttrib一起使用以查找设置的属性数组缓冲区.

glBindBuffer(GL_ARRAY_BUFFER设置全局状态GL_ARRAY_BUFFER_BINDING

glBindVertexArray设置全局状态GL_VERTEX_ARRAY_BINDING


rat*_*eak 5

OpenGL是一个有状态的界面。这是不好的,过时的和丑陋的,但这对你们来说是遗产。

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

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