Großer Vertex Buffer gegen mehrere Draw Calls

14

Ich fange gerade mit OpenGL an und versuche, damit ein 2D-Spiel zu erstellen. In diesem Spiel habe ich ein sechseckiges Gitter, das aus einer Vielzahl verschiedenfarbiger Sechsecke besteht. Als neuer OpenGL-Programmierer sehe ich zwei Möglichkeiten, dieses Raster zu zeichnen:

  1. Verwenden Sie einen Scheitelpunktpuffer mit den Daten für ein einzelnes Sechseck, verwenden Sie dann einen einheitlichen Versatzwert und iterieren Sie in der CPU, um dasselbe Programm so oft zu zeichnen, bis ich ein Raster habe.
  2. Erstellen eines singulären, sehr großen, vorberechneten Eckpunktpuffers, der alle Sechsecke in einem einzigen Aufruf zeichnet.

Was ist die effizienteste Methode? Gibt es einen besseren Weg, dies zu tun?

Alexis King
quelle
Ihr vorberechneter Scheitelpunktpuffer muss sich nur um ein Sechseck über den Bildschirm hinaus erstrecken. Sie können ihn dann vortäuschen, indem Sie sanft scrollen, bis Sie ein volles Sechseck bilden, und dann zurück "krümmen", da Sie für die Farben in diesem Szenario ein beibehalten können 2D-Textur auf der GPU, lesen Sie sie im Vertex-Shader und interpolieren Sie sie flach in den Fragment-Shader.
MickLH
Übergänge beziehen sich normalerweise auf eine Situation, in der eine Renderoperation auf den Ergebnissen einer vorherigen Operation beruht. Was Sie in dieser Frage fragen, hängt eigentlich mit der Reduzierung der Anzahl der Draw Calls in einem einzigen Durchgang zusammen. Ich weiß, dass es pedantisch klingt, aber es ist sehr wichtig, den Unterschied zu verstehen, da Multipass-Algorithmen sonst keinen Sinn ergeben;)
Andon M. Coleman
@ AndonM.Coleman Hmm, danke, ich bin eindeutig nicht mit der Grafikterminologie vertraut. Wie würde ich es in diesem Fall beschreiben? Mehrere Shader- / Programmaufrufe?
Alexis King
Sie können sofort erkennen, dass dies ein Single-Pass-Algorithmus ist, da keine Auftragsabhängigkeit besteht. Sie können diese Sechsecke in beliebiger Reihenfolge zeichnen und trotzdem das gleiche Ergebnis erzielen. Sie können mehrere Draw-Aufrufe ausführen , um OpenGL mit den Daten zu versorgen, die zum Rendern erforderlich sind. Es steht OpenGL jedoch frei, alle parallel zu zeichnen, da keine Abhängigkeiten bestehen. Wenn es sich um einen Mehrfachdurchlauf handelt , erfordert Sechseck B möglicherweise das Ergebnis von Sechseck A, bevor es gezeichnet werden kann, oder Sie müssen dasselbe Sechseck mehrmals zeichnen und das Ergebnis kombinieren.
Andon M. Coleman

Antworten:

9

Es gibt in der Tat einige Möglichkeiten, ein solches Raster zu erstellen.

Der effizienteste Weg wäre das Instanzen. Auf diese Weise erstellen Sie Ihr Sechseck nur einmal in einem VBO und rendern dieses hundert-, tausend- oder millionenfach. Sie können es manuell mit Shadern mit Uniformen tun, wie Sie in Punkt 1 gesagt haben, aber es gibt auch eine integrierte OpenGL-Funktionalität dafür. Schauen Sie sich dazu glDrawElementsInstanced an .

Beachten Sie, dass die Instanziierung nur dann schneller ist als andere Methoden, wenn Sie mehr als eine bestimmte Anzahl von instanziierten Objekten zeichnen. Zum Beispiel kann das Zeichnen von 300 mit 1 großen VBO schneller sein, aber das Zeichnen von 2 Millionen kann schneller sein, wenn Sie das instanziierte Rendern verwenden.

Wenn Sie das instanziierte Rendern verwenden, können Sie Objektdaten mit Attributteilern senden . In Ihrem Fall möchten Sie die Position und die Farbe senden.

Ein gutes Tutorial zum instanziierten Rendern: Klicken Sie auf

Am besten probieren Sie beide Methoden aus und überprüfen Sie, wie viele Millisekunden zum Zeichnen eines Frames erforderlich sind. Auf diese Weise lernst du auch beides, was immer gut ist.

Beachten Sie auch, dass das instanziierte Rendern eine moderne OpenGL-Funktionalität ist und dass Sie Shader verwenden müssen, um es zu verwenden. Aber es ist immer am besten, es von Anfang an richtig zu lernen.

Basaa
quelle
2
Instanzen sind nicht unbedingt die effizientesten. In vielen Implementierungen, von denen ich Profile gesehen habe, wurde die Instanzunterstützung für die Konformität angeheftet, aber langsamer als das individuelle Zeichnen vieler Objekte (in der Tat war es wahrscheinlich nur eine schlecht implementierte Schleife im Treiber, die genau das tat). Es ist eine gute Option, aber man sollte darauf achten, ein Profil auf dem Zielbetriebssystem / der Zielhardware zu erstellen und zu testen, bevor man Annahmen über die "effizienteste" macht.
Sean Middleditch
Einverstanden. Ich habe zum Beispiel verschiedene Performances unter Windows / Linux und Ati / nVidia gesehen. Danke für den Zusatz.
Basaa
1
Eigentlich. Wenn Sie mehrere kombinierte Netze in einem einzigen VBO zeichnen (die denselben Raum teilen). Kein Instancing könnte schneller sein. Das Problem beim Instanziieren ist: Scheitelpunkte sind nicht parallel berechnete Cross-Instances. Es beseitigt nur die GPU / CPU / GPU-Synchronisierung / den Drawcall. Es ist also schneller, einen Vertex-Puffer mit 1000 Kugeln zu zeichnen, als 1000 Kugeln mit Hardware-Instanzen zu zeichnen. (keine Kegelstumpf-Keulung / Objektentfernungs-Detailoptimierung)
Jeroen van Langen
3

Methode 1 ist einfacher zu codieren und ist in Ordnung, solange nicht zu viele Sechsecke gleichzeitig angezeigt werden. Möglicherweise möchten Sie dabei bleiben, da Sie noch nicht mit OpenGL vertraut sind, um nicht zu viel Komplexität auf einmal auf Ihren Teller zu bringen.

Wenn eine große Anzahl von Sechsecken (z. B. mehrere Hundert oder mehr als Tausend) gleichzeitig angezeigt wird, ist eine Methode mit geringerem Overhead erforderlich, um den CPU-Overhead bei der Ausführung so vieler Einzeldarstellungen zu vermeiden. Methode 2 würde dafür funktionieren, oder noch besser, Instanzen verwenden. Ich würde erwarten, dass die Instanzen schneller sind als Methode 2 oder sicherlich nicht schlechter, da Sie nur einen Puffer mit instanzbezogenen Daten aktualisieren müssen, anstatt einen (viel größeren) Puffer mit Vertexdaten für alle Instanzen.

Nathan Reed
quelle