разъяснение glVertexAttribPointer

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

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

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

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


Edit: я пропустил шаг... glEnableVertexAttribArray.

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

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

2 ответов


некоторые из терминологии немного не так:

  • A Vertex Array - это просто массив (обычно float[]), который содержит данные вершин. Он не должен быть привязан ни к чему. Не путать с Vertex Array Object или ВАО, о котором я расскажу позже
  • A Buffer Object, называют Vertex Buffer Object при хранении вершин или VBO для краткости, это то, что вы называете просто Buffer.
  • ничто не сохраняется обратно в массив вершин,glVertexAttribPointer работает точно так же как glVertexPointer или glTexCoordPointer работа, только вместо именованных атрибутов, вы можете указать номер, который определяет ваш собственный атрибут. Вы передаете это значение как index. Все твои glVertexAttribPointer вызовы встают в очередь в следующий раз, когда вы звоните glDrawArrays или glDrawElements. Если у вас есть привязка VAO, VAO сохранит настройки для всех ваших атрибутов.

основная проблема здесь в том, что вы путаете атрибуты вершин с VAOs. Атрибуты вершин - это просто новый способ определения вершин, texcoords, нормалы, etc. для рисования. Состояние хранилища VAOs. Сначала я объясню, как работает рисование с атрибутами вершин, а затем объясню, как вы можете сократить количество вызовов методов с помощью VAOs:

  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 различных атрибутов, вершины, texcoords, нормали, цвет и координаты световой карты. Прежде всего, вы бы сделали один glVertexAttribPointer вызовите каждый из этих атрибутов, и вам придется включите все атрибуты заранее. Скажем, вы определяете атрибуты 0-4, как я их перечислил. Вы бы включили все из них так:

for (int i = 0; i < 5; i++)
    glEnableVertexAttribArray(i);

и тогда вам придется привязать разные VBOs для каждого атрибута (если вы не храните их все в одном VBO и не используете смещения/шаг), тогда вам нужно сделать 5 разных glVertexAttribPointer вызовах, с glVertexAttribPointer(0,...); to glVertexAttribPointer(4,...); для вершин к координатам световой карты соответственно.

надеюсь, что только эта система имеет смысл. Теперь я перейдем к VAOs, чтобы объяснить, как использовать их, чтобы сократить количество вызовов методов при выполнении этого типа визуализации. Обратите внимание, что использование VAO не требуется.

A Vertex Array Object или VAO используется для хранения состояния всех glVertexAttribPointer звонки и VBOs, которые были нацелены, когда каждый из glVertexAttribPointer звонки были сделаны.

вы генерируете один с вызовом glGenVertexArrays. Чтобы сохранить все необходимое в ВАО, свяжите его с glBindVertexArray, затем сделайте полную ничью звоните. Все ничья вызовы bind перехватываются и сохраняются ВАО. Вы можете развязать VAO с glBindVertexArray(0);

теперь, когда вы хотите нарисовать объект, вам не нужно повторно вызывать все привязки VBO или glVertexAttribPointer звонки, вам просто нужно связать ВАО с вызов glDrawArrays или glDrawElements а вы будете рисовать то же самое, как если бы вы делали все эти вызовы метода. Вы, вероятно, хотите, чтобы развязать VAO после этого тоже.

как только вы развязываете ВАО, все государство возвращается к тому, как это было до того, как вы связали ВАО. Я не уверен, что любые изменения, которые вы делаете, пока ВАО привязан, сохраняются, но это можно легко выяснить с помощью тестовой программы. Я думаю, вы можете думать glBindVertexArray(0); как привязка к" default " VAO...


обновление: кто-то обратил мое внимание на необходимость привлечения звонок. Как оказалось, вам на самом деле не нужно делать полный вызов draw, когда настройка ВАО, только все связующее. Не знаю, почему я думал, что это было необходимо раньше, но теперь это исправлено.


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

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

надеюсь, что это помогает.

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