Уточнение glVertexAttribPointer
Просто хочу убедиться, что я правильно это понимаю (я бы спросил в SO Chat, но он там мертв!):
У нас есть массив вершин, который мы делаем "текущим", связывая его
тогда у нас есть буфер, который мы связываем с целью
тогда мы заполняем эту цель через glBufferData
который по существу заполняет все, что было связано с этой целью, то есть наш буфер
а потом мы позвоним glVertexAttribPointer
который описывает, как данные выложены - данные, которые связаны с GL_ARRAY_BUFFER
и этот дескриптор сохраняется в нашем исходном массиве вершин
(1) Правильно ли мое понимание?
Документация немного скудна о том, как все соотносится.
(2) Есть ли какой-то массив вершин по умолчанию? Потому что я забыл / опущен glGenVertexArrays
а также glBindVertexArray
и моя программа работала нормально без него.
Изменить: я пропустил шаг... glEnableVertexAttribArray
,
(3) Связан ли атрибут вершины с массивом вершин в то время glVertexAttribPointer
называется, а затем мы можем включить / отключить эту атрибуцию через glEnableVertexAttribArray
в любое время, независимо от того, какой массив вершин в настоящее время связан?
Или (3b) Связан ли атрибут вершины с массивом вершин в то время glEnableVertexAttribArray
называется, и, таким образом, мы можем добавить один и тот же атрибут вершины к нескольким массивам вершин, вызвав glEnableVertexAttribArray
в разное время, когда разные вершинные массивы связаны?
2 ответа
Некоторая терминология немного отличается:
Vertex Array
это просто массив (как правило,float[]
), который содержит данные вершин. Это не должно быть связано с чем-либо. Не путать сVertex Array Object
или ВАО, о котором я расскажу позжеBuffer Object
обычно упоминается какVertex Buffer Object
когда вы сохраняете вершины или VBO для краткости, это то, что вы называете простоBuffer
,- Ничто не будет сохранено обратно в массив вершин,
glVertexAttribPointer
работает так же, какglVertexPointer
или жеglTexCoordPointer
работать, вместо именованных атрибутов, вы предоставляете номер, который определяет ваш собственный атрибут. Вы передаете это значение какindex
, Все твоиglVertexAttribPointer
звонки будут поставлены в очередь при следующем вызовеglDrawArrays
или жеglDrawElements
, Если у вас есть привязка к 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 для каждого атрибута (если вы не храните их все в одном VBO и не используете смещения / шаг), тогда вам нужно сделать 5 разных glVertexAttribPointer
звонки, от glVertexAttribPointer(0,...);
в glVertexAttribPointer(4,...);
для вершин в координаты карты света соответственно.
Надеюсь, что эта система имеет смысл. Теперь я собираюсь перейти к VAO, чтобы объяснить, как их использовать, чтобы сократить количество вызовов методов при выполнении этого типа рендеринга. Обратите внимание, что использование VAO не обязательно.
Vertex Array Object
или VAO используется для хранения состояния всех glVertexAttribPointer
звонки и VBO, которые были направлены, когда каждый из glVertexAttribPointer
звонки были сделаны.
Вы генерируете один с призывом к glGenVertexArrays
, Чтобы хранить все, что вам нужно, в VAO, связать его с glBindVertexArray
, затем сделайте полный колл-розыгрыш. Все вызовы ничьей связываются, перехвачены и сохранены VAO. Вы можете отсоединить VAO с помощью glBindVertexArray(0);
Теперь, когда вы хотите нарисовать объект, вам не нужно повторно вызывать все привязки VBO или glVertexAttribPointer
звонки, вам просто нужно связать VAO с glBindVertexArray
затем позвоните glDrawArrays
или же glDrawElements
и вы будете рисовать точно так же, как если бы вы делали все эти вызовы методов. Вы, вероятно, хотите отменить привязку VAO впоследствии.
После того, как вы открепите VAO, все состояние вернется к тому, что было до того, как вы связали VAO. Я не уверен, сохраняются ли какие-либо изменения, которые вы вносите во время привязки VAO, но это легко выяснить с помощью тестовой программы. Я думаю, вы можете думать о glBindVertexArray(0);
как привязка к "по умолчанию" VAO...
Обновление: кто-то обратил мое внимание на необходимость фактического розыгрыша. Как выясняется, при настройке VAO вам на самом деле не нужно выполнять FULL-вызов, только все связывающие вещи. Не знаю, почему я думал, что это было необходимо раньше, но сейчас это исправлено.
Терминология и последовательность вызываемых API-интерфейсов действительно весьма запутанные. Еще более запутанным является то, как различные аспекты - буфер, общий атрибут вершины и переменная атрибута шейдера - связаны. См. OpenGL-Терминология для довольно хорошего объяснения.
Далее ссылка OpenGL-VBO, шейдер,VAO показывает простой пример с необходимыми вызовами API. Это особенно хорошо для тех, кто переходит из непосредственного режима в программируемый конвейер.
Надеюсь, поможет.
Редактировать: Как видно из комментариев ниже, люди могут делать предположения и делать поспешные выводы. Реальность такова, что это довольно запутанно для начинающих.