Raytracing in Echtzeit mit DirectX und Nvidias RTX-Grafikchips

Raytracing beschert Kinofilmen ihre Spezialeffekte und gilt als heiliger Gral der Echtzeitgrafik. Nun soll die aufwendige Technik auch Spiele verschönern.

In Pocket speichern vorlesen Druckansicht 10 Kommentare lesen
Raytracing in Echtzeit mit DirectX und Nvidias RTX-Grafikchips
Lesezeit: 15 Min.
Inhaltsverzeichnis

Raytracing verspricht realistische Beleuchtung selbst für extrem komplexe 3D-Szenen, dazu einen physikalisch korrekten Schattenwurf aller Objekte und pixelgenaue Reflexionen. Das Ganze kommt ohne typische Rastergrafik-Artefakte wie fehlerhafte Selbstschattierungen (shadow acne), unpassenden Lichteinfall oder Spiegelungen, in denen die Hälfte fehlt.

Das erfordert allerdings enorme Rechenleistung. Bislang ist das Verfahren daher als Offline-Renderverfahren für fotorealistische Bilder etwa in der Automobilwerbung, bei Spezialeffekten für Kinofilme wie The Avengers: Infinity War oder gar zur kompletten Berechnung von Filmen wie Cars oder Wall-E gebräuchlich. Nun hält Raytracing auch in Spiele Einzug, wenn auch zunächst nur für ganz bestimmte Effekte.

Mit DirectX Raytracing will Microsoft die Lücke zwischen Rastergrafik von heute und einer künftig möglichen kompletten Raytracing-Darstellung überbrücken. Dazu sollen Teile der Rechenschritte auf eine parallele, logische Raytracing-Pipeline ausgelagert werden. Diese kann nach Wahl des Entwicklers als Teil der Grafik- oder der freieren Compute-Pipeline als State Object laufen. Nvidias Turing-Architektur sieht spezielle Schaltkreise vor, um Raytracing zu beschleunigen und vermischt die Technik dabei zum Teil mit KI-Berechnungen.

Eingeführt hat Microsoft DXR mit der Windows-10-Version 1809. Nvidia unterstützt Raytracing auch unter Vulkan mit der Extension "VK_NVX_raytracing", aktuelle Treiber vorausgesetzt. Da Vulkan ebenfalls unter Linux läuft, ist man also nicht zwangsläufig auf Windows 10 angewiesen. Microsoft hat die D3D12-Raytracing-Fallback-Version als Open-Source auf GitHub bereitgestellt. Wer sich selbst ein Bild machen möchte, findet dort die nötigen Softwarezutaten.

Eine Szene in einer typischen 3D-Anwendung oder einem Spiel kann man sich wie den Blick ins Kasperle-Theater vorstellen: Der Sichtkegel ergibt sich aus der Breite der Bühne (X-Koordinate), der Höhe des Bühnenraums (Y-Koordinate) und der Tiefe (Z-Achse). Die berechnete Grafik findet zwischen dem Fenster des Kasperle-Theaters und dem Bühnenhintergrund statt (Clipping Planes). Daneben sorgen aber noch Scheinwerfer oder die Sonne im Freien für die Beleuchtung der Szene. Obwohl sie eigentlich nicht innerhalb der Puppenbühne liegen, muss ihr Einfluss trotzdem berücksichtigt werden, da sonst alles im Dunkeln läge. Auch für Schattenwürfe und indirektes, also von Oberflächen zurückgeworfenes Licht sind sie wichtig. Je nach zur Verfügung stehender Leistung berücksichtigt man im Idealfall alle Lichtquellen für die Farbgebung sichtbarer Oberflächen. Werden zusätzlich atmosphärische Effekte und die daraus resultierende Lichtbrechung sowie indirektes Licht einbezogen, also von Objektoberflächen zurückgeworfenes, spricht man von globaler Beleuchtung (Global Illumination).

