So funktioniert das Rendern von 3D-Spielen: Rasterisierung und Raytracing

Bild

Teil 1: Vertexverarbeitung

In diesem Artikel werden wir genauer untersuchen, was mit der 3D-Welt geschieht, nachdem alle ihre Scheitelpunkte verarbeitet wurden. Wir müssen wieder den Staub aus den Mathe-Lehrbüchern abschütteln, uns an die Geometrie der Pyramidenstümpfe gewöhnen und das Rätsel der Perspektiven lösen. Wir werden auch kurz auf die Physik von Raytracing, Beleuchtung und Materialien eingehen.

Das Hauptthema dieses Artikels ist eine wichtige Rendering-Phase, in der die dreidimensionale Welt der Punkte, Segmente und Dreiecke zu einem zweidimensionalen Raster aus mehrfarbigen Blöcken wird. Sehr oft scheint dieser Prozess unsichtbar zu sein, da die Konvertierung von 3D zu 2D nicht sichtbar ist, im Gegensatz zu dem im vorherigen Artikel beschriebenen Prozess, bei dem wir den Einfluss von Vertex-Shadern und Tessellation sofort erkennen konnten. Wenn Sie noch nicht dazu bereit sind, können Sie mit unserem Artikel 3D Game Rendering 101 beginnen .

Vorbereitung für zwei Messungen


Die überwiegende Mehrheit der Leser liest diese Website auf einem vollständig flachen Monitor oder Smartphone-Bildschirm. Aber selbst wenn Sie über eine moderne Technik verfügen - einen gekrümmten Monitor - besteht das von ihm angezeigte Bild ebenfalls aus einem flachen Raster aus mehrfarbigen Pixeln. Wenn Sie jedoch den neuen Call of Mario: Deathduty Battleyard spielen, wirken die Bilder dreidimensional. Objekte bewegen sich in der Szene, werden größer oder kleiner und nähern sich der Kamera und entfernen sich von ihr.


Am Beispiel von Bethesdas Fallout 4 aus dem Jahr 2014 können wir leicht sehen, wie die Peaks verarbeitet werden, wodurch ein Gefühl von Tiefe und Distanz entsteht. Dies macht sich insbesondere im Wireframe-Modus bemerkbar (siehe oben).

Wenn Sie in den letzten zwei Jahrzehnten ein 3D-Spiel spielen, führt fast jedes dieselbe Abfolge von Aktionen aus, um die 3D-Welt der Scheitelpunkte in ein 2D-Pixelarray umzuwandeln. Diese Konvertierung wird oft als Rasterisierung bezeichnet , dies ist jedoch nur einer von vielen Schritten im gesamten Prozess.

Wir müssen die verschiedenen Stadien analysieren und die in ihnen verwendeten Techniken und Berechnungen studieren. Als Referenz verwenden wir die in Direct3D verwendete Sequenz. Das Bild unten zeigt, was mit jedem Scheitelpunkt der Welt passiert:


Direct3D-Konvertierungspipeline

Im ersten Artikel [ Übersetzung auf Habré] haben wir gesehen, was im Weltraum (World Space) passiert: Hier werden die Eckpunkte mithilfe verschiedener Matrixberechnungen transformiert und farbig dargestellt. Wir werden den nächsten Schritt überspringen, da im Kamerabereich nur die Scheitelpunkte konvertiert und nach dem Verschieben angepasst werden, sodass die Kamera zu einem Referenzpunkt wird.

Die folgenden Schritte sind zu kompliziert, um sie zu überspringen, da sie für den Übergang von 3D zu 2D unbedingt erforderlich sind. Bei korrekter Implementierung betrachtet unser Gehirn einen Flachbildschirm, "sieht" jedoch eine Szene mit Tiefe und Skalierung. Wenn alles falsch gemacht wird, wird das Bild sehr seltsam sein!

Es geht nur um Perspektive


Der erste Schritt in dieser Sequenz ist das Einstellen des Bereichs aus Sicht der Kamera. Dazu müssen Sie zuerst die Winkel des horizontalen und vertikalen Sichtfelds einstellen - die ersten Änderungen treten häufig in Spielen auf, da die Menschen die horizontale periphere Sicht besser als die vertikale entwickelt haben.

Wir können das herausfinden, indem wir das Bild mit dem Blickfeld einer Person betrachten:


Zwei Ecken des Sichtfelds (Sichtfeld, fov) definieren die Form der Kegelstumpfpyramide - eine 3D-Pyramide mit einer quadratischen Basis, die von der Kamera ausgeht. Die erste Ecke legt das vertikale Sichtfeld fest, die zweite das horizontale . wir bezeichnen sie mit den Symbolen α und β . In der Tat sehen wir die Welt nicht ganz so, aber aus der Sicht der Berechnungen ist es viel einfacher, mit der Trunkierungspyramide zu arbeiten, als zu versuchen, ein realistisches Maß an Sichtbarkeit zu generieren.


