Die effizienteste Methode zum Zeichnen von Scheitelpunkten mit OpenGL

8

Ich schreibe ein OpenGL 3D-Spiel. Es werden Tonnen von Dreiecken für Gelände und Objekte verwendet.

Ich lerne aus dem offiziellen OpenGL-Handbuch und die erste vorgestellte Methode besteht darin, für jeden Scheitelpunkt, den Sie zeichnen möchten, eine Funktion glVertexnach dem aufzurufen glBegin.

Diese Methode klingt jedoch ziemlich antik und ineffizient, wenn Sie Tausende von Dreiecken zeichnen müssen. Ich denke, es gibt Methoden, um die Informationen, die sich nicht bei jedem Frame ändern, im Grafikkartenspeicher zu speichern, sodass diese Informationen beim Rendern jedes Frames bereits vorhanden sind. Für "instabilere" Informationen müssen Sie wahrscheinlich in jedem Frame neue Scheitelpunktdaten senden, sodass eine andere Technik möglicherweise effizienter ist.

Kommen wir zu meiner Frage: Ich möchte eine klare Erklärung der modernsten und effizientesten OpenGL-Funktionen und -Techniken, die zum Senden von Scheitelpunktinformationen an die Hardware und zum Rendern von Anweisungen verwendet werden.

Was sind die besten Möglichkeiten und OpenGL-Tools, um die Reihenfolge zum Rendern von Scheitelpunktdaten zu senden, die sich während der Frames selten ändern (ich denke an Gelände und Objekte), und wie lassen sich Scheitelpunktdaten, die sich während der Frames stark ändern, am besten senden?

Marco
quelle
Sie haben Recht, glBegin () und glEnd () sind Teil des Sofortmodus-Renderings und ziemlich altmodisch. Heutzutage würde man zum Beispiel VBOs verwenden. Wie genau und was Sie verwenden, hängt jedoch von Ihrem Szenario ab!
Thalador
Ich bin auch ein Student, der versucht, OpenGL zu lernen. Ich weiß, dass Sie Vertex Buffer Objects (VBOs) verwenden müssen, um Vertexes schneller zu zeichnen. Obwohl ich einfach nicht genug weiß, um Ihnen zu sagen, wie das geht, kann ich Ihnen einen Link zu einem großartigen (kostenlosen Online-) Buch geben, das dies tut. Ich habe es überflogen und ein kleines Dreieck gemacht, aber es ist wirklich komplex. Arcsynthesis Modern OpenGL Tutorial Ich gehe gerade die LazyFoo Tutorials durch. Sie beginnen mit der Pipeline mit festen Funktionen glBegin (), glEnd () und wechseln dann in die modernere OpenGL, wobei die Pipeline mit festen Funktionen als Grundlage dient. Ich werde j
benbot

Antworten:

19

Im Großen und Ganzen lautet die Antwort "es kommt darauf an". Die Art und Weise, wie Partikel-Updates gesendet werden, unterscheidet sich erheblich von der Art und Weise, wie Sie eine Reihe von GPU-Modellen senden.

Scheitelpunkt- / Indexpuffer

Im allgemeinen Sinne wird heutzutage jedoch alles mit einem VBO ( Vertex Buffer Object ) erledigt . Die alte Sofortmodus-API ( glBegin/ glEnd) ist wahrscheinlich nur als Fat Wrapper um das interne Vertex-Puffersystem des Treibers implementiert.

Erstellen Sie für statische Objekte ein statisches VBO und füllen Sie es mit Ihren Scheitelpunktdaten. Wenn einer der Scheitelpunkte gemeinsam genutzt wird (normalerweise), möchten Sie wahrscheinlich auch einen Indexpuffer erstellen. Dies reduziert die Notwendigkeit, dieselben Daten mehr als einmal zu senden, um Netze zu ändern (möglicherweise Einsparung bei der Übertragungsbandbreite) und bei der Verarbeitung (Einsparung von Vertex-Shading-Zeit). Beachten Sie, dass Sie beim Erstellen von indizierten und nicht indizierten Zeichnungen mit unterschiedlichen Funktionen zeichnen.

Machen Sie dasselbe für dynamische Objekte, allerdings mit einem dynamischen Satz von Puffern.

Erweiterte Hinweise

Bei größeren Teilen wie Gelände werden Sie das Netz wahrscheinlich nicht in mehrere Teile aufteilen. Es ist eine enorme Verschwendung, die GPU dazu zu bringen, hundert Millionen Dreiecke zu rendern, wenn nur zweihunderttausend davon sichtbar sind, insbesondere wenn sie unsortiert sind und viele Überzieh- und verschwendete Fragment-Shader-Aufrufe vorliegen. Teilen Sie das Netz in große Stücke auf und rendern Sie dann nur diejenigen, die sich in der Ansicht befinden, frustrierend. Es gibt auch verschiedene fortgeschrittenere Keulungstechniken, mit denen Sie Brocken aussortieren können, die sich möglicherweise im Frustrum befinden, sich aber vollständig hinter einem Hügel oder Gebäude oder etwas anderem befinden. Es ist gut, den Countdown für Draw Calls niedrig zu halten, aber es besteht ein Gleichgewicht zwischen dem Minimieren von Draw Calls und dem Minimieren des Zeichnens versteckter Geometrie (das Sie für Ihre spezifische App / Hardware finden müssen).

