Роз’яснення glVertexAttribPointer


94

Просто хочу переконатися, що я це правильно зрозумів (я б попросив ТАК "Чат", але він там мертвий)

У нас є масив Vertex, який ми робимо "поточним", прив'язуючи його,
потім маємо буфер, який ми прив'язуємо до цілі,
потім заповнюємо ту ціль, через glBufferData яку по суті заповнюємо все, що було прив'язане до цієї цілі, тобто наш буфер
а потім ми викликаємо, glVertexAttribPointerякий описує, як дані викладаються - дані є тим, до чого прив’язані, GL_ARRAY_BUFFER і цей дескриптор зберігається в нашому вихідному масиві Vertex

(1) Чи правильно я розумію? Документація трохи розріджена про те , як усіх коррелятах.

(2) Чи існує якийсь масив вершин за замовчуванням? Тому що я забув / опущена glGenVertexArraysі glBindVertexArrayта моя програма працювала відмінно без нього.


Змінити: Я пропустив крок ... glEnableVertexAttribArray.

(3) Чи викликаний Vertex Attrib прив’язаний до масиву Vertex на той час glVertexAttribPointer, і тоді ми можемо ввімкнути / вимкнути цю атрибуцію glEnableVertexAttribArrayв будь-який час, незалежно від того, який масив Vertex на даний момент пов’язаний?

Або (3b) Чи викликається вершина Attrib, прив’язана до масиву вершин у той час glEnableVertexAttribArray, і, отже, ми можемо додати ту саму вершину Attrib до декількох масивів вершин, викликаючи glEnableVertexAttribArrayв різний час, коли різні масиви вершин пов’язані?

Відповіді:


210

Деяка термінологія дещо відхилена:

  • A Vertex Array- це просто масив (як правило, а float[]), який містить дані вершин. Це не потрібно ні до чого прив'язувати. Не плутати з Vertex Array ObjectaO або VAO, про що я перейду пізніше
  • A Buffer Object, яку зазвичай називають Vertex Buffer Objectпри зберіганні вершин або коротким VBO - це те, що ви називаєте просто a Buffer.
  • Ніщо не зберігається назад у масиві вершин, glVertexAttribPointerпрацює точно так само glVertexPointerабо glTexCoordPointerпрацює, просто замість іменованих атрибутів ви отримуєте число, яке вказує ваш власний атрибут. Ви передаєте це значення як index. Усі ваші glVertexAttribPointerдзвінки стають в чергу на наступний раз, коли ви телефонуєте glDrawArraysабо glDrawElements. Якщо у вас пов’язаний VAO, 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 і використання зсувам / крок), то вам необхідно зробити 5 різних glVertexAttribPointerвикликів, від glVertexAttribPointer(0,...);до glVertexAttribPointer(4,...);вершин , до Lightmap координат відповідно.

Будемо сподіватися, що одна система має сенс. Тепер я перейду до VAO, щоб пояснити, як використовувати їх для скорочення кількості викликів методів під час здійснення цього виду візуалізації. Зауважте, що використовувати VAO не потрібно.

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

Ви генеруєте такий із викликом до glGenVertexArrays. Щоб зберегти все необхідне в VAO, зв’яжіть його glBindVertexArray, а потім здійсніть повний дзвінок . Усі дзвінки прив'язки притягування перехоплюються і зберігаються VAO. Ви можете відв’язати VAO за допомогоюglBindVertexArray(0);

Тепер , коли ви хочете намалювати об'єкт, вам не потрібно повторно закличуть все VBÖ прив'язки або glVertexAttribPointerдзвінки, вам просто потрібно зв'язати з ВАО glBindVertexArrayпотім зателефонувати glDrawArraysабо glDrawElementsі ви будете малювати одне і те ж , як якщо б вас робили всі ці виклики методів. Можливо, ви також захочете відв’язати VAO пізніше.

Після того, як ви від'єднаєте VAO, весь стан повертається до того, яким він був до того, як ви зв’язали VAO. Я не впевнений, чи зберігаються будь-які зміни, які ви робите під час зв’язку VAO, але це легко можна зрозуміти за допомогою тестової програми. Я думаю, ви можете вважати glBindVertexArray(0);прив'язкою до "за замовчуванням" VAO ...


