Co to są obiekty tablicy wierzchołków?

114

Właśnie zaczynam uczyć się OpenGL dzisiaj z tego samouczka: http://openglbook.com/the-book/
Dotarłem do rozdziału 2, w którym rysuję trójkąt i rozumiem wszystko oprócz VAO (czy ten akronim jest w porządku?). Samouczek ma ten kod:

glGenVertexArrays(1, &VaoId);
glBindVertexArray(VaoId);

Chociaż rozumiem, że kod jest niezbędny, nie mam pojęcia, co robi. Chociaż nigdy nie używam VaoId po tym punkcie (z wyjątkiem zniszczenia go), kod nie działa bez niego. Zakładam, że dzieje się tak, ponieważ jest to wymagane, ale nie wiem dlaczego. Czy dokładnie ten kod musi po prostu być częścią każdego programu OpenGL? Samouczek wyjaśnia VAO jako:

Obiekt Vertex Array (lub VAO) to obiekt, który opisuje, w jaki sposób atrybuty wierzchołków są przechowywane w obiekcie Vertex Buffer Object (lub VBO). Oznacza to, że VAO nie jest rzeczywistym obiektem przechowującym dane wierzchołków, ale deskryptorem danych wierzchołków. Atrybuty wierzchołków można opisać funkcją glVertexAttribPointer i jej dwiema siostrzanymi funkcjami glVertexAttribIPointer i glVertexAttribLPointer, z których pierwszą zajmiemy się poniżej.

Nie rozumiem, jak VAO opisuje atrybuty wierzchołków. W żaden sposób ich nie opisałem. Czy pobiera informacje z glVertexAttribPointer? Myślę, że to musi być to. Czy VAO to po prostu miejsce docelowe dla informacji z glVertexAttribPointer?

Na marginesie, czy samouczek, który śledzę, jest do przyjęcia? Czy jest coś, na co powinienem uważać lub lepszy poradnik do naśladowania?

Patrick
źródło

Odpowiedzi:

100

„Obiekt Vertex Array” został dostarczony przez Podkomisję OpenGL ARB ds. Silly Names.

Pomyśl o tym jak o obiekcie geometrycznym. (Jako stary programista SGI Performer nazywam je zestawami geograficznymi.) Zmienne instancji / elementy obiektu to wskaźnik wierzchołka, wskaźnik normalny, wskaźnik koloru, wskaźnik atrybutu N, ...

Kiedy VAO jest po raz pierwszy związane, przydzielasz tych członków, dzwoniąc

glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer...;
glEnableClientState(GL_NORMAL_ARRAY); glNormalPointer...;

i tak dalej. Które atrybuty są włączone i podane wskaźniki są przechowywane w VAO.

Po tym, gdy ponownie powiążesz VAO, wszystkie te atrybuty i wskaźniki również staną się aktualne. Zatem jedno glBindVertexArraywywołanie jest równoważne z całym kodem potrzebnym wcześniej do skonfigurowania wszystkich atrybutów. Jest to przydatne do przekazywania geometrii między funkcjami lub metodami bez konieczności tworzenia własnych struktur lub obiektów.

(Jednorazowa konfiguracja, wielokrotne użycie to najłatwiejszy sposób korzystania z VAO, ale możesz także zmienić atrybuty, po prostu wiążąc je i wykonując więcej wywołań włączania / wskaźnika. VAO nie są stałymi).

Więcej informacji w odpowiedzi na pytania Patricka:

Domyślnie dla nowo utworzonego VAO jest puste (AFAIK). W ogóle nie ma geometrii, nawet wierzchołków, więc jeśli spróbujesz ją narysować, otrzymasz błąd OpenGL. Jest to rozsądnie rozsądne, jak w przypadku „zainicjuj wszystko na False / NULL / zero”.

Musisz to zrobić tylko glEnableClientStatewtedy, gdy wszystko skonfigurujesz. VAO zapamiętuje stan włączenia / wyłączenia dla każdego wskaźnika.

