GlVertexAttribPointer-Klarstellung

94

Ich möchte nur sicherstellen, dass ich das richtig verstehe (ich würde im SO Chat fragen, aber es ist dort drin tot!):

Wir haben ein Vertex-Array, das wir durch Binden "aktuell" machen,
dann haben wir einen Puffer, den wir an ein Ziel binden,
dann füllen wir das Ziel, über glBufferData das im Wesentlichen alles gefüllt wird, was an dieses Ziel gebunden war, dh unser Puffer
und dann rufen wir auf, glVertexAttribPointerwas beschreibt, wie die Daten angeordnet sind - die Daten sind alles, woran sie gebunden sind, GL_ARRAY_BUFFER und dieser Deskriptor wird in unserem ursprünglichen Vertex-Array gespeichert

(1) Ist mein Verständnis richtig?
Die Dokumentation ist etwas spärlich darüber, wie alles korreliert.

(2) Gibt es eine Art Standard-Vertex-Array? Weil ich vergessen / weggelassen habe glGenVertexArraysund glBindVertexArraymein Programm ohne es gut funktioniert hat.


Edit: Ich vermisste einen Schritt ... glEnableVertexAttribArray.

(3) Wird das zum Zeitpunkt des Aufrufs an das Vertex-Array gebundene Vertex-Attribut glVertexAttribPointeraufgerufen, und dann können wir dieses Attribut jederzeit über aktivieren / deaktivieren glEnableVertexAttribArray, unabhängig davon, welches Vertex-Array derzeit gebunden ist?

Oder (3b) Wird das zum Zeitpunkt des Aufrufs an das Vertex-Array gebundene Vertex-Attribut glEnableVertexAttribArrayaufgerufen, und daher können wir mehreren Vertex-Arrays dasselbe Vertex-Attribut hinzufügen, indem wir glEnableVertexAttribArrayzu unterschiedlichen Zeiten aufrufen , wenn verschiedene Vertex-Arrays gebunden sind?

mpen
quelle

Antworten:

210

Einige der Begriffe sind etwas abweichend:

  • A Vertex Arrayist nur ein Array (normalerweise a float[]), das Scheitelpunktdaten enthält. Es muss an nichts gebunden sein. Nicht zu verwechseln mit einem Vertex Array Objectoder VAO, auf das ich später noch eingehen werde
  • A Buffer Object, im Allgemeinen als Vertex Buffer Objectbeim Speichern von Scheitelpunkten oder kurz VBO bezeichnet, ist das, was Sie nur als a bezeichnen Buffer.
  • Nichts wird im Vertex-Array gespeichert, glVertexAttribPointerfunktioniert genau wie glVertexPointeroder glTexCoordPointerfunktioniert, nur dass Sie anstelle von benannten Attributen eine Zahl angeben müssen, die Ihr eigenes Attribut angibt. Sie übergeben diesen Wert als index. Alle Ihre glVertexAttribPointerAnrufe werden beim nächsten Anruf glDrawArraysoder in die Warteschlange gestellt glDrawElements. Wenn Sie eine VAO-Bindung haben, speichert die VAO die Einstellungen für alle Ihre Attribute.

Das Hauptproblem hierbei ist, dass Sie Scheitelpunktattribute mit VAOs verwechseln. Scheitelpunktattribute sind nur die neue Methode zum Definieren von Scheitelpunkten, Texcoords, Normalen usw. zum Zeichnen. VAOs speichern den Status. Ich werde zuerst erklären, wie das Zeichnen mit Scheitelpunktattributen funktioniert, und dann erklären, wie Sie die Anzahl der Methodenaufrufe mit VAOs reduzieren können:

  1. Sie müssen ein Attribut aktivieren, bevor Sie es in einem Shader verwenden können. Wenn Sie beispielsweise Scheitelpunkte an einen Shader senden möchten, senden Sie ihn höchstwahrscheinlich als erstes Attribut 0. Bevor Sie also rendern, müssen Sie ihn mit aktivieren glEnableVertexAttribArray(0);.
  2. Nachdem ein Attribut aktiviert wurde, müssen Sie die Daten definieren, die verwendet werden sollen. Dazu müssen Sie Ihren VBO - binden glBindBuffer(GL_ARRAY_BUFFER, myBuffer);.
  3. Und jetzt können wir das Attribut definieren - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);. In der Reihenfolge des Parameters: 0 ist das Attribut, das Sie definieren, 3 ist die Größe jedes Scheitelpunkts, GL_FLOATist der Typ, GL_FALSEbedeutet, nicht jeden Scheitelpunkt zu normalisieren, die letzten 2 Nullen bedeuten, dass die Scheitelpunkte keinen Schritt oder Versatz aufweisen.
  4. Zeichne etwas damit - glDrawArrays(GL_TRIANGLES, 0, 6);
  5. Das nächste, was Sie zeichnen, verwendet möglicherweise nicht das Attribut 0 (realistisch gesehen, aber dies ist ein Beispiel), sodass wir es deaktivieren können - glDisableVertexAttribArray(0);