Eines der wichtigsten Dinge, die Sie bei einem GPU-Puffer beachten sollten, ist, dass Sie nicht darauf schreiben können, während die GPU daraus liest. Sie müssen dem Treiber mitteilen, dass es in Ordnung ist, die alte Kopie des Puffers zu verwerfen (wenn dies erledigt ist) und Ihnen eine neue zu geben (wenn die alte beschäftigt ist). Es gibt natürlich lange Zeit keine Funktion von OpenGL, dies zu tun (jetzt gibt es InvalidateBufferData für GL 4.3 und einige ältere Implementierungen als Erweiterung). Vielmehr gibt es ein nicht standardmäßiges, aber allgemeines Verhalten, das die meisten Treiber implementieren. Gehen Sie folgendermaßen vor, um den Puffer zu verwerfen, bevor Sie ihn aktualisieren:

glBindBuffer(GL_ARRAY_BUFFER, my_vbo);
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_DYNAMIC_DRAW);

Natürlich ändern GL_ARRAY_BUFFERund GL_DYNAMIC_DRAWauf die entsprechenden Werte für den Puffer. Statische Puffer werden nicht aktualisiert (oder sollten es auch nicht sein), sodass Sie sich wahrscheinlich keine Gedanken über das Verwerfen eines solchen Puffers machen müssen.

Beachten Sie, dass die Verwendung möglicherweise schneller glBufferDataoder glBufferSubDataschneller ist glMapBuffer. Es hängt wirklich vom Treiber und der Hardware ab. PC-Hardware der aktuellen Generation wird wahrscheinlich am schnellsten sein, glBufferDataaber testen Sie, um sicher zu sein.

Eine andere Technik ist die Verwendung von Instanzen . Mit der Instanz können Sie einen einzelnen Zeichenaufruf ausführen, der mehrere Kopien der Daten in einem Scheitelpunkt- / Indexpuffer zeichnet. Wenn Sie beispielsweise 100 identische Steine ​​hätten, würden Sie sie alle auf einmal zeichnen wollen, anstatt 100 unabhängige Ziehungen durchzuführen.

Bei der Instanz müssen Sie die Daten pro Instanz in einem anderen Puffer ablegen (wie die Objektposition jedes Einzelnen). Dies kann entweder eine sein , gleichförmige Puffer ( konstante Puffer in D3D Terminologie) oder einen Texturpuffer oder ein Pro-Vertex - Instanz - Attribut. Auch hier kommt es darauf an, was schneller ist. Attribute pro Instanz sind wahrscheinlich schneller und definitiv viel einfacher zu verwenden, aber viele gängige GL-Implementierungen werden immer noch nicht unterstützt. glBindingAttribDivisorSie müssen also prüfen, ob sie verfügbar sind und ob sie wirklich schneller sind (einige ältere Treiber haben die Instanzierung durch Anhängen emuliert Puffer und es endete langsamer, Instanzen auf sie zu verwenden, als unabhängige Draw-Aufrufe zu tätigen, und es gibt keine Standardmethode, um herauszufinden ... die Freuden der Verwendung von OpenGL).

Es gibt auch Algorithmen zur Optimierung des Scheitelpunktcaches , bei denen Scheitelpunkte / Indizes in Ihren Puffern so angeordnet werden, dass sie mit dem Scheitelpunktcache auf modernen GPUs abgespielt werden. Eine GPU führt den Shader nur für einen Scheitelpunkt aus und speichert ihn dann im Scheitelpunkt-Cache. Möglicherweise muss er jedoch zu früh entfernt werden, um Platz für andere Scheitelpunkte zu schaffen. (Angenommen, zwei Dreiecke teilen sich einen Scheitelpunkt, aber es werden 100 andere Dreiecke zwischen ihnen gezeichnet. Der gemeinsame Scheitelpunkt wird wahrscheinlich zweimal vom Scheitelpunkt-Shader verschwenderisch ausgewertet.)

Einige dieser Funktionen erfordern eine ausreichend neue Version von GL oder GLES. GLES2 unterstützte beispielsweise keine Instanz.

Immer Profil

Wenn Sie sich erneut für die Leistung interessieren, testen Sie jede mögliche Methode und finden Sie heraus, welche für Ihre App auf Ihrer Zielhardware schneller ist. Unterschiedliche Hardware / Treiber verschiedener Hersteller sind nicht nur unterschiedlich, sondern einige ganze Hardwareklassen unterscheiden sich von Natur aus. Eine typische mobile GPU ist ein ganz anderes Tier als eine typische diskrete Desktop-GPU. Techniken, die bei einem "am besten" sind, sind bei einem anderen nicht unbedingt am besten.

Seien Sie immer skeptisch, wenn es um Leistung geht .

Sean Middleditch
quelle