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

我是OpenGL和graphics编程的新手。 我一直在阅读一本迄今为止写得非常透彻而且写得很好的教科书。但是,我在代码中遇到了一些我不太清楚的地方,所以我想在这之前理解这些内容。继续。

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); 

这本书解释了前三行是创build一个顶点数组对象 ,它用来捆绑关联的数据与顶点数组。 第二行find一个未使用的名称(我猜一个无符号的整数标识符存储在abuffer ),第三行创build对象/使其处于活动状态。

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

这个对象是什么意思? 你什么时候可以使用abuffer ? 顶点数组绑定关联数据是什么意思,什么时候与这个顶点数组关联的数据?

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

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

从低级的angular度来看,你可以把数组看成有两个部分:

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

  • 数组数据,它比一大块字节稍微多一点。

尽pipe低级概念大部分保持不变,但是指定arrays的方式多年来一直在改变。

OpenGL 3.0 / ARB_vertex_array_object

这是你今天应该做的事情。 find不能运行OpenGL 3.x但仍然有钱花在软件上的人是非常罕见的。

OpenGL中的缓冲区对象是一大块位。 将“活动”缓冲区看作是一个全局variables,并且有一堆使用活动缓冲区而不是使用参数的函数。 这些全局状态variables是OpenGL的糟糕的一面(在直接状态访问之前,下面将会介绍)。

 GLuint buffer; // Generate a name for a new buffer. // eg 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); 

现在,你的典型顶点着色器将顶点作为input,而不是一个大块的位。 所以你需要指定如何将blob(缓冲区)解码成顶点。 这是arrays的工作。 同样,还有一个“主动”数组,你可以把它看作是一个全局variables:

 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为您追踪的隐藏的全局variables。 您可以忘记“生成一个对象的名称”和“创build一个对象”之间的区别,因为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] 

你对这本书的解释并不完全正确。 顶点数组对象不存储数据。 它们是一类被称为容器的对象,就像Framebuffer对象一样。 您可以附加/关联其他对象,但是他们自己不会存储数据。 因此,他们不是一个可共享上下文的资源。

基本上,顶点数组对象封装了OpenGL 3.0中的顶点数组状态。 从OpenGL 3.1(代替GL_ARB_compatibility )和OpenGL 3.2+ Coreconfiguration文件开始,您必须始终有一个非零的VAO,以便像glVertexAttribPointer (...)glDrawArrays (...)这样的命令起作用。 绑定的VAO为这些命令形成必要的上下文,并持久地存储该状态。

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

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

但是,VAO 确实记住了与GL_ELEMENT_ARRAY_BUFFER绑定的GL_ELEMENT_ARRAY_BUFFER ,因此,索引的绘图命令(如glDrawElements (...)function与您所期望的相同( 例如, VAO将重新使用最后一个元素数组缓冲区绑定)。

调用glVertexAttribPointer时创build该关系。

VertexArrays概述

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

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

使用GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING和glGetVertexAttrib来查找设置了哪个属性数组缓冲区。

glBindBuffer(GL_ARRAY_BUFFER设置全局状态GL_ARRAY_BUFFER_BINDING

glBindVertexArray设置全局状态GL_VERTEX_ARRAY_BINDING

OpenGL是一个有状态的接口。 这是糟糕的,过时的,丑陋的,但这是雅的遗产。

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

缓冲区绑定告诉opengl使用该缓冲区来处理相关的方法,特别是glVertexAttribPointer方法。

VertexArray和VBO之间没有关系。

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

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