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

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

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

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

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

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

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

Ответ 1

"Объект вершинного массива" представлен вам подкомитетом OpenGL ARB для глупых имен.

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

Ответ 3

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, чтобы лучше это понять.

Ответ 4

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

enter image description here

//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 видит:

enter image description here

Теперь мы можем использовать 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)

enter image description here

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