Einsteigerguide: Ein kleiner Überblick über das Culling.
Im 3D-Rendering beschreibt der Begriff Culling die frühzeitige Ablehnung von Objekten jeglicher Art (Objekte, Draw Calls, Dreiecke und Pixel), die nicht zum endgültigen Bild beitragen. Es gibt viele Techniken, die Objekte in verschiedenen Phasen der Rendering-Pipeline ablehnen. Einige dieser Techniken werden vollständig in Software auf der GPU durchgeführt, andere sind hardwaregestützt (GPU) und wieder andere sind in die Grafikkarte integriert. Es ist hilfreich, alle diese Techniken zu verstehen, um eine gute Leistung zu erzielen. Um den Verarbeitungsaufwand so gering wie möglich zu halten, ist es besser, frühzeitig zu selektieren und mehr zu selektieren. Andererseits sollte das Culling selbst nicht zu viel Leistung und Speicher kosten. Um eine gute Leistung zu gewährleisten, gleichen wir das System automatisch aus. Dies führt zu einer besseren Leistung, macht aber auch die Systemeigenschaften etwas schwieriger zu verstehen.
In der Regel erfolgt das Engine Culling auf der Ebene des Draw Call oder bei mehreren Draw Calls. Wir gehen nicht bis zur Dreiecksebene oder noch weiter, da dies oft nicht effizient ist. Das bedeutet, dass es sinnvoll sein kann, große Objekte in mehrere Teile aufzuteilen, so dass nicht alle Teile zusammen gerendert werden müssen.
Keine statischen Culling-Techniken.
Wir vermeiden bewusst statische Techniken wie vorbereitete PVS (Potential Visibility Tests), die in frühen 3D-Engines üblich waren. Der große Vorteil des PVS sind die sehr geringen Kosten für die Laufzeitleistung, aber bei modernen Computern ist dies weniger problematisch. Die Erstellung des PVS erfolgt in der Regel in einem zeitaufwändigen Vorverarbeitungsschritt und das ist schlecht für die Produktion in modernen Spielwelten. Das Gameplay erfordert oft dynamische Aktualisierungen der Geometrie (z.B das Öffnen/Schließen von Türen, das Zerstören von Gebäuden) und die statische Natur eines PVS ist dafür nicht sehr gut geeignet. Durch die Vermeidung von PVS und ähnlichen vorberechneten Techniken können wir sogar etwas Speicherplatz sparen, was auf Konsolen sehr wertvoll ist.
Portale.
Ein weiterer beliebter Ansatz neben PVS sind Portale. Portale sind in der Regel handplatzierte, flache, konvexe 2D Objekte. Die Idee ist, dass, wenn ein Portal sichtbar ist, der Weltabschnitt hinter dem Portal gerendert werden muss. Wenn der Weltabschnitt als sichtbar angesehen wird, kann die Geometrie weiter getestet werden, da die Portalform oder der Schnittpunkt mehrerer Portale ein höheres Culling ermöglicht. Durch die Trennung von Weltabschnitten mit Portalen können Designer effiziente Ebenen schaffen. Gute Portalpositionen befinden sich in engen Bereichen wie Türen und für die Leistung ist es vorteilhaft, Umgebungen zu schaffen, in denen Portale viele Inhalte entfernen. Es gibt Algorithmen zur automatischen Platzierung von Portalen, aber es ist wahrscheinlich, dass das resultierende Niveau weniger optimal in der Leistung ist, wenn die Designer nicht in den Optimierungsprozess eingebunden sind.
Handplatzierte Portale vermeiden zeitaufwändige Vorverarbeitungsschritte, verbrauchen wenig zusätzlichen Speicher und ermöglichen einige dynamische Updates. Portale können ein- und ausgeschaltet werden und einige Engines implementieren sogar Spezialeffekte mit Portalen (z.B. Spiegel, Transporter). In CryEngine werden ausschließlich Portale zur Verbesserung der Renderleistung verwendet. Wir haben uns entschieden, die Nutzung von Portalen nicht weiter auszubauen, um den Code- und Portal-Workflow einfach und effizient zu gestalten. Portale haben ihre Vorteile, aber es ist zusätzlicher Aufwand von Designern erforderlich und oft ist es schwierig, gute Portalpositionen zu finden. Offene Umgebungen wie Städte oder reine Natur erlauben oft keine effiziente Portalnutzung. Portale werden von CryEngine unterstützt, sollten aber nur dort eingesetzt werden, wo sie besser funktionieren als der Coverage-Puffer.
Anti-Portale.
Die Portaltechnik kann durch das Gegenteil von Portalen erweitert werden, die allgemein als Anti-Portale bezeichnet werden. Die Objekte, die in 2D oder 3D oft konvex sind, können andere Portale verschließen. Stellen Sie sich eine große Säule in einem Raum mit mehreren Türen vor, die zu anderen Räumen führen. Dies ist ein schwieriger Fall für klassische Portale, aber der typische Anwendungsfall für Anti-Portale. Anti-Portale können mit geometrischen Kreuzungen von Objekten implementiert werden, aber diese Methode hat Probleme mit der Fusion mehrerer Anti-Portale und die Effizienz leidet. Anstelle von geometrischen Anti-Portalen haben wir den Abdeckungspuffer, der dem gleichen Zweck dient, aber bessere Eigenschaften aufweist.
GPU Z Test.
In modernen Grafikkarten wird der Z-Puffer verwendet, um das Problem der versteckten Oberfläche zu lösen. Hier eine einfache Erklärung: Für jedes Pixel auf dem Bildschirm wird der sogenannte Z- oder Tiefenwert gespeichert, der die Entfernung der Kamera zur nächsten Geometrie an dieser Pixelposition darstellt. Alle renderbaren Objekte müssen aus Dreiecken bestehen. Alle von den Dreiecken abgedeckten Pixel führen einen Z-Vergleich durch (Z Pufferwert vs. Z-Wert des Dreiecks) und je nach Ergebnis wird das Dreieckspixel verworfen oder nicht. Dadurch wird die Entfernung von verdeckten Oberflächen auch bei sich überschneidenden Objekten elegant gelöst. Das bereits erwähnte Problem der Okkluderfusion wird ohne weiteren Aufwand gelöst. Der Z-Test ist ziemlich spät in der Rendering-Pipeline, was bedeutet, dass viele Engine-SetUp-Kosten (z.B. Skinning, Shader-Konstanten) bereits erledigt sind.
In einigen Fällen erlaubt es, Pixel-Shader-Ausführung oder Frame-Buffer-Blending zu vermeiden, aber sein Hauptzweck ist es, das Problem der versteckten Oberflächen zu lösen, das Culling ist ein Nebeneffekt. Durch die grobe Sortierung von Objekten von vorne nach hinten kann die Culling-Leistung verbessert werden. Die frühe Z-Pass-Technik (manchmal auch als Z-Pre-Pass bezeichnet) macht dies weniger unerlässlich, da der erste Durchgang explizit schnell auf die Per-Pixel-Leistung ausgerichtet ist. Einige Hardware läuft sogar mit doppelter Geschwindigkeit, wenn Farbschreiben deaktiviert sind. Leider müssen wir dort Daten ausgeben, um die G-Buffer-Daten für die verzögerte Beleuchtung einzurichten.
Die Z-Puffer-Präzision wird durch das Pixelformat (24bit), den Z-Pufferbereich und in sehr extremer (nicht-linearer) Weise durch den Z-Nahwert beeinflusst. Der Z Near-Wert definiert, wie nah ein Objekt dem Betrachter sein kann, bevor es weggeschnitten wird. Durch Halbierung des Z Near (z.B. von 10cm oben 5cm) halbieren Sie die Genauigkeit des Z-Puffers effektiv. Dies hat keinen Einfluss auf die meisten Objekt-Renderings, aber Decals werden oft nur korrekt gerendert, da ihr Z-Wert etwas kleiner ist als die Oberfläche unter ihnen. Es ist ratsam, das Z zur Laufzeit nicht in der Nähe zu ändern.
GPU Z Cull / HiZ.
Effiziente Z-Pufferimplementierungen in den GPU-Cull-Fragmenten (Pixel oder mehrfach abgetastete Teilproben) in groberen Blöcken zu einem früheren Zeitpunkt. Dies hilft, die Ausführung von Pixel-Shadern zu reduzieren. Es gibt viele Bedingungen, die erforderlich sind, um diese Optimierung zu ermöglichen und scheinbar harmlose Rendereränderungen können diese Optimierung leicht brechen. Die Regeln sind kompliziert und abhängig von der Grafikkarte.
GPU Occlusion-Abfragen.
Die Occlusion Abfrage Funktion moderner Grafikkarten ermöglicht es der CPU, Informationen über zuvor durchgeführte Z-Puffertests zurückzubekommen. Diese Funktion kann verwendet werden, um fortgeschrittenere Culling-Techniken zu implementieren. Nach dem Rendern einige Occluders (vorzugsweise von vorne nach hinten, zuerst große Objekte) können Objekte (Occludees) auf ihre Sichtbarkeit getestet werden. Die Grafikhardware ermöglicht es, mehrere Objekte effizient zu testen, aber es gibt ein großes Problem. Da das gesamte Rendering stark gepuffert ist, wird die Information, ob ein Objekt sichtbar ist, um eine lange Zeit verzögert (bis zu mehreren Frames). Das ist inakzeptabel, da dies entweder sehr schlechte Stände (Frame-Rate-Kupplungen), schlechte Frame-Rate im Allgemeinen oder für eine Weile unsichtbare Objekte bedeutet, wo sie nicht sein sollten.
Bei einigen Hardware/Treibern ist dieses Latenzproblem weniger schwerwiegend als bei anderen, aber eine Verzögerung von etwa einem Frame ist etwa das Beste, was es gibt. Das bedeutet auch, dass wir keine effizienten hierarchischen Tests effizient durchführen können, z.B. wenn ein geschlossener Kasten sichtbar ist und dann feinkörnige Tests mit Unterteilen durchführen. Die Funktionalität des Occlusion Tests ist in der Engine implementiert und wird derzeit für das Ozean Rendering verwendet. Wir verwenden sogar die Anzahl der sichtbaren Pixel, um die Aktualisierungsfrequenz der Reflexion zu skalieren. Leider können wir auch die Situation haben, dass der Ozean aufgrund von schnellen Positionsänderungen der Ansicht für ein oder zwei Bilder nicht sichtbar ist. Dies geschah zum Beispiel in Crysis, als der Spieler das U-Boot verließ.
Software Coverage Buffer (cbuffer).
Die Z Puffer Performance hängt von der Dreieckszahl, der Scheitelpunktzahl und der Anzahl der abgedeckten Pixel ab. Dies alles ist auf Grafikhardware sehr schnell und würde auf der CPU sehr langsam sein. Allerdings hätte die CPU nicht das Latenzproblem der Occlusion-Abfragen und moderne CPUs werden schneller. Deshalb haben wir eine Software-Implementierung auf der CPU durchgeführt, die wir „Coverage Buffer“ genannt haben. Um eine gute Leistung zu erzielen, verwenden wir vereinfachte Occluder und Occludees. Künstler können ein paar Occluder Dreiecke zu gut okkludierenden Objekten hinzufügen und wir testen auf die Occlusion der Objektbegrenzungsbox. Animierte Objekte werden nicht berücksichtigt. Wir verwenden auch eine niedrigere Auflösung und handoptimierten Code für die Dreiecksrasterung. Das Ergebnis ist eine ziemlich aggressiv optimierte Menge von Objekten, die gerendert werden müssen. Es ist möglich, dass ein Objekt gecullt wird, obwohl es noch sichtbar sein sollte, aber das ist sehr selten und liegt oft an einem schlechten Asset (z.B. ist das Okkluderpolygon etwas größer als das Objekt). Wir haben uns entschieden, Leistung, Effizienz und Einfachheit des Codes gegenüber Korrektheit zu bevorzugen.
Abdeckungspruffer mit Z Buffer Readback.
Auf mancher Hardware (PlayStation 3, Xbox360) können wir den Z-Puffer effizient in den Hauptspeicher kopieren und damit Coverage-Puffertests durchführen. Dies führt immer noch zu denselben Latenzproblemen, integriert sich aber gut in die Softwareimplementierung des Abdeckungspuffers und ist effizient, wenn es für viele Objekte verwendet wird. Diese Methode führt eine Frame-Verzögerung ein, so dass z.B. schnelle Rotationen ein Problem sein können.
Backface Culling.
Normalerweise ist das Backface Culling ein Kinderspiel für Grafikprogrammierer. Abhängig von der Dreiecksorientierung (im Uhrzeigersinn oder gegen den Uhrzeigersinn in Bezug auf den Betrachter) muss die Hardware die rückwärtigen Dreiecke nicht darstellen und wir erhalten eine gewisse Beschleunigung. Nur für einige Alpha-Blended-Objekte oder Spezialeffekte müssen wir das Backface Culling deaktivieren. Bei der PS3 muss dieses Thema noch einmal überdacht werden. Die GPU-Leistung bei der Verarbeitung von Nodes oder dem Abrufen von Nodes kann ein Engpass sein und die gute Verbindung der SPUs mit der CPU ermöglicht es, Daten bei Bedarf zu erstellen. Der Aufwand für die SPU-Transformation und das Testen von Dreiecken kann sich lohnen. Eine effiziente CPU-Implementierung könnte Frustum Culling durchführen, indem sie kleine Dreiecke, Backface Culling, Mesh Skinning und sogar Lighting kombiniert. Dies ist jedoch keine einfache Aufgabe. Neben der Aufrechterhaltung dieses PS3-spezifischen Codepfades müssen die Mesh-Daten im CPU-Speicher verfügbar sein. Zum Zeitpunkt der Erstellung dieses Artikels haben wir diese Optimierung noch nicht durchgeführt, da der Hauptspeicher knapp ist. Wir könnten dies überdenken, sobald wir ein effizientes Streaming von der CPU zum Hauptspeicher haben (Code, der diese Daten verwendet, könnte mit einer Framelatenz zu tun haben).
Konditionales Rendering.
Die Occlusion-Abfrage-Funktion könnte für eine andere Culling-Technik verwendet werden. Viele Draw Calls müssen in mehreren Durchgängen durchgeführt werden und nachfolgende Durchgänge könnten vermieden werden, wenn der frühere Durchgang geholt worden wäre. Dies erfordert eine Menge Okklusionsabfragen und buchhalterischen Aufwand. Auf der meisten Hardware würde sich das nicht auszahlen, aber unsere PS3-Renderer-Implementierung kann auf Datenstrukturen auf einem sehr niedrigen Niveau zugreifen, so dass der Overhead geringer ist.
Heightmap Raycasting.
Heightmaps ermöglichen effiziente Ray Cast Tests. Mit diesen Objekten in der Nähe kann der hinter einem Gelände versteckte Boden abgeholzt werden. Diese Culling-Technik ist bereits seit CryEngine 1 verfügbar, aber da wir jetzt viele andere Methoden haben und die Heightmap-Daten in komprimierter Form speichern, wurde die Technik weniger effizient. Dies ist umso mehr der Fall, wenn man die seither erfolgten Veränderungen in der Hardware betrachtet. Mit der Zeit stieg die Rechenleistung schneller an als die Speicherleistung. Diese Culling-Technik kann im Laufe der Zeit durch andere ersetzt werden.
Vielen Dank für ihren Besuch.