Welche Rolle spielen glBindVertexArrays gegenüber glBindBuffer und in welcher Beziehung stehen sie?

74

Ich bin neu in OpenGL und Grafikprogrammierung. Ich habe ein Lehrbuch gelesen, das bisher sehr gründlich und gut geschrieben war. Ich habe jedoch einen Punkt im Code getroffen, den ich nicht ganz verstehe, und ich möchte diese Zeilen vor mir verstehen mach weiter.

GLuint abuffer;

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

GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

Das Buch erklärt, dass die ersten drei Zeilen ein Vertex-Array-Objekt erstellen, mit dem zugeordnete Daten mit einem Vertex-Array gebündelt werden. Die zweite Zeile findet einen nicht verwendeten Namen (ich vermute, eine vorzeichenlose Ganzzahlkennung ist darin gespeichert abuffer) und die dritte Zeile erstellt das Objekt / macht es aktiv.

Das Buch erklärt, dass die 4.-7. Zeile ein Pufferobjekt zum Speichern unserer Daten erstellt, wobei die 5. Zeile eine nicht verwendete Kennung enthält (ähnlich wie Zeile 2 für das Vertex-Array-Objekt?), Die 6. Zeile den Puffer erstellt und die 7. Zeile Zeile, die ausreichend Speicher auf der CPU reserviert und einen Zeiger auf unsere Daten (Punkte) für erstellt GL_STATIC_DRAW.

Was bedeutet es für das Objekt, aktiv zu sein? Wann würden Sie später verwenden abuffer? Was bedeutet es für ein Vertex-Array, zugeordnete Daten zu bündeln, und wann wurden die Daten diesem Vertex-Array-Objekt zugeordnet?

Ich bin verwirrt über die Beziehung zwischen abufferund buffer. Ich bin verwirrt darüber, wie die Beziehung des Vertex-Arrays zum Pufferobjekt ist und an welchem ​​Punkt diese Beziehung hergestellt wird. Ich bin mir nicht sicher, ob sie tatsächlich verwandt sind, aber sie werden unmittelbar nacheinander im Lehrbuch vorgestellt.

Jede Hilfe wäre dankbar. Vielen Dank.

DashAnimal
quelle

Antworten:

145

Aus einer einfachen Perspektive können Sie sich ein Array mit zwei Teilen vorstellen:

  • Informationen zu Größe, Form und Typ des Arrays (z. B. 32-Bit-Gleitkommazahlen mit Vektorreihen mit jeweils vier Elementen).

  • Die Array-Daten, die kaum mehr als ein großer Byte-Blob sind.

Obwohl das Low-Level-Konzept größtenteils gleich geblieben ist, hat sich die Art und Weise, wie Sie Arrays angeben, im Laufe der Jahre mehrmals geändert.

OpenGL 3.0 / ARB_vertex_array_object

So sollten Sie heute wahrscheinlich vorgehen. Es ist sehr selten, dass Leute gefunden werden, die OpenGL 3.x nicht ausführen können und dennoch Geld für Ihre Software haben.

Ein Pufferobjekt in OpenGL ist ein großer Bitklumpen. Stellen Sie sich den "aktiven" Puffer nur als globale Variable vor, und es gibt eine Reihe von Funktionen, die den aktiven Puffer anstelle eines Parameters verwenden. Diese globalen Statusvariablen sind die hässliche Seite von OpenGL (vor dem direkten Statuszugriff, der unten behandelt wird).

GLuint buffer;

// Generate a name for a new buffer.
// e.g. buffer = 2
glGenBuffers(1, &buffer);

// Make the new buffer active, creating it if necessary.
// Kind of like:
// if (opengl->buffers[buffer] == null)
//     opengl->buffers[buffer] = new Buffer()
// opengl->current_array_buffer = opengl->buffers[buffer]
glBindBuffer(GL_ARRAY_BUFFER, buffer);

