OpenGL: Ist es möglich, VAOs ohne Angabe eines VBOs zu verwenden?

12

In allen Tutorials zu VAOs (Vertex Array Objects) wird gezeigt, wie Sie diese verwenden, indem Sie Vertex-Attribute konfigurieren und ein VBO (Vertex Buffer Object) binden. Ich möchte jedoch ein VAO erstellen, das für eine Reihe von VBOs in Kombination mit einem festen Shader konfiguriert wird, wobei jeder Puffer dasselbe Datenmuster (Scheitelpunkt, UV, Farbe usw.) verwendet. Ich möchte also eine VAO für mehrere VBOs erstellen, die mit einem Shader gezeichnet werden.

Ich konnte keine Demo dazu finden, also habe ich beschlossen, es einfach zu versuchen. Aber es funktioniert nicht und stürzt beim glDrawArrayAnruf ab. Es sieht so aus, als ob der VBO nicht gebunden ist. Hier ist der Code, den ich benutze:

Rendern:

/* Prepare vertex attributes */
glBindVertexArrayOES(vao);

/* Upload the buffer to the GPU */
glBindBuffer(GL_ARRAY_BUFFER, pool->next());
glBufferSubData(GL_ARRAY_BUFFER, 0, parts * buffer.stride() * 6, buffer.getBuffer());

/* Draw the triangles */
glDrawArrays(GL_TRIANGLES, 0, parts * 6);

glBindVertexArrayOES(0);

VAO-Erstellung:

glBindVertexArrayOES(vao);

glEnableVertexAttribArray(ls.position);
glVertexAttribPointer(ls.position, 2, GL_FLOAT, GL_FALSE, buffer.stride(), 0);

glEnableVertexAttribArray(ls.color);
glVertexAttribPointer(ls.color, 3, GL_FLOAT, GL_FALSE, buffer.stride(), GL_BUFFER_OFFSET(buffer.dataTypeSize * 2));

glBindVertexArrayOES(0);

Wo lsist ein einfaches struct, das die Attributpositionen enthält.

Im Rendering-Teil funktionierte auch das Vertauschen von glBindBufferund glBindVertexArrayOESnicht.

Die Frage ist also: Ist es überhaupt möglich, oder muss ich für jeden Puffer eine VAO erstellen? Und wenn ich für jeden VBO einen VAO erstellen muss, ist es dann möglich, die Daten des VBO glBufferSubDatain Kombination mit einem VAO zu aktualisieren ?

Martijn Courteaux
quelle

Antworten:

18

VAOs enthalten keinen "glBindBuffer" -Status mit Ausnahme des GL_ELEMENT_ARRAY_BUFFERBindungsstatus. Was Sie nicht verstehen, ist, dass glBindBuffer(GL_ARRAY_BUFFER) das nichts bringt . Nun, was das Rendern angeht, macht es nichts. Versuch es; kurz vor dem Aufruf von glDraw * call glBindBuffer(GL_ARRAY_BUFFER, 0); Ihr Rendering wird gut funktionieren.

Die Art und Weise, wie dies glVertexAttribPointerfunktioniert, ist, dass es das betrachtet, was zum Zeitpunkt desglVertexAttribPointer Aufrufs an GL_ARRAY_BUFFER gebunden ist . Nicht zum Rendern. Nicht später. Genau in diesem Moment. Es nimmt das Pufferobjekt, das sich dort befand, und speichert es in einem anderen OpenGL-Zustand, der in der VAO eingekapselt ist.

Im Allgemeinen haben Sie zwei Möglichkeiten, eine davon ist neu und sollte zu diesem Zeitpunkt wahrscheinlich nicht verwendet werden.

Ihre erste Option besteht darin, alle Objekte, die dasselbe Scheitelpunktformat verwenden (dh alles außer Pufferobjekt und Versatz), in dasselbe Pufferobjekt zu setzen. Erstellen Sie im Grunde genommen ein riesiges Array, das alle Scheitelpunkte für alle Objekte enthält, die dasselbe Format verwenden.

Wenn es Zeit zum Rendern ist, können Sie einen Teil der Scheitelpunkte rendern. glDrawArraysZum Rendern wird eine Reihe von Elementen benötigt, und Sie können Ihre Indizes anpassen glDrawElements, um dasselbe zu tun. Alternativ können Sie verwenden , glDrawElementsBaseVertexum die Ecken Bias , so dass ein zu jedem Index hinzugefügt versetzt ist. Dieser Versatz ist die Anzahl der Scheitelpunkte vor diesem Scheitelpunkt im großen Array.

Die andere Alternative besteht darin, das in GL 4.3 hinzugefügte relativ neue Formatattribut-Trennsystem zu verwenden . Es würde Ihnen erlauben, Puffer zu ändern, ohne das Format zurückzusetzen. Sie würden also eine einzelne VAO binden und diese dann bei Bedarf durch andere Puffer ersetzen glBindVertexBuffer. Dies ist auch in ES 3.1 verfügbar.

Nicol Bolas
quelle
1
Die NV GL4.3-Treiber sind jetzt vollständig freigegeben.
Maximus Minimus
Vielen Dank für die Erklärung der Funktionsweise von glVertexAttribPointer. Das war aufschlussreich und erklärt, warum und wie es funktioniert.
Martijn Courteaux
5

Die VAO speichert den Status glVertexAttribPointer. Das Ändern des VAO wirkt sich weder auf den aktuellen glBindBuffer aus, noch wirkt sich das Ändern des glBindBuffer auf den VAO aus. Nur der Aufruf glVertexAttribPointer wirkt sich auf die VAO aus, indem der beim Aufruf verwendete Puffer aufgezeichnet wird.

Die Antwort auf Ihre Frage lautet also nein.

Wenn Sie die Anzahl der Objekte reduzieren möchten, können Sie alle Ihre Netzdaten in einem großen VBO ablegen und im glDrawArrays-Aufruf mit den Argumenten 'first' und 'count' angeben, wo sich die Netzdaten im VBO befinden.

ccxvii
quelle
Alle Artikel im Internet über VAOs zeigen, dass das Binden einer VAO die VBO automatisch bindet. Ich denke also, ein VAO ist eine Konfiguration, die sowohl den glVertexAttribPointer-Status als auch den glBindBuffer- Status enthält. Und ja, das Binden einer VBO, wenn eine VAO gebunden ist, ändert die VAO. Ich sage das, weil ich das getestet habe und es effektiv tut. Woher kommen diese Informationen?
Martijn Courteaux
2
@MartijnCourteaux: " Alle Artikel im Internet über VAO zeigen, dass das Binden einer VAO automatisch die VBO bindet. " Nein, das tun sie nicht. Das OpenGL-Wiki zur Vertex-Spezifikation nicht. Zeigen Sie mir in der Tat einen einzigen Artikel im Internet, der besagt, dass jeder glBindBufferStaat außer GL_ELEMENT_ARRAY_BUFFER durch die Bindung einer VAO geändert wird.
Nicol Bolas
Die andere Antwort erklärte gut, wie die Methode glVertexAttribPointer funktioniert. Es wird das zu diesem Zeitpunkt gebundene VBO verwendet. Sie haben Recht! Danke, ich verstehe jetzt.
Martijn Courteaux