Einige der Begriffe sind etwas abweichend:
- A
Vertex Array
ist nur ein Array (normalerweise a float[]
), das Scheitelpunktdaten enthält. Es muss an nichts gebunden sein. Nicht zu verwechseln mit einem Vertex Array Object
oder VAO, auf das ich später noch eingehen werde
- A
Buffer Object
, im Allgemeinen als Vertex Buffer Object
beim Speichern von Scheitelpunkten oder kurz VBO bezeichnet, ist das, was Sie nur als a bezeichnen Buffer
.
- Nichts wird im Vertex-Array gespeichert,
glVertexAttribPointer
funktioniert genau wie glVertexPointer
oder glTexCoordPointer
funktioniert, 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 glVertexAttribPointer
Anrufe werden beim nächsten Anruf glDrawArrays
oder 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:
- 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);
.
- 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);
.
- 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_FLOAT
ist der Typ, GL_FALSE
bedeutet, nicht jeden Scheitelpunkt zu normalisieren, die letzten 2 Nullen bedeuten, dass die Scheitelpunkte keinen Schritt oder Versatz aufweisen.
- Zeichne etwas damit -
glDrawArrays(GL_TRIANGLES, 0, 6);
- 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 glVertexAttribPointer
fü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 glVertexAttribPointer
Aufrufe 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 Object
oder VAO wird verwendet, um den Status aller glVertexAttribPointer
Anrufe 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 glBindVertexArray
und 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 glBindVertexArray
anschließenden Aufruf binden , glDrawArrays
oder glDrawElements
Sie 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.
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.glDrawArrays
oder aktiviert habenglDrawElements
. 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.layout(location = x)
im Shader oderglBindAttributeLocation
beim Kompilieren des Shaders erfolgt. BeispielDie 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.
quelle
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid * pointer)
wird der Bezeichner als bezeichnetindex
. Der gleiche Bezeichner im Kontext des Programms wirdlocation
in der API glGetAttribLocation aufgerufen .