Schließen Sie das in glUseProgram()Anrufe ein und Sie haben ein Rendering-System, das mit Shadern richtig funktioniert. Angenommen, Sie haben 5 verschiedene Attribute, Eckpunkte, Texcoords, Normalen, Farben und Lightmap-Koordinaten. Zunächst würden Sie glVertexAttribPointerfür jedes dieser Attribute einen einzigen Aufruf tätigen, und Sie müssten alle Attribute im Voraus aktivieren. Angenommen, Sie definieren die Attribute 0-4 so, wie ich sie aufgelistet habe. Sie würden alle so aktivieren:

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

Und dann müssten Sie für jedes Attribut verschiedene VBOs binden (es sei denn, Sie speichern sie alle in einem VBO und verwenden Offsets / Stride), dann müssen Sie 5 verschiedene glVertexAttribPointerAufrufe ausführen , von glVertexAttribPointer(0,...);bis glVertexAttribPointer(4,...);für Eckpunkte bis hin zu Lightmap-Koordinaten.

Hoffentlich macht dieses System allein Sinn. Jetzt gehe ich zu VAOs über, um zu erklären, wie man sie verwendet, um die Anzahl der Methodenaufrufe beim Rendern dieser Art zu reduzieren. Beachten Sie, dass die Verwendung eines VAO nicht erforderlich ist.

Ein Vertex Array Objectoder VAO wird verwendet, um den Status aller glVertexAttribPointerAnrufe und der VBOs zu speichern, auf die bei jedem Anruf abgezielt wurde glVertexAttribPointer.

Sie generieren eine mit einem Anruf an glGenVertexArrays. Um alles, was Sie brauchen, in einem VAO zu speichern, binden Sie es an glBindVertexArrayund führen Sie dann einen vollständigen Draw-Aufruf durch . Alle Draw- Bind-Aufrufe werden vom VAO abgefangen und gespeichert. Sie können die VAO mit lösenglBindVertexArray(0);

Wenn Sie nun das Objekt zeichnen möchten, müssen Sie nicht alle VBO-Bindungen oder glVertexAttribPointer-Aufrufe erneut aufrufen. Sie müssen nur die VAO mit dem glBindVertexArrayanschließenden Aufruf binden , glDrawArraysoder glDrawElementsSie zeichnen genau das Gleiche wie Sie machten all diese Methodenaufrufe. Sie möchten die VAO wahrscheinlich auch danach wieder lösen.

Sobald Sie die VAO gelöst haben, kehrt der gesamte Status zu dem Zustand zurück, in dem er vor der Bindung der VAO war. Ich bin nicht sicher, ob Änderungen, die Sie vornehmen, während die VAO gebunden ist, beibehalten werden, aber das kann mit einem Testprogramm leicht herausgefunden werden. Ich denke, Sie können sich vorstellen, glBindVertexArray(0);dass dies für die "Standard" -VVO bindend ist ...


Update: Jemand hat mich auf die Notwendigkeit des eigentlichen Draw Calls aufmerksam gemacht. Wie sich herausstellt, müssen Sie beim Einrichten des VAO keinen vollständigen Draw-Aufruf ausführen, sondern nur das gesamte Bindungsmaterial. Ich weiß nicht, warum ich es früher für notwendig gehalten habe, aber es ist jetzt behoben.

