Ответ 1
С точки зрения низкого уровня вы можете думать, что массив имеет две части:
-
Информация о размере, форме и типе массива (например, 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]