Sie müssen auch zwei weitere Parameter angeben - die Position der nahen (oder vorderen) und der fernen (hinteren) Schnittebene (Schnittebene) . Die erste Option schneidet die Oberseite der Pyramide ab, bestimmt jedoch im Wesentlichen, wie nah an der Kameraposition alles gezeichnet wird. Letzteres macht dasselbe, bestimmt aber, wie weit die Grundelemente von der Kamera entfernt gerendert werden.

Die Größe und Position der nahen Schnittebene ist sehr wichtig, da sie zu einem so genannten Ansichtsfenster wird . In der Tat sehen wir dies auf dem Monitor, d.h. gerenderten Rahmen und in den meisten Grafik-APIs wird das Ansichtsfenster von der oberen linken Ecke gezeichnet. Im Bild unten ist der Punkt (a1, b2) der Ursprung der Ebene: Die Breite und Höhe der Ebene werden relativ dazu gemessen.


Das Seitenverhältnis des Ansichtsfensters ist nicht nur wichtig, um die gerenderte Welt anzuzeigen, sondern auch, um das Seitenverhältnis des Monitors anzupassen. Für viele Jahre war der Standard 4: 3 (oder 1,3333 ... als Dezimalzahl). Heutzutage spielt die Mehrheit jedoch in einem 16: 9- oder 21: 9-Seitenverhältnis, das als Widescreen und Ultra-Widescreen bezeichnet wird.

Die Koordinaten jedes Scheitelpunkts im Kameraraum müssen so transformiert werden, dass sie alle auf die nahe Kürzungsebene passen, wie unten gezeigt:


Pyramidenseite und -oberseite trimmen

Die Transformation wird unter Verwendung einer anderen Matrix ausgeführt, die als perspektivische Projektionsmatrix bezeichnet wird . Im folgenden Beispiel verwenden wir zum Ausführen der Transformationen die Winkel des Bereichs und die Position der Kürzungsebenen. Sie können jedoch stattdessen die Größe des Ansichtsfensters verwenden.


Der Vertex-Positionsvektor wird mit dieser Matrix multipliziert, wodurch wir einen neuen Satz transformierter Koordinaten erhalten.


Voila! Jetzt sind alle Scheitelpunkte so geschrieben, dass die Quellwelt als 3D-Perspektive dargestellt wird und die Primitiven in der Nähe der vorderen Kürzungsebene größer erscheinen als diejenigen, die näher an der fernen Ebene liegen.

Die Größe des Ansichtsfensters und der Betrachtungswinkel hängen zwar zusammen, sie können jedoch einzeln verarbeitet werden. Mit anderen Worten, Sie können die Kürzungspyramide so einstellen, dass Sie eine nahe Kürzungsebene erhalten, die sich in Größe und Seitenverhältnis vom Ansichtsfenster unterscheidet. Dazu ist ein zusätzlicher Schritt in der Operationskette erforderlich, bei dem die Scheitelpunkte in der nahen Kürzungsebene erneut transformiert werden müssen, um diesen Unterschied zu berücksichtigen.

Dies kann jedoch zu einer Verzerrung der sichtbaren Perspektive führen. Am Beispiel des Bethesda Skyrim 2011-Spiels können wir sehen, wie sich das Ändern des horizontalen Winkels des Sichtbarkeitsbereichs β bei gleichem Seitenverhältnis des Ansichtsfensters stark auf die Szene auswirkt:


In diesem ersten Bild setzen wir β = 75 ° und die Szene sieht völlig normal aus. Versuchen wir nun β = 120 ° zu setzen:


Zwei Unterschiede sind sofort erkennbar - erstens sehen wir jetzt viel mehr auf den Seiten unseres "Sichtfeldes"; zweitens scheinen Objekte jetzt viel weiter entfernt zu sein (insbesondere Bäume). Der visuelle Effekt auf der Wasseroberfläche sieht jetzt jedoch falsch aus, da der Prozess nicht für einen solchen Sichtbereich ausgelegt war.

Stellen wir uns nun vor, unser Charakter hätte fremde Augen und setze β = 180 °!


Ein derartiger Sichtbereich erzeugt eine fast panoramische Szene, die Sie jedoch mit einer erheblichen Verzerrung der an den Rändern gerenderten Objekte bezahlen müssen. Dies geschah erneut aufgrund der Tatsache, dass die Spieleentwickler eine solche Situation nicht vorausgesehen und die Ressourcen und visuellen Effekte des Spiels für einen solchen Betrachtungswinkel nicht erstellt haben (der Standardwert beträgt ungefähr 70 °).

Es mag den Anschein haben, dass sich die Kamera in den obigen Bildern bewegt hat, aber dies ist nicht der Fall. Die einzige Änderung besteht darin, die Pyramidenabschneidung zu modifizieren, wodurch sich wiederum die Abmessungen der nahen Abschneidungsebene geändert haben. Bei jedem Bild bleibt das Seitenverhältnis des Ansichtsfensters gleich, sodass die Skalierungsmatrix auf die Scheitelpunkte angewendet wird, sodass alles hineinpasst.

Bleibst du oder gehst du?


