З точки зору низького рівня ви можете думати про масив, що складається з двох частин:
Інформація про розмір, форму та тип масиву (наприклад, 32-розрядні числа з плаваючою комою, що містять рядки векторів з чотирма елементами в кожному).
Дані масиву, що є трохи більше, ніж велика крапка байтів.
Незважаючи на те, що концепція низького рівня здебільшого залишається незмінною, спосіб вказівки масивів за кілька років змінювався кілька разів.
OpenGL 3.0 / ARB_vertex_array_object
Це шлях, напевно слід робити сьогодні. Дуже рідко можна зустріти людей, які не можуть запустити OpenGL 3.x, але при цьому все одно мають гроші на своє програмне забезпечення.
Буферний об'єкт у OpenGL - це велика крапка бітів. Подумайте про "активний" буфер як про просто глобальну змінну, і є купа функцій, які використовують активний буфер замість використання параметра. Ці глобальні змінні стану є потворною стороною OpenGL (до прямого доступу до стану, про що йдеться нижче).
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
Тепер ваш типовий вершинний шейдер приймає вершини як вхідні дані, а не велику крапку бітів. Отже, вам потрібно вказати, як BLOB-біт (буфер) декодується у вершини. Це робота масиву. Так само існує "активний" масив, який ви можете сприймати як просто глобальну змінну:
GLuint array;
glGenVertexArrays(1, &array);
glBindVertexArray(array);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glVertexAttribPointer(attr, 4, GL_FLOAT, GL_FALSE, 0, 0);
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;
glGenVertexArrays(1, &array);
glBindVertexArray(array);
glEnableVertexAttribArray(loc_attrib);
glEnableVertexAttribArray(normal_attrib);
glEnableVertexAttribArray(texcoord_attrib);
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);
glVertexAttribBinding(loc_attrib, 0);
glVertexAttribBinding(normal_attrib, 0);
glVertexAttribBinding(texcoord_attrib, 0);
glBindVertexBuffer(0, buffer, 0, 32);
glBindVertexBuffer(0, buffer2, 0, 32);
OpenGL 4.5 / ARB_direct_state_access
У OpenGL 4.5, або якщо у вас є розширення ARB_direct_state_access, вам більше не потрібно телефонувати glBindBuffer()
або glBindVertexArray()
просто налаштовувати ситуацію ... ви вказуєте масиви та буфери безпосередньо. Вам потрібно лише прив’язати масив в кінці, щоб намалювати його.
GLuint array;
glCreateVertexArrays(1, &array);
glEnableVertexArrayAttrib(array, loc_attrib);
glEnableVertexArrayAttrib(array, normal_attrib);
glEnableVertexArrayAttrib(array, texcoord_attrib);
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);
glVertexArrayAttribBinding(array, loc_attrib, 0);
glVertexArrayAttribBinding(array, normal_attrib, 0);
glVertexArrayAttribBinding(array, texcoord_attrib, 0);
glVertexArrayVertexBuffer(array, 0, buffer, 0, 32);
glVertexArrayVertexBuffer(array, 0, buffer2, 0, 32);
glBindVertexArray(array);
glDrawArrays(...);
ARB_direct_state_access приємний з багатьох причин. Ви можете забути про прив'язку масивів та буферів (крім випадків, коли малюєте), тому вам не доведеться думати про приховані глобальні змінні, які OpenGL відстежує для вас. Ви можете забути про різницю між "генеруванням імені для об'єкта" та "створенням об'єкта", оскільки glCreateBuffer()
іglCreateArray()
виконуйте обидва дії одночасно.
Вулкан
Вулкан іде ще далі, і ви пишете код, як псевдокод, який я писав вище. Отже, ви побачите щось на зразок:
VkVertexInputAttributeDescription attrib[3];
attrib[0].location = 0;
attrib[0].binding = 0;
attrib[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attrib[0].offset = 0;
GL_EXT_direct_state_access
, але це виключає запуск коду на драйверах Mesa, Intel або Apple, на жаль: - \ Однак, я думаю, важливо зазначити, що масиви Vertex існують з GL 1.1. Єдине, що змінилося в GL 2.0, - це введення загальних слотів атрибутів вершин.