Что такое объекты массива вершин?

115

Сегодня я только начинаю изучать OpenGL из этого руководства: http://openglbook.com/the-book/
Я перешел к главе 2, где рисую треугольник и понимаю все, кроме VAO (это аббревиатура в порядке?). В руководстве есть этот код:

glGenVertexArrays(1, &VaoId);
glBindVertexArray(VaoId);

Хотя я понимаю, что код необходим, я понятия не имею, что он делает. Хотя я никогда не использую VaoId после этого момента (кроме как для его уничтожения), код без него не работает. Я предполагаю, что это связано с тем, что это необходимо, но я не знаю почему. Должен ли этот точный код быть частью каждой программы OpenGL? В учебнике VAO объясняются как:

Объект массива вершин (или VAO) - это объект, который описывает, как атрибуты вершин хранятся в объекте буфера вершин (или VBO). Это означает, что VAO - это не фактический объект, хранящий данные вершины, а дескриптор данных вершин. Атрибуты вершин можно описать с помощью функции glVertexAttribPointer и двух ее сестринских функций glVertexAttribIPointer и glVertexAttribLPointer, первую из которых мы рассмотрим ниже.

Я не понимаю, как VAO описывает атрибуты вершин. Я их никак не описал. Получает ли он информацию из glVertexAttribPointer? Я думаю, это должно быть оно. Является ли VAO просто местом назначения информации от glVertexAttribPointer?

Кстати, приемлемо ли руководство, которому я следую? Есть ли что-нибудь, чего мне следует остерегаться, или лучшее руководство, которому нужно следовать?

Патрик
источник

Ответы:

101

«Vertex Array Object» предоставлен вам Подкомитетом OpenGL ARB для глупых имен.

Думайте об этом как о геометрическом объекте. (Как бывший программист SGI Performer, я называю их геосетами.) Переменные / члены экземпляра объекта - это указатель вершины, обычный указатель, указатель цвета, указатель attrib N, ...

Когда VAO впервые связывается, вы назначаете эти члены, вызывая

glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer...;
glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer...;

и так далее. Какие атрибуты включены и указанные вами указатели хранятся в VAO.

После этого, когда вы снова связываете VAO, все эти атрибуты и указатели также становятся текущими. Таким образом, один glBindVertexArrayвызов эквивалентен всему коду, ранее необходимому для настройки всех атрибутов. Это удобно для передачи геометрии между функциями или методами без необходимости создавать собственные структуры или объекты.

(Одноразовая настройка, многократное использование - это самый простой способ использования VAO, но вы также можете изменить атрибуты, просто привязав их и выполнив больше вызовов включения / указателя. VAO не являются константами.)

Дополнительная информация в ответ на вопросы Патрика:

По умолчанию для вновь созданного VAO он пуст (AFAIK). Никакой геометрии, даже вершин, поэтому, если вы попытаетесь ее нарисовать, вы получите ошибку OpenGL. Это разумно, например, «инициализировать все до False / NULL / нуля».

Вам это нужно только glEnableClientStateтогда, когда вы все настраиваете. VAO запоминает состояние включения / выключения для каждого указателя.

Да будет хранить glEnableVertexAttribArrayи ВАО glVertexAttrib. Старые массивы вершин, нормалей, цвета, ... такие же, как массивы атрибутов, вершина == # 0 и так далее.

Хью
источник
62
«Объект массива вершин» предоставлен вам Подкомитетом OpenGL ARB по глупым именам. Да, такое глупое название для объекта , хранящего привязки массивов вершин .
Никол Болас
2
Кроме того, связаны ли вообще VAO сglVertexAttribPointer
Патрик
2
Добавьте информацию об использовании общих атрибутов вершин для людей, которые используют основной профиль.
Оскар
8
@NicolBolas Лучшее имя было бы VertexArrayMacroили что-то подобное.
bobobobo 07
7
@NicolBolas "Объект массива вершин" - ужасное имя. Речь идет о привязке данных к атрибутам . Речь идет не о массиве вершин, как следует из названия. В названии нет ссылки на привязки или атрибуты, а поскольку «массив вершин» сам по себе является отдельным понятием, это еще более затрудняет понимание. IMHO, "(Vertex) Attributes Binding Object" легче понять. Даже Geometry Object лучше: мне он не нравится, но по крайней мере не перегружен.
AkiRoss
8

Объекты массива вершин похожи на макросы в программах обработки текстов и т.п. Хорошее описание можно найти здесь .

Макросы просто запоминают ваши действия, такие как активация этого атрибута, привязка этого буфера и т. Д. Когда вы вызываете glBindVertexArray( yourVAOId ), он просто воспроизводит эти привязки указателя атрибутов и привязки буфера.

Итак, ваш следующий вызов для рисования использует все, что было связано с VAO.

VAO не хранят данные о вершинах . Нет. Данные вершин хранятся в буфере вершин или в массиве клиентской памяти.

бобобобо
источник
19
-1: Они не похожи на макросы. Если бы это было так, то привязка нового VAO не отключила бы массивы вершин, включенные предыдущим VAO, если только новый VAO не «записал», что вы явно отключили эти массивы. VAO, как и все объекты OpenGL, хранят состояние , а не команды. Команды просто изменяют состояние, но объекты поставляются с установленным состоянием по умолчанию. Вот почему привязка вновь созданного VAO всегда будет отключать все атрибуты.
Никол Болас
7

