Ответ 1
Некоторая терминология немного выключена:
- A
Vertex Array
- это просто массив (обычно afloat[]
), который содержит данные вершин. Это не обязательно должно быть связано ни с чем. Не путать сVertex Array Object
или VAO, которые я перейду позже. - A
Buffer Object
, обычно называемыйVertex Buffer Object
при хранении вершин, или короткое замыкание на VBO - это то, что вы вызываете толькоBuffer
. - Ничто не возвращается обратно в массив вершин,
glVertexAttribPointer
работает точно так же, какglVertexPointer
илиglTexCoordPointer
, просто вместо названных атрибутов вы получаете номер, который указывает ваш собственный атрибут. Вы передаете это значение какindex
. Все ваши вызовыglVertexAttribPointer
получаются в очереди в следующий раз, когда вы вызываетеglDrawArrays
илиglDrawElements
. Если у вас есть привязка VAO, VAO сохранит настройки для всех ваших атрибутов.
Основная проблема здесь заключается в том, что вы смешиваете атрибуты вершин с VAO. Атрибуты вершин - это просто новый способ определения вершин, текскодов, нормалей и т.д. Для рисования. Состояние магазина VAO. Сначала я расскажу, как работает рисунок с атрибутами вершин, а затем объясните, как вы можете сократить количество вызовов методов с помощью VAO:
- Вы должны включить атрибут, прежде чем сможете использовать его в шейдере. Например, если вы хотите отправить вершины в шейдер, вы, скорее всего, отправите его в качестве первого атрибута 0. Итак, перед рендерингом вам нужно включить его с помощью
glEnableVertexAttribArray(0);
. - Теперь, когда атрибут включен, вам необходимо определить данные, которые он будет использовать. Для этого вам нужно привязать свой VBO -
glBindBuffer(GL_ARRAY_BUFFER, myBuffer);
. - И теперь мы можем определить атрибут -
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
. В порядке параметра: 0 - атрибут, который вы определяете, 3 - размер каждой вершины,GL_FLOAT
- тип,GL_FALSE
означает не нормализовать каждую вершину, последние 2 нуля означают, что нет шага или смещения на вершинах. - Нарисуйте что-нибудь с ним -
glDrawArrays(GL_TRIANGLES, 0, 6);
- Следующее, что вы рисуете, может не использовать атрибут 0 (реально это будет, но это пример), поэтому мы можем его отключить -
glDisableVertexAttribArray(0);
Оберните это в вызовы glUseProgram()
, и у вас есть система рендеринга, которая работает с шейдерами должным образом. Но предположим, что у вас есть 5 различных атрибутов, вершин, текскодов, нормалей, цветов и координаты lightmap. Прежде всего, вы должны сделать один вызов glVertexAttribPointer
для каждого из этих атрибутов, и вам нужно будет включить все атрибуты заранее. Скажем, вы определяете атрибуты 0-4, поскольку я их перечисляю. Вы бы включили их все так:
for (int i = 0; i < 5; i++)
glEnableVertexAttribArray(i);
И тогда вам придется привязывать разные VBO для каждого атрибута (если вы не сохраните их все в одном VBO и не используйте смещения/шаг), тогда вам нужно сделать 5 различных вызовов glVertexAttribPointer
, от glVertexAttribPointer(0,...);
до glVertexAttribPointer(4,...);
для вершин в координаты lightmap соответственно.
Надеюсь, что эта система имеет смысл. Теперь я перейду к VAO, чтобы объяснить, как использовать их, чтобы сократить количество вызовов методов при выполнении этого типа рендеринга. Обратите внимание, что использование VAO не требуется.
A Vertex Array Object
или VAO используется для хранения состояния всех вызовов glVertexAttribPointer
и VBOs, которые были нацелены, когда были сделаны все вызовы glVertexAttribPointer
.
Вы создаете один с вызовом glGenVertexArrays
. Чтобы сохранить все, что вам нужно в VAO, привяжите его с помощью glBindVertexArray
, затем выполните полный вызов рисования. Все вызовы привязки draw перехватываются и сохраняются VAO. Вы можете отвязать VAO с помощью glBindVertexArray(0);
Теперь, когда вы хотите нарисовать объект, вам не нужно повторно переписывать все привязки VBO или вызовы glVertexAttribPointer
, вам просто нужно привязать VAO к glBindVertexArray
, затем вызвать glDrawArrays
или glDrawElements
, и вы будете рисовать то же самое, что и все эти вызовы методов. Вероятно, вы захотите снова отвязать VAO.
Как только вы отвяжете VAO, все состояние вернется к тому, как это было до того, как вы связали VAO. Я не уверен, что какие-либо изменения, внесенные вами во время VAO, сохраняются, но это легко понять с помощью тестовой программы. Думаю, вы можете придумать glBindVertexArray(0);
как привязку к VAO по умолчанию...
Обновление: Кто-то привлек мое внимание к необходимости вызова вызова. Как оказалось, на самом деле вам не нужно делать FULL draw call при настройке VAO, просто все обязательные вещи. Не знаю, почему я думал, что это необходимо раньше, но теперь это исправлено.