Nachdem wir die Transformationen in der Projektionsphase durchgeführt haben, fahren wir mit dem sogenannten Clip-Raum fort . Obwohl dies nach der Projektion erfolgt, ist es einfacher zu zeigen, was passiert, wenn wir die Vorgänge im Voraus ausführen:


In der Abbildung oben sehen wir, dass sich bei der Gummiente, einem der Fledermäuse und einem Teil der Bäume, die Dreiecke innerhalb der Pyramidenstümpfe befinden. Die andere Fledermaus und der am weitesten entfernte Baum befinden sich jedoch außerhalb der Grenzen der Pyramidenstümpfe. Obwohl die Eckpunkte, aus denen diese Objekte bestehen, bereits verarbeitet wurden, werden sie im Ansichtsfenster nicht angezeigt. Dies bedeutet, dass sie abgeschnitten sind .

Beim Kürzen entlang der Pyramide (Kegelstumpfbeschneidung) werden alle Primitive außerhalb der Kegelstumpfpyramide vollständig gelöscht und die an den Rändern liegenden in neue Primitive umgewandelt. Durch das Abschneiden wird die Leistung nicht wesentlich verbessert, da alle diese unsichtbaren Scheitelpunkte bereits vor dieser Phase in Scheitelpunktshadern usw. verarbeitet wurden. Bei Bedarf kann der gesamte Kürzungsschritt sogar vollständig übersprungen werden, diese Funktion wird jedoch nicht von allen APIs unterstützt (z. B. lässt die Standard-OpenGL das Überspringen nicht zu, dies kann jedoch mit der API-Erweiterung erfolgen).


Es ist anzumerken, dass die Position der Fernkürzungsebene in Spielen nicht immer gleich der Ziehstrecke ist , da letztere von der Spielmaschine selbst gesteuert wird. Die Engine schneidet auch die Pyramide ab (kegelstumpfförmiges Keulen). Sie führt einen Code aus, der festlegt, ob das Objekt in die Pyramidenstumpfzeichnung einbezogen wird und ob sichtbare Objekte betroffen sind. Wenn die Antwort Nein lautet, wird das Objekt nicht zum Rendern übertragen. Dies ist nicht dasselbe wie Frustrum-Clipping, da es auch Primitive außerhalb der Pyramide verwirft, aber sie haben die Vertex-Verarbeitungsstufe bereits durchlaufen. Beim Keulen werden sie überhaupt nicht verarbeitet, was eine Menge Ressourcen einspart.

Wir haben alle Transformationen und Kürzungen vorgenommen, und es scheint, dass die Eckpunkte endlich für den nächsten Schritt in der Rendering-Sequenz bereit sind. Tatsächlich ist dies jedoch nicht der Fall, da alle Berechnungen, die in der Scheitelpunktverarbeitungsphase und bei den Transformationsoperationen vom Weltraum zum Trunkierungsraum durchgeführt werden, in einem einheitlichen Koordinatensystem durchgeführt werden müssen (d. H. Jeder Scheitelpunkt hat 4 Komponenten, nicht 3). . Das Ansichtsfenster ist jedoch vollständig zweidimensional, dh die API erwartet, dass die Scheitelpunktinformationen nur die Werte für x, y enthalten (obwohl der Wert für die Tiefe z gespeichert ist).

Um die vierte Komponente loszuwerden, wird eine perspektivische Unterteilung durchgeführt, bei der jede Komponente durch den Wert von w geteilt wird . Diese Operation beschränkt x und y auf das Intervall möglicher Werte [-1,1] und z auf das Intervall [0,1]. Diese werden als normalisierte Gerätekoordinaten (NDC) bezeichnet.

Wenn Sie mehr über das, was wir gerade erklärt haben, erfahren möchten und Mathe mögen, lesen Sie das hervorragende Tutorial zu diesem Thema Song Ho An. Lassen Sie uns nun diese Eckpunkte in Pixel umwandeln!

Wir beherrschen die Rasterung


Wie bei Transformationen werden wir uns die Regeln und Prozesse ansehen, die verwendet werden, um ein Ansichtsfenster in ein Pixelraster umzuwandeln, wobei Direct3D als Beispiel dient. Diese Tabelle ähnelt einer Excel-Tabelle mit Zeilen und Spalten, in denen jede Zelle unterschiedliche Datenwerte enthält (z. B. Farbe, Tiefenwerte, Texturkoordinaten usw.). Normalerweise wird dieses Raster als Rasterbild bezeichnet , und der Prozess seiner Erzeugung wird als Rasterisierung bezeichnet . Im Artikel 3D-Rendering 101 haben wir diese Prozedur vereinfacht:


Das obige Bild vermittelt den Eindruck, dass die Grundelemente einfach in kleine Blöcke geschnitten werden, in Wirklichkeit gibt es jedoch viel mehr Operationen. Der allererste Schritt besteht darin, zu bestimmen, ob das Grundelement der Kamera zugewandt ist. Beispielsweise sind in dem obigen Bild mit einer Pyramidenstumpf die Grundelemente, aus denen der Rücken des grauen Kaninchens besteht, nicht sichtbar. Daher müssen sie nicht gerendert werden, obwohl sie im Ansichtsfenster vorhanden sind.