Robert Rouhani
quelle
10
"Ein Vertex-Pufferobjekt oder VBO (manchmal auch nur als Pufferobjekt bezeichnet)" Es wird "manchmal" so genannt, weil es eigentlich so heißt. Es ist nur ein Pufferobjekt, das sich nicht von anderen Pufferobjekten unterscheidet, die Sie für einheitliche Blöcke, Pixelübertragung, Transformationsrückkopplung oder andere Zwecke verwenden können. Die OpenGL-Spezifikation bezieht sich niemals auf etwas als "Scheitelpunktpufferobjekt". Selbst die ursprüngliche Erweiterungsspezifikation nennt es nie so.
Nicol Bolas
3
Hervorragende Antwort. Vielen Dank, dass Sie sich die Zeit genommen haben, dies aufzuschreiben! Einige weitere Fragen: (1) Sie sagten: "Bevor Sie rendern und bevor Sie das Attribut definieren, müssen Sie es mit glEnableVertexAttribArray (0) aktivieren." - Sind Sie sicher, dass es vor dem Aufruf von aktiviert werden muss glVertexAttribPointer? In meinen Tests scheint die Reihenfolge keine Rolle zu spielen. (2) Wenn ich Sie richtig verstehe, sind die Vertex-Attribute global und nur ihr aktivierter / deaktivierter Status wird in der aktuell gebundenen VAO gespeichert.
Mpen
1
(1) Ich denke nicht, dass die Reihenfolge wichtig ist, solange Sie sie zuvor glDrawArraysoder aktiviert haben glDrawElements. Ich werde den Beitrag aktualisieren, um dies widerzuspiegeln. (2) Ja, aber es wird nicht nur der Aktivierungs- / Deaktivierungsstatus gespeichert, sondern alles, was mit diesen Aufrufen zusammenhängt - was zu der Zeit an GL_ARRAY_BUFFER gebunden war, Typ, Schritt und Versatz. Im Wesentlichen wird genug gespeichert, um alle Vertex-Attribute wieder so zu ändern, wie Sie sie mit dem VAO eingerichtet haben.
Robert Rouhani
2
Ja, VAOs sind so konzipiert, dass Sie den größten Teil einer Zeichenmethode durch das Binden eines VAO ersetzen können. Jede Entität kann eine separate VAO haben und es wird immer noch gut funktionieren. Sie müssen jedoch immer noch Uniformen aktualisieren und Ihre eigenen Texturen binden. Und Sie müssen dieselben Attributindizes verwenden, mit denen Sie die Attribute Ihres Shaders mit Attributindizes verknüpfen müssen, unabhängig davon, ob dies layout(location = x)im Shader oder glBindAttributeLocationbeim Kompilieren des Shaders erfolgt. Beispiel
Robert Rouhani
8
Beachten Sie, dass es in modernem OpenGL kein Standard-Vertex-Array-Objekt mehr gibt. Sie müssen eines selbst erstellen, da Ihre Anwendung sonst nicht in einem vorwärtskompatiblen Kontext funktioniert.
Overv
3

Die Terminologie und Reihenfolge der aufzurufenden APIs ist in der Tat ziemlich verwirrend. Noch verwirrender ist, wie die verschiedenen Aspekte - Puffer, generisches Vertex-Attribut und Shader-Attributvariable - miteinander verknüpft werden. Eine ziemlich gute Erklärung finden Sie unter OpenGL-Terminologie .

Ferner zeigt der Link OpenGL-VBO, Shader, VAO ein einfaches Beispiel mit den erforderlichen API-Aufrufen. Dies ist besonders gut für Benutzer geeignet, die vom Sofortmodus in die programmierbare Pipeline wechseln.

Ich hoffe es hilft.

Bearbeiten: Wie Sie den Kommentaren unten entnehmen können, können Personen Annahmen treffen und zu Schlussfolgerungen gelangen. Die Realität ist, dass es für Anfänger ziemlich verwirrend ist.

ap-osd
quelle
" Eine ziemlich gute Erklärung finden Sie unter OpenGL-Terminologie. " In weniger als einer Minute fand ich bereits eine Fehlinformation: "Diese werden durch generische Vertex-Attribute mit einem Bezeichner (Index genannt) ersetzt, der einer Shader-Variablen zugeordnet ist (z Koordinaten, Farben usw.), die das Attribut verarbeiten. " Sie werden nicht "Indizes" genannt; Sie sind "Orte". Dies ist eine sehr wichtige Unterscheidung, da Scheitelpunktattribute auch "Indizes" haben , die sich stark von den Standorten unterscheiden. Das ist eine sehr schreckliche Website.
Nicol Bolas
2
Das ist ein fairer Kommentar, aber nicht ganz richtig. Wenn Sie sich die OpenGL-API ansehen, um ein generisches Attribut glVertexAttribPointer zu definieren , void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)wird der Bezeichner als bezeichnet index. Der gleiche Bezeichner im Kontext des Programms wird locationin der API glGetAttribLocation aufgerufen .
ap-osd