2015-06-14 10 views
5

Ich baute eine 2D-Grafik-Engine, und ich erstellt ein Batch-System für sie, also, wenn ich 1000 Sprites mit der gleichen Textur habe, kann ich sie mit einem einzigen Anruf zu zeichnen ÖffnenGl.Render mehrere Modelle in OpenGL mit einem einzigen Zeichenaufruf

Dies wird erreicht, indem alle Vertices aller Sprites mit derselben Textur in ein einzelnes vbo-Vertex-Array eingefügt werden.

Anstatt "diese Scheitelpunkte drucken, diese Scheitelpunkte drucken, diese Scheitelpunkte drucken", mache ich "alle Scheitelpunkte zusammen, drucke", um ganz klar zu sein. Einfach genug, aber jetzt versuche ich das Gleiche in 3D zu erreichen, und ich habe ein großes Problem.

Das Problem ist, dass ich eine Model View Projection-Matrix verwende, um meine Modelle zu platzieren und zu rendern. Dies ist der übliche Ansatz, um ein Modell im 3D-Raum zu rendern.

Für jedes Modell auf dem Bildschirm muss ich die MVP-Matrix an den Shader übergeben, so dass ich es verwenden kann, um jeden Eckpunkt an die richtige Position zu transformieren.

Wenn ich die Transformation außerhalb des Shaders tun würde, würde es von der CPU ausgeführt werden, was aus offensichtlichen Gründen keine gute Idee ist.

Aber das Problem liegt da. Ich muss die Matrix an den Shader übergeben, aber für jedes Modell ist die Matrix anders.

Also kann ich nicht dasselbe tun wie bei 2D-Sprites, weil das Ändern einer Shader-Uniform jedes Mal ein Unentschieden erfordert.

Ich hoffe, ich war klar, vielleicht haben Sie eine gute Idee, die ich nicht hatte oder Sie hatten bereits das gleiche Problem. Ich weiß eine Tatsache, dass es irgendwo eine Lösung gibt, denn in Engine wie Unity können Sie den gleichen Shader für mehrere Modelle verwenden und mit einem Zeichenaufruf davonkommen.

Antwort

7

Es gibt ein Feature genau wie Sie suchen für, und es heißt Instancing. Mit Instancing speichern Sie n Matrizen (oder was Sie sonst noch brauchen) in einem Uniform Buffer und rufen Sie glDrawElementsInstanced an, um n Kopien zu zeichnen. Im Shader erhalten Sie eine zusätzliche Eingabe gl_InstanceID, mit der Sie in den Uniform Buffer indexieren, um die Matrix abzurufen, die Sie für diese bestimmte Instanz benötigen.

Lesen Sie hier mehr über Instancing: https://www.opengl.org/wiki/Vertex_Rendering#Instancing

+0

Super Antwort! Ich habe über das Implementieren von etwas ähnlichem selbst unterrichtet, aber ich wusste nicht wirklich wie, das ist genau, was ich suchte – Ryno

1

Die Antwort hängt davon ab, ob die Vertex-Daten für jedes Element identisch sind oder nicht. Wenn dies der Fall ist, können Sie die Instanziierung wie in @ orosts Antwort unter Verwendung von glDrawElementsInstanced und gl_InstanceID innerhalb des Vertex-Shaders verwenden. Diese Methode sollte bevorzugt werden.

Wenn jedoch für jedes 3D-Modell andere Vertexdaten erforderlich sind (was häufig der Fall ist), können Sie sie immer noch mit einem einzigen Zeichenaufruf rendern. Zu diesem Zweck fügen Sie Ihren Scheitelpunktdaten einen weiteren Stream mit glVertexAttribPointer (und glEnableVertexAttribArray) hinzu. Dieser zusätzliche Datenstrom würde den Index der Matrix innerhalb des einheitlichen Puffers enthalten, den der Scheitelpunkt beim Rendern verwenden sollte - so würde jedes Netz innerhalb der VBO einen identischen Index im Extrastrom haben. Der Uniform Buffer enthält die gleichen Daten wie im Instancing Setup.

Beachten Sie, dass diese Methode möglicherweise eine zusätzliche CPU-Verarbeitung erfordert, wenn Sie die Stapelverarbeitung wiederholen müssen. Beispielsweise sollte ein Objekt in einem Stapel nicht mehr gerendert werden. Wenn dieser Prozess häufig benötigt wird, sollte festgestellt werden, ob Batch-Elemente tatsächlich nützlich sind oder nicht.

+0

Beste Antwort, die ich bis jetzt hatte, es half mir wirklich zu verstehen, wie ich es angehen kann ... Vielleicht werde ich versuche, ein System zu schreiben, das automatisch Instanziierung oder diese Methode wählt ... Ich kann zusammen mit den Meshes die Anzahl ihrer Eckpunkte speichern. Dann wird jedes Mal, wenn ich ein Netz zum System hinzuzufügen, wenn die Zahl das gleiche des Netzes vor ist, halte ich eine Bool isinstance = true, aber sobald ich eine andere Anzahl von Eckpunkten zu bekommen, habe ich es falsch. Am Ende des Frames render ich mit der einen oder anderen Methode, je nach bool Wert – Ryno

+0

Scheint Ihnen sinnvoll? – Ryno

+0

@ user3578051 Nicht nur die Anzahl der Scheitelpunkte ist wichtig, sondern auch der tatsächliche Inhalt. Instancing zeichnet im Grunde genommen dasselbe Mesh mehrmals. Vermutlich würden Sie die einheitlichen Daten zwischen Instanzen variieren. Bei diesem Verfahren können die Inhalte der Vertexpufferdaten zwischen den Instanzen vollständig unterschiedlich sein. Ein System, das beides beherrscht (und die Instanziierung bevorzugt), wäre wahrscheinlich die richtige Herangehensweise, es sei denn, Ihre Chargen ändern jeden Frame. – MuertoExcobito

0

Neben Instancing und Hinzufügen eines weiteren Vertex-Attribut als eine Objekt-ID, würde ich auch eine andere Strategie erwähnen möchte (die, obwohl moderne OpenGL erfordert):

Die Erweiterung ARB_multi_draw_indirect (in Kern seit GL 4.3) ergänzt indirekt Zeichenbefehle. Diese Befehle beziehen ihre Parameter (Anzahl der Vertices, Startindex usw.) direkt von einem anderen Pufferobjekt. Mit diesen Funktionen können viele verschiedene Objekte mit einem einzigen Zeichenaufruf gezeichnet werden.

Doch wie Sie noch einige pro-Objekt-Zustand wie Transformationsmatrizen wollen, ist diese Funktion nicht genug. Aber in Kombination mit ARB_shader_draw_parameters (noch nicht im Haupt-GL), erhalten Sie den Parameter , der für jedes einzelne Objekt in einem indirekten Mult-Draw-Aufruf um eins erhöht wird. Auf diese Weise können Sie in einige UBO, TBO oder SSBO (oder was auch immer) indizieren, wo Sie pro Objekt Daten speichern.

Verwandte Themen