Grundsätzlich bereitet der Hauptprozessor die Grafikdarstellung dieser dreidimensionalen Szene vor. Denn nur er kennt das Resultat der Aktionen des Nutzers und der gegnerischen Reaktion etwa in Spielen. Die CPU schickt deren Aufbau als dreidimensionale Koordinatensammlung (Vertices) in Verbindung mit Anweisungen zur Farbberechnung, den Shaderprogrammen, an die Grafikkarte.

Der Steuerprozessor der Grafikkarte ordnet die Objektkoordinaten auf Basis der X- und Y-Achse Ausführungseinheiten zu. Dort wird die Sichtbarkeitsprüfung mithilfe des Tiefenpuffers (Z-Buffer) durchgeführt. Jeder Bildpunkt, für den man die aufwendige Farbberechnung (Shading) weglassen kann, spart Zeit (Viewport Clipping, Backface-Culling, Z-Buffering). Um die Übertragungszeit zu verringern, hat der Prozessor Objekte, die sich komplett außerhalb des Sichtkegels befinden, in der Regel vor der Übertragung verworfen (Frustum Culling).

Anschließend verschiebt die GPU die Eckpunkte entsprechend des Betrachtungswinkels (Vertex Transformation) und erzeugt aus dem Koordinatenhaufen ein Dreieckssystem (Triangle Setup), fügt eventuell zusätzliche Vertices für eine realistischere Darstellung hinzu (Tessellation). Danach geht es zur Rasterisierung, welche die Szene auf die Bildschirmauflösung projiziert und jedem Vertex und damit den Dreiecksinhalten eine Position auf dem zweidimensionalen Pixelraster zuordnet. Erst jetzt berechnet die GPU die Farbe jedes einzelnen Bildpunkts. Das erledigen Pixel- oder Compute-Shader, die es so auch beim Raytracing gibt.

Sichtbarkeitsprüfung bei Rastergrafik: Um Rechenzeit zu sparen, wird bei herkömmlicher Rastergrafik ein großer Teil der 3D-Szene verworfen, was im Nachgang zu Problemen führt.

Für Echtzeitdarstellung, also Spiele, setzte sich Raytracing bisher nie durch, weil es zu viel Rechenleistung braucht. Rastergrafik hingegen zielte von vornherein darauf ab, bereits mit begrenzter Rechenleistung in hoher Geschwindigkeit zumindest halbwegs glaubhafte 3D-Welten darzustellen. Dafür bediente man sich verschiedener Tricks und Kniffe, die anfangs gut funktionierten. Mit zunehmender Realitätsnähe der Grafik fielen sie aber immer stärker auf und mussten selbst kaschiert werden.

Der wohl bekannteste dieser Tricks sind bildgebende Texturen. Diese gaukeln Oberflächen vor, die im rechenintensiven Teil der Szene nur grob vereinfacht dargestellt werden. So lassen 3D-Künstler den Grafikchip etwa ein hochkant stehendes Rechteck mit der Bildtextur einer Tür bemalen, anstatt Türgriff, Schloss und Verzierungen mit geometrischen Elementen zu modellieren. Ein weiteres Rechteck beklebt die GPU mit dem Bild eines Fensters, durch das man aber nicht hindurchsehen kann. Mit wachsender Auflösung fällt solcher Pfusch stärker auf. Zu den fortschrittlicheren Behelfstechniken gehört das Parallax-Occlusion-Mapping. Das nutzt mit Raymarching sogar eine dem Raytracing entfernt verwandte Technik, um künstlich Höheninformationen aus flachen Texturen zu gewinnen und so Dreidimensionalität vorzutäuschen.

Große Problemkinder der Rastergrafik sind Schatten und Spiegelungen. Selbst für eine grobe Annäherung an realistische Schatten muss der Grafikchip die Szene in einem separaten Durchgang aus Sicht einer globalen Lichtquelle wie der Sonne berechnen. Dabei greift er auf die Informationen des Tiefenpuffers (Z-Buffer) zu und speichert die Ergebnisse in einer separaten Schattentextur (Shadow Map). Weitläufige 3D-Welten benötigen aufgrund begrenzter Rechengenauigkeit mehrere Stufen dieser Schattentextur, damit es bei hoher Sichtweite nicht zu unschönen Artefakten kommt. Und selbst damit erzeugt man nur leidlich realistisch wirkende Schatten mit harten Kanten.

