Очистка glVertexAttribPointer

Просто хочу удостовериться, что я правильно понял (я бы спросил о SO Chat, но он там мертв!):

У нас есть Vertex Array, который мы делаем "текущим", привязывая его/ то у нас есть буфер, который мы привязываем к цели. то мы заполняем эту цель с помощью glBufferData который по существу заполняет все, что связано с этой целью, то есть наш буфер и затем мы называем glVertexAttribPointer, который описывает, как данные выкладываются - данные являются тем, что связано с GL_ARRAY_BUFFER и этот дескриптор сохраняется в нашем первоначальном массиве вершин

(1) Правильно ли я понимаю?
Документация немного скудна о том, как все коррелирует.

(2) Есть ли какой-то тип вершинного массива по умолчанию? Потому что я забыл/пропустил glGenVertexArrays и glBindVertexArray, и моя программа отлично работала без него.


Изменить: Я пропустил шаг... glEnableVertexAttribArray.

(3) Вызывается привязка вершинного атрибута к вершинному массиву в момент времени glVertexAttribPointer, и тогда мы можем включить/отключить атрибут через glEnableVertexAttribArray в любое время, независимо от того, в какой момент привязан массив вершин?

Или (3b) Является ли атрибут вершины привязан к вершинному массиву в момент времени glEnableVertexAttribArray, и поэтому мы можем добавить тот же атрибут вершины в несколько массивов вершин, вызывая glEnableVertexAttribArray в разное время, когда разные вершины Массивы связаны?

Ответ 1

Некоторая терминология немного выключена:

  • A Vertex Array - это просто массив (обычно a float[]), который содержит данные вершин. Это не обязательно должно быть связано ни с чем. Не путать с 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, просто все обязательные вещи. Не знаю, почему я думал, что это необходимо раньше, но теперь это исправлено.

Ответ 2

Терминология и последовательность API, которые должны быть вызваны, действительно запутывают. Еще более запутанным является то, как связаны различные аспекты - буфер, общий атрибут вершины и атрибут атрибута шейдера. См. OpenGL-Terminology для довольно хорошего объяснения.

Кроме того, ссылка OpenGL-VBO, шейдер, VAO показывает простой пример с необходимыми вызовами API. Это особенно хорошо для тех, кто переходит от непосредственного режима к программируемому конвейеру.

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

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