// Upload a bunch of data into the active array buffer
// Kind of like:
// opengl->current_array_buffer->data = new byte[sizeof(points)]
// memcpy(opengl->current_array_buffer->data, points, sizeof(points))
glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);

Jetzt verwendet Ihr typischer Vertex-Shader Scheitelpunkte als Eingabe, keinen großen Bit-Blob. Sie müssen also angeben, wie der Bit-Blob (der Puffer) in Scheitelpunkte dekodiert wird. Das ist die Aufgabe des Arrays. Ebenso gibt es ein "aktives" Array, das Sie sich nur als globale Variable vorstellen können:

GLuint array;
// Generate a name for a new array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);

// Make the buffer the active array buffer.
glBindBuffer(GL_ARRAY_BUFFER, buffer);
// Attach the active buffer to the active array,
// as an array of vectors with 4 floats each.
// Kind of like:
// opengl->current_vertex_array->attributes[attr] = {
//     type = GL_FLOAT,
//     size = 4,
//     data = opengl->current_array_buffer
// }
glVertexAttribPointer(attr, 4, GL_FLOAT, GL_FALSE, 0, 0);
// Enable the vertex attribute
glEnableVertexAttribArray(attr);

OpenGL 2.0 (der alte Weg)

In OpenGL 2.x gab es keine Vertex-Arrays und die Daten waren nur global. Sie haben noch zu nennen glVertexAttribPointer()und glEnableVertexAttribArray(), aber man musste sie anrufen , jedes Mal , dass Sie einen Puffer verwendet. In OpenGL 3.x haben Sie das Array nur einmal eingerichtet.

Zurück zu OpenGL 1.5, Sie könnten tatsächlich Puffer verwenden, aber Sie haben eine separate Funktion verwendet, um jede Art von Daten zu binden. Zum Beispiel glVertexPointer()für Scheitelpunktdaten und glNormalPointer()für normale Daten. Vor OpenGL 1.5 gab es keine Puffer, aber Sie konnten Zeiger in Ihren Anwendungsspeicher verwenden.

OpenGL 4.3 / ARB_vertex_attrib_binding

In 4.3 oder wenn Sie die Erweiterung ARB_vertex_attrib_binding haben, können Sie das Attributformat und die Attributdaten separat angeben. Dies ist hilfreich, da Sie auf einfache Weise ein Scheitelpunktarray zwischen verschiedenen Puffern wechseln können.

GLuint array;
// Generate a name for a new array array.
glGenVertexArrays(1, &array);
// Make the new array active, creating it if necessary.
glBindVertexArray(array);