Auch Problem Nummer zwei, die Spiegelungen, geht man in der Regel über eine separate Umgebungstextur an (Cube Environment Map). In ihr speichert die GPU beim Erzeugen der Szene eine Rundumsicht und bringt sie passend verzerrt auf die spiegelnden Oberflächen auf. Diesen Spiegelungen fehlen in der Regel dynamische Objekte und Lichtquellen, weil sie eine für jedes einzelne Bild aufs Neue erzeugte Umgebungstextur erfordern. Spiegel, welche die Spielfigur reflektieren, befinden sich nicht ohne Grund meist in kleinen Innenräumen. Korrekte Verschattung bereitet ebenfalls Probleme, sodass etwa Pfützen die simulierte Sonne als Lichtquelle reflektieren, obwohl sie im Schatten liegen.

Ein Grund dieser Art von Problemen ist, dass die Rastergrafik jede Menge Information wegwirft, um die Basisfunktionen schneller auszuführen. Sie wechselt vom World Space, in dem noch alle relevanten Objekte vorhanden sind, in den Screen-Space. Dort sind viele Bestandteile einer Szene nicht mehr oder nur noch über Umwege verfügbar. Raytracing hingegen verzichtet auf diese Unterscheidung, weshalb viele Rastergrafikprobleme mit Raytracing gar nicht erst auftreten oder vergleichsweise einfach lösbar sind. Geometrie oder Lichtquellen etwa lassen sich problemlos mehrfach verwenden. Raytracing reagiert unempfindlicher auf höhere Dreieckszahlen als Rastergrafik.

Raytracing projiziert für jeden Bildpunkt einen virtuellen Blickstrahl in die Szene, der diese von vorn nach hinten durchläuft, bis er auf ein Objekt trifft – daher der Name Raytracing, zu deutsch Strahlverfolgung. Zwar können auch herkömmliche Shader-Einheiten diese Schnittpunktprüfungen durchführen. Laut Nvidia wären dabei aber jeweils Tausende von Instruktionsslots nötig, die bei der nach wie vor anfallenden Shading-Arbeit fehlen. Die Shader-Multiprozessoren (SMs) der Turing-Grafikchips enthalten daher Schaltkreise, welche die Schnittpunktprüfung übernehmen, von Nvidia RT-Cores genannt.

Die immer kleiner werdenden Platzhalterquader führen bis auf die Dreiecksebene hinab. Dort wird die Sichtbarkeitsprüfung dann von einer speziellen Untereinheit innerhalb des RT-Cores vorgenommen.

(Bild: Nvidia)

Damit die Rechenwerke nicht für jeden Strahl – in Full-HD-Auflösung 2,07 Millionen, in 4K schon 8,29 Millionen – die komplette Liste an Vertex-Koordinaten einzeln durchgehen müssen, nutzt auch Raytracing Optimierungen. Eine davon ist eine Staffelung von zunehmend kleineren Platzhalterquadern, bis in letzter Instanz ein einzelnes Dreieck steht – die Bounding Volume Hierarchy (BVH). Anstelle der kompletten Liste wird immer nur gegen das Koordinatenset der jeweiligen Box geprüft. Einzelne Quader, die keine Geometrie enthalten, verzichten auf kleinere Boxen und führen so schneller zur Abbruchbedingung für den verfolgten Strahl. Ähnliche Strukturen verwendet Voxel-Rendering zur Beschleunigung.

