Die verzögerte Schattierung ist nur eine Technik, um die eigentliche Schattierungsoperation für spätere Stadien "aufzuschieben". Dies kann großartig sein, um die Anzahl der Durchgänge zu verringern, die (zum Beispiel) benötigt werden, um 10 Lichter zu rendern, die 10 Durchgänge benötigen. Mein Punkt ist, dass es unabhängig von der verwendeten Rendering-Technik bestimmte mögliche Rendering-Optimierungen gibt, die die Anzahl der Objekte (Scheitelpunkte, Normalen usw.) reduzieren, die Ihre Rendering-Pipeline verarbeiten muss.
Es gibt keinen De-facto-Standard für Rendering-Optimierungen, sondern eine Reihe von Techniken, die austauschbar oder zusammen verwendet werden können, um bestimmte Leistungsmerkmale zu erzielen. Die Verwendung jeder Technik hängt stark von der Art der gerenderten Szene ab.
Beim verzögerten Rendern wird versucht, das Problem zu lösen, wenn die Anzahl der Lichter zunimmt, wodurch beim vorwärts gerenderten Rendern die Anzahl der Durchgänge explodieren kann.
Diese Techniken optimieren den verzögerten Schattierungsteil nicht direkt, aber gemäß Ihrer Beschreibung ist der verzögerte Schattierungsteil NICHT Ihr Problem. Ihr Problem ist jedoch, dass Sie die gesamte Szene an die Rendering-Pipeline senden. Ihre Engine muss also alle 100 Millionen Vertices in Ihrer Szene verarbeiten (zum Beispiel alle 100 Millionen Vertices), um das Ergebnis an den G-Buffer übergeben zu können, während die meisten dieser 100 Millionen Vertices trivial entfernt und nicht an den übergeben werden können Scheitelpunkt und Fragmente werden vorverarbeitet.
Im Falle eines Forward-Renderers wird der N-Vertex von der Vertex-Stufe insgesamt vertex count*lights count
und von der Fragment-Stufe insgesamt verarbeitet fragments count*number Lights
. Eine verzögerte Schattierung reduziert dies effektiv auf nur vertex count
für die Vertex-Stufe und fragments count
für die Fragment-Zählung, bevor das aufgelöst wird tatsächliche Schattierung. Dennoch kann N zu viel sein, um verarbeitet zu werden, insbesondere wenn die meisten von ihnen trivial gekeult werden können.
Dies macht das Culling bei Forward-Rendering / mehreren Durchläufen effektiver. Bedenken Sie jedoch, dass die meisten Engines einen Dual-Rendering-Ansatz verwenden, da verzögertes Shading allein keine transparenten Objekte auflösen kann. Daher ist die Verwendung dieser Optimierungen ein Muss. Ich kenne keine kommerzielle Engine, die nicht alle von ihnen ausführt.
Frustum Culling
Es müssen immer nur Objekte an die Rendering-Pipeline übergeben werden, die ganz oder teilweise im Ansichtsstumpf enthalten sind. Dies ist das Grundkonzept der Kegelstumpfentfernung. Leider kann es eine teure Operation sein, zu prüfen, ob sich ein Netz in der Ansicht befindet oder nicht. Stattdessen verwenden Konstrukteure ein ungefähres Begrenzungsvolumen, wie z Auch wenn dies möglicherweise nicht so genau ist wie die Verwendung des tatsächlichen Netzes, lohnt es sich nicht, den Genauigkeitsunterschied mit dem tatsächlichen Netz zu überprüfen.
Selbst bei Bounding-Volumes müssen Sie nicht jedes einzeln überprüfen. Alternativ können Sie eine Bounding-Volume-Hierarchie erstellen, um ein früheres Culling durchzuführen. Dies hängt in hohem Maße von der Komplexität der Szene ab.
Dies ist eine gute und einfache Technik für einen kleineren Motor und wird fast in jedem Motor verwendet, den ich jemals verwendet habe. Ich empfehle die Verwendung einer "normalen" Bounding Volume / Frustum-Prüfung ohne Hierarchien, wenn Ihre Engine nicht das Rendern sehr komplexer Szenen erfordert.
Keulen des hinteren Gesichts
Dies ist ein Muss, warum Gesichter zeichnen, die sowieso nicht sichtbar sind? Rendering-APIs bieten eine Schnittstelle zum Ein- und Ausschalten des Culling von Rückseiten. Dies ist ein Muss, es sei denn, Sie haben einen wichtigen Grund, warum Sie es nicht einschalten sollten, wie einige CAD-Anwendungen, die unter bestimmten Umständen Backfaces zeichnen müssen.
Occlusion Culling
Mit dem Z-Buffer können Sie die Sichtbarkeitsbestimmung auflösen. Das Problem ist jedoch, dass der Z-Buffer nicht immer sehr leistungsfähig ist, da der Z-Buffer erst in späteren Phasen der Pipeline aufgelöst werden kann. Zu verschließende Objekte sollten gerastert und möglicherweise in den Z-Buffer und in den geschrieben werden Farbpuffer vor dem Nichtbestehen des Z-Tests.
Occlusion Culling behebt dieses Problem, indem einige frühe Tests durchgeführt werden, um verschlossene Objekte zu entfernen, die sich im Rendering-Kegelstumpf befinden. Eine praktische Implementierung von Okklusions-Culling besteht darin, punktbasierte Abfragen zu verwenden und zu überprüfen, ob bestimmte Objekte aus einer bestimmten Perspektive sichtbar sind. Dies kann auch zum Löschen von Lichtquellen verwendet werden, die nicht zum endgültigen Bild beitragen. Dies ist insbesondere in einem verzögerten Engine-Renderer hilfreich.
Ein großartiges Beispiel für eine solche Technik in der realen Welt ist GTA5, wo die Wolkenkratzer strategisch in der Mitte der Stadt platziert sind, nicht nur Dekorationen sind, sondern auch als Okkluder fungieren, um den Rest der Stadt effektiv zu verschließen und zu verhindern, dass er ist gerastert.
Detaillierungsgrad
Der Detaillierungsgrad ist eine weit verbreitete Technik. Die Idee ist, eine einfachere Version des Netzes zu verwenden, wenn das Netz weniger zur Szene beiträgt. Es gibt zwei gängige Implementierungen. Man tauscht einfach das Netz gegen ein einfacheres aus, wenn es keinen großen Beitrag mehr leistet. Das Netz wird basierend auf einem Faktor wie dem Abstand und der Anzahl der Pixel (Fläche auf dem Geröll) ausgewählt, die das Netz einnimmt. Bei der anderen Version wird das Netz dynamisch tesselliert, was beim Rendern im Gelände weit verbreitet ist.
Was ist, wenn all dies nicht funktioniert?
Das ist eine gute Frage.
Als Erstes müssen Sie Ihre Anwendung mit einem Grafikprofiler profilieren und den Engpass ermitteln. Beachten Sie, dass sich der Engpass ändern kann, wenn sich der gerenderte Inhalt ändert. Möglicherweise sind auch Engpässe Teil des auf der CPU ausgeführten Codes, sodass Sie dies ebenfalls messen müssen.
Nachdem Sie einige Optimierungen am Engpass vorgenommen haben, müssen Sie berücksichtigen, dass es keine richtige Antwort dafür gibt und sich von Hardware zu Hardware unterscheiden wird.
Einige gebräuchliche Tricks zur GPU-Optimierung:
- Verzweigung in Shadern vermeiden.
- Probieren Sie verschiedene Scheitelpunktstrukturen aus, die beispielsweise
{VNT}
in demselben Array oder {V},{N},{T}
in verschiedenen Arrays verschachtelt sind .
- Szene von vorne nach hinten zeichnen.
- Schalten Sie den Z-Buffer an einigen Stellen aus, zum Beispiel wenn ein Bild keinen Z-Test benötigt.
- Verwenden Sie komprimierte Texturen.
Einige allgemeine Tricks zur CPU-Optimierung:
- Verwenden Sie Inline-Funktionen für kleine Funktionen.
- Verwenden Sie nach Möglichkeit SIMD (Single Instruction Multiple Data).
- Vermeiden Sie Cache-unfreundliche Speichersprünge.
- Verwenden Sie VBOs mit der "richtigen" Datenmenge. (je nach Hardware) Meistens sind aber weniger Draw Calls besser.
Aber was ist, wenn mein Engpass in der verzögerten Schattierung lag?
In diesem Fall besteht der offensichtlichste Teil darin, die tatsächlichen Schattenberechnungen zu optimieren, da sich die verzögerte Schattierung mehr um das Licht kümmert. Einige Punkte, die Sie im Auge behalten sollten:
- Rendern Sie Lichter, die sich tatsächlich auf das endgültige Bild auswirken. Mit anderen Worten: Schalten Sie die Lichter aus, die nicht dazu beitragen. Dies kann unter Verwendung des zuvor erwähnten Okklusions-Cullings effektiv implementiert werden.
- Benötigt diese Leuchte das Spiegelglas oder andere Komponenten? Vielleicht nicht.
- Wirft dieses Licht Schatten? Einige Lichter müssen keine Schatten werfen.
- Kann dieser Lichtbeitrag vorberechnet werden? Wenn es sich nicht bewegt, können wahrscheinlich einige Aspekte vorberechnet werden.