Я всегда думаю о VAO как о массиве буферов данных, используемых OpenGL. Используя современный OpenGL, вы создадите объекты VAO и Vertex Buffer.

введите описание изображения здесь

//vaoB is a buffer
glGenVertexArrays(1, vaoB); //creates one VAO
glBindVertexArray(vao.get(0));
glGenBuffers(vbo.length, vbo, 0); //vbo is a buffer
glBindVertexArray(vao.get(1));
glGenBuffers(vbo1.length, vbo1, 0); //vbo1 is a buffer
glBindVertexArray(vao.get(2));
glGenBuffers(vbo2.length, vbo2, 0); //vbo2 is a buffer

Следующий шаг - привязать данные к буферу:

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER,vertBuf.limit()*4, vertBuf, GL_STATIC_DRAW); //vertf buf is a floatbuffer of vertices

В этот момент OpenGL видит:

введите описание изображения здесь

Теперь мы можем использовать glVertexAttribPointer, чтобы сообщить OpenGL, что представляют данные в буфере:

glBindBuffer(GL_ARRAY_BUFFER, 0); //bind VBO at 0
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); //each vertex has 3 components of size GL_FLOAT with 0 stride (space) between them and the first component starts at 0 (start of data)

введите описание изображения здесь

OpenGL теперь имеет данные в буфере и знает, как данные организованы в вершины. Тот же процесс можно применить к координатам текстуры и т.д., но для координат текстуры будет два значения.

glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER,coordBuf.limit()*4, coordBuf, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);

Затем вы можете связать текстуры и нарисовать массивы, вы захотите создать шейдер Vert и Frag, скомпилировать и прикрепить его к программе (не включенной здесь).

glActiveTexture(textureID); //bind our texture
glBindTexture(GL_TEXTURE_2D, textureID);
glDrawArrays(GL_TRIANGLES,0,6); //in this case 6 indices are used for two triangles forming a square
васмос
источник
1
+1 за диаграмму. Более значимого, чем все, что я видел. Из того, что мне удалось собрать, VAO похожа на структуру, в которой каждый член представляет собой массив равной длины. Самый важный член содержит точки в трехмерном пространстве («вершины»). Другие члены содержат вспомогательные данные («атрибуты») об этих точках, такие как цвет и нормаль. Действительно раздражает, как учебники просто говорят вам копировать и вставлять код, не объясняя, что код концептуально делает. От простого копирования и вставки можно
узнать
5

VAO - это объект, который представляет стадию выборки вершин конвейера OpenGL и используется для подачи входных данных в вершинный шейдер.

Вы можете создать объект массива вершин, подобный этому

GLuint vao;
glCreateVertexArrays(1, &vao);
glBindVertexArray(vao);

Сначала давайте рассмотрим простой пример. Рассмотрим такой входной параметр в коде шейдера.

layout (location = 0) in vec4 offset; // input vertex attribute

Чтобы заполнить этот атрибут, мы можем использовать

glVertexAttrib4fv(0, attrib); // updates the value of input attribute 0

Хотя объект массива вершин хранит эти значения статических атрибутов для вас, он может делать гораздо больше.

После создания объекта массива вершин мы можем приступить к заполнению его состояния. Мы попросим OpenGL заполнить его автоматически, используя данные, хранящиеся в предоставляемом нами буферном объекте. Каждый атрибут вершины получает данные из буфера, привязанного к одной из нескольких привязок буфера вершин. Для этого мы используем glVertexArrayAttribBinding(GLuint vao, GLuint attribindex, GLuint bindingindex). Также мы используем glVertexArrayVertexBuffer()функцию для привязки буфера к одной из привязок буфера вершин. Мы используем glVertexArrayAttribFormat()функцию для описания макета и формата данных, и, наконец, мы включаем автоматическое заполнение атрибута путем вызова glEnableVertexAttribArray().

Когда атрибут вершины включен, OpenGL будет передавать данные в вершинный шейдер на основе информации о формате и местоположении, которую вы предоставили с помощью glVertexArrayVertexBuffer()и glVertexArrayAttribFormat(). Когда атрибут отключен, вершинному шейдеру будет предоставлена ​​статическая информация, которую вы предоставляете при вызове glVertexAttrib*().

// First, bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(vao, 0, buffer, 0, sizeof(vmath::vec4));

// Now, describe the data to OpenGL, tell it where it is, and turn on automatic
// vertex fetching for the specified attribute
glVertexArrayAttribFormat(vao, 0, 4, GL_FLOAT, GL_FALSE, 0);

glEnableVertexArrayAttrib(vao, 0);

И код в шейдере

layout (location = 0) in vec4 position;

Ведь вам нужно позвонить glDeleteVertexArrays(1, &vao).


Вы можете прочитать OpenGL SuperBible, чтобы лучше понять это.

Йола
источник
3
Приятно видеть людей, продвигающих использование OpenGL в стиле DSA.
Никол Болас