Die BVH ist logisch gesehen eine baumartige Struktur, an deren Ende das einzelne Dreieck steht. Der Algorithmus durchläuft die Struktur (BVH Traversal) von groß nach klein. In der ersten Stufe prüft er, ob das gesuchte Pixel im oberen rechten, oberen linken, unteren rechten oder unteren linken Quadranten liegt. Sobald die Antwort "Ja" lautet – der sogenannte Intersection-Test, also die Schnittpunktprüfung, erfolgreich ist –, ist die nächstkleinere Ebene dran. Dieses Spielchen wiederholt sich so lange, bis die Prozessoren auf der untersten Ebene einzelne Dreiecke vorfinden und dort dann einen Treffer für den Triangle/Ray-Intersection-Test zurückmelden.

Ab hier geht es dann entweder direkt mit dem finalen Shaderprogramm "Hit Shader" weiter oder zunächst mit einem Shader mit der Bezeichnung "Any-Hit". Beim Auslösen des Hit Shaders berechnet der Grafikchip die Pixelfarbe anhand der Oberflächenbeschaffenheit und der einwirkenden Lichtquellen. Any-Hit wird hingegen beim Aufeinandertreffen des Strahls mit einem transparenten Objekt ausgelöst. Dabei wird der Strahl nicht aufgehalten, sondern erhält zusätzliche Eigenschaften wie Lichtbrechung und Farbigkeit. Diese Eigenschaften werden dann so lange weiter aufgesammelt, bis der Strahl einen Hit Shader auslöst und endet. Trifft ein Strahl auf seinem festgelegten Weg kein Dreieck, wird der Miss**Shader ausgelöst und zum Beispiel die Farbe des Hintergrunds für diesen Pixel angenommen.

Alternativ löst dieses Shaderprogramm in DXR noch eine weitere Strahlverfolgung aus, sogenannte Secondary Rays. Das ist zum Beispiel bei reflektierenden Oberflächen nötig, denn dafür muss man wissen, was und aus welchem Winkel gespiegelt wird. Zudem können beide Shader einen weiteren Strahl in Richtung einer oder mehrerer Lichtquellen aussenden, um zu ermitteln, ob der Bildpunkt im Schatten liegt. Das tut er, wenn der Schattenfühler keinen Treffer auf der Lichtquelle zurückmeldet. Mehrfache Schattenfühler pro Pixel können auf so auf einfache Weise weiche Schatten erzeugen.

Das Grundprinzip von Raytracing: Raytracing verfolgt Sichtbarkeitsstrahlen in eine Szene. Treffen sie auf ein Objekt, wird das zugehörige Shaderprogramm aufgerufen, um die Farbe zu bestimmen. Sekundärstrahlen dienen realistischen Schatten und Reflexionen.

Beim Erzeugen und Aktualisieren der BVH und natürlich beim Shading selbst helfen die RT-Cores nicht. Gerade Ersteres bedeutet erst einmal Mehraufwand, den der Treiber auf die CPU abwälzt. Ob oder ab wann sich dieser amortisiert und der Raytracing-Hybrid nicht nur schöner, sondern auch schneller ist, variiert von Fall zu Fall. Rechenzeit lässt sich beim hybriden Rendering etwa durch Verzicht auf Shadowmapping und Screen-Space-basierte Reflexionen im Rasterteil der Grafik einsparen. Diese erledigt Raytracing effizienter und realistischer.

Die Shader-Multiprozessoren (SMs) der Turing-Grafikchips enthalten Schaltkreise, die die Schnittpunktprüfung übernehmen, von Nvidia vollmundig RT-Cores genannt. Da die RT-Cores parallel zu den anderen Recheneinheiten der Shader-Multiprozessoren arbeiten, bringen sie eine echte Entlastung. Allerdings benötigen die Schaltkreise zusätzlichen Strom und zwacken diesen vom Gesamtbudget ab, sodass für die anderen Einheiten weniger elektrische Leistung zur Verfügung steht, was wiederum den Takt senken kann.

