Уточнение 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:

  1. Вы должны включить атрибут, прежде чем использовать его в шейдере. Например, если вы хотите отправить вершины в шейдер, вы, скорее всего, отправите его в качестве первого атрибута, 0. Поэтому перед рендерингом необходимо включить его с помощью glEnableVertexAttribArray(0);,
  2. Теперь, когда атрибут включен, вам нужно определить данные, которые он будет использовать. Для этого вам нужно привязать свой VBO - glBindBuffer(GL_ARRAY_BUFFER, myBuffer);,
  3. И теперь мы можем определить атрибут - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);, В порядке параметра: 0 - это атрибут, который вы определяете, 3 - размер каждой вершины, GL_FLOAT это тип, GL_FALSE означает не нормализовать каждую вершину, последние 2 нуля означают, что на вершинах нет шага или смещения.
  4. Нарисуйте что-нибудь с этим - glDrawArrays(GL_TRIANGLES, 0, 6);
  5. Следующая вещь, которую вы рисуете, может не использовать атрибут 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. Это особенно хорошо для тех, кто переходит из непосредственного режима в программируемый конвейер.

Надеюсь, поможет.

Редактировать: Как видно из комментариев ниже, люди могут делать предположения и делать поспешные выводы. Реальность такова, что это довольно запутанно для начинающих.

Другие вопросы по тегам