Was ist für das Voxel-Rendering effizienter: vorgefertigtes VBO oder ein Geometrie-Shader?

26

Was ist bei einem relativ statischen Voxel-Array effizienter: Verwenden der CPU zum Vorgenerieren eines VBO zum Rendern der Voxel-Flächen (wobei fortgeschrittenere Formen des Renderns wie z. B. Marching Cubes derzeit ignoriert werden) oder Verwenden eines Geometrie-Shaders auf der GPU zum Generieren des fliegende Gesichter?

Ich mache mir keine Sorgen um die Aktualisierung von Voxelwechseln, aber das ist natürlich ein Vorteil der GPU-Version, da Sie die VBOs nicht neu erstellen müssen. Außerdem fühlt sich der GS-Ansatz etwas moderner an :)

Andererseits habe ich mir nicht die Details angesehen, wie ein GS mit der Rasterisierungs-Pipeline in modernen GPUs tatsächlich funktioniert. Gibt es die Vertices in eine Art Stream-Cache aus oder werden die Vertices dazwischen in den normalen GPU-Speicher geschrieben? In letzterem Fall könnte die On-the-Fly-Generierung die verfügbare Bandbreite und die Rechenleistung der restlichen GPU-Aufgaben verringern, und es wäre vorteilhafter, dies auf der CPU zu tun.

Björn Wesen
quelle

Antworten:

9

Ich denke an eine Minecraft-Szene, in der mit Voxel eine Welt von Blöcken gemeint ist, die tatsächlich mit Polygonen gerendert werden:

Wenn Sie einen Geometrie-Shader verwenden, ist es schwierig, genau drei Flächen (oder was auch immer) pro Voxel zu vermeiden.

Wenn Sie viele benachbarte Blöcke haben, die die gleiche Textur haben, können Sie das Kacheln der Texturen verwenden, um in einem VBO-Ansatz viel weniger Dreiecke in Ihrem (entarteten) Streifen zu haben. Ich meine, wenn es eine schöne große, flache 6x6-Fläche mit Grasvoxeln gibt, können Sie die gesamte Oberseite in nur zwei Dreiecken anstatt in 64 zeichnen.

Mit dem GS-Ansatz können Sie die triviale Auslese von Gesichtern, die von benachbarten Voxeln verdeckt werden, nicht durchführen, was auch mit einem VBO-Ansatz sehr einfach ist.

Ich habe den GS-Ansatz nicht ausprobiert, aber ich kann sagen, dass der VBO-Ansatz mit dem Kombinieren von sich wiederholenden benachbarten Kacheln sehr gut funktioniert. Ich fand, dass das Durcheinander mit Elementindizes viel langsamer ist, als nur die Eckpunkte zu wiederholen. Wenn Sie Ihre Welt in schöne kleine Würfel aufteilen, können Sie in der Regel nur ein Byte pro Komponente und Scheitelpunkt verwenden und sogar die Texturinformationen und Normalen (eine Fläche auf einem achsenausgerichteten Würfel hat nur 3 mögliche Normalen) usw. in ein viertes Byte packen 4 Bytes pro Vertex, das ist schön und schnell.

Ich habe für jedes der 6 Gesichter separate VBOs verwendet - es müssen immer nur maximal 3 davon gezeichnet werden. Dies passt gut zu den verschiedenen Texturen, die normalerweise für die oberen Teile von Minecraft-Voxeln verwendet werden. Denn für jeden Satz ist das Normal und So dann einheitlich.

Mit vertikal gekachelten Pixmaps in einem Atlas mit GL_REPEAThorizontaler Achse und um 90 Grad gedrehten Versionen der Pixmaps im selben Atlas kann ich mit demselben VBO im selben Aufruf massenhaft scheinbar unterschiedliche Blöcke zeichnen. Im 6x6-Grasflächenbeispiel hätte ich das in 12 Dreiecke aufgeteilt, da ich in meinem Atlas nur Wiederholungen in einer Dimension habe.

Ich habe es meistens auf das sehr niedrige Niveau von integrierten Grafikchips und Mobilgeräten gebracht, bei denen GS nur etwas ist, wovon ich träumen kann, wenn ich eines Tages damit spiele.

Wille
quelle
3
Sie müssen nur maximal 3 Gesichter pro Voxel zeichnen. Je nach Ansicht müssen Sie jedoch möglicherweise unterschiedliche Gesichter für jedes Voxel zeichnen, damit die Optimierung nicht so einfach ist, oder? Ein vorgefertigtes VBO enthält mehr als ein Voxel. Wenn sich Ihr Blickwinkel zwischen den Voxeln befindet, sehen Sie die Ost- und die Westseite der anderen Seite. Das hilft nur, wenn Sie die tatsächlich nach hinten gerichteten Flächen trivial aussortieren können. Im schlimmsten Fall rendern Sie jedoch immer noch 5 von 6 Seiten in einer Gruppe von Voxeln. Wenn Ihr Blickwinkel außerhalb der axialen Grenzen des VBO liegt, müssen Sie nur drei Seiten rendern.
Bjorn Wesen
Genau das Richtige für Björn, es ist machbar. (Aber ich erstelle nach Bedarf VBOs für Blöcke und überlege mir, was ich gebaut habe, wenn sich die Kamera bewegt, anstatt immer die ganze Welt in VBOs zu haben. Ich habe also eine natürliche Zeit, um diese Entscheidungen zu treffen.)
Will
10