// Enable my attributes
glEnableVertexAttribArray(loc_attrib);
glEnableVertexAttribArray(normal_attrib);
glEnableVertexAttribArray(texcoord_attrib);
// Set up the formats for my attributes
glVertexAttribFormat(loc_attrib,      3, GL_FLOAT, GL_FALSE, 0);
glVertexAttribFormat(normal_attrib,   3, GL_FLOAT, GL_FALSE, 12);
glVertexAttribFormat(texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexAttribBinding(loc_attrib,      0);
glVertexAttribBinding(normal_attrib,   0);
glVertexAttribBinding(texcoord_attrib, 0);

// Quickly bind all attributes to use "buffer"
// This replaces several calls to glVertexAttribPointer()
// Note: you don't need to bind the buffer first!  Nice!
glBindVertexBuffer(0, buffer, 0, 32);

// Quickly bind all attributes to use "buffer2"
glBindVertexBuffer(0, buffer2, 0, 32);

OpenGL 4.5 / ARB_direct_state_access

In OpenGL 4.5 oder wenn Sie die Erweiterung ARB_direct_state_access haben, müssen Sie nicht mehr aufrufen glBindBuffer()oder glBindVertexArray()nur noch Dinge einrichten ... Sie geben die Arrays und Puffer direkt an. Sie müssen das Array nur am Ende binden, um es zu zeichnen.

GLuint array;
// Generate a name for the array and create it.
// Note that glGenVertexArrays() won't work here.
glCreateVertexArrays(1, &array);
// Instead of binding it, we pass it to the functions below.

// Enable my attributes
glEnableVertexArrayAttrib(array, loc_attrib);
glEnableVertexArrayAttrib(array, normal_attrib);
glEnableVertexArrayAttrib(array, texcoord_attrib);
// Set up the formats for my attributes
glVertexArrayAttribFormat(array, loc_attrib,      3, GL_FLOAT, GL_FALSE, 0);
glVertexArrayAttribFormat(array, normal_attrib,   3, GL_FLOAT, GL_FALSE, 12);
glVertexArrayAttribFormat(array, texcoord_attrib, 2, GL_FLOAT, GL_FALSE, 24);
// Make my attributes all use binding 0
glVertexArrayAttribBinding(array, loc_attrib,      0);
glVertexArrayAttribBinding(array, normal_attrib,   0);
glVertexArrayAttribBinding(array, texcoord_attrib, 0);

// Quickly bind all attributes to use "buffer"
glVertexArrayVertexBuffer(array, 0, buffer, 0, 32);

// Quickly bind all attributes to use "buffer2"
glVertexArrayVertexBuffer(array, 0, buffer2, 0, 32);

// You still have to bind the array to draw.
glBindVertexArray(array);
glDrawArrays(...);

ARB_direct_state_access ist aus vielen Gründen nett. Sie können das Binden von Arrays und Puffern vergessen (außer beim Zeichnen), damit Sie nicht an versteckte globale Variablen denken müssen, die OpenGL für Sie verfolgt. Sie können zwischen „Erzeugen Sie einen Namen für ein Objekt“ und „Erstellen eines Objekts“ , weil über den Unterschied vergessen glCreateBuffer()und glCreateArray()beides zugleich.

Vulkan

Vulkan geht noch weiter und lässt Sie Code wie den Pseudocode schreiben, den ich oben geschrieben habe. Sie sehen also so etwas wie:

// This defines part of a "vertex array", sort of
VkVertexInputAttributeDescription attrib[3];
attrib[0].location = 0; // Feed data into shader input #0
attrib[0].binding = 0;  // Get data from buffer bound to slot #0
attrib[0].format = VK_FORMAT_R32G32B32_SFLOAT;
attrib[0].offset = 0;
// repeat for attrib[1], attrib[2]
Dietrich Epp
quelle
6
+1 für die Beschreibung des GL-Selektor- / Latch-Mechanismus ( z. B. wird das Pufferobjekt, auf das ein Scheitelpunktzeiger angewendet wird, eher vom gebundenen Objekt als von einem übergebenen Parameter übernommen). Dieses API-Design kann umgangen werden GL_EXT_direct_state_access, aber das schließt leider aus, dass Ihr Code auf Mesa-, Intel- oder Apple-Treibern ausgeführt wird: - \ Ich denke jedoch, dass es wichtig ist, darauf hinzuweisen, dass Vertex-Arrays seit GL 1.1 existieren. Das einzige, was sich in GL 2.0 geändert hat, war die Einführung generischer Vertex-Attribut-Slots.
Andon M. Coleman
4
Apropos OpenGL 1.1, ist diese Version auch Textur eingeführt Objekte . Wie bei Vertex-Array-Objekten wurden Texturen bereits vor der Einführung von Texturobjekten in GL unterstützt, aber die Daten / der Status wurden nicht dauerhaft gespeichert. Anstatt das gebundene Texturobjekt in OpenGL 1.0 zu ändern, wurde die meiste Software geschrieben, um die Texturdaten / den Status in Anzeigelisten einzurichten. GL 3.0 hat etwas Ähnliches für Vertex-Arrays getan, es hat Vertex-Array- Objekte eingeführt .
Andon M. Coleman
7
Ich liebe den historischen Kontext hier. Eines der schrecklichsten Dinge beim Erlernen von GL ist, wie sich die API und die Best Practices im Laufe der Zeit geändert haben.
Cheezmeister
1
Sollten Sie für Ihr Beispiel unter OpenGL4.5 nicht glCreateVertexArrays(1, &array)over verwenden glGenVertexArrays(1, &array)? Meines Erachtens wird das Vao erst erstellt, wenn Sie es zum ersten Mal mit binden glGenVertexArrays, sodass keiner Ihrer glEnableVertexArray * / glVertexArray * -Aufrufe funktionieren würde.
Eichel
1
@Acorn: Guter Fang, ich habe es überarbeitet, um den Unterschied zwischen dem Generieren eines Namens und dem Erstellen eines Objekts deutlich zu machen.
Dietrich Epp
15

Ihre Interpretation des Buches ist nicht ganz richtig. Vertex-Array-Objekte speichern keine Daten. Sie sind eine Klasse von Objekten, die als Container bezeichnet werden, wie z. B. Framebuffer-Objekte. Sie können ihnen andere Objekte anhängen / zuordnen, aber sie speichern niemals Daten selbst. Als solche sind sie keine kontext gemeinsam nutzbare Ressource.

Grundsätzlich kapseln Vertex-Array-Objekte den Vertex-Array-Status in OpenGL 3.0. Beginnend mit OpenGL 3.1 (anstelle von GL_ARB_compatibility) und OpenGL 3.2+ Core-Profilen muss jederzeit ein VAO ungleich Null gebunden sein, damit Befehle wie glVertexAttribPointer (...)oder glDrawArrays (...)funktionieren. Das gebundene VAO bildet den erforderlichen Kontext für diese Befehle und speichert den Status dauerhaft.

In älteren Versionen von GL (und Kompatibilität) war der von VAOs gespeicherte Status Teil der globalen Statusmaschine.

Es ist auch erwähnenswert , dass die „aktuelle“ Bindung für GL_ARRAY_BUFFERist nicht einer der Staaten , dass vaos Spur. Während diese Bindung von Befehlen wie verwendet wird glVertexAttribPointer (...), speichern VAOs die Bindung nicht, sondern nur Zeiger (die GL_ARB_vertex_attrib_bindingneben GL 4.3 eingeführte Erweiterung erschwert dies ein wenig, lassen Sie uns sie der Einfachheit halber ignorieren).

VAOs erinnern sich jedoch daran, woran sie gebunden GL_ELEMENT_ARRAY_BUFFERsind, sodass indizierte Zeichenbefehle wie glDrawElements (...)die erwartete Funktion funktionieren ( z. B. verwenden VAOs den zuletzt gebundenen Elementarray-Puffer wieder).

Andon M. Coleman
quelle
Warum muss für den glVertexAttribPointer ein Puffer wie GL_ARRAY_BUFFER gebunden sein? Richtet der glVertexAttribPointer nicht nur den Status des VAO ein?
Brad Zeis
1
@BradZeis: Erstellt glVertexAttribPointereinen Zeiger, der relativ zum Speicher ist, der dem gehört, GL_ARRAY_BUFFERan den gebunden ist, wenn die Funktion aufgerufen wird. Ehrlich gesagt ist es eine Art dummes Design - in einer API wie D3D würden Sie einfach die ID des Pufferobjekts an das übergeben VertexAttribPointerund müssten nie etwas binden. In OpenGL gab glVertexPointer (...)es jedoch mehr als 10 Jahre, bevor VBOs eingeführt wurden, und sie wollten so viel API wie möglich ohne Modifikation wiederverwenden. Sie haben diese Bindung für ein VBO-Erstgeschäft ungleich Null erstellt , um das Hinzufügen eines weiteren Parameters zu vermeiden.
Andon M. Coleman
Vielen Dank! Es ist viel sinnvoller, sich die VAO als Zeiger vorzustellen.
Brad Zeis
1
Vielen Dank für den Hinweis, dass die Bindung für GL_ARRAY_BUFFER nicht zu den Zuständen gehört, die VAOs verfolgen .
Neevek
6

Die Beziehung wird beim Aufruf von glVertexAttribPointer erstellt .

Übersicht über VertexArrays

GL_VERTEX_ARRAY_BINDINGund GL_ARRAY_BUFFER_BINDINGsind Konstanten, aber sie können auf den globalen Zustand der Bindung verweisen. Ich beziehe mich auf den Zustand, nicht auf die Konstante (orange) im Bild. Verwenden Sie glGet , um Informationen zu verschiedenen globalen Zuständen zu erhalten.

VertexArray gruppiert Informationen (einschließlich Array-Puffer) über einen Scheitelpunkt oder viele parallele Scheitelpunkte.

Verwenden Sie GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDINGmit glGetVertexAttrib, um herauszufinden, welcher Attributarraypuffer festgelegt ist.

glBindBuffer (GL_ARRAY_BUFFER legt den globalen Status festGL_ARRAY_BUFFER_BINDING

glBindVertexArray legt den globalen Status festGL_VERTEX_ARRAY_BINDING

Jossi
quelle
Hey, das ist eine großartige Grafik. Haben Sie etwas dagegen, die Quelle davon zu teilen, bitte?
dawid
@dawid Ich habe es in lucidchart lucidchart.com/invitations/accept/… gemacht . Ich könnte es in Zukunft entfernen, da das kostenlose Konto nur begrenzt verfügbar ist.
Jossi
5

OpenGL ist eine Stateful-Schnittstelle. Es ist schlecht, antiquiert und hässlich, aber das ist ein Vermächtnis für dich.

Ein Vertex-Array-Objekt ist eine Sammlung von Pufferbindungen, mit denen der Treiber die Daten für die Draw-Aufrufe abrufen kann. Die meisten Tutorials verwenden nur das eine und erklären niemals, wie mehrere VAOs verwendet werden.

Eine Pufferbindung weist opengl an, diesen Puffer für verwandte Methoden zu verwenden, insbesondere für die glVertexAttribPointerMethoden.

Ratschenfreak
quelle
1
Eine beeindruckende Heuristik in StackOverflow hat versucht, mich daran zu hindern, Ihnen ein Kompliment zu machen, dass Sie OpenGL für seine Statefulness kritisiert haben.
ArchaeaSoftware
-2

Es gibt keine Beziehung zwischen VertexArray und VBO.

Ein Vertex-Array weist den Speicher im RAM zu und sendet einen Zeiger an die API. VBO weist den Speicher auf der Grafikkarte zu - der Systemspeicher hat keine Adresse dafür. Wenn Sie vom System aus auf die vbo-Daten zugreifen müssen, müssen Sie diese zuerst von vbo auf das System kopieren.

In neueren Versionen von OpenGL werden Vertex-Arrays vollständig entfernt (in 3.0 veraltet, in 3.1 entfernt).

Gasim
quelle
1
Vertex-Arrays werden in 3.x nicht entfernt. Vertex-Array-Objekte wurden in Version 3.0 eingeführt und sind in späteren Versionen des Kernprofils obligatorisch .
Dietrich Epp
2
Die Frage betraf Vertex-Array-Objekte, nicht Vertex-Arrays im Allgemeinen. Trotzdem sind Vertex-Arrays auch nicht veraltet. Die API, mit der sie eingerichtet wurden, hat sich grundlegend geändert (kein spezialisierter VetexPointer, NormalPointer usw.), aber es gibt immer noch Vertex-Arrays ( glVertexAttrib{I|L}Pointer (...)Ersatz für Dinge wie glVertexPointer (...)im modernen GL).
Andon M. Coleman