Wir können uns ungefähr vorstellen, wie es aussieht, wenn wir uns das Diagramm unten ansehen. Der Würfel wurde verschiedenen Transformationen unterzogen, um das 3D-Modell im 2D-Raum des Bildschirms zu platzieren. Aus Sicht der Kamera sind einige Flächen des Würfels nicht sichtbar. Wenn wir annehmen, dass alle Oberflächen undurchsichtig sind, können einige dieser Grundelemente ignoriert werden.


Von links nach rechts: Weltraum> Kameraraum> Projektionsraum> Leinwandraum

In Direct3D kann dies implementiert werden, indem dem System mitgeteilt wird, wie der Rendering-Status lauten soll. Mit dieser Anweisung wird klargestellt, dass die Seiten jedes primitiven Elements entfernt ( abgeschnitten ) werden müssen, die nach vorne oder hinten schauen (oder nicht vollständig abgeschnitten werden müssen, z. B. im Drahtgittermodus ). . Aber woher weiß sie, welche Seite vorwärts oder rückwärts schaut? Als wir die Mathematik der Eckenverarbeitung untersuchten , stellten wir fest, dass Dreiecke (oder vielmehr Ecken) normale Vektoren haben, die dem System mitteilen, in welche Richtung es schaut. Dank dieser Informationen können Sie eine einfache Überprüfung durchführen. Wenn das Grundelement fehlschlägt, wird es aus der Rendering-Kette entfernt.

Jetzt ist es Zeit, das Pixelraster anzuwenden. Dies ist wiederum ein unerwartet komplexer Vorgang, da das System verstehen muss, ob sich das Pixel innerhalb des Grundelements befindet - vollständig, teilweise oder überhaupt nicht. Dazu wird der Coverage-Test durchgeführt. Die folgende Abbildung zeigt, wie Dreiecke in Direct3D 11 gerastert werden:


Die Regel ist recht einfach: Ein Pixel wird als innerhalb des Dreiecks liegend betrachtet, wenn die Mitte des Pixels eine Prüfung besteht, die Microsoft als Regel "oben links" bezeichnet . "Oben" bezieht sich auf die Überprüfung der horizontalen Linie; Die Mitte des Pixels sollte auf dieser Linie liegen. "Links" bezieht sich auf nicht horizontale Linien, und die Mitte des Pixels sollte links von einer solchen Linie liegen. Es gibt andere Regeln, die sich auf Nicht-Primitive beziehen, z. B. einfache Segmente und Punkte, und bei Verwendung von Multisampling zusätzliche, wenn in den Regeln Bedingungen auftreten.

Wenn Sie sich die Microsoft-Dokumentation genau ansehen, können Sie feststellen, dass die durch die Pixel erzeugten Formen den ursprünglichen Grundelementen nicht sehr ähnlich sind. Dies liegt daran, dass die Pixel zu groß sind, um ein realistisches Dreieck zu erstellen. Das Bitmap-Bild enthält nicht genügend Daten zu den Originalobjekten. Dies führt zu einem Phänomen namens Aliasing .

Schauen wir uns das Aliasing anhand eines Beispiels für UL Benchmark 3DMark03 an :


720 x 480 Pixel Rasterisierung

Im ersten Bild hat das Rasterbild eine sehr niedrige Auflösung - 720 x 480 Pixel. Das Aliasing ist auf dem Geländer und im Schatten der Waffen des oberen Soldaten deutlich zu erkennen. Vergleichen Sie dies mit dem Ergebnis, das während der Rasterung mit einer 24-fachen Erhöhung der Pixelanzahl erhalten wurde:


Rasterisierung 3840 x 2160 Pixel

Hier sehen wir, dass das Aliasing auf dem Geländer und dem Schatten vollständig verschwunden ist. Es scheint, dass Sie immer eine große Bitmap verwenden sollten, aber die Rastergröße sollte von dem Monitor unterstützt werden, auf dem der Rahmen angezeigt wird. Unter Berücksichtigung der Tatsache, dass all diese Pixel verarbeitet werden müssen, ist es offensichtlich, dass die Leistung sinken wird.

Multisampling kann hier helfen. So funktioniert es in Direct3D:


Anstatt zu überprüfen, ob die Mitte des Pixels den Rasterungsregeln entspricht, werden mehrere Punkte in jedem Pixel (Subpixel-Abtastwerte oder Sub-Abtastwerte genannt) überprüft , und wenn einige von ihnen die Anforderungen erfüllen, bilden sie einen Teil der Figur. Es mag den Anschein haben, als gäbe es keinen Vorteil, und das Aliasing wird sogar verbessert. Bei Verwendung von Multisampling werden jedoch Informationen darüber, welche Unterabtastwerte vom Grundelement abgedeckt werden und die Ergebnisse der Verarbeitung von Pixeln in einem Pufferspeicher gespeichert.