Was ist mit der dritten Option, bei der instanziierte Arrays verwendet werden? Grundsätzlich zeichnen Sie mit einem einzigen Draw-Aufruf sehr viele Kästchen (bestehend aus einem einfachen 8-Vertex-Würfel), wobei Sie die Positionen (und andere Daten) als instanzspezifische Attribute aus dem Voxeldaten-VBO beziehen (mit glVertexAttribDivisorOpenGL, da bin ich mir sicher) DX hat das auch). Dies ist möglicherweise schneller als der Geometrie-Shader-Ansatz, obwohl der Anwendungscode (kein Shader) ziemlich ähnlich sein sollte, da ich mich an Geometrie-Shader erinnere, die den Ruf haben, langsam zu sein, obwohl ich noch keine Erfahrung mit ihnen habe (oder sie instanziiere) auf 2.1 Hardware.

Auf jeden Fall sollten entweder Geometrie-Shader oder instanziierte Arrays besser geeignet sein als von der CPU erstellte Voxel-Geometrie, insbesondere wenn sich die Voxel-Daten ändern können. In Verbindung mit Transformations-Feedback (Stream-Ausgabe in DX?) Können Sie möglicherweise eine gute GPU-basierte Culling-Technik einrichten.

Chris sagt Reinstate Monica
quelle
Ja, das ist die beste Lösung für dieses Problem. Warum ist mir nichts eingefallen? :)
Notabene
Nach einigem Experimentieren muss ich Ihnen sagen, dass gebackene Geometrie jede Instanz bei weitem übertrifft. Ich habe allerdings noch keine Geometrie-Shader ausprobiert.
Jari Komppa
@JariKomppa können Sie erläutern, was Sie mit gebackener Geometrie meinen?
Steven Lu
Instanzen vorübersetzt und in ein einzelnes Netz kopiert. Als hätte man ein Gitter, das hundert Würfel oder was auch immer darstellt.
Jari Komppa
@JariKomppa Ich habe die gleichen Ergebnisse gesehen, bei denen das Erstellen des Netzes viel schneller geht. Auf dem GTX 680 scheint die Instanzierungsoption jedoch viel schneller und seltsamer zu funktionieren.
Levi H
1

Geometry-Shader-Version klingt für mich viel besser. Sie können nur point vbo und construct box on the fly (Eingabepunkt, Ausgabedreieck-Stream) verwenden. Es ist schnell (noch schneller, wenn Sie die Tessellationseinheit im Shader-Modell 5 (Äq. DX11) verwenden) und reduziert die Bandbreite extrem. Es ist eine schöne und saubere Lösung.

Über GS. Es wird zwischen Vertex-Shader und Pixel-Shader eingefügt und modifiziert den ausgegebenen Vertex-Stream (primitive Streams). Während der Vertex-Shader nur für die Vertices funktioniert, funktioniert der Geometrie-Shader für ganze Primitive. Die Ausgabe dieses Streams geht nur an den Pixel-Shader (und wird davor gerastert :)), und es gibt keine Möglichkeit, ihn zu speichern. (Vielleicht durch ein verrücktes Rendern, um es zu texturieren und dann zu analysieren ... aber keine wirklich einfache Möglichkeit)

Leistungshinweis: Sie sollten in der Lage sein, auf alles im Geometrie-Shader zuzugreifen und den Vertex-Shader zu überspringen (nur Daten zu übergeben). Aber es ist nicht der beste Weg. Besser (schneller) ist es, den Vertex-Shader so gut wie möglich zu transformieren und das Geometrie-Shader-Programm so gering wie möglich zu halten. Haben Sie keine Angst, es für den Zyklus zu verwenden, wenn Sie es benötigen (zum Beispiel zum Erstellen von Boxen). Compiler wird es für Sie ausrollen.

Notabene
quelle
2
Es kann eine gute Idee sein, im Geometrie- und / oder Scheitelpunkt-Shader nach benachbarten Voxeln zu suchen und die Scheitelpunkte zu verwerfen oder die Flächen zu überspringen, wenn sie verdeckt sind. Andernfalls erhöht die GS-Lösung stattdessen die verwendete Bandbreite.
Tamschi,
Bandbreite wird kein großes Problem sein (aus meinen Erfahrungen), aber natürlich ist es wahr. Und Sie können nicht in den anderen Primitiven in GS suchen (ist mir bekannt :)).
Notabene
@Tamschi: ja dieses problem ist mir gleich nach dem schreiben dieser frage aufgetaucht .. bei der CPU-version werden voxel in der mitte von feststoffen unterdrückt, aber dies könnte auf der gpu unmöglich sein ohne einen vorlauf mit einem was sich belaufen würde differencing ..
Bjorn Wesen
1
Sie können den Vertex-Puffer an eine isamplerBuffer- oder usamplerBuffer-Uniform im Shader binden und dann mit texture (name_of_uniform, index) nachschlagen. Eine andere Möglichkeit wäre, den Puffer an ein einheitliches Array zu binden, wodurch Sie mehr Freiheit in dem Scheitelpunktformat haben, das Sie verwenden möchten.
Tamschi