In Nvidia-internen Tests anhand von fünf einfachen Szenen erreichte eine Geforce GTX 1080 Ti im Durchschnitt 1,1 Milliarden verfolgte Primärstrahlen – 1 Gigaray/s. Die RTX 2080 Ti kommt in denselben Tests laut Nvidia auf etwas mehr als 10 Milliarden. Das ist allerdings eine Schönwetterangabe, frei nach dem Motto "Primary Rays Cache, Secondary Rays Trash". Die Daten für Primärstrahlen lassen sich noch recht gut in Caches vorhalten. Sekundärstrahlen hingegen tendieren dazu, in verschiedene Richtungen auseinanderzulaufen und durch divergierende Daten die Caches vollzumüllen. Nicht umsonst hat Nvidia bei Turing nicht nur den Level-2-Cache deutlich vergrößert, sondern auch die L1-Caches in den SMs. Diese fassen jetzt wesentlich mehr Daten und sind schneller an die Load/Store-Einheiten angebunden.

Zudem verwendet Nvidia die KI-Funktionen der Turing-eigenen Tensor Cores ebenfalls für den Raytracing-Prozess. Steht für bestimmte Effekte nur ein begrenztes Zeitbudget zur Verfügung, muss es mit weniger Strahlen als nötig auskommen. Das Bild enthält dann noch schwarze oder fehlfarbige Punkte, die aussehen wie starkes Bildrauschen. Die Tensor-Cores füllen dann mit einer vorab trainierten KI dieses Rauschen mit möglichst passenden Farben. Besonders gut eignet sich das Verfahren etwa für weiche Schatten.

Intel hat vor rund zehn Jahren bereits mit Raytracing geliebäugelt -- damals noch in der Hoffnung, die Many-Core-Prozessoren würden sich dafür eignen. Man ging davon aus, rund eine Milliarde der im Raytracing verwendeten Strahlen berechnen zu müssen. Damit sollte sich eine Szene des Shooters Quake 4 in 1280 × 720 Pixeln inklusive der Raytracing-typischen Effekte Kantenglättung, weiche Schatten und Umgebungsverschattung (Ambient Occlusion) mit 45 Bildern pro Sekunde berechnen lassen, in der pro Pixel 24 Strahlen zur Verfügung stünden. Damals avisierte man rund 1,2 Billionen Rechenschritte pro Sekunde (TFLOPS) für dieses Ziel. Heute sind die Anforderungen an "gute" Grafik wesentlich höher, Spieler verlangen 60 oder mehr Bilder/s und die übliche Einstiegsauflösung gerade für High-End-Features wie Raytracing liegt bei Full HD, ergo rund 2 Millionen Pixeln.

Für komplett via Raytracing berechnete Spiele in hoher Auflösung bei zugleich hohen Bildraten reicht die Leistung der Turing-GPUs nicht aus. Nvidia hält daher einen Zwischenschritt zwischen Raster- und Raytracing-Grafik erst einmal für sinnvoll. Eine solche Hybrid-Engine namens Halcyon hat SEED, ein Experimentierteam des Spielepublishers EA, entwickelt. Hier übernimmt das Raytracing die Berechnung von direkten Schatten, Reflexionen, Global Illumination, Ambient Occlusion und Transparenzen. Direkte Beleuchtung und Post-Processing kann die Compute-Pipeline stemmen, und Rastergrafik erledigt Deferred Shading und optional direkte Schatten.

Raytracing ist eine bewährte Technik und lässt sich für einige Effekte sehr effizient nutzen. Zudem ist Nvidia mit RTX nicht allein auf weiter Flur, sondern hat Microsofts DirectX-Erweiterung im Rücken. Die Chancen sind also geringer als etwa bei GPU-beschleunigter Physik (PhysX), dass Raytracing in Spielen eine winzige Nische bleibt. Bisher laufen Spieledemos mit aktivierter Strahlverfolgung allerdings selbst auf teuren High-End-Grafikkarten wie der Geforce RTX 2080 höchstens in Full-HD-Auflösung flüssig. Das zeigte sich auch in einem ersten Test von Battlefield V.

Damit sich Spiele-Raytracing auf breiter Front durchsetzt, muss es auch auf Mittelklasse-Grafikkarten laufen und idealerweise auch auf AMD-Karten, auf Konsolen und künftig auf Intels Grafikchips. (csp)