Część terminologii jest trochę nieaktualna:
- A
Vertex Array
to po prostu tablica (zwykle a float[]
) zawierająca dane wierzchołków. Nie musi być z niczym związany. Nie należy mylić z a Vertex Array Object
lub VAO, o czym opowiem później
- A
Buffer Object
, powszechnie określane jako a Vertex Buffer Object
podczas przechowywania wierzchołków lub w skrócie VBO, jest tym, co nazywasz po prostu a Buffer
.
- Nic nie zostaje zapisane z powrotem w tablicy wierzchołków,
glVertexAttribPointer
działa dokładnie tak samo glVertexPointer
lub glTexCoordPointer
działa, tylko zamiast nazwanych atrybutów, otrzymujesz liczbę określającą twój własny atrybut. Przekazujesz tę wartość jako index
. Wszystkie Twoje glVertexAttribPointer
połączenia są umieszczane w kolejce do następnego połączenia glDrawArrays
lub glDrawElements
. Jeśli masz związane z VAO, VAO zapisze ustawienia dla wszystkich atrybutów.
Głównym problemem jest to, że mylisz atrybuty wierzchołków z VAO. Atrybuty wierzchołków to tylko nowy sposób definiowania wierzchołków, tekstów, normalnych itp. Do rysowania. Stan sklepu VAO. Najpierw wyjaśnię, jak działa rysowanie z atrybutami wierzchołków, a następnie wyjaśnię, jak zmniejszyć liczbę wywołań metod za pomocą VAO:
- Musisz włączyć atrybut, zanim będzie można go użyć w module cieniującym. Na przykład, jeśli chcesz wysłać wierzchołki do modułu cieniującego, najprawdopodobniej wyślesz go jako pierwszy atrybut 0. Dlatego przed renderowaniem musisz go włączyć za pomocą
glEnableVertexAttribArray(0);
.
- Teraz, gdy atrybut jest włączony, musisz zdefiniować dane, które będą używane. Aby to zrobić, musisz powiązać swoje VBO -
glBindBuffer(GL_ARRAY_BUFFER, myBuffer);
.
- A teraz możemy zdefiniować atrybut -
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
. W kolejności parametrów: 0 to atrybut, który definiujesz, 3 to rozmiar każdego wierzchołka, GL_FLOAT
to typ, GL_FALSE
czyli brak normalizacji każdego wierzchołka, ostatnie 2 zera oznaczają, że nie ma kroku ani przesunięcia na wierzchołkach.
- Narysuj coś tym -
glDrawArrays(GL_TRIANGLES, 0, 6);
- Następna rzecz, którą narysujesz, może nie używać atrybutu 0 (realistycznie będzie, ale to jest przykład), więc możemy go wyłączyć -
glDisableVertexAttribArray(0);
Umieść to w glUseProgram()
wywołaniach i masz system renderowania, który działa poprawnie z modułami cieniującymi. Ale powiedzmy, że masz 5 różnych atrybutów, wierzchołków, tekstów, normalnych, koloru i współrzędnych mapy światła. Po pierwsze, wykonywałbyś jedno glVertexAttribPointer
wywołanie dla każdego z tych atrybutów i musiałbyś wcześniej włączyć wszystkie atrybuty. Powiedzmy, że definiujesz atrybuty 0-4 tak, jak je mam na liście. Możesz włączyć je wszystkie w ten sposób:
for (int i = 0; i < 5; i++)
glEnableVertexAttribArray(i);
A potem musiałbyś powiązać różne VBO dla każdego atrybutu (chyba że przechowujesz je wszystkie w jednym VBO i używasz przesunięć / kroku), a następnie musisz wykonać 5 różnych glVertexAttribPointer
wywołań, odpowiednio od glVertexAttribPointer(0,...);
do glVertexAttribPointer(4,...);
dla wierzchołków do współrzędnych lightmapy.
Miejmy nadzieję, że sam ten system ma sens. Teraz przejdę do VAO, aby wyjaśnić, jak ich używać, aby zmniejszyć liczbę wywołań metod podczas wykonywania tego typu renderowania. Należy pamiętać, że korzystanie z VAO nie jest konieczne.
A Vertex Array Object
lub VAO są używane do przechowywania stanu wszystkich glVertexAttribPointer
połączeń i VBO, które były celem, gdy każde z glVertexAttribPointer
połączeń zostało wykonane.
Generujesz jeden z wezwaniem do glGenVertexArrays
. Aby przechowywać wszystko, czego potrzebujesz w VAO, glBindVertexArray
połącz go , a następnie wykonaj pełny draw . Wszystkie wywołania związane z remisem są przechwytywane i przechowywane przez VAO. Możesz rozpiąć VAO za pomocąglBindVertexArray(0);
Teraz, gdy chcesz narysować obiekt, nie musisz ponownie wywoływać wszystkich bindów VBO ani glVertexAttribPointer
wywołań, po prostu musisz powiązać VAO, a glBindVertexArray
następnie wywołać glDrawArrays
lub glDrawElements
i będziesz rysować dokładnie to samo, co gdybyś wykonywały wszystkie wywołania metod. Prawdopodobnie zechcesz później również rozwiązać VAO.
Po rozwiązaniu VAO cały stan wraca do stanu sprzed związania VAO. Nie jestem pewien, czy jakiekolwiek zmiany, które wprowadzisz, gdy VAO jest związane, zostaną zachowane, ale można to łatwo ustalić za pomocą programu testowego. Myślę, że możesz myśleć o tym glBindVertexArray(0);
jako o powiązaniu z „domyślnym” VAO ...
Aktualizacja: Ktoś zwrócił mi uwagę na potrzebę faktycznego wezwania do remisu. Jak się okazuje, nie musisz wykonywać FULL draw call podczas konfigurowania VAO, tylko wszystkie wiążące rzeczy. Nie wiem, dlaczego myślałem, że to konieczne wcześniej, ale teraz zostało to naprawione.
glVertexAttribPointer
? W moich testach kolejność nie wydaje się mieć znaczenia. (2) Jeśli dobrze cię rozumiem, atrybuty wierzchołków są globalne i tylko ich stan włączony / wyłączony jest zapisywany w aktualnie powiązanym VAO?glDrawArrays
lubglDrawElements
. Zaktualizuję post, aby odzwierciedlić to (2) Tak, ale nie chodzi tylko o stan włączenia / wyłączenia, który jest przechowywany, to wszystko, co dotyczy tych wywołań - to, co było wówczas powiązane z GL_ARRAY_BUFFER, typ, krok i przesunięcie. Zasadniczo ma wystarczająco dużo miejsca, aby zmienić wszystkie atrybuty wierzchołków z powrotem do sposobu, w jaki je skonfigurowałeś w VAO.layout(location = x)
w module cieniującym, czy zglBindAttributeLocation
podczas kompilowania modułu cieniującego. PrzykładTerminologia i sekwencja wywoływanych interfejsów API są rzeczywiście dość zagmatwane. Jeszcze bardziej zagmatwany jest sposób, w jaki różne aspekty - bufor, ogólny atrybut wierzchołka i zmienna atrybutu modułu cieniującego są powiązane. Zobacz terminologię OpenGL, aby uzyskać całkiem dobre wyjaśnienie.
Ponadto łącze OpenGL-VBO, shader, VAO pokazuje prosty przykład z niezbędnymi wywołaniami API. Jest to szczególnie dobre dla osób przechodzących z trybu bezpośredniego do programowalnego potoku.
Mam nadzieję, że to pomoże.
Edycja: Jak widać z poniższych komentarzy, ludzie mogą przyjmować założenia i wyciągać pochopne wnioski. W rzeczywistości jest to dość mylące dla początkujących.
źródło
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)
identyfikator jest określany jakoindex
. Ten sam identyfikator w kontekście programu jest wywoływanylocation
w API glGetAttribLocation .