web-dev-qa-db-de.com

Erläuterung von glVertexAttribPointer

Ich möchte nur sicherstellen, dass ich das richtig verstehe. (Ich würde auf SO Chat, aber es ist tot da drin!) Fragen.)

Wir haben ein Vertex-Array, das wir "aktuell" machen, indem wir es binden
dann haben wir einen Puffer, den wir an ein Ziel binden
Dann füllen wir dieses Ziel über glBufferData, das im Wesentlichen alles bevölkert, was an dieses Ziel gebunden war, d. h. unseren Puffer
und dann rufen wir glVertexAttribPointer auf, das beschreibt, wie die Daten angeordnet sind - die Daten sind alles, was an GL_ARRAY_BUFFER gebunden ist, und dieser Deskriptor wird in unserem ursprünglichen Vertex-Array gespeichert

(1) Ist mein Verständnis korrekt?
Die Dokumentation ist ein wenig spärlich, wie alles korreliert.

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


Bearbeiten: Ich habe einen Schritt verpasst ... glEnableVertexAttribArray.

(3) Ist das Vertex-Attribut zum Zeitpunkt des Aufrufs von glVertexAttribPointer an das Vertex-Array gebunden und können wir dieses Attribut jederzeit über glEnableVertexAttribArray aktivieren/deaktivieren, unabhängig davon, welches Vertex-Array sich befindet derzeit gebunden?

Oder (3b) Ist das Vertex-Attribut zum Zeitpunkt des Aufrufs von glEnableVertexAttribArray an das Vertex-Array gebunden und können wir dasselbe Vertex-Attribut mehreren Vertex-Arrays hinzufügen, indem wir glEnableVertexAttribArray zu verschiedenen Zeiten aufrufen. Wann sind verschiedene Vertex Arrays gebunden?

92
mpen

Ein Teil der Terminologie ist ein bisschen abweichend:

  • Ein Vertex Array Ist nur ein Array (normalerweise ein float[]), Das Eckendaten enthält. Es muss an nichts gebunden sein. Nicht zu verwechseln mit Vertex Array Object Oder VAO, auf die ich später noch eingehen werde
  • Ein Buffer Object, Allgemein als Vertex Buffer Object Beim Speichern von Vertices oder kurz VBO bezeichnet, ist das, was Sie als Buffer bezeichnen.
  • Es wird nichts in das Vertex-Array zurückgespeichert, glVertexAttribPointer funktioniert genauso wie glVertexPointer oder glTexCoordPointer, nur dass Sie anstelle der benannten Attribute eine Zahl angeben müssen, die angibt, welche Attribute verwendet werden Ihr eigenes Attribut. Sie übergeben diesen Wert als index. Alle Ihre glVertexAttribPointer Anrufe werden in die Warteschlange gestellt, wenn Sie das nächste Mal glDrawArrays oder glDrawElements anrufen. 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, Texkoorden, Normalen usw. zum Zeichnen. VAOs Speicherstatus. Ich werde zunächst erklären, wie Zeichnen mit Scheitelpunktattributen funktioniert, und dann erklären, wie Sie die Anzahl der Methodenaufrufe mit VAOs verringern 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 glEnableVertexAttribArray(0); aktivieren.
  2. Nachdem ein Attribut aktiviert wurde, müssen Sie die zu verwendenden Daten definieren. Dazu müssen Sie Ihren VBO - glBindBuffer(GL_ARRAY_BUFFER, myBuffer); binden.
  3. Und jetzt können wir das Attribut definieren - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);. In der Reihenfolge der Parameter: 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 es keinen Schritt oder Versatz auf den Eckpunkten gibt.
  4. Zeichne etwas damit - glDrawArrays(GL_TRIANGLES, 0, 6);
  5. Das nächste, was Sie zeichnen, verwendet möglicherweise nicht das Attribut 0 (realistisch, aber dies ist ein Beispiel), sodass wir es deaktivieren können - glDisableVertexAttribArray(0);

Wenn Sie dies in glUseProgram() -Aufrufen einschließen, verfügen Sie über ein Rendering-System, das ordnungsgemäß mit Shadern arbeitet. Angenommen, Sie haben 5 verschiedene Attribute, Scheitelpunkte, Texkoords, Normalen, Farben und Lightmap-Koordinaten. Zunächst würden Sie für jedes dieser Attribute einen einzelnen glVertexAttribPointer -Aufruf ausführen, und Sie müssten zuvor alle Attribute aktivieren. Angenommen, Sie definieren die Attribute 0-4 so, wie ich sie aufgelistet habe. Sie würden sie alle wie folgt aktivieren:

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

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

Hoffentlich macht dieses System allein Sinn. Jetzt gehe ich zu VAOs über und erkläre, wie man mit ihnen die Anzahl der Methodenaufrufe beim Rendern dieser Art verringert. Beachten Sie, dass die Verwendung eines VAO nicht erforderlich ist.

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

Sie generieren eine mit einem Aufruf von glGenVertexArrays. Um alles, was Sie brauchen, in einem VAO zu speichern, binden Sie es mit glBindVertexArrayFühren Sie dann einen vollständigen Draw-Aufruf durch. All die zeichnen Bindeanrufe werden von der VAO abgefangen und gespeichert. Sie können die VAO mit glBindVertexArray(0); entbinden.

Wenn Sie das Objekt zeichnen möchten, müssen Sie nicht alle VBO-Bindungen oder die glVertexAttribPointer -Aufrufe erneut aufrufen, sondern müssen nur die VAO mit glBindVertexArray binden und dann aufrufen glDrawArrays oder glDrawElements und Sie werden genau das Gleiche zeichnen, als würden Sie all diese Methodenaufrufe ausführen. Sie möchten die VAO wahrscheinlich auch später wieder lösen.

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


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

207
Robert Rouhani

Die Terminologie und Reihenfolge der aufzurufenden APIs ist in der Tat ziemlich verwirrend. Noch verwirrender ist die Zuordnung der verschiedenen Aspekte - Puffer, generisches Vertex-Attribut und Shader-Attributvariable. Siehe OpenGL-Terminologie für eine ziemlich gute Erklärung.

Weiterhin zeigt der Link OpenGL-VBO, Shader, VAO ein einfaches Beispiel mit den notwendigen API-Aufrufen. Es ist besonders gut für diejenigen, 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.

2
ap-osd