Dieser Puffer wird dann verwendet, um diese Unterabtastwerte und Pixel zu mischen, so dass die Kanten des Grundelements weniger zerrissen werden. Wir werden das Aliasing in einem anderen Artikel ausführlicher betrachten, aber diese Informationen reichen vorerst aus, um zu verstehen, was Multisampling tun kann, wenn zu wenige Pixel gerastert werden:


Wie Sie sehen, hat sich das Ausmaß des Aliasing an den Rändern verschiedener Formen erheblich verringert. Eine Rasterung mit höherer Auflösung ist definitiv besser, aber ein Leistungsabfall kann dazu führen, dass Sie Multisampling verwenden.

Auch während der Rasterung wird ein Okklusionstest durchgeführt. Dies ist erforderlich, da das Ansichtsfenster mit überlagerten Grundelementen gefüllt wird. In der obigen Abbildung überlappen beispielsweise die vorausschauenden Dreiecke, aus denen der Soldat im Vordergrund besteht, dieselben Dreiecke eines anderen Soldaten. Neben der Überprüfung, ob das Grundelement ein Pixel abdeckt, können Sie auch die relativen Tiefen vergleichen. Befindet sich eine Oberfläche hinter der anderen, muss sie aus dem verbleibenden Renderprozess entfernt werden.

Wenn jedoch das nahe Grundelement transparent ist, bleibt das ferne Grundelement sichtbar, obwohl es den Überlappungstest nicht besteht. Aus diesem Grund führen fast alle 3D-Engines vor dem Senden von Daten an die GPU Überlappungsprüfungen durch und erstellen stattdessen einen sogenannten Z-Puffer , der Teil des Renderprozesses ist. Hier wird der Rahmen auf die übliche Weise erstellt, aber anstatt die vorgefertigten Pixelfarben im Speicher zu speichern, speichert die GPU nur die Tiefenwerte. Später können sie in Shadern verwendet werden, um die Sichtbarkeit zu überprüfen und Aspekte in Bezug auf überlappende Objekte mit großer Kontrolle und Genauigkeit zu überprüfen.


Je dunkler die Pixelfarbe in dem oben gezeigten Bild ist, desto näher ist das Motiv an der Kamera.Der Frame wird einmal gerendert, um einen Z-Puffer zu erstellen, und dann erneut gerendert. Diesmal wird jedoch während der Verarbeitung der Pixel ein Shader gestartet, der sie auf Werte im Z-Puffer überprüft. Wenn es unsichtbar ist, wird die Pixelfarbe nicht in den Puffer des fertigen Rahmens geschrieben.

Bisher wird unser letzter Hauptschritt die Interpolation von Scheitelpunktattributen sein - im ursprünglichen vereinfachten Schema war das Grundelement ein vollständiges Dreieck, aber vergessen Sie nicht, dass das Ansichtsfenster nur mit den Ecken der Figuren und nicht mit den Figuren selbst gefüllt ist. Das heißt, das System muss bestimmen, welche Farbe, Tiefe und Textur des Grundelements zwischen den Scheitelpunkten liegen soll, und diese Operation wird als Interpolation bezeichnet . Wie Sie vielleicht erraten haben, ist dies eine weitere Berechnung, und es ist nicht so einfach.

Obwohl der gerasterte Bildschirm in 2D dargestellt wird, repräsentieren die darin enthaltenen Strukturen eine 3D-Perspektive. Wenn die Linien wirklich zweidimensional wären, könnten wir eine einfache lineare Gleichung verwenden , um Farben und andere Dinge zu berechnen , da wir uns von einem Scheitelpunkt zum anderen bewegen. Aufgrund des 3D-Aspekts der Szene muss die Interpolation diese Perspektive berücksichtigen. Um mehr über diesen Prozess zu erfahren, lesen Sie den ausgezeichneten Artikel von Simon Young .

Damit ist die Aufgabe erledigt - die 3D-Welt der Eckpunkte verwandelt sich in ein 2D-Raster aus bunten Blöcken. Aber wir sind noch nicht ganz fertig.

Von vorne nach hinten (mit einigen Ausnahmen)


Bevor wir mit der Rasterisierung fertig sind, müssen wir über die Reihenfolge der Rendering-Sequenz sprechen. Wir sprechen nicht über die Phase, in der beispielsweise eine Tessellation in der Verarbeitungssequenz auftritt. Wir meinen die Reihenfolge, in der Primitive verarbeitet werden. Objekte werden normalerweise in der Reihenfolge verarbeitet, in der sie sich im Indexpuffer befinden (ein Speicherblock, der dem System mitteilt, wie Scheitelpunkte gruppiert sind). Dies kann die Verarbeitung transparenter Objekte und Effekte erheblich beeinflussen.

Der Grund dafür ist, dass die Primitiven einzeln verarbeitet werden. Wenn Sie die Primitiven zuerst vorne rendern, sind alle dahinter liegenden Elemente unsichtbar (hier kommt Okklusions-Culling ins Spiel) und können aus dem Prozess entfernt werden (um zu sparen) Leistung). Dies wird normalerweise als Front-to-Back- Rendering bezeichnet , und für diesen Prozess muss der Indexpuffer auf diese Weise sortiert werden.

