Die effizienteste Methode zum Zeichnen einer großen Anzahl derselben Objekte, jedoch mit unterschiedlichen Transformationen

8

Ich möchte eine große Anzahl (mehrere Tausend) einfacher Netze zeichnen (jedes vielleicht ... maximal 50 Dreiecke, aber selbst das ist eine sehr große Obergrenze), die alle genau gleich sind.

Als einfache Brute-Force-Methode mache ich gerade Tausende von Zeichenaufrufen, bei denen ich nur die Transformationsmatrizen ändere, die die Shader benötigen, und dann dieselben Objekte erneut zeichne.

Dies ist natürlich schrecklich langsam, da (ich vermute) zu viele Draw Calls für den Treiber und meinen PC erforderlich sind, um effizient zu arbeiten.

Was kann ich tun, um es schneller zu machen?

TravisG
quelle
4
Ich kann Ihnen keine vollständige Antwort geben, aber die allgemeine Antwort ist die Verwendung von Geometrieinstanzen .
Seth Battin
Suchen Sie in Google nach "Geometry Instancing". Vielleicht finden Sie etwas, das Ihnen dabei hilft
Luis W

Antworten:

11

Die Lösung hierfür ist die Instanz. In diesem Tutorial werden einige Instanzierungsmethoden erläutert. Wenn Sie ARB_draw_instancedund haben ARB_instanced_arrays, verwenden Sie sie.

Die allgemeine Idee besteht darin, alle Transformationen Ihrer Netze in einem separaten Pufferobjekt zu speichern und dieses an ein Attributarray zu binden, das einen "Scheitelpunkt" pro Instanz über verwendet glVertexAttribDivisor.

Wenn Sie danach immer noch nicht so schnell laufen, wie Sie möchten, sind Sie wahrscheinlich an den Vertex-Prozessor gebunden. Führen Sie ein Kegelstumpf-Keulen auf den Maschen durch und bauen Sie vorzugsweise einen BVH zum Durchqueren, anstatt das Keulen auf allen Maschen unabhängig voneinander durchzuführen.

Robert Rouhani
quelle
1
+1; Bei vielen tausend Objekten können Sie auch an die Transformationen CPU-gebunden werden (nur das Senden von Position und Drehung in Ihrem Puffer pro Instanz und das Berechnen der Matrix pro Objekt auf der GPU können hier helfen), und vergessen Sie nicht, zu beobachten, wie Sie aktualisieren diesen dynamischen Scheitelpunktpuffer!
Maximus Minimus
@ Robert Rouhani: Danke, ich habe es recherchiert und umgehend implementiert. Jetzt kann ich 5000 Maschen mit jeweils 20 Tris bei 250 FPS zeichnen. Ihr vorgeschlagener Weg scheint übrigens etwas veraltet zu sein. Da OpenGL 3.something Geometry Instancing offizieller Bestandteil der OpenGL-Kernspezifikation ist, ist ARB_xxx nicht mehr erforderlich. Ich verwende einen einheitlichen Puffer zum Speichern der Matrizen und verwende glDrawElementsInstanced. Im Vertex-Shader befindet sich ein Layout (std140) für einheitliche Matrizen {mat4 array [5000]; } wo ich über das Array [gl_InstanceID] auf das relevante Element zugreifen kann
TravisG
1
@TravisG Ich neige dazu, ältere Versionen zu unterstützen. Ich verstehe, dass diese beiden Erweiterungen für eine Weile zum Core befördert wurden, die Erweiterungszeichenfolge wird jedoch weiterhin vorhanden sein :). Die Verwendung glVertexAttribDivisor(die in keiner Weise veraltet oder veraltet ist) hat außerdem den Vorteil, dass die Anzahl der Instanzen nicht nach oben begrenzt ist. Die Leistung war in der Vergangenheit besser als bei einheitlichen Puffern, ist jedoch jetzt wahrscheinlich gleich oder sehr ähnlich, und beide Möglichkeiten erfüllen die Aufgabe.
Robert Rouhani
@ Robert Rouhani: Mhm ... ich denke es ist wahr. Jemand mit veralteten Treibern könnte ARB_xxx unterstützen, aber nicht die offiziellen OpenGl 3.x-Kernfunktionen. Ich werde mir glVertexAttribDivisor ansehen, danke.
TravisG
@ Robert Rouhani: Ich habe beide Versionen implementiert (unter Verwendung einheitlicher Puffer und alternativ Ihrer vorgeschlagenen Version, bei der ich ein Vertex-Attribut verwende, das sich nur einmal pro Instanz ändert), und die Version mit einheitlichem Puffer ist tatsächlich etwa doppelt so schnell (4000 fps im Release-Modus). vs ~ 2000 mit glVertexAttribDivisor war die 250 fps-Zahl nur für die CPU-Seite).
TravisG