从低层次的角度来看,您可以将数组视为由两部分组成:
尽管低级概念基本保持不变,但多年来指定数组的方式已经发生了数次变化。
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);
现在,您的典型顶点着色器将 vertexes 作为输入,而不是一大块位。因此,您需要指定如何将位块(缓冲区)解码为顶点。这就是数组的工作。同样,有一个“活动”数组,您可以将其视为一个全局变量:
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]