Wenn jedoch einige dieser Grundelemente vor der Kamera transparent sind, führt das Rendern von vorne nach hinten zum Verlust von Objekten, die hinter der Kamera transparent sind. Eine Lösung ist das Rendern von hinten nach vorne, wobei transparente Grundelemente und Effekte zuletzt berechnet werden.


Von links nach rechts: Die Reihenfolge in der Szene, Rendern von vorne nach hinten, Rendern von hinten nach vorne

Das heißt, in allen modernen Spielen wird das Rendern von hinten nach vorne ausgeführt? Wie auch immer - vergessen Sie nicht, dass das Rendern jedes einzelnen Grundelements zu einem viel stärkeren Leistungsabfall führt als das Rendern nur des, was wir sehen. Es gibt andere Möglichkeiten, transparente Objekte zu verarbeiten, aber im Allgemeinen gibt es keine ideale Lösung, die für jedes System geeignet ist, und jede Situation muss separat betrachtet werden.

Auf diese Weise können wir die wichtigsten Vor- und Nachteile der Rasterung verstehen. Bei modernen Geräten handelt es sich um einen schnellen und effizienten Vorgang, der jedoch nur annähernd das widerspiegelt, was wir sehen. In der realen Welt kann jedes Objekt Licht absorbieren, reflektieren und manchmal brechen, und all dies beeinflusst das endgültige Erscheinungsbild der angezeigten Szene. Wenn wir die Welt in Primitive aufteilen und nur Teile davon rendern, werden wir schnell. aber ein sehr grobes Ergebnis.

Nun, wenn es einen anderen Weg gäbe ...

Ein anderer Weg ist: Raytracing!


Vor fast fünfzig Jahren arbeitete ein Informatiker namens Arthur Eppel an einem System zum Rendern von Bildern auf einem Computer, bei dem ein Lichtstrahl von der Kamera in einer geraden Linie ausgestrahlt wurde, bis er mit einem Objekt kollidierte. Nach der Kollision änderten die Eigenschaften des Materials (Farbe, Reflexionsvermögen usw.) die Helligkeit des Lichtstrahls. Für jedes Pixel im gerenderten Bild wurde ein Strahl ausgesendet, und der Algorithmus führte eine Reihe von Berechnungen durch, um die Farbe des Pixels zu bestimmen. Eppels Verfahren nennt man Ray Casting .

Etwa zehn Jahre später wurde ein anderer Wissenschaftler namens John Whitedentwickelten einen mathematischen Algorithmus, der den Eppel-Prozess implementiert. Wenn jedoch ein Strahl mit einem Objekt kollidiert, werden je nach Material des Objekts zusätzliche Strahlen erzeugt, die in verschiedene Richtungen divergieren. Da dieses System bei jeder Interaktion mit Objekten neue Strahlen erzeugt, war der Algorithmus von Natur aus rekursiv und rechenintensiver. Es hatte jedoch einen signifikanten Vorteil gegenüber Eppels Methode, da er Reflexionen, Brechungen und Schatten korrekt berücksichtigen konnte. Dieses Verfahren nennt man Raytracing (ray Player Tracing) (genau genommen ist es die umgekehrte Strahlverfolgung, weil wir den Strahl der Kamera und nicht durch die Objekte folgen) , und seitdem es hat sich zu einem heiligen Gral für Computergrafik und Filme .


Aus dem obigen Bild können Sie ersehen, wie der Whited-Algorithmus funktioniert. Für jedes Pixel im Bild wird ein Strahl von der Kamera ausgesendet und bewegt sich bis zur Oberfläche. In diesem Beispiel ist die Oberfläche durchscheinend, sodass Licht reflektiert und durch sie gebrochen werden kann. In beiden Fällen werden Sekundärstrahlen erzeugt, die sich fortbewegen, bis sie mit der Oberfläche kollidieren. Neue Sekundärstrahlen werden ebenfalls erzeugt, um die Farbe der Lichtquellen und die von ihnen erzeugten Schatten zu berücksichtigen.

Die rekursive Natur des Prozesses besteht darin, dass jedes Mal, wenn ein neuer emittierter Strahl die Oberfläche schneidet, Sekundärstrahlen erzeugt werden können. Dies kann schnell außer Kontrolle geraten, sodass die Anzahl der erzeugten Sekundärstrahlen immer begrenzt ist. Nach Abschluss des Strahlengangs wird die Farbe an jedem Endpunkt anhand der Materialeigenschaften dieser Oberfläche berechnet. Dieser Wert wird dann entlang des vorherigen Strahls übertragen und ändert die Farbe für diese Oberfläche usw., bis wir den Startpunkt des Primärstrahls erreichen, nämlich das Pixel im Frame.

