Некоторая терминология немного неверна:
- A
Vertex Array
- это просто массив (обычно a float[]
), содержащий данные вершин. Его не нужно ни к чему привязывать. Не путать с a Vertex Array Object
или VAO, о которых я расскажу позже.
- A
Buffer Object
, обычно называемый a 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 различных атрибутов, вершины, текскорд, нормали, цвет и координаты карты освещения. Прежде всего, вы должны сделать один glVertexAttribPointer
вызов для каждого из этих атрибутов, и вам нужно будет заранее активировать все атрибуты. Допустим, вы определяете атрибуты 0-4, как я их перечислил. Вы можете включить их все так:
for (int i = 0; i < 5; i++)
glEnableVertexAttribArray(i);
И тогда вам придется связать различные РВО для каждого атрибута (если не хранить их все в одном VBO и использование смещениям / шаг), то вам необходимо сделать 5 различных glVertexAttribPointer
вызовов, от glVertexAttribPointer(0,...);
до glVertexAttribPointer(4,...);
вершин , до Lightmap координат соответственно.
Надеюсь, эта система имеет смысл. Теперь я собираюсь перейти к VAO, чтобы объяснить, как их использовать, чтобы сократить количество вызовов методов при выполнении этого типа рендеринга. Обратите внимание, что использовать VAO не обязательно.
A Vertex Array Object
или VAO используются для хранения состояния всех glVertexAttribPointer
вызовов и VBO, которые были нацелены при выполнении каждого из glVertexAttribPointer
вызовов.
Вы создаете его с помощью вызова glGenVertexArrays
. Чтобы сохранить все необходимое в VAO, свяжите его glBindVertexArray
, затем выполните полный вызов отрисовки . Все вызовы привязки отрисовки перехватываются и сохраняются VAO. Вы можете отвязать VAO с помощьюglBindVertexArray(0);
Теперь, когда вы хотите нарисовать объект, вам не нужно повторно вызывать все glVertexAttribPointer
привязки VBO или вызовы, вам просто нужно связать VAO с помощью glBindVertexArray
then call, glDrawArrays
или glDrawElements
вы будете рисовать то же самое, как если бы вы делали все эти вызовы методов. Возможно, вы захотите потом отвязать VAO.
После того, как вы отвяжете VAO, все состояние вернется к тому, как было до привязки VAO. Я не уверен, сохраняются ли какие-либо изменения, которые вы вносите во время привязки VAO, но это можно легко выяснить с помощью тестовой программы. Думаю, это можно представить glBindVertexArray(0);
как привязку к "дефолтному" VAO ...
Обновление: кто-то обратил мое внимание на необходимость фактического вызова отрисовки. Как оказалось, вам действительно не нужно выполнять ПОЛНЫЙ вызов отрисовки при настройке VAO, просто все привязки. Не знаю, почему раньше считал это необходимым, но теперь это исправлено.
glVertexAttribPointer
? В моих тестах порядок не имеет значения. (2) Если я правильно вас понял, атрибуты вершин являются глобальными, и только их включенное / отключенное состояние сохраняется в текущем привязанном VAO?glDrawArrays
илиglDrawElements
. Я обновлю сообщение, чтобы отразить это (2) Да, но хранится не только состояние включения / выключения, это все, что связано с этими вызовами - что было привязано к GL_ARRAY_BUFFER в то время, тип, шаг и смещение. По сути, он хранит достаточно, чтобы вернуть все атрибуты вершин так, как вы установили их с помощью VAO.layout(location = x)
в шейдере илиglBindAttributeLocation
при компиляции шейдера. ПримерТерминология и последовательность вызываемых API действительно сбивает с толку. Еще больше сбивает с толку то, как связаны различные аспекты - буфер, общий атрибут вершины и переменная атрибута шейдера. См. OpenGL-терминологию для довольно хорошего объяснения.
Далее по ссылке OpenGL-VBO, shader, VAO показан простой пример с необходимыми вызовами API. Это особенно хорошо для тех, кто переходит из немедленного режима в программируемый конвейер.
Надеюсь, это поможет.
Изменить: как видно из комментариев ниже, люди могут делать предположения и делать поспешные выводы. Реальность такова, что новичков это сбивает с толку.
источник
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)
идентификатор упоминается какindex
. Этот же идентификатор в контексте программы вызываетсяlocation
в API glGetAttribLocation .