Tak, VAO będzie przechowywać pliki glEnableVertexAttribArrayi glVertexAttrib. Stare tablice wierzchołków, normalnych, kolorów, ... są takie same jak tablice atrybutów, wierzchołki == # 0 i tak dalej.

Hugh
źródło
62
„Obiekt Vertex Array” został dostarczony przez Podkomisję OpenGL ARB ds. Silly Names. ” Tak, taka głupia nazwa dla obiektu, który przechowuje wiązania tablic wierzchołków .
Nicol Bolas
2
Poza tym, VAO są w ogóle powiązane zglVertexAttribPointer
Patrickiem
2
Dodaj trochę informacji na temat używania ogólnych atrybutów wierzchołków dla osób, które używają profilu podstawowego.
Oskar
8
@NicolBolas Lepsza byłaby nazwa VertexArrayMacrolub coś podobnego.
bobobobo
7
@NicolBolas "Vertex Array Object" to okropna nazwa. Chodzi o wiązanie danych z atrybutami . Nie chodzi o tablicę wierzchołków, jak sama nazwa wskazuje. W nazwie nie ma odniesienia do powiązań ani atrybutów, a ponieważ „tablica wierzchołków” sama w sobie jest pojęciem oddzielonym, jeszcze trudniej ją zrozumieć. IMHO, „(Vertex) Attributes Binding Object” jest łatwiejsze do zrozumienia. Nawet Geometry Object jest lepszy: nie podoba mi się, ale przynajmniej nie jest przeciążony.
AkiRoss
8

Obiekty tablicy wierzchołków są jak makra w programach do przetwarzania tekstu i tym podobnych. Dobry opis znajduje się tutaj .

Makra po prostu zapamiętują wykonane przez Ciebie czynności, takie jak aktywacja tego atrybutu, powiązanie tego bufora itp. Kiedy wywołujesz glBindVertexArray( yourVAOId ), po prostu odtwarza te powiązania wskaźnika atrybutów i powiązania bufora.

Więc twoje następne sprawdzenie do drawa wykorzystuje wszystko, co było związane z VAO.

VAO nie przechowują danych wierzchołków . Nie. Dane wierzchołków są przechowywane w buforze wierzchołków lub w tablicy pamięci klienta.

bobobobo
źródło
19
-1: nie przypominają makr. Gdyby tak było, powiązanie nowego VAO nie spowodowałoby wyłączenia tablic wierzchołków włączonych przez poprzednie VAO, chyba że nowy VAO „zarejestrował”, że jawnie wyłączasz te tablice. VAO, podobnie jak wszystkie obiekty OpenGL, zatrzymują stan , a nie polecenia. Polecenia po prostu zmieniają stan, ale obiekty mają ustawiony domyślny stan. Dlatego powiązanie nowo utworzonego VAO zawsze wyłączy wszystkie atrybuty.
Nicol Bolas
6

Zawsze myślę o VAO jako o tablicy buforów danych używanych przez OpenGL. Używając nowoczesnego OpenGL, utworzysz obiekty VAO i Vertex Buffer.

wprowadź opis obrazu tutaj

//vaoB is a buffer
glGenVertexArrays(1, vaoB); //creates one VAO
glBindVertexArray(vao.get(0));
glGenBuffers(vbo.length, vbo, 0); //vbo is a buffer
glBindVertexArray(vao.get(1));
glGenBuffers(vbo1.length, vbo1, 0); //vbo1 is a buffer
glBindVertexArray(vao.get(2));
glGenBuffers(vbo2.length, vbo2, 0); //vbo2 is a buffer

Następnym krokiem jest powiązanie danych z buforem:

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]);
glBufferData(GL_ARRAY_BUFFER,vertBuf.limit()*4, vertBuf, GL_STATIC_DRAW); //vertf buf is a floatbuffer of vertices

W tym momencie OpenGL widzi:

wprowadź opis obrazu tutaj

Teraz możemy użyć glVertexAttribPointer, aby powiedzieć OpenGL, jakie dane w buforze reprezentują:

glBindBuffer(GL_ARRAY_BUFFER, 0); //bind VBO at 0
glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0); //each vertex has 3 components of size GL_FLOAT with 0 stride (space) between them and the first component starts at 0 (start of data)

wprowadź opis obrazu tutaj

OpenGL ma teraz dane w buforze i wie, jak dane są zorganizowane w wierzchołki. Ten sam proces można zastosować do współrzędnych tekstury itp., Ale dla współrzędnych tekstury byłyby dwie wartości.

glBindBuffer(GL_ARRAY_BUFFER, vbo[1]);
glBufferData(GL_ARRAY_BUFFER,coordBuf.limit()*4, coordBuf, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0);

Następnie możesz wiązać tekstury i rysować tablice, będziesz chciał stworzyć moduł cieniujący Vert i Frag, skompilować i dołączyć go do programu (nie jest tutaj uwzględniony).

glActiveTexture(textureID); //bind our texture
glBindTexture(GL_TEXTURE_2D, textureID);
glDrawArrays(GL_TRIANGLES,0,6); //in this case 6 indices are used for two triangles forming a square
vasmos
źródło
5

VAO to obiekt, który reprezentuje etap pobierania wierzchołków potoku OpenGL i służy do dostarczania danych wejściowych do Vertex Shader.

Możesz utworzyć obiekt tablicy wierzchołków w ten sposób

GLuint vao;
glCreateVertexArrays(1, &vao);
glBindVertexArray(vao);

Najpierw zróbmy prosty przykład. Rozważmy taki parametr wejściowy w kodzie modułu cieniującego

layout (location = 0) in vec4 offset; // input vertex attribute

Aby wypełnić ten atrybut, możemy użyć

glVertexAttrib4fv(0, attrib); // updates the value of input attribute 0

Chociaż obiekt tablicy wierzchołków przechowuje te statyczne wartości atrybutów dla ciebie, może zrobić o wiele więcej.

Po utworzeniu obiektu tablicy wierzchołków możemy przystąpić do wypełniania jego stanu. Poprosimy OpenGL o automatyczne wypełnienie go przy użyciu danych przechowywanych w dostarczonym przez nas obiekcie buforowym. Każdy atrybut wierzchołka pobiera dane z bufora związanego z jednym z kilku powiązań bufora wierzchołków. W tym celu używamy glVertexArrayAttribBinding(GLuint vao, GLuint attribindex, GLuint bindingindex). Używamy również glVertexArrayVertexBuffer()funkcji do wiązania bufora z jednym z powiązań bufora wierzchołków. Używamy glVertexArrayAttribFormat()funkcji do opisu układu i formatu danych, a na końcu umożliwiamy automatyczne wypełnianie atrybutu przez wywołanie glEnableVertexAttribArray().

Gdy atrybut wierzchołka jest włączony, OpenGL przesyła dane do modułu cieniującego wierzchołki na podstawie formatu i informacji o lokalizacji, które podałeś w glVertexArrayVertexBuffer()i glVertexArrayAttribFormat(). Gdy atrybut jest wyłączony, moduł cieniujący wierzchołków otrzyma statyczne informacje, które podasz w wywołaniu glVertexAttrib*().

// First, bind a vertex buffer to the VAO
glVertexArrayVertexBuffer(vao, 0, buffer, 0, sizeof(vmath::vec4));

// Now, describe the data to OpenGL, tell it where it is, and turn on automatic
// vertex fetching for the specified attribute
glVertexArrayAttribFormat(vao, 0, 4, GL_FLOAT, GL_FALSE, 0);

glEnableVertexArrayAttrib(vao, 0);

I koduj w module cieniującym

layout (location = 0) in vec4 position;

W końcu musisz zadzwonić glDeleteVertexArrays(1, &vao).


Możesz przeczytać OpenGL SuperBible, aby lepiej to zrozumieć.

Yola
źródło
3
Dobrze jest widzieć ludzi promujących użycie OpenGL w stylu DSA.
Nicol Bolas