Ein solches System kann äußerst komplex sein und selbst einfache Szenen können einen großen Rechenaufwand verursachen. Glücklicherweise gibt es Tricks, die die Arbeit vereinfachen: Erstens können Sie Geräte verwenden, die speziell für die Beschleunigung dieser mathematischen Operationen entwickelt wurden, ähnlich wie dies bei der Matrixmathematik in der Vertex-Verarbeitung der Fall ist (dazu später mehr). Ein weiterer wichtiger Trick ist der Versuch, den Prozess der Bestimmung des Objekts, in das der Strahl gefallen ist, und der genauen Stelle ihres Schnittpunkts zu beschleunigen. Wenn das Objekt aus vielen Dreiecken besteht, kann diese Aufgabe überraschend schwierig sein:


Quelle: Raytracing in Echtzeit mit Nvidia RTX:

Anstatt jedes einzelne Dreieck in jedem Objekt zu überprüfen, wird vor dem Raytracing eine Liste der Bounding Volumes (BVs) erstellt. Für verschiedene Strukturen innerhalb des Objekts werden zyklisch kleinere Begrenzungsvolumina erstellt.

Zum Beispiel wird der erste BV das gesamte Kaninchen sein. Das nächste Paar beschreibt seinen Kopf, seine Beine, seinen Körper, seinen Schwanz usw .; Jedes Volumen wird wiederum eine andere Sammlung von Volumen für kleinere Strukturen des Kopfes, des Körpers usw. sein, und die letzte Volumenstufe wird eine kleine Anzahl von Dreiecken zur Überprüfung enthalten. Alle diese Volumes sind häufig in einer geordneten Liste angeordnet ( BV-Hierarchie genannt)oder BVH); Dank dessen prüft das System jedes Mal einen relativ kleinen BV-Betrag:


Obwohl die Verwendung von BVH die Strahlverfolgung nicht beschleunigt, ist die Generierung einer Hierarchie und des erforderlichen anschließenden Suchalgorithmus im Allgemeinen viel schneller als die Überprüfung des Schnittpunkts eines Strahls mit einem von Millionen Dreiecken in der 3D-Welt.

Heutzutage verwenden Programme wie Blender und POV-Ray Ray Tracing mit zusätzlichen Algorithmen (wie Photon Tracing und Radiosity), um sehr realistische Bilder zu erzeugen:


Die naheliegende Frage könnte sich stellen: Wenn Ray Tracing so gut ist, warum wird es nicht überall verwendet? Die Antwort liegt in zwei Bereichen: Erstens erzeugt bereits eine einfache Strahlenverfolgung Millionen von Strahlen, die immer wieder berechnet werden müssen. Das System startet mit nur einem Strahl pro Bildschirmpixel, dh mit einer Auflösung von 800 x 600 erzeugt es 480.000 Primärstrahlen und dann erzeugt jeder von ihnen viele Sekundärstrahlen. Dies ist selbst für moderne Desktop-PCs eine sehr schwierige Aufgabe. Das zweite Problem ist, dass die einfache Strahlverfolgung nicht sehr realistisch ist und für ihre ordnungsgemäße Implementierung eine ganze Reihe zusätzlicher, sehr komplexer Gleichungen erforderlich ist.

Selbst mit moderner Ausrüstung ist der Arbeitsaufwand in 3D-Spielen für die Echtzeitimplementierung unerreichbar. In 3D-Rendering 101Wir haben gesehen, dass ein Raytracing-Benchmark einige zehn Sekunden benötigt, um ein einzelnes Bild mit niedriger Auflösung zu erstellen.

Wie hat der erste Wolfenstein 3D 1992 Ray Casting durchgeführt und warum bieten Spiele wie Battlefield V und Metro Exodus , die 2019 veröffentlicht wurden, Ray Tracing-Funktionen? Führen sie eine Rasterung oder Raytracing durch? Nach und nach von beiden.

Ein hybrider Ansatz für Gegenwart und Zukunft


Im März 2018 gab Microsoft die Veröffentlichung einer neuen API-Erweiterung für Direct3D 12 mit dem Namen DXR (DirectX Raytracing) bekannt. Es war eine neue Grafik-Pipeline, die Standard-Rasterisierungs- und Berechnungs-Pipelines ergänzt. Zusätzliche Funktionen wurden durch das Hinzufügen von Shadern, Datenstrukturen usw. bereitgestellt, erforderten jedoch keine Hardwareunterstützung, mit Ausnahme derjenigen, die bereits für Direct3D 12 benötigt wurde.


Auf der gleichen Spieleentwicklerkonferenz, auf der Microsoft über DXR sprach , sprach Electronic Arts über sein Pica Pica-Projekt - ein Experiment mit einer 3D-Engine, die DXR verwendet. Das Unternehmen hat gezeigt, dass Raytracing verwendet werden kann, jedoch nicht zum Rendern des gesamten Frames. Der Großteil der Arbeit verwendet traditionelle Rastertechniken und computerbasierte Shader, während DXR in bestimmten Bereichen verwendet wird. Das heißt, die Anzahl der erzeugten Strahlen ist viel geringer als für die gesamte Szene.