Оновлення: Хтось звернув до мене увагу необхідності фактичного розіграшу дзвінка. Як виявляється, насправді вам не потрібно робити ПОВНИЙ виклик витягування при налаштуванні VAO, а лише всі пов’язувальні матеріали. Не знаю, чому я вважав це необхідним раніше, але це виправлено зараз.


10
"Об'єкт буферної вершини або VBO (іноді його називають просто буферним об'єктом)" Це "іноді" називається так, тому що це насправді так називається. Це просто буферний об'єкт, який не відрізняється від будь-якого іншого буферного об'єкта, який ви можете використовувати для рівномірних блоків, передачі пікселів, зворотного зв'язку перетворення або будь-якого іншого використання. Специфікація OpenGL ніколи ні до чого не посилається як на "об'єкт вершинного буфера"; навіть оригінальна специфікація розширення ніколи не називає це так.
Nicol Bolas 02

3
Відмінна відповідь. Дякуємо, що знайшли час написати це! Пару слідкуйте за запитаннями: (1) Ви сказали "перед тим, як зробити атрибут, і перед тим, як визначити атрибут, вам потрібно ввімкнути його за допомогою glEnableVertexAttribArray (0)" - ви впевнені, що його потрібно активувати перед викликом glVertexAttribPointer? У моїх тестах, здається, порядок не має значення. (2) Якщо я вас правильно розумію, атрибути Vertex є загальнодоступними, і лише їх увімкнений / вимкнений стан зберігається у поточно пов'язаному VAO?
mpen

1
(1) Я не думаю, що порядок має значення, якщо ви ввімкнули його раніше glDrawArraysабо glDrawElements. Я оновлю повідомлення, щоб відображати це (2) Так, але зберігається не лише стан увімкнення / вимкнення, це все, що стосується цих викликів - те, що було пов’язано з GL_ARRAY_BUFFER на той час, тип, крок та зсув. По суті, він зберігає достатньо для того, щоб змінити всі атрибути вершин назад до того, як ви налаштували їх за допомогою VAO.
Роберт Рухані,

2
так, VAO призначені для того, щоб дозволити замінити більшість методів розіграшу на прив’язку VAO. Кожна організація може мати окремий VAO, і це все одно буде працювати нормально. Вам все одно доведеться оновити форму та зв’язати власні текстури. І вам доведеться використовувати ті самі індекси атрибутів, що і вам потрібно пов'язувати атрибути ваших шейдерів з індексами атрибутів, незалежно від того, чи є це layout(location = x)в шейдері чи з glBindAttributeLocationпід час компілювання шейдера. Приклад
Роберт Рухані, 02

8
Зауважте, що в сучасному OpenGL вже немає об'єкта вершини масиву за замовчуванням, вам доведеться створити його самостійно, або ваша програма не працюватиме в контексті, сумісному з прямим перекладом.
Оверв

3

Термінологія та послідовність API, які потрібно викликати, справді досить заплутані. Що ще більш заплутано - це те, як асоціюються різні аспекти - буфер, загальний атрибут вершини та змінна атрибут shader. Дивіться OpenGL-термінологію для досить хорошого пояснення.

Далі, посилання OpenGL-VBO, шейдер, VAO показує простий приклад з необхідними викликами API. Це особливо добре для тих, хто переходить з негайного режиму в програмований конвеєр.

Сподіваюся, це допомагає.

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


" Див. OpenGL-Термінологія для досить гарного пояснення ". Менше, ніж через 1 хвилину пошуку, я вже знайшов дезінформацію: "Вони замінюються загальними атрибутами вершин з ідентифікатором (так званий індекс), який асоціюється із змінною шейдера (для координати, колір тощо), які обробляють атрибут ". Їх не називають "індексами"; вони "локації". Це дуже важлива відмінність, оскільки в атрибутах вершин також є "індекси" , що сильно відрізняється від локацій. Це дуже жахливий веб-сайт.
Нікол Болас

2
Це справедливий коментар, але не зовсім точний. Якщо ви подивитеся на API OpenGL для визначення загального атрибута glVertexAttribPointer , void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)ідентифікатор посилається на index. Той самий ідентифікатор у контексті програми викликається locationв API glGetAttribLocation .
ap-osd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.