Dieser hybride Ansatz wurde in der Vergangenheit verwendet, wenn auch in geringerem Umfang. Beispielsweise verwendete Wolfenstein 3D Ray Casting , um einen Frame zu rendern, aber es wurde mit einem Strahl pro Pixelspalte und nicht mit einem Pixel gearbeitet. Es mag immer noch beeindruckend erscheinen, es sei denn, Sie erinnern sich, dass das Spiel mit einer Auflösung von 640 x 480 [ca. eigentlich 320 x 200], dh es wurden gleichzeitig nicht mehr als 640 Strahlen ausgesendet.

Anfang 2018 erfüllten Grafikkarten wie die AMD Radeon RX 580 oder die Nvidia GeForce 1080 Ti die DXR-Anforderungen, aber trotz ihrer Rechenleistung gab es Bedenken, dass sie nicht leistungsfähig genug sein könnten, um DXR sinnvoll zu machen.

Die Situation änderte sich im August 2018, als Nvidia seine neueste GPU-Architektur mit dem Codenamen Turing veröffentlichte . Das wichtigste Merkmal dieses Chips war das Auftreten der sogenannten RT-Kerne: separate Logikblöcke zur Beschleunigung der Berechnungen des Schnittpunkts der Strahlendreiecke und des Durchlaufs der Hierarchie der Grenzvolumina (BVH). Diese beiden Verfahren sind zeitaufwändige Verfahren zur Bestimmung der Wechselwirkungspunkte von Licht mit Dreiecken, aus denen Szenenobjekte bestehen. Da es sich bei RT-Kernen um einzigartige Turing-Prozessoreinheiten handelte, konnte der Zugriff nur über die proprietäre Nvidia-API erfolgen.

Das erste Spiel, das dieses Feature unterstützte, war EAs Battlefield V. Als wir DXR darin testeten , waren wir beeindruckt von der Verbesserung der Reflexionen im Wasser, auf Gras und Metallen sowie einer entsprechenden Abnahme der Leistung:


Um ehrlich zu sein, haben die nachfolgenden Patches die Situation verbessert, aber die Geschwindigkeit beim Rendern von Frames war (und ist) immer noch gesunken. Bis 2019 gab es einige andere Spiele, die diese API unterstützen und Raytracing für einzelne Teile des Frames durchführen. Wir haben Metro Exodus und Shadow of the Tomb Raider in der gleichen Situation getestet - durch die aktive Nutzung von DXR wird die Framerate erheblich reduziert.

Etwa zur gleichen Zeit kündigten UL Benchmarks die Erstellung eines DXR-Funktionstests für 3DMark an :


DXR wird in der Nvidia Titan X (Pascal) -Grafikkarte verwendet - ja, das Ergebnis sind 8 fps

Eine Studie zu Spielen mit DXR-Unterstützung und dem 3DMark-Test hat jedoch gezeigt, dass Ray Tracing auch im Jahr 2019 für die GPU eine sehr schwierige Aufgabe bleibt, selbst bei einem Preis von mehr als 1.000 US-Dollar. Bedeutet dies, dass wir keine wirklichen Alternativen zur Rasterisierung haben?

Fortschrittliche Funktionen in Consumer-3D-Grafiktechnologien sind oft sehr teuer und ihre anfängliche Unterstützung für neue API-Funktionen kann sehr fragmentiert oder langsam sein (wie wir beim Testen von Max Payne 3 auf verschiedenen Direct3D-Versionen im Jahr 2012 herausgefunden haben). Das letztere Problem tritt normalerweise auf, weil Spieleentwickler versuchen, so viele moderne Funktionen wie möglich in ihre Produkte zu integrieren, manchmal ohne ausreichende Erfahrung.

Vertex- und Pixel-Shader, Tessellation, HDR-Rendering und die Verschleierung des Bildschirmraums waren jedoch auch einmal kostspielige Techniken, die nur für leistungsstarke GPUs geeignet waren. Jetzt sind sie der Standard für Spiele, und viele Grafikkarten werden unterstützt. Dasselbe wird mit Raytracing passieren; Im Laufe der Zeit wird es einfach zu einem weiteren Detailparameter, der für die meisten Spieler standardmäßig aktiviert ist.

Abschließend


Wir sind also am Ende des zweiten Teils der Analyse angelangt, in dem wir uns eingehender mit der Welt der 3D-Grafik befasst haben. Wir haben gelernt, wie Welten und Modelle aus drei Dimensionen in ein flaches 2D-Bild umgewandelt werden. Wir haben gesehen, dass wir den Umfang berücksichtigen müssen, und festgestellt, welche Auswirkungen dies hat. Wir untersuchten den Konvertierungsprozess dieser Verines in Pixel und schlossen mit einem kurzen Blick auf Alternativen zum herkömmlichen Rasterungsprozess.

Wie im vorherigen Artikel war es unwahrscheinlich, dass wir alle Themen enthüllen konnten, und wir haben einige Details übersehen - am Ende ist dies kein Lehrbuch! Aber wir hoffen, dass Sie etwas Neues gelernt haben und nun die Arbeit von Programmierern und Ingenieuren respektieren, die Computer und Wissenschaft verwendet haben, um all dies in Ihren Lieblings-3D-Spielen umzusetzen.

Source: https://habr.com/ru/post/de480338/


All Articles