Was ist Echtzeit-Raytracing. Back-Trace-Methode

Dieser Artikel konzentriert sich auf die Verwendung des Reverse-Raytracing-Verfahrens zum Rendern von Bildern in Computerspielen. Ihre Vor- und Nachteile werden im Vergleich zu herkömmlicher Technologie betrachtet. Es erzählt von einem konzeptionellen 3D-Spiel, das erstmals eine vollständig auf dem Prinzip des Reverse Raytracing aufgebaute Grafik-Engine nutzt. Auch die Fragen der Entwicklung von Spielvideobeschleunigern werden angesprochen.

traditionelle Technologie

Für diejenigen, die mit der Theorie der 3D-Grafik nicht vertraut sind, werde ich kurz erklären, was die Reverse-Raytracing-Methode ist und wie sie sich von der traditionellen Spielgrafikmethode unterscheidet. Bei der traditionellen Methode der Bildwiedergabe in Computerspielen wird die Szene oder, wenn Sie so wollen, die Spielwelt durch eine Reihe von Dreiecken dargestellt. Für jedes Dreieck werden Texturen und Beleuchtungsgrad eingestellt. Als nächstes werden die Dreiecke in einem Haufen in den 3D-Beschleuniger geschoben und gezeichnet, ungefähr so, wie ein Künstler ein festes Dreieck auf ein Blatt Papier zeichnet. Der Unterschied liegt in der Verwendung des Tiefenpuffers. Der Tiefenpuffer ist erforderlich, um das Zeichnen von Dreiecken zu vermeiden, die von anderen Objekten in der Szene verdeckt werden. Beim Zeichnen der Punkte eines neuen Dreiecks wird der entsprechende Wert des Tiefenpuffers überprüft. Der Tiefenpuffer, oder auch Z-Puffer genannt, speichert den Abstand des Betrachters zum bereits gezeichneten Bild. Wenn der Abstand zum Punkt des neuen Dreiecks kleiner ist als der in den Z-Puffer geschriebene Wert, dann wird dieser Punkt nicht von Punkten von enger beieinander liegenden Dreiecken bedeckt und kann gezeichnet werden, während der Wert des Tiefenpuffers es ist auch aktualisiert. Mit dieser Methode können Sie ein Bild einer Szene erstellen, die aus Dreiecken beliebiger Komplexität besteht. Einer der Vorteile dieser Methode besteht darin, dass sie auf den "alten" Prozessoren der i386-, i486-Generation implementiert werden konnte, dh eine ziemlich aussagekräftige Spielszene in Echtzeit und in hoher Auflösung zu rendern.

Verschiedene Arten, ein Bild zu konstruieren, können sich in der Arbeitsgeschwindigkeit sowie in der Qualität, dem Realismus oder der Schönheit des konstruierten Bildes unterscheiden. Natürlich erfordern Verfahren, die es ermöglichen, ein realistischeres Bild zu zeichnen, auch große Rechenressourcen. Natürlich berücksichtigen wir keine offensichtlich schlechten Methoden, die sowohl langsam sind als auch schlecht zeichnen. Zu Beginn der Entwicklung der Computerspielindustrie, als Personalcomputer relativ wenig Energie aufwiesen, wurde natürlich das schnellste, am wenigsten rechenintensive Verfahren zum Rendern gewählt, das oben erwähnte Z-Puffer-Verfahren.

Eine dreidimensionale Szene besteht jedoch nicht nur aus einigen geometrischen Details, sie ist ohne Licht nicht denkbar, weil wir sie sonst einfach nicht sehen würden. Und mit der Z-Buffer-Methode können Sie nur die Geometrie der Szene zeichnen. Was zu tun ist? Das genaue physikalische Modell der Lichtausbreitung ist sehr komplex, wir können über einige Annäherungen an natürliches Licht sprechen. Es ist erforderlich, dass es an schattigen Orten, an denen keine direkten Lichtstrahlen fallen, neben Lichtquellen dunkel sein sollte - Licht. Um in Bezug auf die Beleuchtung realistische Bilder der Szene zu erstellen, begannen sie, vorberechnete Texturen, die sogenannte Lightmap, zu verwenden, die die Beleuchtungswerte statischer Objekte in der Szene enthielt. Eine solche Textur wird an einer Stelle mit einer regelmäßigen Materialtextur überlagert und verdunkelt diese je nach Position des Objekts auf der Bühne, dessen Beleuchtung. Dies erfordert natürlich eine völlig statische Szene und Lichtquellen, da die Berechnung dieser Lightmaps extrem lange dauert. Diese Technologie wird seit vielen Jahren in Computerspielen verwendet, und ihre Verwendung hat dazu geführt, dass sich dreidimensionale Spiele in Bezug auf die Grafik-Engine nur in der Anzahl der Dreiecke und Texturen im Level zu unterscheiden begannen. Genauso wie es keine dynamischen Lichtquellen und die Fähigkeit gab, das Level zu zerstören, gibt es auch keine, da es keine dynamische Berechnung von Beleuchtungsschatten gibt. Wenn Sie die Lampe bewegen oder das Fenster schließen, ändert niemand die Beleuchtung der Szene, daher gibt es in den Spielen keine solche Möglichkeit. Es gibt nur sogenannte Scheinlösungen, wenn an einem bestimmten Ort etwas getan werden kann, weil diese Möglichkeit im Voraus vorgesehen und alles im Voraus berechnet wird.

Erst vor kurzem tauchten Schatten von dynamischen Modellen, allen möglichen Monstern und Bots auf. Wir werden das Thema ansprechen, wie diese Schatten berechnet werden, aber oft sehen sie nicht natürlich aus, weil es beispielsweise viele Lichtquellen gibt und der Schatten nur von einer kommt, scharf und nicht schön.
Viele Jahre lang wurde der Fortschritt der Spielgrafik ausschließlich mit dem Aufkommen neuer Generationen von Grafikbeschleunigern in Verbindung gebracht. In der Tat hat es sich als sehr praktisch erwiesen, die Arbeit zum Zeichnen von Dreiecken auf den Beschleuniger zu verlagern. Die Aufgabe der Rasterung und Texturierung eines Dreiecks ist das Herzstück der Spielgrafik, daher ist es nur natürlich, dass diese sehr private und spezifische Operation durch die Entwicklung speziell optimierter Hardware dramatisch beschleunigt wurde. Die Verwendung von Beschleunigern führte jedoch nur zu einer verbesserten Bildqualität, hochwertigen Textur-Mapping-Modi, drei linearen und anisotropen Filtern, der Möglichkeit, hohe Auflösungen zu verwenden und einer Vollbild-Bildglättung. An der Berechnung der Ausleuchtung und Dynamik der Szene hat sich bisher nichts geändert. Der Mangel an dynamischem Licht macht die Levels des modernen Spiels langweilig. Statische Beleuchtung und Szenen können allmählich störend werden. Als ob die Zeit stehen geblieben wäre und die Spieler in dieser angehaltenen Zeit laufen würden. Wenn sie jetzt die Fähigkeiten neuer Beschleuniger erkunden, untersuchen sie den Bildschirm gerne unter einer Lupe und suchen nach einer weiteren ohnehin unbedeutenden Steigerung der Bildqualität, die in einem Spiel äußerst schwer zu erkennen ist.

Raytracing-Methode


Welche Methode wird interessanterweise verwendet, um realistische Beleuchtung zu berechnen, wenn realistische Szenen, Cartoons, animierte Szenen gerendert werden, welches Prinzip liegt der Konstruktion derselben Lightmap zugrunde? In diesem Bereich hat sich das Raytracing-Verfahren und seine Modifikationen durchgesetzt.

Prozessorbewertungen erwähnen oft Testergebnisse in 3D-Grafikpaketen wie 3DMax, LightWave und anderen. Die Zeit, die zum Rendern einer komplexen Szene mit realistischer Beleuchtung, Reflexion und Lichtbrechung benötigt wird, wird gemessen. Das ist nur die Szene und wird mit der Raytracing-Methode gezeichnet.

Im Gegensatz zur Z-Buffer-Methode ist die Raytracing-Methode zunächst darauf ausgelegt, ein realistisches Bild mit einem komplexen Beleuchtungsmodell zu erstellen. Das Prinzip der umgekehrten Strahlverfolgung besteht darin, dass ein Rückstrahl durch jeden Punkt des Bildschirms gezogen wird, bis er sich mit dem nächstgelegenen Objekt in der Szene schneidet, dann wird ein Strahl von diesem Punkt in Richtung der Lichtquelle gezogen und so modelliert die Lichtausbreitung. Wenn der Strahl, der auf die Lichtquelle geschossen wird, nichts auf seinem Weg kreuzt, wird dieser Punkt beleuchtet, ansonsten liegt er im Schatten. Trifft der Strahl auf eine Spiegelfläche, so wird nach den Gesetzen der Optik ein reflektierter Strahl emittiert, der es ermöglicht, eine Reflexion aufzubauen. Abhängig von den Eigenschaften des Mediums, das der Strahl durchdringt, kann er gebrochen werden, wodurch Sie komplexe realistische Lichteffekte simulieren können. Mit dieser Methode können Sie nicht nur Schatten von Objekten erhalten, sondern auch die sekundäre Beleuchtung berechnen, wenn reflektiertes schwaches Licht direkt auf schattige Bereiche trifft und die Schatten verwischt.

Es ist jedoch leicht einzusehen, dass dieses Verfahren äußerst rechenaufwändig ist. Sie können auf die Tests von Prozessoren in 3D-Modellierungsprogrammen achten, wie lange sogar 1 Frame berücksichtigt wird. Keine echte Zeit hier und riecht nicht. Darüber hinaus war es früher auf PCs noch langsamer, was keinen Raum für seine Verwendung in Computerspielen ließ.
Aber in den letzten Jahren hat die Leistung von Personalcomputern erheblich zugenommen und ermöglichte die Implementierung des Raytracing-Verfahrens nahezu in Echtzeit, wenn auch mit starken Einschränkungen in Bildqualität und Auflösung.

Da für jeden Punkt des Bildschirms ein sehr aufwendiges Raytracing-Verfahren durchgeführt werden muss, hängt die Tracing-Geschwindigkeit sehr stark von der Bildschirmauflösung, von dessen Fläche ab. Das Rendern eines Bildes mit 1024 x 768 dauert also zehnmal länger als das Rendern eines Bilds mit 320 x 240. Es ist möglich, das Echtzeit-Raytracing-Verfahren zu implementieren, die ganze Frage ist, in welcher Auflösung und mit welcher Bildqualität.


Bis vor kurzem war Echtzeit-Raytracing auf dem PC die Domäne kleiner Demoprogramme, die schöne Bilder zeichnen, aber mit langsamer Geschwindigkeit und niedriger Auflösung laufen. Auf www.scene.org gibt es viele solcher Programme. Allerdings gelang es mir, indem ich zeitweilig auf viele Reize der Raytracing-Methode verzichtete, eine vollwertige 3D-Engine und darauf aufbauend das erste Computerspiel mit Echtzeit-Raytracing zu erschaffen.

Konzeptspiel mit einer 3D-Engine basierend auf der Back-Raytracing-Methode

Sogenannte Concept-Cars, reale Prototypen zukünftiger Serienautos, werden auf verschiedenen Automobilausstellungen vorgeführt. Sie sind extrem teuer, aus Verbrauchersicht nicht ausgetestet, aber sie repräsentieren neue Ideen. Ich habe ein Konzeptspiel erstellt. Was haben Sie umgesetzt, das auf modernen PCs in Echtzeit funktioniert?
Die Raytracing-Engine wurde ursprünglich mit zwei Hauptanforderungen festgelegt: dass die gesamte Szenenbeleuchtung in Echtzeit berechnet wird und dass keine vorberechneten Pegelinformationen verwendet werden. All dies sollte es Ihnen ermöglichen, das Niveau der Dynamik beliebig zu ändern. Etwas, das moderne Motoren nicht bieten können.
Gekoppelt mit der dynamischen Beleuchtungsberechnung macht das Fehlen von Vorinformationen das Zeichnen von unendlichen Welten recht einfach, da Sie nur nicht sehr umfangreiche Informationen über die Levelgeometrie hinterlegen müssen.

Die Erfüllung dieser strengen Anforderungen an moderne Prozessoren erforderte die Einführung anderer schwerwiegender Einschränkungen, glücklicherweise nicht grundlegender. Mit dem Wachstum der verfügbaren Rechenleistung werden diese Einschränkungen jedoch aufgehoben, aber das Wesentliche bleibt.
Zunächst habe ich mich geweigert, die irdische Realität zugunsten fremder Welten zu modellieren. Dies ermöglichte es, auf die Verwendung eines Dreiecks, das für Raytracing nicht sehr praktisch ist, als Hauptgrundelement für die Konstruktion einer Szene zu verzichten. Die fremde Welt muss nicht eckig sein, lass sie rund sein. Als Grundelement für die Konstruktion der Szene wurde eine Kugel gewählt. Da moderne Spiele mit hohen Auflösungen wie 1024x768 arbeiten müssen, musste auf die Berechnung von Reflexionen und Brechungen verzichtet werden, da dies die Verarbeitung des dem Bildschirmpunkt entsprechenden Strahls stark erschwerte. Aber mit zunehmender Rechenleistung wird es möglich sein, sowohl den Satz von Primitiven als auch die Tiefe der Strahlverfolgung zu erweitern, dh Reflexionen, Brechungen usw. hinzuzufügen.

Was sind also die Hauptmerkmale von VirtualRay – einer 3D-Engine, die auf der Raytracing-Methode basiert? Auf den modernsten Prozessoren für PCs läuft es mit einer mehr oder weniger akzeptablen Geschwindigkeit bei einer Auflösung von 1024x768x32. Wir gehen davon aus, dass diese Auflösung verwendet wird, denn wenn Sie eine niedrigere Auflösung verwenden, können die Leistungsparameter anders sein.

Rendern von Szenen, die aus vielleicht Tausenden von sich schneidenden Kugeln bestehen. In Wirklichkeit kann die Szene unendlich sein, dh nur der sichtbare Bereich.

Frame-by-Frame-Berechnung aller Beleuchtungen und Schattierungen. Alle Lichter sind dynamisch (sogar statisch), weil sie tatsächlich dynamisch sind, nur ihre Position nicht von Frame zu Frame ändern.

Pixelweise Berechnung der Ausleuchtung und pixelweise Überlagerung von Schatten, natürlich dynamisch.

Wiedergabe von weichen Schatten basierend auf der physikalischen Annäherung an volumetrische Lichtquellen. Das heißt, die Schattengrenze ist nicht scharf, sondern stark unscharf, der Grad der Unschärfe kann angepasst werden. Das sind zwar nicht ganz reale, physikalisch verlässliche weiche Schatten, aber ungefähre.

Bis zu 8 Lichtquellen können jeweils eine Kugel beleuchten, eine Kugel kann bis zu 8 Schatten werfen. Dies ist keine grundsätzliche Einschränkung, es ist nur so, dass bei vielen Lichtquellen in einem Bereich natürlich alles stark verlangsamt wird.

Unterstützung für Punktlichter und unendlich weit entfernte Lichter wie die Sonne. In der Regel wird die Szene von einer "solaren" Lichtquelle und mehreren lokalen Lichtquellen beleuchtet.

Eine vollständig dynamische Szene, dh die Position von Objekten kann sich beliebig ändern.

Textur-Mapping und bilineare Filterung.

Begrenzte Verwendung von transparenten Kugeln mit dynamischer Opazität.

Eine nicht so raffinierte Darstellung der Planetenoberfläche als eine große Kugel, die einen Horizonteffekt bildet, bei dem weit entfernte Objekte hinter der Horizontlinie verborgen sind.

Zu den lokalen Mängeln der Engine gehört zunächst einmal, dass sie mit "billigen" Effekten wie Sprite-Blitzen etc. geizt, die moderne Videobeschleuniger so wunderbar hinbekommen.
Welche Art von Spiel konnte mit der VirtualRay-Engine erstellt werden? Im Allgemeinen können Sie sehr viele Spiele darauf erstellen, die von einem Weltraumsimulator bis zu einem Multiplayer-Online-Universum reichen. Übrigens sind bei letzterem Typ die Vorteile der Engine bei der Umsetzung dynamischer Szenenwechsel besonders deutlich. Als "Konzeptspiel" habe ich ein Projekt namens AntiPlanet erstellt - ein einfacher 3D-Shooter mit geradlinigen Monstern mit Doom-ähnlichem Verhalten. Die Levels für das Spiel sind Stücke fremden Geländes unterschiedlicher Größe, die von lokalen Sonnen beleuchtet werden. Übrigens bewegt sich die Sonne über den Himmel, entsprechend ändert sich die Beleuchtung und Schattierung der Szene. Insgesamt stehen in der aktuellen Version des Spiels 5 Level zur Verfügung, von denen eines ein Indoor-Labyrinth aus Höhlen ist. Der Rest ist meist offen. Die Engine ist vielseitig genug, um sowohl offene als auch geschlossene Szenen ohne spezielle Optimierungen zu zeichnen.

Es gibt 5 Arten von Monstern, die den Spieler jagen, die Monster unterscheiden sich in der Art der verwendeten Waffen, Geschwindigkeit und Stärke. Übrigens stehen dem Spieler zehn Arten von Waffen zur Verfügung, die eine Vielzahl von Projektilen, Raketen und Bomben abfeuern. Die kugelförmige Natur der Waffe macht sie etwas eintönig, aber die Projektile explodieren in ein Bündel von Fragmenten, wenn sie explodieren. Es gibt 3 Hauptarten von Spielen - nur die Jagd nach Monstern, wenn der Spieler eine bestimmte Anzahl von Monstern in einer bestimmten Zeit zerstören muss. Die zweite Art von Spiel besteht darin, spezielle Artefakte zu finden, die im Level versteckt sind. Und im dritten Fall muss der Spieler einfach eine bestimmte Zeit auf einem unbekannten Planeten überleben. Bei der Auswahl eines Spiels können Sie die Anzahl der Erste-Hilfe-Kits, Waffen und Monster auf dem Level selbst festlegen, sie werden an zufälligen Orten platziert. Wenn Sie eine sehr große Anzahl von Monstern einstellen, wird das Spiel natürlich langsam laufen.

Leider offenbart das Spiel nicht das volle Potenzial der Engine, da der Modellbauer und ich einfach keine Zeit dafür hatten. So gibt es zum Beispiel keine Zerstörung des Levels, sondern nur separate dynamische Teile, weil dann blöde Monster den Weg nicht finden, andererseits ist dies von der Spielidee nicht vorgesehen. Die Möglichkeiten der Engine bezüglich der Animation von Modellen sind nicht vollständig umgesetzt. Die Engine erlaubt einen willkürlichen, unabhängigen Wechsel von Modellen auf jedem Frame, wodurch es möglich wird, die ausgefeiltesten Animationen zu implementieren.
Ich habe mich entschieden, keine Screenshots vom Spiel bereitzustellen, da sie die Würde der Engine, wie dynamische Beleuchtung und weiche Schatten, überhaupt nicht vermitteln. Laden Sie die Demo herunter, es dauert nur ein paar Megabyte. Stellen Sie sich also ein surreales Alien-Terrain vor, bestehend aus einer Vielzahl von Bällen, Monstern aus kleinen Kugeln, die bei einer Explosion in kleine Stücke zerspringen. Die aktuelle Demo können Sie über diesen Link herunterladen.

Das Spiel benötigt Windows95 und höher, am besten mehr als 128 Megabyte Speicher, ansonsten abgeschaltete Musik, DirectX, eine Grafikkarte mit 32-Bit-Farbunterstützung und vor allem einen stärkeren Prozessor. Zum Beispiel der Intel Pentium 4-Prozessor mit Unterstützung für die Hyper-Threading-Technologie oder der neue AthlonXP. Das Spiel sollte auf jedem Prozessor mit MMX-Technologie laufen, für die volle Funktionalität ist jedoch SSE-Unterstützung erforderlich, also ein Prozessor ab Pentium-III. Videobeschleuniger ist nicht erforderlich. Übrigens unterstützt die Engine Multiprocessing, einschließlich der Hyper-Threading-Technologie. Nicht alle Programme verwenden mehrere Threads, um Hyper-Threading erfolgreich zu verwenden, aber die Haupt-Raytracing-Schleife ist parallelisiert, und es werden Gewinne von mehreren zehn Prozent erzielt. Und bei einem Multiprozessorsystem ist die Verstärkung proportional zur Anzahl der Prozessoren.

Entwicklung der VirtualRay-Engine

Im Moment erlaubt Ihnen die Engine, fantastische Szenen darzustellen. Aber die Verwendung von Sphären als Primitiven ist keine grundsätzliche Einschränkung, gerade die Verwendung komplexerer Primitiven ist im Hinblick auf die Geschwindigkeit problematisch. Wenn die Leistung zunimmt, können Sie die Verarbeitung von Ellipsen anstelle von Kugeln implementieren, was die Szene bereichert.

Und jetzt können Sie die Verarbeitung von Dreiecken zusammen mit Kugeln implementieren. Aber um mit Dreiecken etwas Sinnvolles darzustellen, braucht man viele davon, und sie werden mit der Geschwindigkeit von Kugeln verarbeitet. Es ist einfach, den Satz von Grundelementen zu erweitern, indem ausgeschnittene Kugeln, Kugelsegmente und Kugeldreiecke eingeführt werden. Dies wirkt sich aber auch negativ auf die Geschwindigkeit aus.
Es gibt verschiedene Methoden zur Verbesserung der Raytracing-Leistung auf Kosten einer Verschlechterung der Bildqualität. Nicht alle Strahlen werden verfolgt, sondern nur die wichtigsten, und der fehlende Teil des Bildes wird durch Interpolation aufgebaut. Dieser Ansatz ist jedoch nicht auf alle Szenen anwendbar, manchmal kann er mehr oder weniger hochwertige Ergebnisse liefern, und manchmal kann er das Bild ernsthaft verderben.

Übrigens, über die Qualität. Hier gibt es viel Raum für Verbesserungen. Tatsache ist, dass der Texturiervorgang nur einmal pro Punkt durchgeführt wird und nicht viel Zeit in Anspruch nimmt. Das Texturieren dauert jetzt etwa 10 % der Renderzeit. Um die Qualität der Texturierung zu verbessern, ist also geplant, eine trilineare Filterung pro Pixel zu implementieren, dies sollte die Geschwindigkeit nicht wesentlich verringern.

Raytracing und moderne 3D-Beschleuniger

Vor kurzem hat die 3D-Beschleunigerindustrie einen Übergang zur weitverbreiteten Verwendung von sogenannten Pixel- und Vertex-Shadern vollzogen. Beim Rastern eines Dreiecks führt der Beschleuniger für jedes Bildfragment ein vordefiniertes Programm aus, das die Farbe des Fragments auf komplexe Weise ändert. Es kann viel mehr, zum Beispiel einige Zwischenberechnungen in Texturen schreiben, die dann gelesen und beim Rendern von etwas anderem verwendet werden. Ein typisches Beispiel für einen modernen Pixel- oder auch Fragment-Shader ist ein Shader, der die Beleuchtung eines gegebenen Punktes eines Dreiecks berechnet. Es ist wie folgt angeordnet: Es wird ein Vektor genommen - die globale Position der Quelle, die aktuelle Koordinate des Dreieckspunkts im dreidimensionalen Raum, die im Beschleunigerchip berechnet wird, wenn das Dreieck gerastert wird, und die Normale dazu das Dreieck an dieser Stelle. Als nächstes wird ein Vektor von einem gegebenen Punkt in Richtung der Lichtquelle berechnet, und abhängig von dem Winkel, den er mit dem normalen senkrechten Vektor bildet, wird die Beleuchtung berechnet. Je größer der Lichtwinkel ist, desto geringer ist seine Intensität.
Wie wir sehen können, kann ein moderner Shader ein sinnvolles Geometrieprogramm sein. Heute ist es üblich, neue Beschleuniger zu testen, indem man die Ausführungsgeschwindigkeit solcher und komplexerer Shader misst. Die Leistung ist sehr groß. Der Pro-Pixel-Lighting-Shader läuft mit 1024 x 768 bei 100–200 fps auf den neuesten Beschleunigern wie Radeon9700 oder GeForceFX. Es bedeutet nur die Betriebszeit des Shaders selbst. Längst taucht in diesem Zusammenhang die Idee auf, eine so beachtliche Rechenleistung für die unterschiedlichsten Zwecke einzusetzen, auch fernab von 3D-Grafiken. Und versuchen Sie unter anderem, die Raytracing-Methode zu implementieren.

Betrachtet man diese Leistung jedoch in Form von skalaren und vektoriellen Gleitkommaberechnungen pro Zeiteinheit, so stellt sich heraus, dass sie mit der Rechenleistung moderner Prozessoren vergleichbar ist. Nehmen wir den neuesten GeForceFX5900Ultra-Beschleuniger, er hat eine Frequenz von 450 MHz, 4 Pixel-Prozessoren, von denen jeder 1 Vektoroperation pro Takt ausführen kann. Tatsächlich gibt es zwar mehr Operationen pro Takt, aber wir interessieren uns nur für Berechnungen mit voller Float32-Genauigkeit, da Berechnungen mit geringerer Genauigkeit vor allem für die Berechnung von Farbe sinnvoll sind, deren Reichweite durch die nicht sehr große Farbauflösung noch begrenzt ist der Monitor. Und für geometrische Berechnungen ist eine gute Genauigkeit erforderlich. Als grobe Schätzung der Leistung ergibt sich 450 Mx4 = 1800 Millionen Vektoroperationen pro Sekunde. Wenn wir den Pentium 4 nehmen, können wir mit SSE eine Vektoroperation pro eineinhalb Zyklen erreichen, dh bei einer Frequenz von 2700 MHz erhalten wir die gleichen 1800 Millionen Vektoroperationen pro Sekunde. In beiden Fällen meint er natürlich Spitzenleistung, wenn der gesamte Code nur noch aus Berechnungen besteht.
Es ist ersichtlich, dass die VPU keine Überlegenheit bei der Rechenleistung hat. Sein Vorteil in der Grafik liegt in der Fähigkeit, parallel zu den Shader-Berechnungen die begleitenden Berechnungen durchzuführen, die zum Rastern des Dreiecks erforderlich sind. Berechnen Sie irgendwie den Wert des Tiefenpuffers, interpolieren Sie die an den Scheitelpunkten angegebenen Werte über die Oberfläche des Dreiecks und sampeln und filtern Sie Texturen pro Zyklus. All dies wird von verschiedenen parallel arbeitenden Videobeschleunigereinheiten durchgeführt.

Daher werden wir natürlich keinen besonderen Vorteil bei der Implementierung von Raytracing durch die Verwendung eines Videobeschleunigers erzielen, da der Beschleuniger vollständig optimiert und in Bezug auf die Optimierung des Zeichnens von Dreiecken gebaut wurde.
Hinsichtlich der Optimierung von Raytracing mit einem Videobeschleuniger gibt es eine andere Idee: Zeichnen Sie die gesamte Geometrie auf der VPU und führen Sie die Beleuchtungsberechnung mit der Raytracing-Methode mit der CPU durch und kombinieren Sie dann das Ergebnis. Dies wird jedoch nicht viel Sinn machen, da die Hauptberechnungsschwierigkeiten gerade bei der Berechnung der Beleuchtung liegen. Darüber hinaus werden, je komplexer die Szene und je größer der Gewinn aus der Verwendung der VPU ist, desto mehr Ressourcen erforderlich sein, um die Beleuchtung einer komplexen Szene zu berechnen, und das Zeichnen der Szene wird im Verhältnis zur Beleuchtungsberechnungszeit viel weniger Zeit in Anspruch nehmen .

Lichtberechnung mit modernen Beschleunigern

Okay, wie wird vorgeschlagen, die Schattierung der Szene in neuen Spielen mit dynamischen Lichtquellen wie Doom III zu berechnen? Sind wir jetzt für immer dazu verdammt, vorberechnete statische Beleuchtung in Computerspielen zu sehen? Nein, interessante Methoden zur Berechnung von Schatten basierend auf der Verwendung der Standardmethode zum Zeichnen von texturierten Dreiecken unter Verwendung eines Z-Puffers sind seit langem bekannt. Sie sind seit langem bekannt, aber sie beanspruchen Rechenressourcen so sehr, dass ihr Einsatz in Computerspielen, und selbst dann, erst seit kurzem mit dem Aufkommen einer neuen Generation von Videobeschleunigern möglich wurde.

Beginnen wir mit der Methode, mit der dynamische Schatten im oben erwähnten Spiel Doom III gezeichnet werden. Ein Spiel, auf das viele Gamer warten. Dieses Verfahren wird Schattenvolumenverfahren oder das Verfahren zum Rendern von Schatten unter Verwendung eines Schablonenpuffers genannt. Hier ist das Grundschema seiner Arbeit: Zuerst wird eine unbeleuchtete Szene gezeichnet, dann wird für jedes Objekt, das einen Schatten in der Szene wirft, sein Schattenvolumen aufgebaut. Das Schattenvolumen ist eine Figur, die den Schattenbereich begrenzt, den Bereich des Raums, in den kein Licht fällt, der von einem bestimmten Objekt beschattet wird. Wir scheinen uns die Schwärze vorzustellen, die sich hinter dem Objekt in Form eines Körpers ausbreitet. Das Schattenvolumen ist sogar in der Realität zu sehen, wenn Sie einen Raum mit scharfem Licht ausleuchten, in dem Staubpartikel fliegen. Nicht schattierte Partikel leuchten, während schattierte einen schwarzen Bereich hinter dem Objekt bilden, der das Licht blockiert. Der nächste Schritt besteht darin, die Dreiecke zu zeichnen, die die Grenze dieses Schattenvolumens bilden. Durch Vergleich des Tiefenpufferwerts mit der Tiefe der Vorder- und Rückwand des Schattenvolumens wird bestimmt, ob ein gegebener Punkt im Schattenvolumen liegt und somit verschattet ist oder nicht. Hier wird beim Vergleich der Tiefe der Wände des Schattenvolumens und der Tiefe des Bildes ein Schablonenpuffer verwendet - ein Array von Werten, die Bildschirmpixeln entsprechen. Es speichert die Zwischenergebnisse des Vergleichs der Tiefe der Wände des Schattenvolumens mit der Tiefe des Bildes. Diese Methode ist insofern "gut", als sie die Füllrate des Beschleunigers voll ausnutzt, da Schattenvolumen dazu neigen, mehr Fläche auf dem Bildschirm zu haben als das Objekt, das den Schatten wirft. Das Verfahren stand für die Implementierung auf Riva TNT2-Beschleunigern zur Verfügung, ist jedoch so anspruchsvoll, dass seine Anwendung erst seit kurzem möglich ist.

Andererseits ist das Konstruieren optimaler Schattenvolumina für komplexe nicht-konvexe Objekte eine rechentechnisch schwierige Aufgabe. Die "Frontal"-Lösung führt zum Erscheinen einer großen Anzahl zusätzlicher Wände des Schattenvolumens, deren Wiedergabe zusätzliche Ressourcen erfordert. Die Zeit, um das effektive Volumen zu finden, wächst sehr schnell mit dem Detaillierungsgrad des Modells. Vielleicht sind die Monstermodelle in NewDoom deshalb weniger detailliert als erwartet.
Aber das sind noch nicht alle Mängel. Bei vielen kleinen Objekten kann die Fläche der Wände des Schattenvolumens einen gigantischen Wert erreichen. Zum Beispiel ein Kamm. Sein Schattenbereich ist nicht groß, aber sehr gewunden. Außerdem ist das Verfahren nicht sehr kompatibel mit transparenten Oberflächen. Fällt beispielsweise eine transparente Fläche in das Schattenvolumen, so hinterlässt das Objekt dahinter seine Information nicht im Tiefenpuffer, da diese Information durch die Tiefe der transparenten Fläche überschrieben wird. Und es ist unmöglich festzustellen, ob ein Objekt in einem Schattenvolumen liegt. Alle Fälle dieser Art müssen separat behandelt werden, was zu einer Erhöhung der Anzahl der Renderdurchgänge führen wird.

Dieses Verfahren ist schwierig zu verbessern, um unscharfe Schatten zu erhalten. Diejenigen, die die Vorabversion von Doom III gesehen haben, haben vielleicht die Härte der Schatten bemerkt. Und tatsächlich ist diese Methode nur zum Zeichnen von Schatten geeignet, sekundäre Beleuchtung kann mit ihrer Hilfe nicht berechnet werden, auch Brechung und Reflexion des Lichts. Es wird nur der Schattenkegel des Objekts in die Stirn gezeichnet und das war's.

Eine weitere beliebte Methode zum Rendern dynamischer Schatten in modernen Spielen ist die Verwendung von projektivem Textur-Mapping. Moderne Beschleuniger haben gelernt, eine Textur auf ein Objekt zu projizieren, so wie ein Diaprojektor ein Dia auf eine Leinwand projiziert. Es ist nur so, dass beim Zeichnen eines Objekts berechnet wird, welcher Punkt der Textur auf einen bestimmten Punkt des Objekts projiziert wird. Jetzt können Sie von der Lichtquelle aus das Objekt mit schwarzer Farbe in die Textur malen, Sie erhalten eine Schattensilhouette. Es ist wie der Schatten eines Objekts an einer senkrechten weißen Wand. Und diese Textur mit einem Schatten wird Schattenmaske genannt, sie kann auf beschattete Objekte projiziert werden.

Diese Methode wird in neuen Spielen verwendet, um Schatten von dynamischen Objekten, Monstern und Autos darzustellen. Damit können Sie verschwommene Schatten zeichnen, dafür wird die ursprüngliche Textur mit einem Schatten verwischt und wechselt von Schwarz und Weiß zu Weiß und Grau.

Ich weiß nicht einmal, welche der oben beschriebenen Methoden die Füllrate des Beschleunigers stärker beansprucht. Tatsache ist, dass es zum Erhalten eines Schattens guter Qualität erforderlich ist, dass die Schattentextur eine sehr hohe Auflösung hat. Neuere Spiele wie Splinter Cell verwenden Texturen, die mehrere tausend Pixel groß sind. Denn beim Projizieren vervielfachen sich kleinste Details um ein Vielfaches. Die Pixel, aus denen das Bild besteht, werden sichtbar. Daher kann diese Methode nur verwendet werden, um Schatten auf eng beieinander liegende Objekte zu werfen. Der zweite Nachteil dieser Methode besteht darin, dass das Objekt nicht von sich aus beschattet werden kann, es erforderlich ist, das Objekt, das den Schatten wirft, genau auszuwählen, und seine Teile werfen keinen Schatten aufeinander. Und außerdem impliziert diese Methode natürlich keine Verallgemeinerung für die Berechnung von Sekundärbeleuchtung, Reflexionen und Lichtbrechung.

Und schließlich betrachten wir die meiner Meinung nach vielversprechendste Methode zur Verwendung von Schatten in modernen Spielen. Es ist eine Weiterentwicklung der bisherigen projektiven Methode. Nur statt der Silhouette des Objekts wird in der Schattentextur die Entfernung der Punkte des Objekts zur Lichtquelle erfasst. Wenn eine Schattentextur projiziert wird, werden diese Informationen ferner verwendet, um zu bestimmen, ob ein Punkt des potenziell beschatteten Objekts weiter oder näher an der Lichtquelle liegt als der Shader. Der Vorteil dieser Methode ist die korrekte Selbstverschattung des Objekts. Und seine Nachteile sind ähnlich wie bei der vorherigen Methode. Diese Art, dynamische Schatten zu erstellen, ist bei Spieleentwicklern nicht beliebt. Der "Fehler" der Methode besteht darin, dass bestimmte Funktionen der Grafikkarte erforderlich sind, die zuerst in GeForce3 - Geforce4 - auftauchten, aber aus Geforce4MX - einer reduzierten Version von Geforce4 - entfernt wurden. Es ist unmöglich, die Methode ohne Hardwareunterstützung zu implementieren, daher müssen Sie eine Methode verwenden, die auf allen gängigen Grafikkarten machbar ist.

Alle oben genannten Methoden haben den Vorteil einer guten Kompatibilität mit vorhandener Hardware. Für sie wird tatsächlich nichts benötigt, außer der Füllrate und den einfachsten Operationen. Als Ergebnis können wir den Schluss ziehen, dass Videobeschleuniger schon jetzt weit davon entfernt sind, die Szenenbeleuchtung in Echtzeit zu berechnen. Und es wird nichts Revolutionäres erwartet. Es gibt Schatten von einigen dynamischen Objekten, begrenztes dynamisches Licht im neuen Doom III, diese Technologien werden über einen langen Zeitraum beherrscht.

Entwicklung von Beschleunigern im Sinne von Raytracing

Wie ich bereits erwähnt habe, werden moderne Beschleuniger immer programmierbarer und ihre Leistung wächst stetig. Grafikkartenhersteller verwenden sogar den Begriff "Visual Processor", um neue Produkte zu bezeichnen. Tatsächlich erinnern Beschleuniger in ihren Fähigkeiten immer mehr an herkömmliche Prozessoren für Personal Computer. Gerade mit der Erhöhung des Grades der VPU-Programmierbarkeit verbinden sich Hoffnungen auf die Implementierung intelligenter Bildgebungsverfahren, wie beispielsweise des Raytracing-Verfahrens. Damit kann der Beschleuniger in geeigneter Weise umprogrammiert werden.

Lassen Sie uns die Aussichten für die Entwicklung von Beschleunigern in dieser Richtung bewerten. Jetzt arbeiten die neuesten Beschleuniger mit Frequenzen von etwa 500 MHz, wie fünf Jahre alte Prozessoren, und haben 4-8 parallele Pipelines. Jetzt werden die meisten Shader-Vektoroperationen, Addition, Skalarprodukt, pro Takt durchgeführt. Auch viele Hilfsoperationen, wie das Interpolieren von Werten entlang der Fläche eines Dreiecks, werden pro Takt durchgeführt. Die Berechnung trigonometrischer Funktionen, wie z. B. sin und cos, wird, obwohl ungefähr, ebenfalls pro Takt durchgeführt. Dies verwendet eine Auswahl aus Tabellen mit vorberechneten Werten, aber die Leistung ist dennoch erstaunlich. Außerdem ist es seltsam, dass moderne CPUs für Personalcomputer nichts dergleichen können. Im Gegenteil, es gibt eine Tendenz, komplexe Befehle loszuwerden und sie durch ein paar einfache zu ersetzen. Diese Maßnahmen sind erforderlich, um die Frequenz erhöhen zu können. Ohne auf technische Details einzugehen, können wir sagen, dass der mit zunehmender Frequenz immer kürzer werdende Prozessortakt kürzere Instruktionen erfordert. Komplexe Anweisungen werden in modernen Prozessoren immer noch in Mikrooperationen aufgeteilt. Diese Aufteilung ist auch ein separates Problem, ganze Blöcke des Prozessors sind daran beteiligt.

Was ist mit Videobeschleunigern? Es ist wahrscheinlich, dass zur Erhöhung der Frequenz die Architektur moderner VPUs ernsthaft überarbeitet werden muss. Aber das ist die halbe Miete. Echte Programmierbarkeit erfordert, dass der Prozessor Verzweigungen ausführt, d. h. Anweisungen, die die Programmausführung steuern. Und das ist immer das größte Problem. Wie leiden moderne Prozessoren unter unvorhersehbaren bedingten Sprüngen in Programmen? Hier erhalten die Vertex-Shader in GeForceFX bedingte Sprungbefehle, man kann den neusten Tests entnehmen, wie stark die Performance "eingebrochen" ist. Und das bei einer relativ niedrigen Frequenz unter 500 MHz. Und mit zunehmender Frequenz werden die Verluste durch bedingte Sprünge nur zunehmen, und ihre Implementierung selbst wird schwieriger. Übrigens, die fantastische Leistung von Beschleunigern wird erreicht, wenn die sogenannten Streaming-Operationen ausgeführt werden, wenn die Daten in einem durchgehenden Streifen gehen und nach einem streng definierten Schema verarbeitet werden, keine zufälligen bedingten Sprünge usw. All diese Fakten deuten darauf hin, dass wir in naher Zukunft nicht mit einer Zunahme der Frequenz von Videobeschleunigern rechnen sollten.

Ein wichtiger Parameter einer Grafikkarte ist die Anzahl der Pixelprozessoren. Sie malen Pixel parallel, also je mehr, desto besser. Bei den neuesten Radeons sind es bis zu acht. Von neuen Beschleunigern werden immer mehr Fragmentprozessoren erwartet. Aber nicht alles ist so einfach. Der Punkt ist, dass, wenn die Größe des Dreiecks der Anzahl der Pixel-Pipelines entspricht, sie nicht alle zusammenarbeiten können. In einem kleinen Dreieck haben sie nicht genug Platz. Das ist auch der Grund, warum Hersteller von Videobeschleunigern so gern Anti-Aliasing-Modi verwenden, die die gesamte Szene mit doppelter Auflösung rendern. Dann werden die kleinen Dreiecke größer. Wenn eine Szene mit großen Dreiecken in kleinere unterteilt wird, ohne die Form zu ändern, nimmt die Leistung von Pixel-Shadern erheblich ab, obwohl die Gesamtfläche der Dreiecke gleich bleibt.

Die Entwicklung moderner Spielgrafikbeschleuniger ist bereits mit großen Schwierigkeiten behaftet und praktisch nur auf die Verbesserung des technologischen Prozesses zur Herstellung von Videochips zurückzuführen. Alle NVIDIA und ATI denken darüber nach, wie man einfache dynamische Schatten effizient machen kann. Es gibt keine gute Lösung - sie sind Raytracing nicht gewachsen.

Dedizierter Raytracing-Beschleuniger

Wenn moderne Gaming-VPUs ursprünglich entwickelt wurden, um den Standardalgorithmus zum Zeichnen von Dreiecken zu beschleunigen, und sich nicht sehr gut für die Implementierung von Raytracing eignen, ist es dann vielleicht sinnvoll, zunächst einen Beschleuniger zu bauen, um Raytracing zu implementieren? Leider ist die Beschleunigung von Raytracing eine undankbare Aufgabe.


Der Raytracing-Algorithmus ist so komplex, dass ein Raytracing-Beschleuniger fast ein universeller Prozessor ist. Streaming-Algorithmen ohne zufällige Verzweigungen eignen sich gut für die Hardwarebeschleunigung, aber Raytracing ist völlig anders. Das heißt, die Erstellung eines Raytracing-Beschleunigers ist dasselbe wie die Erstellung einer echten CPU.


Aber Raytracing hat noch einen weiteren Vorteil – es ist gut parallelisiert. Jeder Strahl kann unabhängig berechnet werden, was es ermöglicht, den Algorithmus effizient auf Mehrprozessorsystemen zu implementieren. Als billiger Raytracing-Beschleuniger, weißt du was? Ein System basierend auf vier Celerons mit einer Frequenz von 3 GHz oder vier AthlonXP mit reduziertem Cache. Der Raytracing-Algorithmus erfordert bei richtiger Optimierung keine große Cache-Größe, sodass er sich als billig und funktionsreich herausstellen wird. Die kombinierte Rechenleistung wird aktuelle Desktops bei weitem übertreffen. Aber das wird es nicht, weil Multiprozessorsysteme für einen anderen Markt gedacht sind, nicht für Heimsysteme.

Fazit

Auf der Grundlage des Vorhergehenden können wir schlussfolgern, dass die realistische Wiedergabe von Szenen in Echtzeit auf Personalcomputern sehr schwierig ist und viele der Probleme grundlegender Natur sind. Es muss genügend Zeit vergehen, damit Spielcomputergrafiken ein qualitativ neues Niveau erreichen. Und welche Methoden zur Visualisierung in künftigen grafischen Anwendungen zum Einsatz kommen werden, ist heute sehr schwer zu sagen.

Verknüpfungen


http://www.art-render.com/

Seite von Herstellern von "Raytracing-Beschleunigern" zur Optimierung des Renderings in 3DMax und anderen Grafikeditoren. Ein Beschleuniger ist ein Satz von mehreren (von 8) Prozessoren, die für Raytracing optimiert sind. Sie können eine typische Verfolgungsoperation durchführen – den Schnittpunkt eines Strahls mit einem Dreieck finden – in einem Zyklus. Aber anscheinend arbeiten sie mit einer nicht sehr hohen Frequenz. Beschleunigung wird durch Parallelbetrieb erreicht. Jetzt ist es schwierig, Preise auf der Website zu finden, aber ich habe sie schon einmal gesehen, sie sind überhaupt nicht klein.


http://www.acm.org/tog/resources/RTNews/html

Eine umfangreiche Liste verschiedener Ressourcen zum Thema Raytracing.


http://www.realstorm.com/

Raytracing-Engine. Ermöglicht das Zeichnen einer Vielzahl typischer Nachzieheffekte, zB Reflexionen und Lichtbrechungen in Echtzeit. Aber es funktioniert in kleinen Auflösungen und verwendet Näherung. Basierend auf der Engine wird ein Spiel gebaut - ein Bowling-Simulator.


http://www.kge.msu.ru/workgroups/compcenter/dmitri/projects/sphericworld/index.htm

http://www.kge.msu.ru/workgroups/compcenter/dmitri/projects/polyworld/index.htm

Ein weiteres Projekt, das sich der Raytracing-Methode widmet. Ein kugelförmiger und polygonaler Raytracer wurde implementiert, der sehr hochwertige realistische Bilder erstellt, aber langsam bei hohen Auflösungen.


http://www.virtualray.ru/

Dies ist in der Tat eine Website, die dem Thema des Artikels gewidmet ist - der VirtualRay-Engine und dem AntiPlanet-Spiel - dem ersten 3D-Shooter, der auf der Raytrace-Engine basiert.

Bildungsministerium der Russischen Föderation

Moskauer Staatliches Institut für Elektronik und Mathematik

(Technische Universität)

Abteilung für Information und Kommunikation

Technologien

Studienarbeit zum Thema:

"Analyse der Perspektiven der Verwendung des Raytracing-Verfahrens in der 3D-Modellierung"

Abgeschlossen:

Gulian Boris

Podsorow Ivan

Gruppe C-35

Moskau 2010

1. 3D-Grafik. Einführung

3. Raytracing-Algorithmen

4. Die wichtigsten Vor- und Nachteile von Raytracing

5. Anwendung des Raytracing-Verfahrens

6. Experiment.

Aufgabe: „Analyse der Einsatzmöglichkeiten des Raytracing-Verfahrens in der 3D-Modellierung“

Formulierung des Problems

Machen Sie sich mit der Raytracing-Methode und ihrer Verwendung im Bereich der 3D-Grafik vertraut, machen Sie ein Experiment mit einem der Raytracing-Algorithmen.

In unserem Experiment betrachten wir:
1) die Leistung des Raytracing-Algorithmus in Abhängigkeit von der Anzahl der Modellpolygone (3 Kugeln werden als Modell genommen: matt, transparent und spiegelnd).

2) Analyse der erhaltenen Bilder mit und ohne Raytracing.

Als Medium für das Experiment wird die Blender-Software verwendet.

3D-Grafik. Einführung.

Dreidimensionale Grafik ist ein Bereich der Computergrafik, eine Reihe von Techniken und Werkzeugen zur Darstellung dreidimensionaler Objekte. Es wird am häufigsten verwendet, um Bilder auf einer Bildschirmebene oder einem Bogen von Druckprodukten in der Architekturvisualisierung, der Unterhaltungsindustrie, Druckprodukten sowie in Wissenschaft, Industrie und Augmented-Reality-Technologie zu erstellen.

Jedes 3D-Bild wird durch die folgenden Parameter und Objekte definiert:

· Geometrie (gebautes Modell)

· Materialien (Informationen über die visuellen Eigenschaften des Modells)

· Lichtquellen (Richtung, Leistung, Lichtspektrumeinstellungen)

· Virtuelle Kameras (Auswahl von Punkt und Winkel der Projektionskonstruktion)

· Kräfte und Einflüsse (Einstellungen zur dynamischen Verzerrung von Objekten, hauptsächlich in Animationen verwendet)

· Zusätzliche Effekte (Objekte, die atmosphärische Phänomene simulieren: Licht im Nebel, Wolken, Flammen usw.)

Aufgabe der 3D-Modellierung ist es, diese Objekte zu beschreiben und durch geometrische Transformationen gemäß den Anforderungen an das zukünftige Bild auf die Bühne zu bringen.

Das Hauptproblem der 3D-Grafik und -Modellierung besteht darin, mit minimalen Computerressourcen und minimalem Zeitaufwand für die Szenenverarbeitung ein möglichst fotorealistisches Bild zu erhalten. Da es in verschiedenen Bereichen unterschiedliche Bedürfnisse gibt, entstehen unterschiedliche Ideen und Algorithmen, um ein bestimmtes Problem zu lösen. Eine dieser Ideen ist Raytracing, die wir in unserer Arbeit berücksichtigen werden.

Vorwärts- und Rückwärts-Raytracing

Raytracing ist eine Methode zur Verarbeitung von 3D-Modellen, um ein fotorealistisches Bild zu erhalten, das die relative Position von Objekten sowie physikalische Eigenschaften von Objekten wie Reflexions- und Brechkraft berücksichtigt.

Es gibt 2 Raytracing-Methoden: vorwärts und rückwärts.

Beim direkten Raytracing werden alle Strahlen von Lichtquellen berücksichtigt, die auf Objekte treffen und schließlich ins Auge des Betrachters gelangen. Aber ein solches Verfahren ist in Bezug auf die Leistung nicht rational, da es notwendig ist, alle Strahlen der Umgebung (ausgehend und brechend) in alle Richtungen zu verarbeiten, einschließlich derjenigen, die nicht auf die für den Betrachter sichtbare Szene fallen.

Beim Reverse Raytracing treten alle Strahlen aus dem Auge des Betrachters aus und definieren so eine Szene mit Objekten, für die eine weitere Verarbeitung durchgeführt wird. Diese Methode ermöglicht es, Objekte, die nicht in den sichtbaren Bereich fallen, nicht zu verarbeiten, wodurch der Umfang der erforderlichen Berechnungen erheblich reduziert wird.

Alle Raytracing-Algorithmen basieren auf dem Reverse-Raytracing-Verfahren.

Raytracing-Algorithmen

Betrachten Sie den grundlegenden Ablaufverfolgungsalgorithmus (Abb. 1). Nehmen wir als Objekt eine Kugel.

1. Für jeden Pixel auf dem Bildschirm wird ein Strahl aus dem Auge des Betrachters geschossen.

2. Nachdem der Strahl das Objekt gekreuzt hat, wird Folgendes bestimmt:

· Die Transparenz/Deckkraft des Objekts. Wenn das Objekt transparent ist, wird ein Brechungsstrahl von der Schnittlinie emittiert, wenn es undurchsichtig ist, wird er nicht emittiert.

· Licht Schatten. Von dem Punkt, an dem der Strahl die Kugel schneidet, wird ein Strahl in Richtung der Lichtquelle (oder wiederum für jede Lichtquelle, wenn es mehrere gibt) emittiert. Wenn dieser Strahl andere undurchsichtige Objekte oder Oberflächen nicht schneidet, beeinflusst die Lichtquelle direkt die Beleuchtung dieses Punktes. Bei mehreren Lichtquellen wird das durch den RGB-Wert dieses Punktes bestimmte Ergebnis aus dem Einfluss aller Strahlen berechnet.

· Reflexionsfähigkeit. Wenn das Objekt Strahlen reflektieren kann, wird von dem Punkt, an dem der Strahl die Kugel schneidet, ein reflektierter Strahl zu den Objekten emittiert, die in der Kugel reflektiert werden.

Als Ergebnis erhalten wir mehrere Arten von Strahlen. Die Primärstrahlen werden verwendet, um die Sichtbarkeit eines Objekts zu bestimmen, und die Sekundärstrahlen werden wie folgt unterteilt:

· Brechungsstrahlen;

· Schattenstrahlen / Beleuchtung;

Reflexionsstrahlen.

Reis. 1 Diagramm des Raytracing-Algorithmus


Alle anderen Algorithmen basieren auf dem oben gezeigten Algorithmus und sind darauf ausgelegt, Berechnungen zu optimieren.

kd-Baum

Der Algorithmus zum Konstruieren eines kd-Baums kann wie folgt dargestellt werden (wir werden eine rechteckige Box mit dem englischen Wort "box" (Box) bezeichnen).

1. "Hinzufügen" Sie alle Grundelemente zum Begrenzungsrahmen. Das heißt, einen Kasten zu bauen, der alle Grundelemente begrenzt, die dem Wurzelknoten des Baums entsprechen.

2. Wenn der Knoten nur wenige Primitive enthält oder die Baumtiefengrenze erreicht ist, stoppen Sie die Konstruktion.

3. Wählen Sie eine Teilungsebene aus, die den angegebenen Knoten in zwei Kinder teilt. Wir nennen sie die rechten und linken Knoten des Baums.

4. Fügen Sie die Grundelemente, die die linke Knotenbox schneiden, dem linken Knoten hinzu, die Grundelemente, die die rechte Knotenbox schneiden, dem rechten Knoten.

5. Führen Sie diesen Algorithmus für jeden der Knoten rekursiv aus, beginnend mit Schritt 2.

regelmäßiges Raster

Der gesamte 3D-Raum ist in ein kleines regelmäßiges Gitter unterteilt, das aus N*N*N Würfeln besteht. Die Idee ist, dass man nur über die Würfel laufen kann, durch die der Strahl gegangen ist.

Die Methode wird in der Praxis nicht angewendet.

DVor-und Nachteile

Neben der Tatsache, dass das Raytracing-Verfahren das fotorealistischste Bild liefert, hat es eine Reihe weiterer Vorteile:

1. Fähigkeit, glatte Objekte zu rendern, ohne sie mit polygonalen Oberflächen (wie Dreiecken) zu interpolieren.

2. Die Rechenkomplexität des Verfahrens hängt schwach von der Komplexität der Szene ab.

3. Hohe algorithmische Parallelität der Berechnungen - es ist möglich, zwei oder mehr Strahlen parallel und unabhängig zu verfolgen.

4. Mit dem Raytracing-Verfahren werden Reflexionen perfekt dargestellt (Abb. 2), und das ohne komplexe Algorithmen, da alles vom Hauptrenderalgorithmus berechnet wird.

font-size:14.0pt"> Abb. 2 Spiegelungen zweier Spiegelkugeln ineinander

Das Raytracing-Verfahren weist Nachteile auf, die bei allen Algorithmen beobachtet werden, die den Umfang dieses Verfahrens bestimmen.

1. Der Hauptnachteil dieses Rendering-Algorithmus ist seine Langsamkeit. Der Raytracing-Algorithmus ist jedoch stark parallelisiert und die Anzahl der CPU-Kerne steigt jedes Jahr, sodass wir eine lineare Steigerung der Raytracing-Leistung sehen sollten. Dieser Ansatz berücksichtigt jedoch keine Sekundärstrahlen (Reflexionen, Brechungen und Schattierungsdefinitionen), und das Rendern mit Primärstrahlen verbessert die Bildqualität im Vergleich zum klassischen Algorithmus praktisch nicht.

2. Das Problem bei Sekundärstrahlen ist, dass sie absolut keine Kohärenz (Gleichrichtung) haben. Beim Übergang von einem Pixel zum anderen müssen völlig andere Daten berechnet werden, was alle üblichen Caching-Techniken, die für eine gute Leistung sehr wichtig sind, zunichte macht. Das bedeutet, dass die Berechnung von Sekundärstrahlen sehr von Speicherverzögerungen abhängig ist.

3. Fehlende Hardwareunterstützung für die Methode (alle GPUs sind auf Rasterung spezialisiert).

4. Ein weiteres charakteristisches Problem des Raytracing-Verfahrens betrifft das Anti-Aliasing (AA). Strahlen werden als einfache mathematische Abstraktion gezeichnet und berücksichtigen nicht die tatsächliche Größe. Der Dreiecksschnitttest ist eine einfache boolesche Funktion, die eine „Ja“- oder „Nein“-Antwort gibt, aber keine Details wie „der Strahl schneidet das Dreieck zu 40 %“. Eine direkte Folge dieses Effekts ist das Auftreten von "Leitern" (Abb. 3).

Reis. 3 Schattenglättung

Und die einzige Technologie, die gute Ergebnisse liefern kann, ist die Berechnung von mehr Strahlen als Pixel vorhanden sind, dh Supersampling (Oversampling oder Anti-Aliasing) (Rendering mit einer höheren Auflösung).

Es sollte auch daran erinnert werden, dass die Rendering-Geschwindigkeit und Qualität von Raytracing stark von der Code-Optimierung abhängt.

Anwendung der Raytracing-Methode

Aufgrund ihrer Eigenschaften (fotorealistisches Bild, Langsamkeit der Berechnungen) wird diese Methode in Bereichen verwendet, in denen die Qualität des Bildes wichtig ist und nicht der Zeitpunkt des Renderns (in diesem Fall werden am häufigsten kombinierte Rendering-Methoden verwendet, was sich verbessert Leistung). Zu diesen Bereichen gehören:

· 3D-Animation;

· Spezialeffekte der Filmindustrie;

· Realistische Fotowiedergabe;

· Cad - Systeme.

Sonderkonditionen:

Ein Polygonnetz ist eine Sammlung von Scheitelpunkten und Polygonen, die die Form eines gerenderten Objekts definieren.

Rendern (Rendern) - (englisches Rendern - „Visualisierung“) - der Prozess, ein Bild von einem Modell zu erhalten.

Ein Modell ist dabei eine Beschreibung beliebiger Objekte oder Phänomene in einer fest definierten Sprache oder in Form einer Datenstruktur. Eine solche Beschreibung kann geometrische Daten, die Position des Beobachterpunktes, Informationen über die Beleuchtung, den Grad der Anwesenheit einer Substanz usw. enthalten.


Abb. 4. Polygonnetz

Experiment.

Als Software für das Experiment wählten wir den 3D-Editor Blender.

Es ist recht einfach zu erlernen und enthält alle notwendigen Funktionen:

· Bildwiedergabe mit der Möglichkeit, den Tracer zu verbinden und zu trennen.

Oversampling (Anti-Aliasing oder Anti-Aliasing)

Wir haben die Zeit gemessen, die erforderlich ist, um 3 verschiedene Kugeln (glasig, glänzend und matt) auf verschiedenen Multeris-Gleichungen zu rendern (jede Stufe erhöht die Anzahl der Polygone um das 4-fache). Mit zunehmendem Pegel wurde die Zeit von 0 an gezählt.

0 "style="margin-left:48.35pt;border-collapse:collapse">

Lv. Multeris

Renderzeit jeder ur. ab 0

Ohne RayT [c]

Mit RayT[c]

0,53

3,36

0,46

0,54

2,84

0,55

3,02

0,61

3,85

0,96

5,96

10,64

29,12

43,9

Tabelle 1.

Das Rendern wurde mit maximalen Parametern durchgeführt, um den Unterschied in der Verarbeitungsgeschwindigkeit zu erhöhen.

Als Ergebnis sehen wir, dass die Zeit, die für die Verarbeitung von drei Kugeln mit Ebene 4 (256 Polygone pro Kugel) aufgewendet wird, geringer ist als die Zeit, die für die Verarbeitung von Kugeln mit Ebene 2 (jeweils 16 Polygone) aufgewendet wird.


Abbildung 5. Polygonnetze für verschiedene Ebenen

Ergebnis

Aus dem Experiment ist ersichtlich, dass der Zeitaufwand für das Rendern von 3 Bällen mit Raytracing erheblich größer ist als der Zeitaufwand für das Rendern ohne Raytracing. Aber während des Experiments wurde eine interessante Beobachtung festgestellt: Die Zeit für die Verarbeitung von Modellen mit 3, 4 und 5 Ebenen ist kürzer als die Zeit für die Verarbeitung eines Modells mit zwei Ebenen.

Analyse empfangener Bilder:
1) In dem Bild, das ohne Verwendung von Raytracing (im Folgenden A) erhalten wurde, ist zu sehen, dass die transparente Kugel nicht die Wirkung einer Linse hat (Anwenden eines Alphakanals), während in dem Bild mit Verwendung von Raytracing (im Folgenden B) die transparente Kugel vergrößert Objekte hinter ihm (Abb. 6).

Reis. 6 transparente Kugeln (Alphakanal links, Raytracing rechts)


2) In Bild A gibt es keine Spiegelkugel, weil die Spiegelung darauf auf Raytracing basiert (Abb. 7).

Abbildung 7. Modell des Experiments (Alphakanal oben, Raytracing unten).


3) Abbildung 8 zeigt, dass beim Rendern ohne Verwendung von Raytracing interne Hohlräume beleuchtet werden, in die logischerweise kein Licht eindringen sollte.


Abb. 8 Lichteinfall bei Einmündung in die Kugel (links A, rechts B)

Aus dieser Analyse ist ersichtlich, dass die Qualität von Bildern mit Raytracing deutlich besser ist als Bilder, die ohne Raytracing erhalten wurden, was die Verwendung dieser Methode in Bereichen rechtfertigt, in denen die Qualität des resultierenden Bildes wichtig ist und nicht die Verarbeitungszeit.

Ich weiß, es ist ein wenig enttäuschend. Wo sind die Reflexionen, Schatten und das schöne Aussehen? Wir werden alles bekommen, weil wir gerade erst begonnen haben. Aber das ist ein guter Anfang - die Kugeln sehen aus wie Kreise, was besser ist, als wenn sie wie Katzen aussehen würden. Sie sehen nicht wie Kugeln aus, weil wir eine wichtige Komponente übersehen haben, die es einer Person ermöglicht, die Form eines Objekts zu bestimmen – wie es mit Licht interagiert.

Beleuchtung

Der erste Schritt, um unserem Szenen-Rendering "Realismus" zu verleihen, ist die Beleuchtungssimulation. Beleuchtung ist ein wahnsinnig komplexes Thema, daher präsentiere ich ein sehr vereinfachtes Modell, das für unsere Zwecke gut genug ist. Einige Teile dieses Modells sind nicht einmal in der Nähe von physischen Modellen, sie sind einfach schnell und sehen gut aus.

Wir beginnen mit einigen vereinfachenden Annahmen, die uns das Leben erleichtern werden.

Zuerst erklären wir, dass die gesamte Beleuchtung weiß ist. Dies wird es uns ermöglichen, jede Lichtquelle durch eine einzige reelle Zahl, die ich genannt habe, zu charakterisieren Helligkeit Beleuchtung. Farbbeleuchtungssimulation ist nicht so schwer (benötigt nur drei Helligkeitswerte, einen pro Kanal, und die Berechnung aller Farben und Beleuchtung pro Kanal), aber um unsere Arbeit zu erleichtern, werde ich es nicht tun.

Zweitens werden wir die Atmosphäre los. Das bedeutet, dass die Lichter unabhängig von ihrer Reichweite nicht weniger hell werden. Auch die Abschwächung der Lichthelligkeit in Abhängigkeit von der Entfernung ist nicht allzu schwierig umzusetzen, aber der Übersichtlichkeit halber überspringen wir sie vorerst.

Lichtquellen

Licht muss von irgendwo Handlung. In diesem Abschnitt werden wir drei verschiedene Arten von Lichtquellen definieren.

Punktquellen

Punktquelle emittiert Licht von einem festen Punkt im Raum, der seine genannt wird Position. Licht wird gleichmäßig in alle Richtungen abgegeben; deshalb heißt es auch omnidirektionale Beleuchtung. Daher wird eine Punktquelle vollständig durch ihre Position und Helligkeit charakterisiert.

Eine Glühbirne ist ein gutes reales Beispiel dafür, was eine Punktlichtquelle ungefähr ist. Obwohl eine Glühlampe kein Licht von einem einzigen Punkt ausstrahlt und nicht perfekt omnidirektional ist, ist die Annäherung ziemlich gut.

Lassen Sie uns einen Vektor als die Richtung vom Punkt P in der Szene zur Lichtquelle Q definieren. Dieser Vektor, genannt Lichtvektor, ist einfach gleich . Beachten Sie, dass, da Q festgelegt ist und P ein beliebiger Punkt in der Szene sein kann, es im Allgemeinen für jeden Punkt in der Szene anders sein wird.

Richtungsquellen

Wenn eine Punktquelle eine gute Annäherung an eine Glühlampe ist, was ist dann eine gute Annäherung an die Sonne?

Dies ist eine knifflige Frage und die Antwort hängt davon ab, was Sie rendern möchten.

Auf der Skala des Sonnensystems kann die Sonne grob als Punktquelle betrachtet werden. Immerhin emittiert es Licht von einem Punkt (wenn auch einem ziemlich großen) und strahlt es in alle Richtungen ab, also erfüllt es beide Anforderungen.

Wenn Ihre Szene jedoch auf der Erde spielt, dann ist dies keine sehr gute Annäherung. Die Sonne ist so weit entfernt, dass jeder Lichtstrahl tatsächlich dieselbe Richtung hat (Anmerkung: Diese Annäherung gilt im Maßstab einer Stadt, aber nicht bei größeren Entfernungen - tatsächlich konnten die alten Griechen den Radius von berechnen die Erde mit erstaunlicher Genauigkeit basierend auf verschiedenen Richtungen des Sonnenlichts an verschiedenen Orten.). Obwohl es möglich ist, dies mit einer sehr weit von der Szene entfernten Punktquelle anzunähern, sind dieser Abstand und der Abstand zwischen Objekten in der Szene so unterschiedlich groß, dass Genauigkeitsfehler auftreten können.

Für solche Fälle werden wir eingestellt gerichtete Lichtquellen. Wie Punktquellen hat eine gerichtete Quelle Helligkeit, aber im Gegensatz zu Punktquellen hat sie keine Position. Stattdessen hat er Richtung. Sie können es als unendlich weit entfernte Punktquelle wahrnehmen, die in eine bestimmte Richtung scheint.

Im Fall von Punktquellen müssen wir für jeden Punkt P der Szene einen neuen Lichtvektor berechnen, aber in diesem Fall ist er gegeben. In der Szene mit der Sonne und der Erde werden gleich sein.

Umgebungsbeleuchtung

Kann jede reale Beleuchtung als Punkt- oder gerichtetes Licht modelliert werden? Fast immer ja Ideal.). Reichen diese beiden Arten von Quellen für unsere Zwecke aus? Leider gibt es keine.

Stellen Sie sich vor, was auf dem Mond passiert. Die einzige nennenswerte Lichtquelle in der Nähe ist die Sonne. Das heißt, die „vordere Hälfte“ des Mondes relativ zur Sonne erhält die gesamte Beleuchtung, und die „hintere Hälfte“ befindet sich in völliger Dunkelheit. Wir sehen dies aus verschiedenen Blickwinkeln auf der Erde, und dieser Effekt erzeugt das, was wir die "Phasen" des Mondes nennen.

Die Situation auf der Erde ist jedoch etwas anders. Sogar Punkte, die nicht direkt von einer Lichtquelle beleuchtet werden, sind nicht vollständig dunkel (sehen Sie einfach auf den Boden unter dem Tisch). Wie gelangen Lichtstrahlen an diese Stellen, wenn die „Sicht“ auf die Lichtquellen durch etwas verdeckt ist?

Wie ich im Abschnitt erwähnt habe Farbmodelle Wenn Licht auf ein Objekt trifft, wird ein Teil davon absorbiert, aber der Rest wird in die Szene gestreut. Das bedeutet, dass Licht nicht nur von Lichtquellen kommen kann, sondern auch von anderen Objekten, die es von Lichtquellen empfangen und zurückstreuen. Aber warum dort aufhören? Diffuses Licht wiederum fällt auf ein anderes Objekt, ein Teil davon wird absorbiert und ein Teil wird wieder in der Szene gestreut. Mit jeder Reflexion verliert das Licht etwas an Helligkeit, aber theoretisch geht es weiter Ad infinitum(Anmerkung: nicht wirklich, weil Licht von Natur aus Quanten ist, aber nahe genug daran.).

Das bedeutet, dass Sie die Beleuchtungsquelle berücksichtigen müssen jedes Objekt. Wie Sie sich vorstellen können, erhöht dies die Komplexität unseres Modells erheblich, sodass wir diesen Weg nicht gehen werden (Hinweis: Sie können jedoch zumindest Global Illumination googeln und sich die schönen Bilder ansehen.).

Aber wir wollen immer noch nicht, dass jedes Objekt entweder direkt beleuchtet oder komplett dunkel ist (es sei denn, wir rendern ein Sonnensystemmodell). Um diese Barriere zu überwinden, definieren wir eine dritte Art von Lichtquellen, genannt Umgebungsbeleuchtung, die sich nur durch Helligkeit auszeichnet. Es wird angenommen, dass es der bedingungslose Beitrag der Beleuchtung zu jedem Punkt der Szene ist. Dies ist eine sehr starke Vereinfachung der äußerst komplexen Interaktion zwischen Lichtern und Szenenoberflächen, aber es funktioniert.

Beleuchtung eines Punktes

Im Allgemeinen gibt es in einer Szene ein Umgebungslicht (weil Umgebungslicht nur einen Helligkeitswert hat und eine beliebige Anzahl davon sich trivialerweise zu einem einzigen Umgebungslicht kombinieren lässt) und eine beliebige Anzahl von Punkt- und Richtungslichtern.

Um die Beleuchtungsstärke eines Punkts zu berechnen, müssen wir einfach die Lichtmenge berechnen, die von jeder Quelle beigetragen wird, und sie addieren, um eine Zahl zu erhalten, die die Gesamtlichtmenge darstellt, die von dem Punkt empfangen wird. Wir können dann die Farbe der Oberfläche an diesem Punkt mit dieser Zahl multiplizieren, um die richtige beleuchtete Farbe zu erhalten.

Was passiert also, wenn ein Lichtstrahl mit einer Richtung von einer gerichteten oder Punktquelle auf den P-Punkt eines Objekts in unserer Szene trifft?

Intuitiv können wir Objekte in zwei allgemeine Klassen einteilen, je nachdem, wie sie sich mit Licht verhalten: „matt“ und „glänzend“. Da die meisten Objekte um uns herum als „matt“ betrachtet werden können, beginnen wir mit ihnen.

diffuse Streuung

Wenn ein Lichtstrahl auf ein mattes Objekt fällt, reflektiert es aufgrund der Rauheit seiner Oberfläche auf mikroskopischer Ebene den Strahl gleichmäßig in alle Richtungen in die Szene, dh es wird eine „gestreute“ („diffuse“) Reflexion erhalten .

Um dies zu sehen, schauen Sie sich ein mattes Objekt, zum Beispiel eine Wand, genau an: Wenn Sie sich an der Wand entlang bewegen, ändert sich ihre Farbe nicht. Das heißt, das Licht, das Sie von einem Objekt reflektiert sehen, ist das gleiche, egal wo Sie das Objekt betrachten.

Andererseits hängt die Menge des reflektierten Lichts vom Winkel zwischen dem Lichtstrahl und der Oberfläche ab. Dies ist intuitiv klar - die vom Strahl getragene Energie sollte je nach Winkel auf eine kleinere oder größere Oberfläche verteilt werden, dh die in die Szene reflektierte Energie pro Flächeneinheit wird höher bzw. niedriger sein:

Um dies mathematisch auszudrücken, wollen wir die Orientierung einer Fläche durch ihre charakterisieren normaler Vektor. Der Normalenvektor oder einfach "normal" ist ein Vektor, der an einem Punkt senkrecht zur Oberfläche steht. Es ist auch ein Einheitsvektor, das heißt, seine Länge ist 1. Wir nennen diesen Vektor .

Diffuse Reflexionsmodellierung

Ein Lichtstrahl mit einer Richtung und Helligkeit fällt also auf eine Fläche mit einer Normalen. Welcher Teil wird als Funktion von , und in die Szene zurückreflektiert?

Stellen wir uns für eine geometrische Analogie die Helligkeit des Lichts als die "Breite" des Strahls vor. Seine Energie verteilt sich über eine Fläche der Größe . Wenn und die gleiche Richtung haben, dh der Strahl senkrecht zur Oberfläche steht, bedeutet dies, dass die pro Flächeneinheit reflektierte Energie gleich der einfallenden Energie pro Flächeneinheit ist;< . С другой стороны, когда угол между и приближается к , приближается к , то есть энергия на единицу площади приближается к 0; . Но что происходит в промежутках?

Die Situation ist im Diagramm unten dargestellt. Wir wissen, und; Ich habe Winkel und sowie Punkte , und hinzugefügt, um die Notation in Bezug auf dieses Diagramm zu vereinfachen.

Da ein Lichtstrahl technisch gesehen keine Breite hat, gehen wir davon aus, dass alles auf einem unendlich kleinen flachen Bereich der Oberfläche passiert. Selbst wenn es sich um die Oberfläche einer Kugel handelt, ist die betreffende Fläche so unendlich klein, dass sie im Verhältnis zur Größe der Kugel fast flach ist, so wie die Erde in kleinen Maßstäben flach aussieht.

Ein Lichtstrahl mit einer Breite trifft an einem Punkt unter einem Winkel auf eine Oberfläche ein. Die Normale am Punkt ist, und die vom Strahl getragene Energie wird darüber verteilt. Wir müssen rechnen.

Einer der Winkel ist , und der andere ist . Dann ist der dritte Winkel . Aber es sollte beachtet werden, dass und auch einen rechten Winkel bilden, das heißt, sie sollten es auch sein. Folglich, :

Betrachten wir ein Dreieck. Seine Winkel sind , und . Die Seite ist , und die Seite ist .

Und jetzt ... Trigonometrie zur Rettung! Per Definition ; Ersetzen Sie durch , und durch , und wir erhalten


die umgewandelt wird
Wir sind fast fertig. ist der Winkel zwischen und , das heißt, es kann ausgedrückt werden als
Und endlich
Wir haben also eine sehr einfache Gleichung, die den reflektierten Teil des Lichts mit dem Winkel zwischen der Normalen zur Oberfläche und der Richtung des Lichts in Beziehung setzt.

Beachten Sie, dass bei größeren Winkeln der Wert negativ wird. Wenn wir diesen Wert bedenkenlos verwenden, erhalten wir als Ergebnis Lichtquellen, subtraktiv hell. Es ergibt keinen physikalischen Sinn; ein größerer Winkel bedeutet einfach, dass das Licht tatsächlich reicht der Rücken Oberfläche und trägt nicht zur Beleuchtung des beleuchteten Punktes bei. Das heißt, wenn es negativ wird, dann betrachten wir es als gleich .

Gleichung für diffuse Reflexion

Wir können nun eine Gleichung formulieren, um die Gesamtlichtmenge zu berechnen, die von einem Punkt mit einer Normalen in einer Szene mit Umgebungsleuchtdichte und Punkt- oder gerichtetem Licht mit entweder bekannter Leuchtdichte und Lichtvektoren (für gerichtete Quellen) oder berechnet für P (für Punkt Quellen):
Es lohnt sich, noch einmal zu wiederholen, dass die Begriffe in denen nicht zur Beleuchtung des Punktes hinzugefügt werden sollten.

Sphärennormale

Hier fehlt nur noch eine Kleinigkeit: Woher kommen die Normalen?

Diese Frage ist viel kniffliger als es aussieht, wie wir im zweiten Teil des Artikels sehen werden. Glücklicherweise gibt es für den Fall, den wir betrachten, eine sehr einfache Lösung: Der Normalenvektor jedes Punktes auf der Kugel liegt auf einer geraden Linie, die durch den Mittelpunkt der Kugel geht. Das heißt, wenn der Mittelpunkt der Kugel ist, dann ist die Richtung der Normalen zu den Punkten:

Warum habe ich "normale Richtung" und nicht "normal" geschrieben? Die Normale muss nicht nur senkrecht zur Oberfläche stehen, sondern auch ein Einheitsvektor sein; dies wäre wahr, wenn der Radius der Kugel wäre, was nicht immer wahr ist. Um die Normale selbst zu berechnen, müssen wir den Vektor durch seine Länge dividieren und erhalten so die Länge:


Dies ist hauptsächlich von theoretischem Interesse, da die oben geschriebene Beleuchtungsgleichung eine Division durch beinhaltet, aber das Erstellen "echter" Normalen ist ein guter Ansatz; Das wird unsere Arbeit in Zukunft erleichtern.

Diffuse Reflexionswiedergabe

Lassen Sie uns das alles in Pseudocode übersetzen. Zuerst fügen wir der Szene ein paar Lichter hinzu:

Licht ( Typ = Umgebungsintensität = 0,2 ) Licht ( Typ = Punkt Intensität = 0,6 Position = (2, 1, 0) ) Licht ( Typ = gerichtete Intensität = 0,2 Richtung = (1, 4, 4) )
Beachten Sie, dass sich die Helligkeit praktischerweise zu summiert, da aus der Beleuchtungsgleichung folgt, dass kein Punkt eine Lichthelligkeit größer als eins haben kann. Das bedeutet, dass wir keine Bereiche mit "zu langer Belichtung" bekommen.

Die Beleuchtungsgleichung lässt sich ziemlich einfach in Pseudocode übersetzen:

ComputeLighting(P, N) (i = 0.0 für Licht in Szene.Lichter ( if light.type == ambient ( i += light.intensity ) else ( if light.type == point L = light.position - P else L = light.direction n_dot_l = dot(N, L) if n_dot_l > 0 i += light.intensity*n_dot_l/(length(N)*length(L)) ) ) return i )
Und das Einzige, was übrig bleibt, ist die Verwendung von ComputeLighting in TraceRay. Wir werden den String ersetzen, der die Farbe der Kugel zurückgibt

Nächste_Sphäre.Farbe zurückgeben
zu diesem Ausschnitt:

P = O + next_t*D # Schnittpunkt berechnen N = P - next_sphere.center # Kugelnormale am Schnittpunkt berechnen N = N / length(N) return next_sphere.color*ComputeLighting(P, N)
Lassen Sie uns nur zum Spaß eine große gelbe Kugel hinzufügen:

Kugel ( Farbe = (255, 255, 0) # Gelbes Zentrum = (0, -5001, 0) Radius = 5000 )
Wir starten den Renderer und siehe da, die Kugeln fangen endlich an, wie Kugeln auszusehen!

Aber warte, wie wurde aus der großen gelben Kugel ein flacher gelber Boden?

Das war es nicht, es ist nur so, dass sie im Vergleich zu den anderen drei so groß ist und die Kamera so nah bei ihr ist, dass sie flach aussieht. So wie unser Planet flach aussieht, wenn wir auf seiner Oberfläche stehen.

Reflexion von einer glatten Oberfläche

Jetzt wenden wir uns den "glänzenden" Objekten zu. Im Gegensatz zu „matten“ Objekten ändern „glänzende“ Objekte ihr Aussehen, wenn Sie sie aus verschiedenen Blickwinkeln betrachten.

Nehmen Sie eine Billardkugel oder ein frisch gewaschenes Auto. Solche Objekte weisen ein bestimmtes Lichtausbreitungsmuster auf, normalerweise mit hellen Bereichen, die sich zu bewegen scheinen, wenn Sie um sie herumgehen. Im Gegensatz zu matten Objekten hängt es tatsächlich vom Blickwinkel ab, wie Sie die Oberfläche dieser Objekte wahrnehmen.

Beachten Sie, dass die roten Billardkugeln rot bleiben, wenn Sie ein paar Schritte zurücktreten, aber der helle weiße Fleck, der ihnen ein "glänzendes" Aussehen verleiht, scheint sich zu bewegen. Das bedeutet, dass der neue Effekt die diffuse Reflexion nicht ersetzt, sondern ergänzt.

Warum passiert das? Wir können mit dem Warum beginnen nicht tritt auf matten Objekten auf. Wie wir im vorherigen Abschnitt gesehen haben, streut ein Lichtstrahl, wenn er auf die Oberfläche eines matten Objekts trifft, gleichmäßig in alle Richtungen zurück in die Szene. Intuitiv ist dies auf die Rauheit der Oberfläche des Objekts zurückzuführen, das heißt, auf mikroskopischer Ebene sieht es aus wie viele kleine Oberflächen, die in zufällige Richtungen zeigen:

Aber was passiert, wenn die Oberfläche nicht so uneben ist? Nehmen wir das andere Extrem – ein perfekt polierter Spiegel. Wenn ein Lichtstrahl auf einen Spiegel trifft, wird er in einer einzigen Richtung reflektiert, die symmetrisch zum Einfallswinkel in Bezug auf die Normale des Spiegels ist. Wenn wir die Richtung des reflektierten Lichts benennen und vereinbaren, dass dies anzeigt auf der Lichtquelle erhalten wir die folgende Situation:

Je nach Grad der "polierten" Oberfläche gleicht es mehr oder weniger einem Spiegel; Das heißt, wir erhalten eine „Spiegel“-Reflexion (spiegelnde Reflexion, vom lateinischen „Speculum“, dh „Spiegel“).

Bei einem perfekt polierten Spiegel wird ein einfallender Lichtstrahl in eine einzige Richtung reflektiert. Dies ermöglicht es uns, Objekte im Spiegel klar zu sehen: Für jeden einfallenden Strahl gibt es einen einzigen reflektierten Strahl. Aber nicht jedes Objekt ist perfekt poliert; Obwohl das meiste Licht in Richtung von reflektiert wird, wird ein Teil davon in Richtungen in der Nähe von reflektiert; Je näher an , desto mehr Licht wird in diese Richtung reflektiert. Das "Funkeln" eines Objekts bestimmt, wie schnell das reflektierte Licht abnimmt, wenn es sich entfernt von:

Was uns interessiert, ist, wie wir herausfinden können, wie viel Licht in Richtung unseres Betrachtungspunkts zurückreflektiert wird (denn das ist das Licht, das wir verwenden, um die Farbe jedes Punkts zu bestimmen). Wenn der "Ansichtsvektor" auf die Kamera zeigt und der Winkel zwischen und ist, dann haben wir Folgendes:

Wenn alles Licht reflektiert wird. Wenn das Licht nicht reflektiert wird. Wie bei der diffusen Reflexion benötigen wir einen mathematischen Ausdruck, um zu bestimmen, was bei Zwischenwerten von passiert.

Modellierung von „Spiegel“-Reflexionen

Erinnern Sie sich, wie ich bereits erwähnt habe, dass nicht alle Modelle auf physikalischen Modellen basieren? Nun, hier ist ein Beispiel dafür. Das unten gezeigte Modell ist willkürlich, wird aber verwendet, weil es einfach zu berechnen ist und gut aussieht.

Lass uns nehmen . Es hat schöne Eigenschaften: , , und die Werte nehmen allmählich von bis entlang einer sehr schönen Kurve ab:

Erfüllt alle Anforderungen an eine „Spiegel“-Funktion, warum also nicht nutzen?

Aber uns fehlt noch ein Detail. Bei dieser Formulierung strahlen alle Objekte gleichermaßen. Wie ändert man die Gleichung, um unterschiedliche Glanzgrade zu erhalten?

Denken Sie daran, dass dieser Glanz ein Maß dafür ist, wie schnell die Reflexionsfunktion als abnimmt. Eine sehr einfache Möglichkeit, verschiedene Lichtkurven zu erhalten, besteht darin, den Grad eines positiven Exponenten zu berechnen. Da ist es offensichtlich, dass ; das heißt, es verhält sich genauso wie , nur "bereits". Hier für verschiedene Werte:

Je größer der Wert von , desto "schmaler" wird die Funktion drumherum und desto glänzender sieht das Objekt aus.

Allgemein genannt Reflexionsindex, und es ist eine Eigenschaft der Oberfläche. Da das Modell nicht auf der physikalischen Realität basiert, können die Werte nur durch Versuch und Irrtum ermittelt werden, d.h. durch Anpassen der Werte, bis sie beginnen, „natürlich“ auszusehen (Hinweis: zur Verwendung eines auf Physik basierenden Modells siehe die Zweistrahl-Reflexionsfunktion (DPRF). )).

Fassen wir alles zusammen. Der Strahl trifft auf die Oberfläche an dem Punkt, an dem die Normale ist und der Reflexionsindex ist. Wie viel Licht wird in Blickrichtung reflektiert?

Wir haben bereits entschieden, dass dieser Wert ist, wobei der Winkel zwischen und ist, der wiederum relativ zu gespiegelt wird. Das heißt, der erste Schritt ist die Berechnung von und .

Wir können in zwei Vektoren und zerlegen, so dass , wobei parallel zu und senkrecht zu ist:

Dies ist eine Projektion auf ; durch die Eigenschaften des Skalarprodukts und basierend auf der Tatsache, dass die Länge dieser Projektion gleich ist. Wir haben festgelegt, was parallel sein wird, also .

Da können wir sofort zugreifen.

Schauen wir uns nun an ; da es um symmetrisch ist, ist seine parallele Komponente dasselbe wie y, und die senkrechte Komponente ist entgegengesetzt zu ; also :

Durch Ersetzen der zuvor erhaltenen Ausdrücke erhalten wir


und ein wenig vereinfachen, erhalten wir

Die Bedeutung von „Spiegel“-Reflexion

Jetzt können wir die "Spiegel" -Reflexionsgleichung aufschreiben:

Wie bei diffuser Beleuchtung kann es negativ sein, und auch dies müssen wir ignorieren. Außerdem muss nicht jedes Objekt glänzend sein; für solche Objekte (die wir durch darstellen werden) wird der Wert "Spiegelung" überhaupt nicht berechnet.

Rendern mit "spiegelnden" Reflexionen

Fügen wir der Szene die "Spiegel"-Reflexionen hinzu, an denen wir gearbeitet haben. Nehmen wir zunächst einige Änderungen an der Szene selbst vor:

Kugel ( Zentrum = (0, -1, 3) Radius = 1 Farbe = (255, 0, 0) # Rot glänzend = 500 # Glänzend ) Kugel ( Zentrum = (-2, 1, 3) Radius = 1 Farbe = ( 0, 0, 255) # Blau glänzend = 500 # Glänzend ) Kugel ( Zentrum = (2, 1, 3) Radius = 1 Farbe = (0, 255, 0) # Grün glänzend = 10 # Leicht glänzend ) Kugel ( Farbe = (255, 255, 0) # Gelbes Zentrum = (0, -5001, 0) Radius = 5000 Spiegel = 1000 # Sehr glänzend)
Im Code müssen wir ComputeLighting so ändern, dass es den „spiegelnden“ Wert nach Bedarf berechnet und zur Gesamtbeleuchtung hinzufügt. Beachten Sie, dass jetzt beides erforderlich ist:

ComputeLighting(P, N, V, s) (i = 0,0 für Licht in Szene.Lichter ( if light.type == ambient ( i += light.intensity ) else ( if light.type == point L = light.position - P else L = light.direction # Diffuse n_dot_l = dot(N, L) if n_dot_l > 0 i += light.intensity*n_dot_l/(length(N)*length(L)) # Specular if s != -1 ( R = 2*N*dot(N, L) - L r_dot_v = dot(R, V) if r_dot_v >
Schließlich müssen wir TraceRay ändern, um die neuen ComputeLighting-Parameter zu übergeben. offensichtlich; es wird aus den Sphärendaten entnommen. Aber was ist mit? ist ein Vektor, der vom Objekt zur Kamera zeigt. Glücklicherweise haben wir in TraceRay bereits einen Vektor, der von der Kamera zum Objekt zeigt – das ist die Richtung des verfolgten Strahls! Das heißt, es ist einfach.

Hier ist der neue TraceRay-Code mit "Spiegel"-Reflektion:

TraceRay(O, D, t_min, t_max) (nächste_t = inf, nächste_Kugel = NULL für Kugel in Szene.Kugeln (t1, t2 = IntersectRaySphere(O, D, Kugel) wenn t1 in und t1< closest_t closest_t = t1 closest_sphere = sphere if t2 in and t2 < closest_t closest_t = t2 closest_sphere = sphere } if closest_sphere == NULL return BACKGROUND_COLOR P = O + closest_t*D # Вычисление пересечения N = P - closest_sphere.center # Вычисление нормали сферы в точке пересечения N = N / length(N) return closest_sphere.color*ComputeLighting(P, N, -D, sphere.specular) }
Und hier ist unsere Belohnung für all das Jonglieren mit Vektoren:

Schatten

Wo Licht und Gegenstände sind, muss es Schatten geben. Wo sind also unsere Schatten?

Beginnen wir mit einer grundlegenderen Frage. Warum muss ein Schatten sein? Schatten erscheinen dort, wo Licht ist, aber seine Strahlen können das Objekt nicht erreichen, weil sich ein anderes Objekt in ihrem Weg befindet.

Sie werden feststellen, dass wir uns im vorherigen Abschnitt für Winkel und Vektoren interessiert haben, aber wir haben nur die Lichtquelle und den Punkt berücksichtigt, den wir einfärben müssen, und alles andere, was in der Szene passiert, vollständig ignoriert - zum Beispiel ein Objekt, das bekommt Im weg.

Stattdessen müssen wir etwas Logik hinzufügen, die sagt: " Befindet sich ein Objekt zwischen dem Punkt und der Quelle, muss keine Beleuchtung von dieser Quelle hinzugefügt werden".

Wir möchten die folgenden zwei Fälle hervorheben:

Es sieht so aus, als hätten wir alle Werkzeuge, die wir dafür brauchen.

Beginnen wir mit einer Richtungsquelle. Wir wissen ; Das ist der Punkt, der uns interessiert. Wir wissen ; dies ist Teil der Definition der Lichtquelle. Mit und können wir einen Strahl definieren, nämlich , der von einem Punkt zu einer unendlich weit entfernten Lichtquelle verläuft. Schneidet dieser Strahl ein anderes Objekt? Wenn nicht, dann ist nichts zwischen dem Punkt und der Quelle, das heißt, wir können die Beleuchtung von dieser Quelle berechnen und zur Gesamtbeleuchtung hinzufügen. Wenn es sich kreuzt, ignorieren wir diese Quelle.

Wir wissen bereits, wie man den nächsten Schnittpunkt zwischen einem Strahl und einer Kugel berechnet; Wir verwenden es für Raytracing von der Kamera. Wir können es wieder verwenden, um den nächsten Schnittpunkt zwischen dem Lichtstrahl und dem Rest der Szene zu berechnen.

Allerdings sind die Parameter etwas anders. Die Strahlen gehen nicht von der Kamera aus, sondern von der . Die Richtung ist nicht , aber . Und wir interessieren uns für Schnittpunkte mit allem, was über eine unendliche Entfernung hinausgeht; das bedeutet, dass und .

Wir können Punktquellen auf sehr ähnliche Weise handhaben, mit zwei Ausnahmen. Erstens ist nicht gegeben, aber es ist sehr einfach, aus der Position der Quelle und zu berechnen. Zweitens sind wir an allen Schnittpunkten interessiert, die mit beginnen, aber nur bis (ansonsten Objekte pro eine Lichtquelle könnte Schatten erzeugen!); das heißt, in diesem Fall, und .

Es gibt einen Grenzfall, den wir berücksichtigen müssen. Nehmen wir einen Strahl. Wenn wir nach Schnittpunkten suchen, die mit beginnen, finden wir uns höchstwahrscheinlich bei , weil es wirklich auf der Kugel liegt, und ; mit anderen Worten, jedes Objekt wirft Schatten auf sich selbst (Anmerkung: Genauer gesagt möchten wir die Situation vermeiden, in der ein Punkt und nicht das gesamte Objekt einen Schatten auf sich selbst wirft; ein Objekt mit einer komplexeren Form als eine Kugel (nämlich jedes konkave Objekt) kann wahre Schatten auf sich selbst werfen!

Der einfachste Weg, damit umzugehen, besteht darin, einen niedrigen Wert anstelle einer Untergrenze zu verwenden. Geometrisch möchten wir, dass der Strahl etwas entfernt von der Oberfläche beginnt, d. h. in der Nähe, aber nicht genau bei . Das heißt, für gerichtete Quellen ist das Intervall , und für Punktquellen - .

Rendern mit Schatten

Lassen Sie uns dies in Pseudocode umwandeln.

In der vorherigen Version berechnete TraceRay den nächstgelegenen Strahl-Kugel-Schnittpunkt und berechnete dann die Beleuchtung am Schnittpunkt. Wir müssen den nächstgelegenen Schnittpunktcode extrahieren, da wir ihn erneut verwenden möchten, um die Schatten zu berechnen:

ClosestIntersection(O, D, t_min, t_max) (nest_t = inf closest_sphere = NULL for sphere in scene.Spheres (t1, t2 = IntersectRaySphere(O, D, sphere) if t1 in and t1< closest_t closest_t = t1 closest_sphere = sphere if t2 in and t2 < closest_t closest_t = t2 closest_sphere = sphere } return closest_sphere, closest_t }
Dadurch ist TraceRay viel einfacher:

TraceRay(O, D, t_min, t_max) ( näheste_Kugel, näheste_t = NahesteKreuzung(O, D, t_min, t_max) if näheste_Kugel == NULL gebe HINTERGRUNDFARBE zurück P = 0 + näheste_t*D # Schnittpunkt berechnen N = P - näheste_Kugel.Zentrum # Berechne die Kugelnormale am Schnittpunkt N = N / length(N) return next_sphere.color*ComputeLighting(P, N, -D, sphere.specular) )
Jetzt müssen wir ComputeLighting eine Schattenprüfung hinzufügen:

ComputeLighting(P, N, V, s) (i = 0,0 für Licht in Szene.Lichter ( if light.type == ambient ( i += light.intensity ) else ( if light.type == point ( L = light. position - P t_max = 1 ) else ( L = light.direction t_max = inf ) # Auf Schatten prüfen shadow_sphere, shadow_t = ClosestIntersection(P, L, 0.001, t_max) if shadow_sphere != NULL Continue # Diffuse n_dot_l = dot(N, L ) if n_dot_l > 0 i += light.intensity*n_dot_l/(length(N)*length(L)) # Specular if s != -1 ( R = 2*N*dot(N, L) - L r_dot_v = dot(R, V) if r_dot_v > 0 i += light.intensity*pow(r_dot_v/(length(R)*length(V)), s) ) ) ) return i )
So wird unsere neu gerenderte Szene aussehen:


Quellcode und funktionierende Demo >>

Jetzt wir haben schon was.

Betrachtung

Wir haben glänzende Objekte. Aber ist es möglich, Objekte zu erschaffen, die sich tatsächlich wie Spiegel verhalten? Natürlich und in der Tat ist ihre Implementierung in einem Raytracer sehr einfach, aber auf den ersten Blick mag es verwirrend erscheinen.

Mal sehen, wie Spiegel funktionieren. Wenn wir in einen Spiegel schauen, sehen wir Lichtstrahlen, die vom Spiegel reflektiert werden. Lichtstrahlen werden symmetrisch zur Flächennormalen reflektiert:

Nehmen wir an, wir verfolgen einen Strahl und der nächste Schnittpunkt ist ein Spiegel. Welche Farbe hat der Lichtstrahl? Offensichtlich ist dies nicht die Farbe des Spiegels, sondern jede Farbe, die der reflektierte Strahl hat. Alles, was wir tun müssen, ist die Richtung des reflektierten Strahls zu berechnen und herauszufinden, welche Farbe das Licht hatte, das aus dieser Richtung kam. Wenn wir nur eine Funktion hätten, die für einen bestimmten Strahl die Farbe des Lichts zurückgibt, das aus dieser Richtung fällt ...

Oh warte, wir haben es: es heißt TraceRay .

Wir beginnen also mit der Haupt-TraceRay-Schleife, um zu sehen, was der von der Kamera emittierte Strahl "sieht". Wenn TraceRay feststellt, dass der Strahl ein reflektierendes Objekt sieht, muss es nur die Richtung des reflektierten Strahls berechnen und sich selbst aufrufen.

An dieser Stelle schlage ich vor, dass Sie die letzten drei Absätze noch einmal lesen, bis Sie sie verstanden haben. Wenn Sie zum ersten Mal etwas über rekursives Raytracing lesen, müssen Sie es möglicherweise ein paar Mal erneut lesen und ein wenig nachdenken, bevor Sie es wirklich tun verstehe.

Keine Eile, ich warte.

Jetzt die Euphorie von diesem schönen Moment Eureka! ein wenig geschlafen, lasst es uns ein wenig formalisieren.

Das Wichtigste bei allen rekursiven Algorithmen ist es, eine Endlosschleife zu verhindern. In diesem Algorithmus gibt es eine offensichtliche Austrittsbedingung: Wenn der Strahl entweder auf ein nicht reflektierendes Objekt trifft oder wenn er auf nichts trifft. Aber es gibt einen einfachen Fall, wo wir in einer Endlosschleife landen können: der Effekt endloser Korridor. Es manifestiert sich, wenn Sie einen Spiegel vor einen anderen Spiegel stellen und darin endlose Kopien von sich selbst sehen!

Es gibt viele Möglichkeiten, dieses Problem zu vermeiden. Wir werden vorstellen Rekursionsgrenze Algorithmus; er wird die "Tiefe" kontrollieren, in die er gehen kann. Nennen wir es. Wenn , dann sehen wir Objekte, aber ohne Reflexionen. Wenn wir einige Objekte und Reflexionen einiger Objekte sehen. Wenn wir einige Objekte sehen, Reflexionen einiger Objekte und Reflexionen einiger Reflexionen einiger Objekte. Usw. Generell macht es nicht viel Sinn, tiefer als 2-3 Stufen zu gehen, da der Unterschied in diesem Stadium bereits kaum spürbar ist.

Wir werden eine weitere Unterscheidung schaffen. "Reflectivity" muss nicht auf yes oder no gesetzt werden - Objekte können teilweise reflektierend und teilweise farbig sein. Wir weisen jeder Oberfläche eine Zahl von bis zu, die ihr Reflexionsvermögen bestimmt. Danach mischen wir die lokal beleuchtete Farbe und die reflektierte Farbe proportional zu dieser Zahl.

Schließlich müssen Sie entscheiden, welche Parameter der rekursive Aufruf von TraceRay annehmen soll? Der Strahl beginnt an der Oberfläche des Objekts, Punkt . Strahlrichtung ist die Richtung des von reflektierten Lichts; In TraceRay haben wir , das heißt, die Richtung von der Kamera zu , entgegengesetzt zur Bewegung des Lichts, das heißt, die Richtung des reflektierten Strahls wird , relativ zu reflektiert. Ähnlich wie bei Schatten möchten wir nicht, dass Objekte sich selbst reflektieren, also . Wir möchten, dass Objekte reflektiert werden, egal wie weit sie entfernt sind, also . Und schließlich ist das Rekursionslimit um eins kleiner als das Rekursionslimit, in dem wir uns gerade befinden.

Rendern mit Reflexion

Fügen wir dem Raytracer-Code Reflexion hinzu.

Wie zuvor wechseln wir zunächst die Szene:

Kugel ( Mitte = (0, -1, 3) Radius = 1 Farbe = (255, 0, 0) # Rot spiegelnd = 500 # Glänzend reflektierend = 0,2 # Leicht reflektierend ) Kugel ( Mitte = (-2, 1, 3) Radius = 1 Farbe = (0, 0, 255) # Blau spiegelnd = 500 # Glänzend reflektierend = 0,3 # Etwas stärker reflektierend) Kugel ( Mitte = (2, 1, 3) Radius = 1 Farbe = (0, 255, 0) # Grün spiegelnd = 10 # Leicht glänzend reflektierend = 0,4 # Noch stärker reflektierend) Kugel (Farbe = (255, 255, 0) # Gelbes Zentrum = (0, -5001, 0) Radius = 5000 Spiegelnd = 1000 # Sehr glänzend reflektierend = 0,5 # Halb reflektierend)
Wir verwenden die Formel "reflektierender Strahl" an einigen Stellen, damit wir sie loswerden können. Es nimmt einen Strahl und einen normalen , zurückkehrenden , reflektierten relativ zu :

ReflectRay(R, N) (return 2*N*dot(N, R) - R; )
Die einzige Änderung in ComputeLighting besteht darin, die Reflexionsgleichung durch einen Aufruf dieses neuen ReflectRay zu ersetzen.

An der Hauptmethode wurde eine kleine Änderung vorgenommen - wir müssen dem TraceRay der obersten Ebene ein Rekursionslimit übergeben:

Farbe = TraceRay(O, D, 1, inf, Rekursionstiefe)
Die recursion_depth-Konstante kann auf einen vernünftigen Wert gesetzt werden, z. B. 3 oder 5.

Die einzigen wichtigen Änderungen treten gegen Ende des TraceRay auf, wo wir die Reflexionen rekursiv berechnen:

TraceRay(O, D, t_min, t_max, Tiefe) (nächste_Sphäre, nächste_t = NahesteSchnittmenge(O, D, t_min, t_max) wenn nächste_Sphäre == NULL gebe HINTERGRUNDFARBE zurück # Lokale Farbe berechnen P = O + nächste_t*D # Schnittpunkt N berechnen = P - next_sphere.center # Berechne die Normale zur Kugel am Schnittpunkt N = N / length(N) local_color = next_sphere.color*ComputeLighting(P, N, -D, sphere.specular) # Wenn wir die treffen Rekursionsgrenze oder das Objekt reflektiert nicht, dann sind wir fertig mit r=nächste_Sphäre.reflektierend, wenn Tiefe<= 0 or r <= 0: return local_color # Вычисление отражённого цвета R = ReflectRay(-D, N) reflected_color = TraceRay(P, R, 0.001, inf, depth - 1) return local_color*(1 - r) + reflected_color*r }
Lassen Sie die Ergebnisse für sich sprechen:

Um das Rekursionstiefenlimit besser zu verstehen, werfen wir einen genaueren Blick auf das Rendern mit :

Und hier ist dieselbe vergrößerte Ansicht derselben Szene, diesmal gerendert mit:

Wie Sie sehen können, besteht der Unterschied darin, ob wir Reflexionen von Reflexionen von Reflexionen von Objekten oder nur Reflexionen von Objekten sehen.

Benutzerdefinierte Kamera

Ganz am Anfang der Diskussion über Raytracing haben wir zwei wichtige Annahmen getroffen: Die Kamera ist auf fixiert und darauf gerichtet, und die „Aufwärts“-Richtung ist . In diesem Abschnitt werden wir diese Einschränkungen aufheben, sodass wir die Kamera überall in der Szene positionieren und in jede Richtung richten können.

Beginnen wir mit der Position. Sie haben vielleicht bemerkt, dass es im gesamten Pseudocode nur einmal verwendet wird: als Ausgangspunkt der Strahlen, die von der Kamera in der Top-Level-Methode kommen. Wenn wir die Position der Kamera ändern wollen. dann Das einzige Was getan werden muss, ist, einen anderen Wert für zu verwenden.

Beeinflusst die Änderung Bestimmungen auf der Richtung Strahlen? Auf keinen Fall. Die Richtung der Strahlen ist ein Vektor, der von der Kamera zur Projektionsebene verläuft. Wenn wir die Kamera bewegen, bewegt sich die Projektionsebene mit der Kamera, was bedeutet, dass sich ihre relativen Positionen nicht ändern.

Wenden wir uns nun der Richtung zu. Nehmen wir an, wir haben eine Rotationsmatrix, die sich in die gewünschte Blickrichtung dreht, und - in die gewünschte Richtung "nach oben" (und da dies eine Rotationsmatrix ist, sollte sie per Definition tun, was erforderlich ist). Position Die Kamera ändert sich nicht, wenn Sie die Kamera einfach herumdrehen. Aber die Richtung ändert sich, sie erfährt nur die gleiche Drehung wie die gesamte Kamera. Das heißt, wenn wir eine Richtungs- und eine Rotationsmatrix haben, dann ist Rotieren einfach.

Nur die Top-Level-Funktion ändert sich:

Für x in [-Cw/2, Cw/2] ( für y in [-Ch/2, Ch/2] ( D = camera.rotation * CanvasToViewport(x, y) color = TraceRay(camera.position, D, 1, inf) canvas.PutPixel(x, y, color) ) )
So sieht unsere Szene aus einer anderen Position und mit einer anderen Ausrichtung aus:

Wohin soll es als nächstes gehen

Wir beenden den ersten Teil der Arbeit mit einem kurzen Überblick über einige interessante Themen, die wir nicht untersucht haben.

Optimierung

Wie in der Einleitung erwähnt, haben wir uns überlegt, wie die verschiedenen Möglichkeiten am verständlichsten erklärt und umgesetzt werden können. Daher ist der Raytracer voll funktionsfähig, aber nicht besonders schnell. Hier sind einige Ideen, die Sie selbst lernen können, um den Tracer zu beschleunigen. Versuchen Sie nur zum Spaß, die Ausführungszeit vor und nach ihrer Implementierung zu messen. Sie werden sehr überrascht sein!

Parallelisierung

Der naheliegendste Weg, einen Raytracer zu beschleunigen, besteht darin, mehrere Strahlen gleichzeitig zu verfolgen. Da jeder Strahl, der aus der Kamera kommt, unabhängig von allen anderen ist und die meisten Strukturen schreibgeschützt sind, können wir ohne allzu große Probleme oder Komplikationen aufgrund von Zeitproblemen einen Strahl zu jedem CPU-Kern verfolgen.

Tatsächlich gehören Raytracer zu einer Klasse von Algorithmen, die als Algorithmen bezeichnet werden extrem parallelisierbar Gerade weil es ihre Natur sehr einfach macht, sie zu parallelisieren.

Caching von Werten

Betrachten Sie die von IntersectRaySphere berechneten Werte, wo der Raytracer normalerweise die meiste Zeit verbringt:

K1 = Punkt(D, D) k2 = 2*Punkt(OC, D) k3 = Punkt(OC, OC) - r*r
Einige dieser Werte sind in der gesamten Szene konstant – sobald Sie wissen, wo sich die Kugeln befinden, ändern sich r*r und dot(OC, OC) nicht mehr. Sie können sie beim Szenenladen einmalig berechnen und in den Kugeln selbst speichern; Sie müssen sie nur neu berechnen, wenn sich die Kugeln im nächsten Frame bewegen sollen. dot(D, D) ist eine Konstante für den gegebenen Strahl, sodass Sie ihn in ClosestIntersection auswerten und an IntersectRaySphere übergeben können.

Schattenoptimierungen

Wenn ein Objektpunkt relativ zur Lichtquelle im Schatten liegt, weil ein anderes Objekt im Weg erkannt wird, dann ist die Wahrscheinlichkeit groß, dass der benachbarte Punkt ebenfalls aufgrund desselben Objekts relativ zur Lichtquelle im Schatten liegt (dies ist genannt Schattenkonsistenz):

Das heißt, wenn wir nach Objekten zwischen einem Punkt und einer Lichtquelle suchen, können wir zunächst prüfen, ob das letzte Objekt, das relativ zur gleichen Lichtquelle einen Schatten auf den vorherigen Punkt wirft, keinen Schatten auf den aktuellen Punkt wirft. Wenn ja, dann können wir fertig werden; Wenn nicht, prüfen wir einfach die restlichen Objekte auf die übliche Weise weiter.

Ebenso brauchen wir bei der Berechnung des Schnittpunkts zwischen einem Lichtstrahl und Objekten in der Szene nicht wirklich den nächsten Schnittpunkt – es reicht zu wissen, dass es mindestens einen Schnittpunkt gibt. Wir können eine spezielle Version von ClosestIntersection verwenden, die das Ergebnis zurückgibt, sobald es die erste Kreuzung findet (und dafür müssen wir nicht next_t berechnen und zurückgeben, sondern nur einen booleschen Wert).

Räumliche Strukturen

Das Berechnen des Schnittpunkts eines Strahls mit jeder Kugel ist eine ziemliche Ressourcenverschwendung. Es gibt viele Datenstrukturen, die es erlauben, ganze Gruppen von Objekten auf einen Schlag zu verwerfen, ohne einzelne Schnittpunkte berechnen zu müssen.

Eine detaillierte Diskussion solcher Strukturen ist nicht Gegenstand unseres Artikels, aber die allgemeine Idee ist folgende: Angenommen, wir haben mehrere Sphären nahe beieinander. Sie können den Mittelpunkt und den Radius der kleinsten Kugel berechnen, die alle diese Kugeln enthält. Wenn der Strahl diese Grenzkugel nicht schneidet, dann kann man sicher sein, dass er keine der darin enthaltenen Kugeln schneidet, und dies kann in einem einzigen Schnitttest durchgeführt werden. Wenn es eine Kugel schneidet, müssen wir natürlich noch prüfen, ob es eine der darin enthaltenen Kugeln schneidet.

Sie können mehr darüber erfahren, indem Sie über lesen Hierarchien von Bounding Volumes.

Downsampling

Hier ist eine einfache Möglichkeit, die Raytracer-Zeiten schneller zu machen: Rechenzeiten weniger Pixel!

Angenommen, wir führen Raytracing für Pixel und durch und sie fallen auf dasselbe Objekt. Wir können logischerweise davon ausgehen, dass der Strahl für ein Pixel auch auf dasselbe Objekt fällt, die anfängliche Suche nach Schnittpunkten mit der gesamten Szene überspringen und direkt zur Berechnung der Farbe an diesem Punkt übergehen.

Wenn Sie dies in horizontaler und vertikaler Richtung tun, können Sie bis zu 75 % weniger primäre Berechnungen von Schnittpunkten zwischen Strahlenszenen durchführen.

Natürlich kann dies ein sehr dünnes Objekt leicht übersehen: Anders als die zuvor besprochenen ist dies eine „falsche“ Optimierung, da die Ergebnisse ihrer Verwendung keine sind identisch was würden wir ohne sie bekommen; gewissermaßen „betrügen“ wir diese Wirtschaft. Der Trick besteht darin, zu erraten, wie man richtig speichert und zufriedenstellende Ergebnisse liefert.

Andere Primitive

In den vorangegangenen Abschnitten haben wir Kugeln als Grundelemente verwendet, da sie aus mathematischer Sicht leicht zu manipulieren sind. Aber nachdem Sie dies erreicht haben, können Sie einfach andere Primitive hinzufügen.

Beachten Sie, dass aus der Sicht von TraceRay jedes Objekt ausreichen kann, solange es nur zwei Werte berechnen muss: den Wert für den nächsten Schnittpunkt zwischen dem Strahl und dem Objekt und die Normale am Schnittpunkt. Alles andere im Raytracer ist unabhängig vom Objekttyp.

Dreiecke sind eine gute Wahl. Zuerst müssen Sie den Schnittpunkt zwischen dem Strahl und der Ebene berechnen, die das Dreieck enthält, und wenn es einen Schnittpunkt gibt, dann bestimmen Sie, ob der Punkt innerhalb des Dreiecks liegt.

Strukturelle Blockgeometrie

Es gibt einen sehr interessanten Objekttyp, der relativ einfach zu implementieren ist: eine boolesche Operation zwischen anderen Objekten. Beispielsweise kann das Kreuzen zweier Kugeln etwas erzeugen, das wie eine Linse aussieht, und das Subtrahieren einer kleinen Kugel von einer größeren Kugel kann etwas erzeugen, das wie ein Todesstern aussieht.

Wie es funktioniert? Für jedes Objekt können Sie die Stellen berechnen, an denen der Strahl in das Objekt eintritt und aus ihm austritt; zum Beispiel tritt der Strahl bei einer Kugel ein und aus. Angenommen, wir müssen den Schnittpunkt zweier Kugeln berechnen; Der Strahl befindet sich innerhalb des Schnittpunkts, wenn er sich innerhalb beider Kugeln befindet, und ansonsten außerhalb. Bei der Subtraktion ist der Strahl innen, wenn er sich im ersten Objekt befindet, aber nicht im zweiten.

Allgemeiner gesagt, wenn wir den Schnittpunkt zwischen einem Strahl und berechnen wollen (wo ist irgendein boolescher Operator), dann müssen wir zuerst den Schnittpunkt von ray- und ray- separat berechnen, was uns das „innere“ Intervall jedes Objekts und gibt . Dann berechnen wir , was im „inneren“ Intervall liegt . Wir müssen nur den ersten Wert finden, der sowohl im "inneren" Intervall als auch in dem Intervall liegt, an dem wir interessiert sind:

Die Normale am Schnittpunkt ist entweder die Normale des Objekts, das den Schnittpunkt erzeugt, oder ihr Gegenteil, je nachdem, ob wir "außerhalb" oder "innerhalb" des ursprünglichen Objekts schauen.

Natürlich müssen sie auch keine Primitiven sein; sie können selbst das Ergebnis boolescher Operationen sein! Bei sauberer Implementierung brauchen wir es nicht einmal zu wissen wie Sie sind so lang, wie wir Schnittpunkte und Normalen von ihnen erhalten können. Wir können also drei Sphären nehmen und beispielsweise berechnen.

Transparenz

Nicht alle Objekte müssen undurchsichtig sein, einige können teilweise transparent sein.

Die Implementierung von Transparenz ist der Implementierung von Reflexion sehr ähnlich. Wenn der Strahl auf eine teilweise transparente Oberfläche trifft, berechnen wir wie zuvor die lokale und reflektierte Farbe, berechnen aber auch die Komplementärfarbe - die Farbe des durchfallenden Lichts durch ein Objekt, das durch einen anderen Aufruf von TraceRay erhalten wird. Dann müssen Sie diese Farbe unter Berücksichtigung der Transparenz des Objekts mit den lokalen und reflektierten Farben mischen, und das war's.

Brechung

Wenn im wirklichen Leben ein Lichtstrahl durch ein transparentes Objekt fällt, ändert er seine Richtung (wenn ein Strohhalm also in ein Glas Wasser getaucht wird, sieht er "zerbrochen" aus). Richtungswechsel abhängig Brechungsindex jedes Material nach folgender Gleichung:
Dabei sind und die Winkel zwischen dem Strahl und der Normalen vor und nach dem Überqueren der Oberfläche und und die Brechungsindizes des Materials außerhalb und innerhalb der Objekte.

Zum Beispiel ungefähr gleich und ungefähr gleich . Das heißt, für einen Strahl, der in einem Winkel in das Wasser eintritt, erhalten wir




Halten Sie einen Moment inne und stellen Sie fest: Wenn Sie konstruktive Blockgeometrie und Transparenz implementieren, können Sie eine Lupe (den Schnittpunkt zweier Kugeln) modellieren, die sich wie eine physikalisch korrekte Lupe verhält!

Supersampling

Supersampling ist das ungefähre Gegenteil von Subsampling, bei dem wir auf Genauigkeit statt Geschwindigkeit abzielen. Angenommen, die Strahlen, die zwei benachbarten Pixeln entsprechen, fallen auf zwei verschiedene Objekte. Wir müssen jedes Pixel mit der entsprechenden Farbe einfärben.

Vergessen Sie jedoch nicht die Analogie, mit der wir begonnen haben: Jeder Strahl sollte die "definierende" Farbe von jedem definieren Quadrat"Gitter", durch das wir schauen. Indem wir einen Strahl pro Pixel verwenden, nehmen wir herkömmlicherweise an, dass die Farbe eines Lichtstrahls, der durch die Mitte des Quadrats geht, das gesamte Quadrat bestimmt, aber das muss nicht der Fall sein.

Dieses Problem kann gelöst werden, indem mehrere Strahlen pro Pixel – 4, 9, 16 usw. – verfolgt und dann gemittelt werden, um die Farbe des Pixels zu erhalten.

Dies macht den Raytracer natürlich 4x, 9x oder 16x langsamer, aus dem gleichen Grund, aus dem er durch Downsampling 1x schneller wird. Glücklicherweise gibt es einen Kompromiss. Wir können davon ausgehen, dass sich die Eigenschaften eines Objekts entlang seiner Oberfläche gleichmäßig ändern, d. h. das Emittieren von 4 Strahlen pro Pixel, die an leicht unterschiedlichen Punkten auf dasselbe Objekt fallen, verbessert die Szene nicht allzu sehr. Daher können wir mit einem Strahl pro Pixel beginnen und benachbarte Strahlen vergleichen: Wenn sie auf andere Objekte treffen oder sich ihre Farbe um mehr als einen umverteilten Schwellenwert unterscheidet, wenden Sie die Pixelunterteilung auf beide an.

Raytracer-Pseudocode

Unten ist die vollständige Version des Pseudocodes, den wir in den Raytracing-Kapiteln erstellt haben:

CanvasToViewport(x, y) (return (x*Vw/Cw, y*Vh/Ch, d) ) ReflectRay(R, N) (return 2*N*dot(N, R) - R; ) ComputeLighting(P, N, V, s) ( i = 0.0 für Licht in Szene.Lichter ( if light.type == ambient ( i += light.intensity ) else ( if light.type == point ( L = light.position - P t_max = 1 ) else ( L = light.direction t_max = inf ) # Auf Schatten prüfen shadow_sphere, shadow_t = ClosestIntersection(P, L, 0.001, t_max) if shadow_sphere != NULL Continue # Diffuse n_dot_l = dot(N, L) if n_dot_l > 0 i += light.intensity*n_dot_l/(length(N)*length(L)) # Shine if s != -1 ( R = ReflectRay(L, N) r_dot_v = dot(R, V) if r_dot_v > 0 i += light.intensity*pow(r_dot_v/(length(R)*length(V)), s) ) ) ) return i ) ClosestIntersection(O, D, t_min, t_max) (most_t = inf closest_sphere = NULL for sphere in scene.Spheres ( t1, t2 = IntersectRaySphere(O, D, sphere) if t1 in and t1< closest_t closest_t = t1 closest_sphere = sphere if t2 in and t2 < closest_t closest_t = t2 closest_sphere = sphere } return closest_sphere, closest_t } TraceRay(O, D, t_min, t_max, depth) { closest_sphere, closest_t = ClosestIntersection(O, D, t_min, t_max) if closest_sphere == NULL return BACKGROUND_COLOR # Вычисление локального цвета P = O + closest_t*D # Вычисление точки пересечения N = P - closest_sphere.center # Вычисление нормали сферы в точке пересечения N = N / length(N) local_color = closest_sphere.color*ComputeLighting(P, N, -D, sphere.specular) # Если мы достигли предела рекурсии или объект не отражающий, то мы закончили r = closest_sphere.reflective if depth <= 0 or r <= 0: return local_color # Вычисление отражённого цвета R = ReflectRay(-D, N) reflected_color = TraceRay(P, R, 0.001, inf, depth - 1) return local_color*(1 - r) + reflected_color*r } for x in [-Cw/2, Cw/2] { for y in [-Ch/2, Ch/2] { D = camera.rotation * CanvasToViewport(x, y) color = TraceRay(camera.position, D, 1, inf) canvas.PutPixel(x, y, color) } }
Und hier ist die Szene, die zum Rendern der Beispiele verwendet wurde:

viewport_size = 1 x 1 project_plane_d = 1 sphere ( center = (0, -1, 3) radius = 1 color = (255, 0, 0) # Red specular = 500 # Glänzend reflektierend = 0,2 # Leicht reflektierend ) sphere ( center = (-2, 1, 3) Radius = 1 Farbe = (0, 0, 255) # Blau spiegelnd = 500 # Glänzend reflektierend = 0,3 # Etwas stärker reflektierend) Kugel (Zentrum = (2, 1, 3) Radius = 1 Farbe = (0, 255, 0) # Grün spiegelnd = 10 # Leicht glänzend reflektierend = 0,4 # Noch stärker reflektierend) Kugel (Farbe = (255, 255, 0) # Gelbes Zentrum = (0, -5001, 0) Radius = 5000 spiegelnd = 1000 # sehr glänzend reflektierend = 0,5 # halb reflektierend ) Licht ( Typ = Umgebungsintensität = 0,2 ) Licht ( Typ = Punktintensität = 0,6 Position = (2, 1, 0) ) Licht ( Typ = Richtungsintensität = 0,2 Richtung = (1, 4, 4) )

Tags: Tags hinzufügen

Raytracing und Rasterisierung – was ist der Unterschied?

Wir sind nicht sicher, ob alle unsere Leser wissen oder sich erinnern, was Raytracing wie sich verschiedene Rendering-Methoden prinzipiell unterscheiden und welche Vor- und Nachteile sie haben. Lassen Sie uns daher zunächst versuchen, ohne komplexe Mathematik ganz kurz und mehr oder weniger klar darüber zu sprechen. Bevor wir zum Raytracing übergehen, müssen wir uns an die Grundlagen des klassischen Algorithmus erinnern Rasterung mit Z-Buffer.

Bei der heute in modernen Echtzeitgrafiken allgemein akzeptierten Rasterisierungsmethode gibt es zum Zeichnen jedes Objekts eine Projektion auf die Bildschirmebene von geometrischen Grundelementen (Polygone, meistens Dreiecke), aus denen das Objekt besteht. Dreiecke werden Pixel für Pixel gezeichnet, wobei ein Tiefenpuffer verwendet wird, der den Abstand zur Bildschirmebene enthält und erforderlich ist, um sicherzustellen, dass die Dreiecke, die der Kamera am nächsten sind, die am weitesten entfernten beim Rendern überlappen.

Zusätzlich zu den Scheitelpunkten und den Polygonen, die sie verbinden, werden auch Informationen über Farbe, Texturkoordinaten und Normalen gespeichert, die zum Definieren der Vorder- und Rückseite jeder Oberfläche benötigt werden. Die Pixelfarbe wird durch komplexe Berechnungen in Vertex- und Pixel-Shadern bestimmt, und Effekte wie Schatten werden mit zusätzlichen Durchgängen, aber auch mit Rasterung gerendert.

Schattierungsprozess ( Schattierung) besteht darin, die Beleuchtungsmenge für ein Pixel zu berechnen, wobei das Auferlegen einer oder mehrerer Texturen auf das Pixel berücksichtigt wird, was seine endgültige Farbe bestimmt. All dies erfordert viele Berechnungen, da die Szenen moderner Spiele in hochauflösenden Bildschirmen mehrere Millionen Polygone und mehrere Millionen Pixel enthalten und die Informationen auf dem Bildschirm mit einer Frequenz von mindestens 30 Bildern pro Sekunde und besser aktualisiert werden müssen - 60 FPS. Ganz zu schweigen von Virtual-Reality-Helmen, bei denen Sie gleichzeitig Bilder für zwei Augen mit einer Frequenz von 90 FPS zeichnen müssen.

Da GPUs aber mit einer sehr hohen Taktrate arbeiten und über eine große Anzahl von Hardwareeinheiten verfügen, die auf bestimmte Berechnungen spezialisiert sind, und sich die Rasterung sehr gut für die Parallelisierung eignet, gibt es keine besonderen Probleme mit der Rendering-Leistung und wird von der überwiegenden Mehrheit der 3D-Computerspiele verwendet Rasterung. In Wirklichkeit sind die Dinge etwas komplizierter, da viele zusätzliche Optimierungen verwendet werden, um nicht viele unsichtbare Dreiecke zu zeichnen, aber die Essenz der Rasterung im Allgemeinen ist genau das.

Bei der Entwicklung von GPUs für die gesamte Zeit ihrer Entwicklung wurde viel Zeit darauf verwendet, die Arbeit zum Verwerfen unsichtbarer Geometrien zu optimieren und die Rechenlast zu reduzieren. Zuerst wurde das Rendern von Objekten außer Sicht verworfen, dann Objekte, die von anderen näher an der Kamera verdeckt wurden usw. Die Optimierungen, die während der Rasterung entwickelt wurden, sind sehr effektiv, in modernen Spielen verbrauchen unsichtbare Objekte fast keine Rechenressourcen und reduzieren sie erheblich der Arbeitsaufwand für die Rasterung der Szene. Sie werden weiter verstehen, warum wir das Thema unsichtbare Objekte berühren.

Um die globale Beleuchtung zu berechnen, Schatten und andere Effekte zu zeichnen, müssen Sie knifflige Hacks verwenden, die auf derselben Rasterung basieren. Infolgedessen sind GPUs im Laufe der Jahre ziemlich ausgefeilt geworden und haben gelernt, wie man die Geometrieverarbeitung in Vertex-Shadern beschleunigt, Pixel mit Pixel-Shadern gut rendert und sogar universelle Compute-Shader für Physik, Post-Effekte und eine Vielzahl anderer Berechnungen verwendet . Aber der Kern der GPU ist immer gleich geblieben.

Beim Raytracing ist die Grundidee ganz anders, aber in der Theorie fast einfacher. Tracing simuliert die Ausbreitung von Lichtstrahlen über eine 3D-Szene. Raytracing kann in zwei Richtungen erfolgen: von Lichtquellen oder von jedem Pixel in entgegengesetzter Richtung, dann werden meist mehrere Reflexionen von Objekten in der Szene in Richtung Kamera bzw. Lichtquelle ermittelt. Das Rendern von Strahlen für jedes Pixel in der Szene ist weniger rechenintensiv, und das Projizieren von Strahlen von Lichtquellen ergibt ein Rendering mit höherer Qualität.

Rückspur wurde erstmals 1969 von einem IBM-Mitarbeiter in seinem Werk beschrieben "Einige Techniken zum Schattieren von maschinellen Renderings von Volumenkörpern" und diese Technik berechnet den Pfad des Lichtstrahls für jedes Pixel auf dem Bildschirm in Abhängigkeit von den 3D-Modellen in der Szene. 10 Jahre später kam es zu einem weiteren technologischen Durchbruch, als der Forscher Turner Whitted (der übrigens jetzt bei Nvidia Research arbeitet) eine Arbeit veröffentlichte "Ein verbessertes Beleuchtungsmodell für schattierte Displays", die zeigte, wie es möglich ist, Schatten, Reflexion und Lichtbrechung beim Durchzeichnen zu berechnen.

Ein paar weitere Artikel in den 1980er Jahren beschrieben die Grundlagen des Raytracing für Computergrafiken weiter, was zu einer ganzen Revolution der synthetischen Bildgebung in der Filmindustrie führte. Also 1984 mehrere Mitarbeiter Lucasfilm beschrieben, wie man Effekte wie Bewegungsunschärfe, Schärfentiefe, weiche Schatten, verschwommene Reflexionen und Lichtbrechungen mit Raytracing erzeugt. Ein paar Jahre später, Caltech-Professor Jim Kajiya in seiner Arbeit "Die Rendering-Gleichung" beschrieb eine genauere Methode, um Licht in einer Szene zu streuen. Und seitdem wird Raytracing buchstäblich überall in der Filmindustrie eingesetzt.

Bei einer gängigen Methode der umgekehrten Strahlverfolgung wird also für jedes Pixel auf dem Bildschirm ein imaginärer Strahl von der Kamera zum Objekt in der Szene gezogen. Dies simuliert einen Lichtstrahl, der von einer Lichtquelle in dieser Richtung in die Kamera einfällt, und der erste Schnittpunkt mit dem Objekt wird verwendet, um die Farbe des Pixels zu bestimmen. Die Primärstrahlen bestimmen die Sichtbarkeit von Objekten (wie der Z-Puffer bei der Rasterung), und um die Farbe zu bestimmen, müssen Sie weitere Sekundärstrahlen von den Schnittpunkten zu verschiedenen Lichtquellen ziehen (wenn die Strahlen durch das Objekt blockiert werden, dann die Lichtquelle beeinflusst die Beleuchtung des Pixels nicht), und der Satz sekundärer Strahlen definiert die Beleuchtung, die auf das Pixel trifft.


Aber das Interessanteste passiert noch weiter - um Fotorealismus zu erreichen, müssen Sie die Eigenschaften von Materialien in Form der von ihnen reflektierten und gebrochenen Lichtmenge berücksichtigen und um die Farbe eines Pixels zu berechnen, müssen Sie zeichnen mehr Reflexions- und Brechungsstrahlen. In der obigen Abbildung sind sie nicht angedeutet, aber man kann sie sich gedanklich als Strahlen vorstellen, die von der Oberfläche der Kugel reflektiert und von ihr gebrochen werden. Dieser verbesserte Raytracing-Algorithmus wurde vor Jahrzehnten erfunden, und diese Ergänzungen waren ein großer Schritt zur Steigerung des Realismus des synthetischen Bildes. Bis heute hat das Verfahren viele Modifikationen erhalten, aber sie basieren immer darauf, den Schnittpunkt von Lichtstrahlen mit Objekten in der Szene zu finden.

Die ersten praktischen Experimente zur Umsetzung von Echtzeit-Raytracing begannen schon vor längerer Zeit auf der renommierten Konferenz SIGGraph Entwicklungen wie diese sind üblich. Echtzeit-Tracing-Demos stammen aus den späten 1980er Jahren und bieten mehrere Frames pro Sekunde unter Verwendung hochoptimierter Techniken und mehrerer Shared-Memory-Computersysteme zum Rendern. Seitdem gab es viele Entwicklungen, die darauf abzielen, die Ablaufverfolgung für die Arbeit zu beschleunigen, auch auf einem einzelnen PC.

Ganz zu schweigen von den zahlreichen 3D-Engines von Demo-Szene-Enthusiasten Ende der 90er Jahre und darüber hinaus, die sich von den Möglichkeiten und der grundsätzlichen Einfachheit der Methode inspirieren ließen und viele nützliche Optimierungen in das Raytracing einbrachten. Früher wurde auf unserer Website eine ganze Reihe von Materialien veröffentlicht, die sich mit einer der Raytracing-Software-Engines befassten, die sehr spezifisch ist und viele schwerwiegende Einschränkungen aufweist, die es nicht erlauben, darauf basierend ernsthafte Spielprojekte zu erstellen:

Hardware-Hersteller blieben nicht zurück, da sie auf Messen experimentelle Prototypen von Tracing-Beschleunigern und für sie optimierte Demoprogramme lange Zeit zeigten. Also, im Juni 2008, das Unternehmen Intel zeigte eine spezielle Version des Spiels Feindliches Territorium: Quake Wars (Quake Wars: Ray Traced), die Raytracing beim Rendern in einer Auflösung von 1280x720 mit 15-30 Bildern pro Sekunde verwendet, was bereits als Echtzeit gilt. Diese Demo verwendete keine Hardwarebeschleuniger, sondern lief auf 16 Xeon-Kernen mit einer Frequenz von unter 3 GHz.

Das Intel-Projekt zeigte deutlich die Vorteile des Raytracing-Renderings und demonstrierte realistisches Wasser, Objektschatten durch transparente Oberflächen und Reflexionen. Die Entwicklung der Demonstration war das Projekt Wolfenstein: Raytracing, und verschiedene Enthusiasten nehmen oft den Motor der Serie Beben Tracing hinzuzufügen - also mit der Einreichung von Moddern Beben 2 realistische Reflexionen erschienen, die durch sehr starkes Rauschen und hohe Systemanforderungen getrübt wurden.

Und mehrere Jahre lang (von 2012 bis 2016) wurden die Prototypen von Hardware-Trace-Beschleunigern von der Firma gezeigt Bildgebende Technologien, das sogar eine offene API für Raytracing bietet - OpenRL. Es wurde festgestellt, dass der Hardware-Entwicklungsbeschleuniger dieses Unternehmens in der Lage ist, in Autodesk Maya zu arbeiten und Echtzeit-Raytracing bereitzustellen. Das Unternehmen verfügte jedoch nicht über genügend Mittel, um hardwarebeschleunigtes Raytracing zum Erfolg zu führen, und das „Gewicht“ dieses Unternehmens auf dem Grafikmarkt als seine Lokomotive. Und die Demoprogramme waren ehrlich gesagt nicht die beeindruckendsten, obwohl sie einige Vorteile des Tracings zeigten:

Das Unternehmen schnitt viel besser ab NVIDIA, das auf der SIGGraph 2009 die Technologie ankündigte OptiX, entwickelt für Echtzeit-Raytracing auf ihren GPUs. Die neue API eröffnet den Zugang zu Raytracing in professionellen Anwendungen mit der nötigen Flexibilität, insbesondere zu bidirektionalem Pathtracing und anderen Algorithmen.

OptiX-basierte Renderer existieren bereits für zahlreiche professionelle Software wie Adobe AfterEffects, Bunkspeed Shot, Autodesk Maya, 3ds max und andere Anwendungen und werden von Profis bei der Arbeit verwendet. Dies kann nur mit gewissen Annahmen auf Echtzeit-Rendering zurückgeführt werden, da bei einer hohen Framerate ein sehr verrauschtes Bild erhalten wurde. Nur wenige Jahre später war die Industrie nahe dran, hardwarebeschleunigtes Raytracing bereits in Spielen einzusetzen.

Vor- und Nachteile von Raytracing

Die Raytracing-Rendering-Technik ist im Vergleich zur Rasterung sehr realistisch, da sie die Ausbreitung von Lichtstrahlen sehr ähnlich wie in der Realität simuliert (natürlich immer noch nicht 100% genau). Das Durchpausen erzeugt äußerst realistische Schatten, Reflexionen und Lichtbrechungen und wird daher seit langem in architektonischen und industriellen Designanwendungen geschätzt. Die Technologie hilft Fachleuten auf diesem Gebiet zu verstehen, wie Materialien bei unterschiedlicher Beleuchtung in der realen Welt lange vor der physischen Verkörperung aussehen werden.

Zu den offensichtlichen Vorteilen der Verfolgung kann auch die Tatsache gehören, dass die Rechenkomplexität der Methode nicht sehr von der geometrischen Komplexität der Szene abhängt und die Berechnungen perfekt parallelisiert sind - Sie können mehrere Strahlen gleichzeitig einfach und unabhängig voneinander verfolgen und teilen die Bildschirmoberfläche in Zonen, um sie auf verschiedenen Rechenkernen zu verfolgen. Sehr nützlich ist auch, dass das Beschneiden unsichtbarer Flächen eine logische Konsequenz des Algorithmus ist.

Wichtiger ist jedoch, dass das Verfahren die reale Ausbreitung von Lichtstrahlen nachahmt und ein endgültiges Bild von höherer Qualität im Vergleich zur Rasterung erhält. Die Rasterung hat offensichtliche Nachteile – zum Beispiel wird ein Objekt, das nicht in der Szene enthalten ist, nicht auf der GPU gezeichnet, aber es kann einen sichtbaren Schatten werfen oder sollte in einer reflektierenden Oberfläche (Spiegel) sichtbar sein, und die Optimierung der Rasterung verwirft es und habe es nicht berücksichtigt. Ganz zu schweigen davon, dass dieses unsichtbare Objekt die globale Beleuchtung der Szene stark beeinflussen kann, indem es Licht auf sichtbare Oberflächen reflektiert. Diese Probleme sind teilweise gelöst, insbesondere ermöglicht die Verwendung von Shadow Maps das Zeichnen von Schatten von Objekten, die in der Szene unsichtbar sind, aber das resultierende Bild ist noch lange nicht ideal. Und hier geht es um das Prinzip selbst, denn die Rasterung funktioniert ganz anders als das menschliche Sehen.

Effekte wie Reflexionen, Brechungen und Schatten, die beim Rastern nur schwer gut umzusetzen sind, sind ein natürliches Ergebnis des Raytracing-Algorithmus. Nehmen Sie Reflexionen - dies ist nur einer der Bereiche, in denen Raytracing merklich besser ist als die Rasterung. In modernen Spielen werden Reflexionen normalerweise mithilfe von Umgebungskarten (Environment Map, statisch oder dynamisch) oder Reflexionen im Bildschirmraum ( Bildschirmbereich), die Reflexionen in den meisten Fällen gut nachahmen, aber dennoch sehr große Einschränkungen haben, insbesondere sind sie nicht für eng beieinander liegende Objekte geeignet.

Die Berechnung von Reflexionen im Bildschirmbereich ermöglicht es Ihnen, mit einigen Einschränkungen mehr oder weniger naturgetreue Reflexionen zu erhalten, jedoch mit Hardwarebeschleunigung auf der GPU mithilfe von Rasterung. Und mit Raytracing werden Reflexionen immer perfekt dargestellt, ohne dass zusätzliche komplexe Algorithmen erforderlich sind. Ein weiterer wichtiger Vorteil des Nachzeichnens besteht darin, Spiegelungen von Teilen desselben Objekts aneinander darzustellen (z. B. damit sich der Griff einer Teekanne oder der Ausguss auf sich selbst spiegelt), was mit der Rasterung viel schwieriger zu bewerkstelligen ist.

Ein weiterer klarer Vorteil von Raytracing ist das Rendern von transparenten Objekten. Mit der Rasterisierung ist es sehr schwierig, Transparenzeffekte zu simulieren, da ihre Berechnung von der Rendering-Reihenfolge abhängt und Sie dafür transparente Polygone vorsortieren müssen, und selbst dann können visuelle Artefakte auftreten. Es wurden mehrere Hacks erfunden, um das Sortieren von Polygonen zu umgehen, aber dies alles führt zu Komplikationen des Verfahrens und zusätzlichen Schwierigkeiten. Aber der Raytracing-Algorithmus selbst ermöglicht es Ihnen, beliebige Transparenzeffekte in perfekter Qualität zu zeichnen.

Nun, das letzte (für den Anfang) Beispiel ist das Rendern von Schatten. Beim Rastern werden in den meisten Fällen Schattenkarten verwendet ( Shadow-Mapping), die ebenfalls auf Rasterung basieren, rendern einfach von einem anderen Punkt in der Szene und mit anderen Parametern. Die Silhouetten des Objekts werden in einen separaten Puffer von der Lichtquelle gezeichnet, der Inhalt des Puffers wird gefiltert und auf die Oberfläche gelegt, wo der Schatten geworfen werden soll. Es gibt mehrere Probleme mit diesen Methoden, einschließlich der Ecken und Kanten, die Sie alle in Spielen gesehen haben, sowie eine erhöhte Auslastung des Videospeichers. Raytracing hingegen ermöglicht es Ihnen, das Schattenproblem automatisch zu lösen, ohne dass zusätzliche Algorithmen und Speicher erforderlich sind. Außerdem ergibt sich bei einem Rasterisierungs-Hack in jedem Fall ein physikalisch falscher Schatten, aber ein per Raytracing gezeichneter weicher Schatten ist realistisch.

Aber Raytracing hat auch eine Kehrseite. Eines, aber sehr wichtig – all dies aus rechnerischer Sicht zu zeichnen, ist um ein Vielfaches schwieriger. Schlechte Leistung auf bestehender Hardware ist der Hauptnachteil der Tracing-Methode, die lange Zeit alle ihre Vorteile durchgestrichen hat. Das Auffinden des Schnittpunkts von Strahlen mit Szenenobjekten wird nicht so einfach beschleunigt wie relativ einfache Dreiecksrasteroperationen, für die seit vielen Jahren spezielle 3D-Beschleuniger verwendet werden, weshalb Echtzeitgrafiken immer noch eine Rastermethode verwenden, mit der Sie a zeichnen können Bild ziemlich schnell, wenn auch in der Qualität einer vollwertigen Durchzeichnung etwas unterlegen, aber gleichzeitig ziemlich realistisch.

Beim Tracing müssen Sie Tausende von Strahlen für jede Lichtquelle berechnen, von denen die meisten nur geringe Auswirkungen auf das endgültige Bild haben werden. Daher sind sowohl zusätzliche Optimierungen für den Raytracing-Algorithmus als auch neue Hardware erforderlich, die das entsprechende beschleunigen kann Operationen. Außerdem garantiert die Verwendung von Durchpausen allein keinen Fotorealismus. Wenn Sie einfache Algorithmen verwenden, ist das Ergebnis gut, aber immer noch nicht realistisch genug, und um die Realität vollständig zu simulieren, müssen Sie zusätzliche Techniken verwenden, z Photonen-Mapping und Pfadverfolgung, die die Ausbreitung des Lichts in der Welt genauer simulieren.

Da der Raytracing-Algorithmus andererseits gut parallelisiert ist, kann er durch die einfachste technische Methode gelöst werden - die Erhöhung der Anzahl der Rechenkerne (Grafik) Prozessoren, deren Anzahl jedes Jahr zunimmt. Gleichzeitig ist eine lineare Leistungssteigerung beim Tracing gewährleistet. Und angesichts des offensichtlichen Mangels an Optimierung von Hardware und Software für GPU-Raytracing können wir jetzt von einem potenziell schnellen Wachstum der Hardware-Raytracing-Fähigkeiten ausgehen.

Hier gibt es aber kleinere Probleme. Die Berechnung nur der Primärstrahlen ist an sich nicht allzu kompliziert, bringt aber im Vergleich zur klassischen Rasterung und selbst bei kniffligen Hacks keine merkliche Verbesserung der Renderqualität. Und die sekundären Strahlen sind viel schwieriger zu berechnen, weil sie keine Kohärenz haben - Unidirektionalität. Für jedes Pixel müssen Sie völlig neue Daten berechnen, was nicht sehr gut für das Caching ist, was wichtig ist, um eine hohe Geschwindigkeit zu erreichen. Daher ist die Berechnung von Sekundärstrahlen stark von Speicherverzögerungen abhängig, die im Gegensatz zu der schnell wachsenden Speicherbandbreite (LBW) fast nicht abnehmen.

Obwohl Raytracing eine ziemlich einfache und elegante Methode zu sein scheint, die in nur wenigen Codezeilen implementiert werden kann, handelt es sich um einen völlig unoptimierten Algorithmus, und es ist äußerst schwierig, leistungsstarken Raytracing-Code zu erstellen. Wenn der Algorithmus beim Rastern schnell arbeitet, Sie sich aber knifflige Methoden für komplexe visuelle Effekte einfallen lassen müssen, kann Raytracing sie alle zunächst zeichnen, aber es zwingt Sie, den Code sehr sorgfältig zu optimieren, damit er schnell genug für Echtzeit läuft .

Es gibt viele Methoden, um die Verfolgung zu beschleunigen. Die produktivsten Raytracing-Algorithmen verarbeiten Strahlen nicht einzeln, sondern verwenden Strahlensätze, wodurch der Prozess der Verarbeitung von Strahlen in derselben Richtung beschleunigt wird. Solche Optimierungen eignen sich hervorragend für die Ausführung auf modernen CPU- und GPU-SIMD-Einheiten, sie sind für Hauptstrahlen mit gleicher Richtung und für Schattenstrahlen wirksam, aber immer noch nicht für Brechungs- und Reflexionsstrahlen geeignet. Daher ist es notwendig, die Anzahl der für jedes Pixel der Szene berechneten Strahlen ernsthaft zu begrenzen und das erhöhte "Rauschen" des Bildes durch spezielle Filterung zu entfernen.

Außerdem erfordert das Raytracing-Verfahren eine geeignete Datenstruktur, um die Elemente der Szene zu speichern, und dies kann für die Leistung kritisch sein. Einige Strukturen eignen sich besser für statische Daten, andere für dynamische Daten. Raytracing scheint also nur oberflächlich eine einfache und elegante Methode zu sein, aber um die gewünschte Leistung zu erzielen, ist eine Menge Arbeit zur Optimierung erforderlich - nicht weniger als die Simulation komplexer Effekte bei der Rasterung. Und diese Arbeit hat gerade erst begonnen.

Mehrere Probleme müssen angegangen werden, bevor Raytracing eine praktikable Alternative zur Rasterung für Spiele wird. Nun scheint es, dass die Vorteile der Ablaufverfolgung nicht so groß sind, wie eine erhebliche Leistungseinbuße bei der Verwendung. Ja, das Nachzeichnen hat sehr wichtige Vorteile in Form von realistischen Reflexionen, Schatten und der Verarbeitung transparenter Objekte, was mit der Rasterung schwierig ist, aber ... gibt es genug solcher Objekte in Spielen, dass der Mangel an Realismus für sie ernst wird? Einerseits reflektieren die meisten Objekte auf der Welt Licht, andererseits haben Spiele bewiesen, dass sich unsere Augen und unser Gehirn damit begnügen, dem Realismus nur näher zu kommen. In den meisten modernen Spielen reichen Reflexionen auf Objekten, obwohl sie nicht vollständig fotorealistisch sind, oft aus, um unser Gehirn auszutricksen.

Ja, Raytracing kann eine bessere Qualität liefern als Rasterung, aber auf welche Weise? Wenn Sie nach vollständigem Realismus streben, dann ist vollwertiges Durchzeichnen mit der Berechnung vieler Strahlen für Beleuchtung und Reflexionen sowie eine Kombination von Techniken wie Radiosität und Photonen-Mapping, wird sehr anspruchsvoll in Bezug auf die Rechenleistung sein. Oft verwenden sogar Offline-Nicht-Echtzeit-Renderer Vereinfachungen. Natürlich wird nach einiger Zeit eine ausreichend hohe Rechenleistung zur Verfügung stehen, um einen Vorteil gegenüber der Rasterung zu erzielen, auch in Sachen Performance, aber von diesem Moment sind wir noch sehr weit entfernt.

Selbst beim Offline-Rendering für die Filmindustrie nimmt die Renderzeit mit zunehmender Rechenleistung nicht ab, da der Appetit der Künstler noch schneller wächst! Und sogar führende Unternehmen in der Produktion von Animationsfilmen, wie z Pixar, versuchen Sie, den Rendering-Prozess zu optimieren, indem Sie Raytracing nur für einen Teil der Effekte verwenden - gerade wegen der erheblichen Auswirkungen auf die Leistung. Man muss also verstehen, dass die Zeiten eines vollwertigen Tracings für die gesamte Szene in Echtzeitspielen noch sehr weit entfernt sind. Und für ein vollwertiges Echtzeit-Rendering mittels Raytracing in Spielen reicht die Rechenleistung definitiv noch nicht aus. Es ist ein langer Weg, auch wenn die Entwicklung der GPU noch im Gange ist.

Aber auf jeden Fall ist Raytracing der physikalisch richtige Weg, der viele große und kleine Probleme des bestehenden Ansatzes lösen kann. Mit Hilfe verschiedener Hacks und Tricks, die derzeit in der Rasterung verwendet werden, können Sie ein gutes Ergebnis erzielen, aber dies ist definitiv keine universelle und ideale Methode zum Rendern von 3D-Grafiken. Ziemlich bald werden die Entwickler von Echtzeit-3D-Anwendungen im Streben nach Realismus an die Grenzen der bestehenden Rasterisierungsmethode stoßen und auf eine Methode mit einem fortschrittlichen Beleuchtungsmodell umsteigen müssen, das der Realität ähnelt. Höchstwahrscheinlich wird es Raytracing sein. Da Raytracing aber eine sehr teure Methode ist und selbst von den leistungsstärksten Systemen kaum durchgezogen werden dürfte, sollte man zunächst auf hybride Rendering-Methoden setzen, die Rasterungsleistung und Raytracing-Qualität vereinen.

Hybrid-Rendering für den Übergang

Aufgrund der Anforderungen des Raytracings selbst mit einer geringen Anzahl berechneter Strahlen pro Pixel kann dieses Verfahren kaum ausschließlich verwendet werden und wird die Rasterung bisher nicht ersetzen. Es besteht jedoch die Möglichkeit, die beiden Methoden zu mischen. Beispielsweise kann eine Grundgeometrie mit hoher Leistung gerastert werden, und dann können nur weiche Schatten und Reflexionen mit Raytracing gerendert werden. Obwohl die Rasterisierung in den kommenden Jahren mit dem Aufkommen des Hybrid-Renderings weiterhin eine große Rolle spielen wird, wird der Anteil der Raytracing-Algorithmen in solchen Engines basierend auf dem Wachstum der Rechenleistung zukünftiger GPUs allmählich zunehmen.

Dieser Ansatz wird seit langem in den gleichen Cartoons des Unternehmens verwendet Pixar, trotz der Tatsache, dass sie in den Anforderungen keine festen Grenzen für die Renderzeit zu haben scheinen. Es ist jedoch einfacher und schneller, Geometrie mit denselben Rendersystem-Mikropolygonen zu rendern. Reyes, und verwenden Sie die Ablaufverfolgung nur dort, wo bestimmte Effekte benötigt werden. Nahezu alle Animationsfilme von Pixar haben zuvor Mikropolygone und Rasterung sowie Raytracing für die Rendering-Engine verwendet. RenderMan später für den Cartoon "Cars" hinzugefügt, wo es selektiv verwendet wurde - um globale Schattierung (Ambient Occlusion) zu berechnen und Reflexionen zu rendern.

Aber in Wirklichkeit sind hybride Lösungen nicht so einfach, denn für effizientes Raytracing müssen Sie die Datenstruktur auf eine spezielle Weise organisieren, um die Anzahl der Überprüfungen auf Schnittpunkte von Strahlen mit Szenenobjekten zu reduzieren. Daher müssen Sie auch beim Hybrid-Rendering eine optimierte Datenstruktur erstellen. Und in Bezug auf die Leistung ist ein großes Problem der Speicherzugriff im Zusammenhang mit sekundären Strahlen, die beim Hybrid-Rendering benötigt werden. Es stellt sich heraus, dass, wenn die beiden Rendering-Methoden kombiniert werden, viele ihrer Mängel kombiniert werden, insbesondere die Einfachheit der Raytracing-Methode und die hohe Leistung der Rasterung sind verschwunden.

Aber wenn die Vorteile dennoch überwiegen, dann macht ein solcher hybrider Ansatz Sinn. Eine Kombination einiger Rasterisierungs- und Verfolgungsfunktionen ist bereits verfügbar, einschließlich GPU-hardwarebeschleunigter Lichtzuordnung, Rendern dynamischer Lichtkarten und Teilschatten, Rendern von Reflexionen und durchscheinenden Objekten mit Brechung. Dies ist bereits eine großartige Leistung, da ein solcher Ansatz viele Jahre lang nur mit Offline-Rendering verfügbar war. Hybrid-Rendering wurde bereits Ende der 90er Jahre in Animationsfilmen zur Effizienzsteigerung eingesetzt und wird nun auch für Echtzeitanwendungen verfügbar.


Aber das ist nur der Anfang vor der kommenden „Goldenen Ära“ des Echtzeit-Renderings. In Zukunft wird sich dieser hybride Ansatz zu etwas mehr entwickeln und anstelle von selektiven Effekten wird es möglich sein, vollwertige Techniken mit fortschrittlicher Beleuchtung, Schattierung und verschiedenen komplexen Effekten zu verwenden.

Ähnlich wie beim Offline-Rendering "Käfer's Leben" zu viel komplexeren Animationsfilmen, wie z Kokos, das bereits ein vollwertiges Pathtracing mit zig oder gar hundert berechneten Strahlen pro Pixel nutzt. Anders als in den Vorjahren gab es keine Shadow Maps mehr, separate Pässe zur Berechnung der Beleuchtung, sondern nur noch vollwertiges Tracing – die Spieleentwickler streben dasselbe an, nur dass ihr Weg etwas länger sein wird, aber das Ziel ist das gleiche.


Und bevor der Übergang von der Rasterung zum vollständigen Tracing stattfindet, müssen Sie Hybrid-Rendering verwenden und den Entwicklungsansatz in vielerlei Hinsicht ändern. Zum Beispiel, um einen Teil der Arbeit der vorbereitenden Vorbereitung und des „Backens“ einiger Daten auf die GPU zu übertragen, Ihre Produktionspipeline zu überarbeiten und Rendering-Engines darauf vorzubereiten, dass ein zunehmender Teil der Berechnungen nach und nach auf Tracing umgestellt wird. Und die partiellen Vorteile des Tracings können jetzt genutzt werden, allerdings mit einer extrem geringen Anzahl von Strahlen pro Pixel und mit obligatorischer Rauschunterdrückung.


Aber auch bei einem schrittweisen Übergang zum Tracing sollte der Bedarf an Optimierungen, die nicht rasterisierungsspezifisch sind, nicht verworfen werden. High-Level-Optimierungen wie Level of Detail (LOD), Occlusion Culling, Tiling und Streaming funktionieren auch hervorragend mit Raytracing. Und bis die Branche zu einer vollwertigen Ablaufverfolgung übergeht, müssen wir weiterhin effektive Screen-Space-Techniken anwenden, wenn eine hohe Leistung erforderlich ist und die Qualität nicht kritisch ist.


Nun, das Rendern mit Raytracing muss optimiert werden. Wenn Sie beispielsweise dynamische Lightmaps mit DXR rendern, ist es effizient, die Beleuchtung in Lightmaps zwischenzuspeichern und dann die Akkumulation von Daten im Laufe der Zeit für den nächsten Frame zu verwenden. Der Prozess ist relativ schnell und sollte verwendet werden, da Lightmap-Space-Raytracing bessere Ergebnisse liefert als Screen-Space-Raytracing. Es stimmt, Sie müssen verwenden Rauschunterdrückung, da nicht besonders viele Strahlen in Echtzeit berechnet werden können.

Sogar herkömmliche Filter zur Rauschunterdrückung mit Einstellungen speziell für die Funktionen von Raytracing funktionieren gut, und wenn Sie die Rauschunterdrückung mithilfe der Fähigkeiten neuronaler Netze anwenden, die Nvidia bereits demonstriert hat, und sogar hardwarebeschleunigt auf den Tensorkernen von GPUs mit Volta-Architektur, dann scheint die Zukunft des hybriden Renderns ziemlich klar zu sein – zumindest einige der Effekte, die leicht zu bestehenden Engines hinzugefügt werden können (Schattenberechnung oder globale Beleuchtung und Schattierung), die Rasterung verwenden, werden ziemlich bald zu Spielen hinzugefügt.

Der offensichtliche Weg, Hybrid-Rendering zu verwenden, besteht also darin, die Szene zu rastern und Raytracing nur auf einen Teil der Beleuchtungsberechnungen anzuwenden sowie Reflexionen mit Brechungen zu berechnen. Dieser Ansatz bietet Rasterungsgeschwindigkeit und Verfolgungsqualität in Form einer genauen Beleuchtungssimulation, einschließlich globaler Beleuchtung, Reflexionen und Brechungen von Lichtstrahlen und der Wiedergabe optisch korrekter Schatten. Darüber hinaus wird die Simulation dieser Effekte mit Rasterisierungs-Hacks und deren Komplexität irgendwann den Punkt erreichen, an dem es so ressourcenintensiv wird, dass es einfacher ist, die Berechnungen durch echtes Raytracing zu ersetzen. Und überhaupt ist das der einzig richtige Weg, wenn man in die Zukunft der Grafikentwicklung blickt.

DirectX Raytracing ist die Standard-API für Raytracing.

Im Laufe der Zeit lernten sie also, die Rasterung sehr beeindruckend zu gestalten, indem sie verschiedene Algorithmen und Hacks hinzufügten, wie z. B. Parallax Mapping, das nicht zu komplexen Oberflächen Volumen verleiht, sowie Shadow Maps. Um die Grafik zu verbessern, war es nur notwendig, die Geschwindigkeit der GPUs zu erhöhen und sie ein wenig vielseitiger zu machen, wobei die Basis in Form der Rasterung fast unangetastet blieb (ohne Optimierungsmethoden in Form der Aufteilung des Rahmens in Kacheln usw.). .

Moderne Techniken wie Screen-Space-Reflexionen und simulierte globale Beleuchtung haben die Rasterung praktisch an ihre praktischen Grenzen gebracht, da diese Algorithmen knifflige Verarbeitungs-Hacks und komplexe Berechnungen erfordern, die manchmal asynchron zum Rendern durchgeführt werden. Und in naher Zukunft werden die Komplexität und Ressourcenintensität solcher Algorithmen weiter zunehmen. Raytracing hingegen ermöglicht komplexe Effekte auf einfache Weise und öffnet gleichzeitig die Tür zu völlig neuen Techniken, die zuvor mit Echtzeit-Rendering nicht möglich waren. Aber wie kann dies erreicht werden, wenn GPUs nur wissen, wie man rastert?

Aktuelle Version DirectX12 Es scheint nur ziemlich neu zu sein, aber tatsächlich wurde diese Grafik-API bereits auf der GDC 2014 angekündigt und ein Jahr später als Teil von Windows 10 veröffentlicht. Bisher war der Einsatz dieser Version alles andere als erwünscht und geschah aus vielen Gründen sofort. Erstens ist der Entwicklungszyklus für Spiele und Engines ziemlich lang, und die Tatsache, dass DirectX 12 nur auf der neuesten Version von Windows funktioniert und auf Konsolen der aktuellen Generation nur begrenzt unterstützt wird, reduziert nur die Argumente für die Verwendung auf dem PC. Trotzdem haben wir bereits in mehreren Spielen die Verwendung einer Low-Level-API gesehen, aber was kommt als nächstes? Und dann drehte die DirectX-Entwicklungslinie wieder scharf und führte Tools zur Unterstützung von Raytracing ein.

Im Rahmen der Game Developers Conference GD 2018 Microsoft hat eine neue Erweiterung der DirectX-API eingeführt, an der viele Partner, die an der Entwicklung von Software und Hardware beteiligt sind, auf die eine oder andere Weise beteiligt waren. Der Zusatz wird aufgerufen DirectX-Raytracing und sein Name deutet darauf hin, dass es sich um eine Standard-API für Software- und Hardwareunterstützung für Raytracing in DirectX-Anwendungen handelt, die es Entwicklern ermöglicht, Algorithmen und Effekte mit der erwähnten Technik zu verwenden. DirectX Raytracing (im Folgenden kurz als DXR bezeichnet) bietet einen standardisierten Ansatz zur Implementierung von GPU-beschleunigtem Raytracing. Diese Erweiterung kombiniert mit den Fähigkeiten der bestehenden DirectX 12-API, sodass sowohl traditionelle Rasterung als auch Raytracing verwendet und in den gewünschten Proportionen gemischt werden können.

Alle Raytracing-bezogenen DXR-API-Arbeiten werden durch Befehlslisten gesteuert, die von der Anwendung gesendet werden. Raytracing ist eng mit Rasterisierungs- und Berechnungsbefehlen integriert und kann in Multithread-Manier ausgeführt werden. Raytracing-Shader (bis zu fünf neue Shader-Typen!) werden ähnlich wie Compute-Shader verwaltet, sodass sie auf der GPU parallelisiert werden können, wodurch ihre Ausführung auf einem relativ niedrigen Niveau gesteuert wird. Gleichzeitig ist die Anwendung vollständig für die Synchronisierung der GPU und die Nutzung ihrer Ressourcen verantwortlich, wie bei der Rasterung und Berechnung, was den Entwicklern die Kontrolle über die Optimierung der Leistung aller Arten von Arbeit gibt: Rasterung, Raytracing, Berechnung, Datenübertragung.

Renderer teilen sich alle Ressourcen wie Texturen, Puffer und Konstanten, ohne dass für den Zugriff von Trace-Shadern eine Konvertierung, Übertragung oder Duplizierung erforderlich ist. Ressourcen, die Raytracing-spezifische Daten speichern, wie Beschleunigungsstrukturen (Datenstrukturen zur Beschleunigung des Tracings – Auffinden von Strahl- und Geometrieschnittpunkten) und Shader-Tabellen (beschreiben die Beziehung zwischen Raytracing-Shadern, Ressourcen und Geometrie) werden vollständig von der Anwendung DXR verwaltet selbst Die API führt von sich aus keine Datenbewegungen durch. Shader können einzeln oder in Stapeln kompiliert werden, ihre Kompilierung wird vollständig von der Anwendung gesteuert und kann über mehrere CPU-Threads hinweg parallelisiert werden.

Auf der höchsten Ebene fügt DXR der DirectX 12-API vier neue Konzepte hinzu:

  1. Beschleunigungsstruktur ( Beschleunigungsstruktur) ist ein Objekt, das eine 3D-Szene in einem Format darstellt, das für das Rendern von Strahlen auf GPUs optimal ist. Als zweistufige Hierarchie dargestellt, bietet diese Struktur ein optimiertes GPU-Ray-Rendering und eine effiziente dynamische Datenmodifikation.
  2. Neue Befehlslistenmethode ( Befehlsliste) berechtigt DispatchRays ist die Grundlage für das Raytracing in der Szene. So lagert das Spiel DXR-Workloads auf die GPU aus.
  3. Eine Reihe neuer Shader-Typen zum Erstellen von Strahlen, die bestimmen, was genau DXR berechnet. Wenn DispatchRays aufgerufen wird, wird der Ray-Generation-Shader gestartet. Bei Verwendung der neuen Funktion spurstrahl In HLSL sendet der Shader zur Strahlerzeugung den Strahl in die Szene, und je nachdem, wo der Strahl in der Szene auftrifft, kann am Schnittpunkt einer von mehreren Treffer-Shadern aufgerufen werden ( Schlag) oder verpassen ( fehlschlagen), mit der Sie jedem Objekt Ihren eigenen Satz von Shadern und Texturen zuweisen und einzigartige Materialien erstellen können.
  4. Ein Tracing-Pipeline-Zustand, der zu den bereits vorhandenen Grafik- und Compute-Pipeline-Zuständen hinzugefügt wird und Raytracing-Shader und andere Zustände im Zusammenhang mit Tracing-Workloads übersetzt.

Daher fügt DXR der bestehenden Grafik- und Rechen-Engine von DirectX 12 keine neue GPU-Engine hinzu. DXR-Workloads können auf bestehenden Engines ausgeführt werden, da DXR im Kern eine Rechenaufgabe ist. DXR-Aufgaben werden als Rechenlasten dargestellt, da GPUs sowieso vielseitiger werden und fast alle Aufgaben ausführen können, die nicht unbedingt grafikbezogen sind, und in Zukunft wahrscheinlich die meisten festen Funktionen der GPU durch Shader-Code ersetzt werden.

Bei der Verwendung von DXR besteht der erste Schritt darin, Beschleunigungsstrukturen in zwei Ebenen aufzubauen. Auf der untersten Ebene der Struktur definiert die Anwendung einen Satz von Geometriedaten (Vertex- und Indexpuffer), die die Objekte in der Szene definieren. Auf der obersten Ebene der Struktur befindet sich eine Liste mit Beschreibungen, die Verweise auf bestimmte geometrische Daten sowie zusätzliche Daten wie Transformationsmatrizen enthalten, die in jedem Frame aktualisiert werden, ähnlich wie Spiele es tun, um Objekte dynamisch zu ändern. Dies ermöglicht ein effizientes Durchqueren einer großen Menge komplexer Geometrie.

Der zweite Schritt bei der Verwendung von DXR besteht darin, den Status der Ablaufverfolgungspipeline zu erstellen. Gruppenziehungsaufrufe für moderne Spiele ( Anrufe ziehen) um die Effizienz ihrer Ausführung in speziellen Gruppen zu erhöhen - Pakete ( Charge), beispielsweise alle Metallobjekte in einem Stapel und alle Kunststoffobjekte in einem anderen. Aber beim Tracing kann man nicht genau wissen, auf welches Material ein bestimmter Strahl treffen wird, und es können keine Chargen aufgetragen werden. Stattdessen ermöglicht der Status der Ablaufverfolgungspipeline die Zuweisung mehrerer Sätze von Ablaufverfolgungsshadern und Texturressourcen. So können Sie beispielsweise festlegen, dass alle Schnittpunkte von Strahlen mit einem Objekt diesen und jenen bestimmten Shader und diese und jene Textur verwenden sollen, und Schnittpunkte mit einem anderen Objekt einen anderen Shader und eine andere Textur verwenden sollen. Dadurch kann die Anwendung den korrekten Shader-Code mit den korrekten Texturen für die von den Strahlen getroffenen Materialien verwenden.

Der letzte Schritt in der Arbeit von DXR ist das Aufrufen von DispatchRays, das den Shader aufruft, um den Strahl zu erzeugen. Darin ruft die Anwendung die TraceRay-Funktion auf, die bewirkt, dass die Beschleunigungsstruktur durchlaufen wird und der entsprechende Shader bei Treffer oder Fehlschlag ausgeführt wird (zwei verschiedene Arten von Shadern). TraceRay kann auch aus diesen beiden Shadern heraus aufgerufen werden, wenn Ray-Rekursion oder Effekte mit mehreren Bounces verwendet werden.


Warum nicht Compute-Shader nutzen, die wir bereits aus DirectX für Raytracing kennen? Erstens können Sie mit DXR separate Shader für getroffene (hit) und verfehlte (miss) Strahlen ausführen, und zweitens kann der Rendering-Prozess auf GPUs beschleunigt werden (mithilfe von Nvidia RTX oder Analoga von Wettbewerbern), und drittens ermöglicht Ihnen die neue API das Binden (Binding) von Ressourcen mithilfe von Shader-Tabellen.

Nvidia RTX ist eine Reihe von Software- und Hardwarealgorithmen, die das Tracing auf Nvidia-Lösungen basierend auf der Grafikarchitektur beschleunigen Volta. Warum werden frühere Architekturen nicht unterstützt, die sich nicht wesentlich von Volta unterscheiden? Vielleicht ist dies teilweise ein Marketing-Trick, um Kunden für neue Produkte zu gewinnen, oder vielleicht gibt es einige Hardware-Optimierungen in Volta, die es uns ermöglichen, das Raytracing auf der GPU ernsthaft zu beschleunigen, was uns noch nicht mitgeteilt wurde. Ja, bisher hat die einzige GPU mit dieser Architektur Tensorkerne, die Aufgaben der künstlichen Intelligenz beschleunigen, aber wenn sie im Raytracing-Rendering verwendet werden kann, dann nur im Prozess der Rauschunterdrückung und sogar dann, nach verfügbaren Daten, in Bei bestehenden Rauschunterdrückungsalgorithmen wurden diese Optionen noch nicht implementiert.

Die Vorteile von DXR und RTX sind ein leistungsstarkes und flexibles Softwaremodell, ähnlich wie Nvidia OptiX, das es relativ einfach macht, effiziente Algorithmen per Raytracing zu schreiben. Um mit der Entwicklung von Anwendungen mit DXR-Raytracing-Hardware zu beginnen, die mit RTX beschleunigt wird, benötigen Sie eine Grafikkarte, die auf der Volta-Architektur basiert (bisher ist dies nur Titan v) und Treiberversion 396 oder höher sowie das Betriebssystem Windows 10 RS4 und das Microsoft DXR Developer Pack mit allem, was Sie brauchen. Auch nützlich zum Debuggen Microsoft-PIX oder NSight-Grafiken Nvidia-Unternehmen, die bereits Unterstützung für die DXR-API haben.

Um die Entwicklung und das Debuggen zu vereinfachen, hat Microsoft sofort eine neue Version des Dienstprogramms veröffentlicht PIX für Windows mit Unterstützung für DXR-Fähigkeiten. Mit diesem Tool können Sie mit DXR erstellte Frames erfassen und analysieren, damit Entwickler genau verstehen, wie DXR mit Hardware funktioniert, alle Fehler erkennen und ihren Code optimieren. Mit dem PIX können Programmierer API-Aufrufe untersuchen, den Zustand von Objekten und Ressourcen anzeigen, die mit Ablaufverfolgungsarbeiten und Beschleunigungsstrukturen verbunden sind. All dies hilft sehr bei der Entwicklung von DXR-Anwendungen.


Infolgedessen ergänzt die DirectX-Raytracing-API die Entwickler mit spezialisierten Shadern und Strukturen, die für Raytracing geeignet sind, der Fähigkeit, gleichzeitig mit dem Rest der traditionellen Grafikpipeline zu arbeiten und Shader zu berechnen usw. Konzeptionell unterscheidet sich dies nicht wesentlich von dem, was Imagination Tech bietet seit einigen Jahren wieder OpenRL und deren Hardwarelösungen an. Leider war ImgTec mit seinen PowerVR-Wizard-Chips seiner Zeit zu weit voraus, aber Sie müssen genug Geld haben, nicht nur für die anfängliche Entwicklung, sondern auch für die Förderung Ihrer Idee. DXR hingegen ist die API eines großen und etablierten Unternehmens wie Microsoft, und beide Gaming-GPU-Hersteller (Nvidia und AMD, und vielleicht kommt bald Intel hinzu, wer weiß) arbeiten bereits mit Microsoft zusammen, um die zu optimieren neue API für ihre Hardware-Architekturen.

Wie alle geschlossenen APIs hat auch DXR einen gewissen Nachteil, da der Entwickler einfach nicht weiß, wie bestimmte Dinge innerhalb der API funktionieren, welche spezifischen Beschleunigungsstrukturen verwendet werden, um ein effizientes paralleles Rendern auf GPUs zu gewährleisten, mit welchen Vor- und Nachteilen, welchen Eigenschaften (Memory Verbrauch, Latenz usw.), wie funktioniert der Raytracing-Scheduler, gibt es ein Gleichgewicht zwischen Speichernutzung, Latenz, Registernutzung usw., wie viel Arbeit des Tracers in GPU-Hardware erledigt wird und wie viel Treiber und API . Alle diese Lösungen sind geschlossen, und DXR ist keine Ausnahme.

Übrigens gibt es eine Alternative zur Nutzung der DXR-API – Nvidia-Mitarbeiter arbeiten an der Erweiterung der Multiplattform Vulkanische API, ausgelegt für Raytracing - VK_NV_raytracing. Das Entwicklungsteam arbeitet mit Kollegen von zusammen Chronos einen plattformübergreifenden offenen Standard zu schaffen, und eines der Hauptziele besteht darin, zu versuchen, Raytracing in DirectX und Vulkan so ähnlich wie möglich zu gestalten.

Spiele, die Rasterisierung verwenden, sehen oft sehr glaubwürdig und realistisch aus, da ihre Entwickler viel Zeit damit verbracht haben, alle notwendigen Effekte und Algorithmen hinzuzufügen, die die Ausbreitung von Lichtstrahlen in der Realität simulieren. Und in den Anfangsjahren wird die Leistungsfähigkeit von DXR auch zur Ergänzung bestehender Rendering-Techniken wie Bildschirm-Raum-Reflexionen genutzt, um verborgene Geometriedaten auszufüllen, die auf dem Bildschirm nicht sichtbar sind, was zu einer Steigerung der Qualität dieser Effekte führt. Aber in den nächsten Jahren können wir eine Zunahme der Verwendung von DXR für Methoden erwarten, die nicht in der Rasterung verwendet werden, wie zum Beispiel vollwertige globale Illumination. In Zukunft kann Raytracing die Rasterung beim Rendern von 3D-Szenen vollständig ersetzen, obwohl die Rasterung noch lange der Träger einer idealen Balance von Leistung und Qualität bleiben wird.

Derzeit verfügen nur Nvidia-Lösungen der Volta-Familie (mit RTX-Technologie) über eine vollwertige Hardwareunterstützung für DirectX Raytracing, also heute nur die teure Titan V, und auf früheren GPUs dieser Firma sowie auf AMD-GPUs , Raytracing wird vollständig mit Compute-Shadern durchgeführt - das heißt, es ist nur grundlegende DXR-Unterstützung mit geringerer Leistung verfügbar. AMD hat jedoch bereits erklärt, dass sie mit Microsoft zusammenarbeiten, um Hardware-Trace-Beschleunigung einzuführen, und bald einen Treiber zur Unterstützung bereitstellen wird, obwohl es bisher den Anschein hat, dass bestehende AMD-Architekturen wahrscheinlich nicht in der Lage sein werden, ein ähnliches hohes Maß an Beschleunigung bereitzustellen NVIDIA Volta. Die hardwarebeschleunigte RTX-Raytracing-Technologie umfasst die noch nicht angekündigten Raytracing-Bder Volta-Architektur und wird voraussichtlich noch in diesem Herbst Gaming-Lösungen unterstützen.

Noch weiter in die Zukunft blickend, läuft das Aufkommen einer API zur Beschleunigung der Rasterung der allgemeinen Vielseitigkeit von GPUs zuwider, die immer mehr zu herkömmlichen Prozessoren werden, die für alle Arten von Berechnungen ausgelegt sind. Seit vielen Jahren wird darüber gesprochen, alle Blöcke, die feste Funktionen ausführen, aus der GPU zu entfernen, obwohl dies bisher nicht sehr gut funktioniert hat (Sie können sich nicht an die erfolgreichsten erinnern Intel Larrabee). Aber im Allgemeinen wird die größere Programmierbarkeit von GPUs das Mischen von Rasterung und Ablaufverfolgung noch einfacher machen, und die vollständige Ablaufverfolgung erfordert möglicherweise keine APIs mehr, um die Hardwarebeschleunigung zu unterstützen. Aber das ist der Zeit zu weit voraus, da wir uns vorerst mit DXR befassen.

DirectX Raytracing und die Unterstützung dieser API-Erweiterung durch Software- und Hardwareentwickler machen es praktisch, Raytracing in Verbindung mit der bekannten „Rasterisierungs“-API zu verwenden. Warum ist das notwendig, da moderne GPUs bereits fast jede Berechnung mit Compute-Shadern durchführen können und Entwickler mit ihrer Hilfe Raytracing durchführen können? Es geht darum, die Fähigkeiten des hardwarebeschleunigten Tracings auf spezialisierte Einheiten in der GPU zu standardisieren, was nicht passieren wird, wenn Sie Universal-Computing-Shader verwenden, die nicht dafür ausgelegt sind. Einige neue Hardwarefunktionen in modernen Grafikarchitekturen ermöglichen ein schnelleres Raytracing, und diese Funktionalität kann nicht mit der vorhandenen DirectX 12-API bereitgestellt werden.

Microsoft bleibt sich treu - wie der Rasterisierungsteil von DirectX definiert die neue API nicht genau, wie die Hardware funktionieren soll, sondern erlaubt GPU-Entwicklern, nur von Microsoft definierte standardisierte Funktionen zu beschleunigen. Hardware-Entwicklern steht es frei, die Ausführung von DXR-API-Befehlen so zu unterstützen, wie sie es möchten, Microsoft sagt ihnen nicht genau, wie GPUs es tun sollen. Microsoft führt DXR als Rechenaufgabe ein, die parallel zum „Rasterisierungs“-Teil ausgeführt werden kann, und DXR führt auch mehrere neue Arten von Shadern für die Ray-Verarbeitung sowie eine optimierte Struktur für die 3D-Szene ein, die für Raytracing geeignet ist.

Da die neue API für Softwareentwickler gedacht ist, bietet Microsoft ihnen eine Basisschicht mit DXR-Raytracing-Unterstützung, die alle vorhandene DirectX 12-fähige Hardware verwenden kann, und Sie können mit DXR auf vorhandenen GPUs experimentieren, obwohl dies nicht der Fall ist schnell genug sein, um in realen Anwendungen eingesetzt zu werden. Alle Hardware mit Unterstützung für DirectX 12 unterstützt auch Raytracing und einige einfache Effekte können sogar mit der bereits vorhandenen Basis von Grafikkarten in den Händen der Spieler durchgeführt werden. Wir werden bereits in diesem Jahr einige Effekte mit DXR in Spielen sehen, aber 2019 ist es absolut sicher – zumindest als eine frühe Demonstration der Fähigkeiten neuer Technologien.

Es ist wahrscheinlich, dass die Effizienz des Tracings auf verschiedenen GPUs zunächst sehr unterschiedlich sein wird. Lösungen ohne native Unterstützung, die eine grundlegende Unterstützung durch Compute-Shader verwenden, werden sehr langsam sein, und GPUs mit Hardwareunterstützung für das Tracing beschleunigen den Prozess sofort um ein Vielfaches - wie in den guten alten Zeiten der anfänglichen Entwicklung der Hardwareunterstützung für Rasterung. Im Laufe der Zeit werden immer mehr Tracing-Berechnungen optimaler und wesentlich effizienter durchgeführt, was jedoch neue grafische Lösungen erfordert. Die erste davon soll in den kommenden Monaten erscheinen.

Visueller Vergleich von Rasterung und Verfolgung

Lassen Sie uns versuchen, uns konkrete Beispiele dafür anzusehen, was Raytracing bieten kann. Tatsächlich wird es bereits jetzt in Spielen verwendet, jedoch in etwas anderen, primitiveren Formen. Insbesondere in Algorithmen, die den Bildschirmraum oder den Voxel-Cone-Tracing-Algorithmus verwenden, wenn die globale Beleuchtung berechnet wird, einschließlich des wohlbekannten Algorithmus Voxel-Umgebungsokklusion (VXAO) Nvidia-Unternehmen. Aber das ist immer noch kein vollwertiges Raytracing, sondern Hacks mit seiner Verwendung in der einen oder anderen Form während der Rasterung, und heute sprechen wir von vollwertigem Raytracing für alle Szenengeometrien.

Heutige GPUs sind bereits ziemlich leistungsfähig und können Lichtstrahlen mit hoher Geschwindigkeit mithilfe von Software wie z Arnold (Autodesk), V-Ray (Chaos Group) oder Renderman (Pixar), und viele Architekten und Designer verwenden bereits hardwarebeschleunigtes Raytracing, um schnell fotorealistische Renderings ihrer Produkte zu erstellen und so die Kosten des gesamten Entwicklungsprozesses zu senken. Seit mehr als einem Jahrzehnt ist Nvidia an der Entwicklung von hardwarebeschleunigten Raytracing-Techniken im professionellen Bereich beteiligt, und jetzt ist es an der Zeit, diese Fähigkeiten in Spiele zu bringen.

Um Spieleentwicklern bei der Einführung von Raytracing zu helfen, hat Nvidia eine bevorstehende Erweiterung angekündigt GameWorks-SDK Funktionen wie spezifische Rauschunterdrückungsalgorithmen, hochwertige globale Schattierung, Schatten von flächigen Lichtquellen ( Bereich Lichter) und einen Algorithmus zum Rendern hochwertiger Reflexionen.

Das Raytracing-Rendering in höchster Qualität erfordert eine große Anzahl von Samples (Strahlen, die pro Pixel berechnet werden), um eine hohe Qualität zu erreichen - von Hunderten bis zu Tausenden! Es hängt von der Komplexität der Szene ab, aber selbst ein paar Dutzend Strahlen sind für Echtzeitberechnungen nicht geeignet, da selbst GPUs der nahen Zukunft mit Hardwareunterstützung für das Tracing eine akzeptable Leistung mit viel weniger Strahlen pro Pixel liefern können - nur wenige stücke. Macht es Sinn sich zu ärgern?

Ja, wenn Sie das resultierende Bild weiterverarbeiten (und wir wollten von Rasterisierungs-Hacks wegkommen, aber es scheint, dass wir uns vorerst mit anderen abfinden müssen). Insbesondere die Implementierung von Tracing auf einer produktiven Lösung der Volta-Architektur ermöglicht Echtzeitleistung bei der Berechnung von 1-2 Samples pro Pixel bei obligatorischer Verwendung von Rauschunterdrückung. Bereits existierende Rauschunterdrückungsalgorithmen, die die Bildqualität nach dem Raytracing erheblich verbessern können, sind nur die ersten Entwicklungen, die im Gange sind.

Die Anforderungen an Echtzeit-Rauschunterdrückungsalgorithmen sind ziemlich hoch. Sie müssen in der Lage sein, sehr verrauschte Eingabebilder mit einer extrem geringen Anzahl von Strahlen pro Pixel (bis zu 1 Abtastung) zu verarbeiten und eine stabile Bewegungsqualität unter Verwendung von Informationen aus vorherigen Frames bereitzustellen und extrem schnell ausgeführt werden, ohne mehr 1 ms GPU-Zeit zu verbringen. Existierende Nvidia-Algorithmen können sehr gute Ergebnisse beim Rendern von Reflexionen, weichen Schatten und globalem Shading erzielen. Für jeden Effekt werden spezifische Algorithmen verwendet, die auch Informationen über die 3D-Szene verwenden.


Schatten wurden mit Raytracing mit einem Sample pro Pixel und aktiviertem Denoising gerendert


Zur Berechnung der globalen Schattierung (Ambient Occlusion) haben wir die Berechnung von zwei Strahlen pro Pixel mit Rauschunterdrückung verwendet


Und beim Rendern von Reflexionen wurde nur ein Strahl pro Pixel berechnet, auch hier ist eine Rauschunterdrückung unverzichtbar

Raytracing-Denoiser im Rahmen GameWorks-SDK ist ein Satz von Bibliotheken zur Verwendung mehrerer schneller Raytracing-Techniken, die eine Rauschunterdrückung verwenden, was sehr wichtig für das Tracing mit einer kleinen Anzahl von Strahlen pro Pixel ist, da das Ergebnis normalerweise extrem verrauscht ist. Die Algorithmen umfassen das Rendern weicher Schatten von flächigen Lichtquellen und Algorithmen zum Rendern von Reflexionen und globaler Schattierung der Umgebungsokklusion. Durch die Verwendung von Rauschunterdrückung können Sie mit einer kleinen Anzahl von Abtastungen pro Pixel eine hohe Geschwindigkeit erreichen, aber die Bildqualität bleibt ausgezeichnet – viel besser als die derzeit verwendeten Techniken mit der Simulation der Lichtausbreitung durch die Szene und der Nutzung des Bildschirmraums.

Lassen Sie uns über die Vorteile von Raytracing beim Rendern von Schatten sprechen. Mit Hilfe des Tracings können Sie physikalisch korrekte Schatten mit weichen Kanten zeichnen, viel realistischer als mit den raffiniertesten Techniken, die Shadow Maps und deren Filterung verwenden. Selbst bei sehr großen Lichtquellen werden realistische weiche Schatten ohne die beim Rastern auftretenden Fehler erzielt.


Raytracing-Schatten


Schatten, die mithilfe von Rasterung und Schattenkarten erhalten wurden

Sie können auch Algorithmen verwenden, die mit Shadow Maps nicht oder nur schwer zu simulieren sind, wie z. B. Schatten von Flächenlichtern. Und das Wichtigste: Alle möglichen visuellen Artefakte werden auf diese Weise vollständig eliminiert: flackernde Pixel an den Rändern, gezackte Linien usw. Ja, während der Entwicklung der Rasterung wurden bereits viele Hacks erfunden, um Artefakte zu unterdrücken, aber Raytracing macht alles mit ein natürlicher Weg.

Um globale Schattierung zu berechnen ( umgebende Okklusion) Ich würde auch gerne Raytracing verwenden, da es eine deutlich höhere Qualität liefert als alle bestehenden Techniken im Bildschirmraum (all diese SSAO, HBAO und sogar VXAO). Fast alle derzeit verwendeten Algorithmen fügen einfach Dunkelheit zu den Ecken hinzu, die sich in einem flachen Bild befinden, und simulieren nur die Lichtausbreitung, und die Verwendung von Nachzeichnen ermöglicht es Ihnen, dies auf physikalisch korrekte Weise zu tun.


Globale Schattierung, die mit Raytracing erhalten wird


Globale Schattierung durch Simulation eines Effekts unter Verwendung des Bildschirmraums

Darüber hinaus berücksichtigen alle Bildschirmraumtechniken nicht die Auswirkungen von geometrischen Objekten hinter der Bühne und hinter der Kamera und fügen völlig unterschiedlichen Oberflächen dieselbe Schattierung hinzu. In dem oben gezeigten Beispiel sind viele dieser Probleme deutlich sichtbar – es fällt auf, dass hier nur versucht wird, die Lichtausbreitung in einer 3D-Szene zu simulieren, durch Tracing aber ein deutlich fotorealistischerer Look erreicht wird.

Beim Rendern Reflexionen Das Nachzeichnen kann auch eine merklich bessere Qualität bieten als derzeit verwendete Methoden, die Bildschirmplatz verwenden, Off-Screen-Daten fehlen (sie sind physikalisch nicht in der Lage, in Reflexion zu rendern, was auf dem Bildschirm nicht sichtbar ist) und die Glanzlichter auf Reflexionen falsch rendern - von -für die Tatsache, dass die direkte Blickrichtung genutzt und nicht gespiegelt wird.


Raytracing-Reflexionen


Reflexionen von der Rasterung unter Verwendung des Bildschirmbereichs

Das Beispiel von Nvidia mag übertrieben und übermäßig explizit in Bezug auf die Probleme der Bildschirm-Raum-Reflexionstechniken sein, aber der Punkt ist klar – physikalisch korrekte Reflexionen können nur mit Raytracing gerendert werden. Andere Methoden zum Rendern von Reflexionen sind nicht universell und bieten eine schlechtere Qualität – beispielsweise funktionieren Reflexionen auf einer Ebene nur mit ebenen Oberflächen. Die Tracing-Methode hat aber auch ein Minus - bei einer geringen Anzahl von Samples ist eine Rauschunterdrückung erforderlich, da das Bild mit einem berechneten Strahl pro Pixel extrem verrauscht ist.

Es stellt sich heraus, dass die Rauschunterdrückung derzeit überhaupt immer verwendet werden sollte und die aktuelle Version bestimmter Techniken mit Nvidia-Rauschunterdrückung ihre Grenzen und Nachteile hat. Beispielsweise erzeugt eine Schattenrenderingtechnik verschlechterte überlappende Schatten von zwei schattenwerfenden Objekten mit einem großen Abstandsunterschied von der schattierten Oberfläche. Der Reflexionswiedergabealgorithmus verschlechtert die Qualität mit zunehmender Oberflächenrauhigkeit, und der globale Scerfordert möglicherweise nicht einen, sondern zwei oder sogar mehr gerenderte Strahlen pro Pixel, um feine Details wiederzugeben.

Dies sind jedoch nur die ersten Versionen von Techniken, die Rauschunterdrückungsfilter verwenden, die sowohl die Qualität als auch die Leistung verbessern werden. Darüber hinaus ist es in Zukunft möglich, die Geräuschreduzierung mithilfe von Technologien der künstlichen Intelligenz anzuwenden, die bereits in der enthalten sind Nvidia OptiX 5.0, wird aber noch nicht beim Tracing mit RTX verwendet. Es ist wahrscheinlich, dass in Zukunft eine einzige Rauschunterdrückung für alle Beleuchtungskomponenten auf einmal verwendet wird (anstelle von drei separaten, wie es jetzt der Fall ist), um Speicherkosten und Leistung zu reduzieren. Es hindert Sie auch nichts daran, einen hybriden Rendering-Ansatz zu verwenden, bei dem Elemente von Screen-Space-Algorithmen mit zusätzlichem Raytracing verwendet werden.

Neben der Verwendung von Raytracing in Echtzeit-Game-Engines kann die Leistung von GPU-beschleunigtem DXR auch bei der Inhaltserstellung genutzt werden. Zum Beispiel zur hochwertigen Berechnung von Beleuchtung, die dann in Lightmaps platziert wird, um vorgerenderte Szenen auf der Game-Engine zu erstellen, aber mit höherer Qualität usw. Außerdem kann man Raytracing gar nicht zum Rendern, sondern gar verwenden in Sound Engines für Virtual Reality ( Nvidia VRWorks Audio), in physikalischen Berechnungen oder sogar in Algorithmen der künstlichen Intelligenz.

Raytracing ist nützlich bei der Erstellung von Inhalten: Feinabstimmung der Eigenschaften von Materialien mit hochwertigem und schnellem Rendering, Hinzufügen und Anpassen der Eigenschaften von Lichtquellen, Debuggen von Rauschunterdrückungsalgorithmen usw. Sie können auch offline noch besser werden Rendern unter Verwendung der gleichen Strukturen mit relativ wenig Aufwand und Ressourcen wie die Echtzeit-Engine. Dies wurde beispielsweise bereits in durchgeführt Unreal-Engine 4- Nvidia selbst hat ein Experiment geschrieben Pfad-Tracer unmittelbar nach der Integration in die DXR-Fähigkeiten-Engine, die zwar noch keine ausreichende Qualität für ein vollwertiges Offline-Rendering liefert, aber eine solche Chance aufzeigt.

Wir sprechen nicht von der Möglichkeit der schnellen und qualitativ hochwertigen Erstellung von Lichtkarten - "Backen" Licht in spezielle Beleuchtungskarten (Lightmaps) für statische Szenenobjekte. Eine solche Engine kann denselben Code im Spiel und im Editor verwenden und verschiedene Arten von Lightmaps (2D, 3D) und Umgebungs-Cubemaps bereitstellen.


Dies ist nicht nur deshalb wichtig, weil Raytracing den Prozess der endgültigen Erzeugung von Lightmaps beschleunigt, sondern auch eine bessere Vorschau solcher Lightmaps bietet, sodass Sie die Position und Eigenschaften von Lichtquellen und Objekten in der Szene sofort und schnell ändern können immer das Ergebnis auf dem Bildschirm - fast das gleiche, was die endgültige Beleuchtung sein wird.

Schließlich bieten wir an, alle Vorteile von Raytracing in der Dynamik zu sehen. Nvidia hat eine ganze Sammlung von Technologie-Demos veröffentlicht, die die Vorteile von hardwarebeschleunigtem Raytracing mit Nvidia RTX-Technologie unter Verwendung der DXR-API zeigen (leider nur in Form eines Videos auf Youtube).

Die Demo zeigt deutlich die Vorteile des Renderns von nachgezeichneten Schatten, einschließlich weicher und farbiger Schatten, den Qualitätsunterschied der globalen Schattierung bei Verwendung von Rasterung und Bildschirmraum im Vergleich zu Raytracing, realistische Reflexionen auf verschiedenen Arten von Materialien mit zahlreichen Reflexionen, knifflige Rauschunterdrückungssysteme und die Verwendung von Ablaufverfolgung bei der Vorbereitung von vorgerenderten statischen Lichtkarten.

Demonstration der Möglichkeiten von Raytracing

Um die Fähigkeiten der DirectX-Raytracing-API und der Nvidia-RTX-Technologie zu demonstrieren, haben mehrere führende Entwickler von Game-Engines und Benchmarks ihre Technologie-Demos für die GDC 2018 veröffentlicht, die einige der Möglichkeiten neuer Technologien mit Raytracing zeigen: 4A Games, Electronic Arts, Epic Games, Remedy Entertainment, Unity und andere. Leider sind sie bisher nur in Form von Screenshots, Präsentationen und Videos auf Youtube verfügbar.

Während solche Echtzeit-Raytracing-Demos bisher entweder in sehr einfachen Szenen mit einfachen Effekten oder bei geringer Leistung gezeigt wurden, können die Fähigkeiten zukünftiger GPUs Raytracing auch unter Spielbedingungen mit akzeptabler Leistung real werden lassen. Die Entwickler von Epic Games und Remedy Entertainment glauben, dass DXR- und RTX-Funktionen den Spielen der Zukunft bessere Grafiken verleihen werden, und die Implementierung der grundlegenden Unterstützung für die neue API in ihren Engines hat sich als relativ einfach erwiesen.

DirectX Raytracing-Techdemo (Futuremark)

Zum Beispiel das Unternehmen, das allen 3D-Grafik-Enthusiasten für seine Testpakete bekannt ist Zukunftszeichen zeigte eine Technologiedemo von DXR, die auf der Grundlage einer speziell entwickelten Hybrid-Engine mit Raytracing für hochwertige Reflexionen in Echtzeit erstellt wurde.

Wir haben bereits gesagt, dass es bei Verwendung der heute üblichen Methoden, realistische und physikalisch korrekte Spiegelungen in einer 3D-Szene zu rendern, sehr schwierig ist. Bei der Erstellung von Algorithmen stoßen Entwickler jedoch auf viele Schwierigkeiten, die sich letztendlich mit anderen Methoden herumschlagen, aber sie bleiben weit entfernt von idealen Reflexionen. Die Entwickler von Futuremark haben in den vergangenen Monaten die Möglichkeit untersucht, DXR im Hybrid-Rendering einzusetzen, und dabei recht gute Ergebnisse erzielt.

Mithilfe von GPU-beschleunigtem Raytracing erhielten sie physikalisch korrekte Reflexionen für alle Objekte in der Szene, einschließlich dynamischer. Öffnen Sie die folgenden Bilder in voller Größe, da es sich um GIF-Animationen handelt, die den Unterschied zwischen dem Nachzeichnen und den bekannteren Methoden des Bildschirmbereichs zeigen:

Da ist ein Unterschied. Neben Unterschieden im Detail der Spiegelung können Sie mit der Nachzeichnung in DXR Spiegelungen von Objekten erhalten, die außerhalb des Bildschirmraums existieren, also nicht in die Sicht der Wildkamera einbezogen werden, wie in den vergleichenden Screenshots zu sehen ist, und die Reflexion selbst als Ganzes sieht viel glaubwürdiger aus. Hier ist ein weiteres Beispiel, vielleicht weniger offensichtlich, aber durchaus eine Idee:

Die Verwendung von Raytracing liefert genaue, perspektivisch korrigierte Reflexionen auf allen Oberflächen in der Szene in Echtzeit. Es ist deutlich zu sehen, dass die Verfolgung dem Realismus viel näher kommt als die vertrauteren Bildschirmreflexionen, die in den meisten modernen Spielen verwendet werden. Hier noch ein Vergleich:

Wenn Sie sich die mit DXR erhaltenen Reflexionen nicht ansehen, dann scheinen herkömmliche Methoden eine gute Qualität zu liefern, aber nur scheinbar. Außerdem – Reflexionen sind nicht nur für Spiegel mit hohem Reflexionsvermögen wichtig, sondern auch für alle anderen Oberflächen – werden sie alle realistischer, auch wenn es nicht sofort sichtbar ist.

In seiner Demo verwendet Futuremark Raytracing-Funktionen nur, um Probleme zu lösen, die mit herkömmlichen Methoden schwer zu bewältigen sind, wie z Objekte mit komplexer Form. Hier sind bessere Screenshots aus der DXR-Demo:




Auf modernen GPUs ist es bereits möglich, hybrides Rendering mit Rasterung für den größten Teil der Arbeit und einen relativ kleinen Beitrag der Nachzeichnung zu verwenden, um die Qualität von Schatten, Reflexionen und anderen Effekten zu verbessern, die mit herkömmlichen Rasterisierungstechniken schwer zu handhaben sind. Und das Futuremark-Demoprogramm ist nur ein Beispiel für diesen Ansatz, es funktioniert in Echtzeit auf einer vorhandenen GPU, wenn auch einer der leistungsstärksten.

Die Hauptsache ist, dass es ihnen laut den Entwicklern von Futuremark recht einfach war, die Raytracing-Unterstützung in die vorhandene DirectX-12-Engine aus dem Benchmark zu implementieren 3D Mark Time Spion Verwenden von Modellen und Texturen aus ihren Tests. Zusammen mit der Tech-Demo haben namhafte 3D-Test-Entwickler die Verwendung von DirectX-Raytracing-Fähigkeiten in ihrem nächsten 3DMark-Benchmark angekündigt, der gegen Ende dieses Jahres erscheinen soll.

Reflections Echtzeit-Raytracing-Demo (epische Spiele)

Gesellschaft epische Spiele zusammen mit ILMxLAB und NVIDIA zeigte auch ihre Version der Integration von Echtzeit-Raytracing-Funktionen in die Engine Unreal-Engine 4. Die Vorführung fand zur Eröffnung der GDC 2018 statt, wo die drei Unternehmen eine experimentelle filmische Demonstration zum Thema der Filmreihe präsentierten. "Krieg der Sterne" mit Charakteren aus der Serie "Das Erwachen der Macht" und "Der letzte Jedi".


Das Demoprogramm von Epic Games verwendet eine modifizierte Version von Unreal Engine 4 und Nvidia RTX-Technologie, deren Fähigkeiten durch die DirectX Raytracing-API offenbart werden. Um eine 3D-Szene zu erstellen, verwendeten die Entwickler echte Assets aus den Filmen Star Wars: Die letzten Jedi Mit Hauptmann Phasma in glänzender Rüstung und zwei Sturmtruppen mit einer Szene im Schiffsfahrstuhl Erste Bestellung.

Die betreffende Tech-Demo bietet dynamisch wechselnde Beleuchtung, die sich dabei anpassen lässt, sowie Raytracing-Effekte, darunter hochwertige weiche Schatten und fotorealistische Reflexionen – all dies wird in Echtzeit und in sehr hoher Qualität gerendert. Eine solche Bildqualität ist ohne den Einsatz von Raytracing einfach nicht verfügbar, und jetzt kann sie auch die bekannte Unreal Engine bieten, von der der Gründer und Präsident von Epic Games sehr beeindruckt war. Tim Sweeney.

Die Liste der fortschrittlichen Tech-Demo-Techniken umfasst: Flächenlichter, einschließlich solcher mit weichen Raytrace-Schatten, sowie das Rendern von Reflexionen und globaler Schattierung mithilfe von Tracing, Tracing-Ergebnis-Rauschunterdrückung aus dem Nvidia GameWorks-Paket und eine hochwertige Tiefenschärfe Effekt (nicht mit Tracing, aber auch hübsch).


Die Screenshots und das Video zeigen die sehr hohe Qualität all dieser Effekte und besonders beeindruckend sind die realistischen Reflexionen, die in der Szene sehr zahlreich sind. Alle Objekte werden in allen Objekten reflektiert, was sehr schwierig, wenn nicht gar unmöglich zu rendern ist, wenn es gerastert wird. Die Methode zum Rendern von Reflexionen im Bildschirmraum würde nur eine Nachahmung der Realität ergeben, in der alles, was nicht im Rahmen war, nicht reflektiert würde, und es ist sehr schwierig, den Rest qualitativ zu zeichnen.

Neben Reflexionen können Sie die weichsten Schatten bemerken, die mit ihren zerrissenen und / oder sehr scharfen Kanten nicht ins Auge fallen, wie dies bei der Verwendung von Schattenkarten der Fall ist. Nun, die Nachbearbeitung hier ist sehr hochwertig. Im Allgemeinen haben die Entwickler ihr Bestes gegeben, und diese Demonstration erwies sich vielleicht als eine der beeindruckendsten für hardwarebeschleunigtes Raytracing.

Für diese Demo hat Epic Games eng mit ILMxLAB-Künstlern und Nvidia-Ingenieuren zusammengearbeitet, um die Leistungsfähigkeit der Nvidia RTX-Technologie über die DXR-API zu demonstrieren. Unreal Engine-Demo läuft in Echtzeit auf einer Workstation DGX-Station Nvidia, das bis zu vier Grafikprozessoren der Volta-Architektur enthält. Durch die Kombination der Leistung der Unreal Engine, der DXR-Raytracing-Grafik-API und der Nvidia RTX-Technologie, die von den GPUs der Volta-Familie unterstützt wird, wird der filmische Realismus näher an die Echtzeit gebracht.

Neben der Technologiedemo hielten Experten von Epic Games eine lange Session auf der GDC ab "Filmische Beleuchtung in der Unreal Engine", gewidmet den neuen Funktionen ihres Motors. Und die Demo selbst wird allen gezeigt, mit der Möglichkeit, die Szene in verschiedenen Modi zu sehen, einschließlich Drahtmodell-Rendering. Es ist davon auszugehen, dass all das früher oder später auch in Spielen verfügbar sein wird, denn die Unreal Engine erfreut sich großer Beliebtheit. Epic Games versprach, noch in diesem Jahr Zugriff auf die DXR-API-Funktionen zu gewähren – wahrscheinlich näher am Herbst, wenn neue Nvidia-GPUs veröffentlicht werden.


Die Unterstützung von DirectX Raytracing und Nvidia RTX öffnet der Unreal Engine 4 den Weg zu einer neuen Klasse von Techniken und Algorithmen, die zuvor mit der Dominanz der Rasterung nicht verfügbar waren. In naher Zukunft werden Spieleentwickler in der Lage sein, einen hybriden Ansatz mit etwas hochwertigem Raytracing für einige Effekte und leistungsstarker Rasterung für den größten Teil der Arbeit zu verwenden. Das ist ein guter Anfang für die Zukunft, denn die Fähigkeit der GPU, Raytracing effizient zu beschleunigen, wird nur noch wachsen.

Pica Pica - Echtzeit-Raytracing-Experiment (Electronic Arts/SEED)

Der nächste Entwickler, der sich für Raytracing durch DXR interessierte, war das Studio SAMEN aus Electronic Arts, der ein spezielles Demoprogramm erstellt hat Pica Pica mit experimenteller Engine Halkyon, das wie die vorherigen Demos Hybrid-Rendering verwendet. Interessant an dieser Demo ist auch, dass darin eine prozedurale Welt ohne Vorkalkulationen erstellt wurde.

Warum haben sich die SEED-Forscher für Raytracing-Hybrid-Rendering entschieden? Empirisch fanden sie heraus, dass diese Methode im Vergleich zur Rasterung ein viel realistischeres Bild liefern kann, das dem vollwertigen Raytracing (Path Tracing) sehr nahe kommt, das die Ressourcen überfordert oder mit einer kleinen Zahl ein zu verrauschtes Bild liefert von berechneten Stichproben. All dies ist in den vergleichenden Screenshots deutlich zu sehen:


Vollständige Rückverfolgung


Hybrid-Rendering


Rasterung

In modernen Spielen werden verschiedene Hacks verwendet, um Reflexionen und Beleuchtung zu berechnen, einschließlich der Vorberechnung der Beleuchtung (zumindest des statischen Teils). All dies erfordert zusätzliche Arbeit von Level-Designern, die künstliche Lichter geschickt arrangieren, mit dem Vorab-Rendering der Beleuchtung beginnen, das dann in Lightmaps aufgezeichnet wird. Und die Verwendung von Raytracing bei Rendering-Aufgaben macht es möglich, diese zusätzliche Arbeit zu verweigern, da Sie mit Raytracing alles, was Sie brauchen, natürlich berechnen können, wie wir bereits oben gesagt haben.

Und da ein vollwertiges Tracing noch nicht möglich ist, verwendet die Halcyon-Engine einen Hybrid-Ansatz. Rasterisierung wird verwendet, um verzögerte Schattierung zu berechnen, entweder Rasterisierung oder Raytracing können verwendet werden, um direkte Schatten zu berechnen, falls erforderlich, Compute-Shader werden für direkte Beleuchtung verwendet, sowohl traditionelle als auch Tracing können auch für Reflexionen verwendet werden, Tracing wird immer für globale Beleuchtung verwendet, und Tracing wird immer für die globale Beleuchtung verwendet, und für die Simulation der globalen Okklusion kann entweder auf herkömmliche Rastermethoden wie SSAO zurückgegriffen werden oder auch Raytracing aktiviert werden. Für das Rendern transparenter Objekte wird nur Tracing verwendet und für die Nachbearbeitung Compute-Shader.


Insbesondere wird Raytracing bei der Berechnung von Schatten und Reflexionen eingesetzt, die wesentlich qualitativer und natürlicher sind als mit derzeit gängigen Techniken. Beispielsweise können solche Reflexionen in allgemeiner Form nicht mit Algorithmen zur Berechnung von Reflexionen während der Rasterung und Verwendung des Bildschirmraums durchgeführt werden:


Das Raytracing für Reflexionen erfolgt mit halber Auflösung, was 0,25 Strahlen/Pixel für Reflexionen und 0,25 Strahlen/Pixel für Schatten bedeutet. Und hier taucht das Problem einer geringen Anzahl berechneter Strahlen in Form eines extrem verrauschten Bildes mit Reflexionen auf, wenn ohne spezielle zusätzliche Verarbeitung das Ergebnis der Strahlverfolgung zu grob aussieht:


Daher wird das Bild nach dem Tracing auf besondere Weise mit mehreren sehr kniffligen Algorithmen (Details siehe Rede des Entwicklerteams auf der GDC 2018) bis zur vollen Rendering-Auflösung rekonstruiert, indem die empfangenen Daten gefiltert und Informationen aus vorherigen Frames hinzugefügt werden gesammelt und berücksichtigt. Das Ergebnis ist ein durchaus akzeptables Ergebnis mit realistischen Reflexionen, nicht viel anders als bei einer vollwertigen Pfadverfolgung:


Aber vielleicht liefern die üblichen Methoden im Bildschirmraum nicht das schlechteste Ergebnis, und wir brauchen einfach kein „teures“ Tracing? Sehen Sie sich selbst den visuellen Vergleich an: Die Bildschirm-Raum-Reflexionen sind links dargestellt, Hybrid-Raytracing ist in der Mitte dargestellt, und das Referenz-Rendering mit vollständigem Raytracing ist rechts dargestellt:


Der Unterschied ist offensichtlich. Die Screen-Space-Methode ist sehr ungefähr, unrealistisch und simuliert nur Spiegelungen, zwar stellenweise nicht schlecht, aber mit deutlichen Artefakten und fehlenden Auflösungsproblemen. Beim Tracing gibt es kein solches Problem, selbst mit der reduzierten Auflösung beim Rendern von Strahlen. Pica Pica verwendet auch Raytracing, um transparente und durchscheinende Objekte zu rendern. Das Demoprogramm berechnet die Lichtbrechung ohne Vorsortierung sowie die Lichtstreuung unter der Oberfläche:

Bislang ist die Engine noch nicht vollständig fertig gestellt und hat einen für den Fotorealismus wichtigen Nachteil – bisher kann sie keine Schatten von durchscheinenden Objekten zeichnen, aber das ist eine Frage der Zeit. Stattdessen verwendet die Demo einen globalen Beleuchtungsberechnungsalgorithmus, der keine Vorberechnungen verwendet und sowohl statische als auch dynamische Objekte unterstützt, wodurch der Bedarf an zusätzlicher Arbeit seitens der Künstler minimiert wird:


Globale Beleuchtung aus


Globale Beleuchtung an

Die globale Beleuchtung wirkt sich erheblich auf einige Objekte in der Szene aus und verleiht ihrer Beleuchtung Realismus. In der Demo können Sie zusätzlich globale Shading-Simulationstechniken verwenden, die zusätzliche Schatten erzeugen. Algorithmen im Bildschirmbereich werden ebenfalls unterstützt - Screen Space Ambient Occlusion (SSAO):


Mit etwas wie VXAO, das Nvidia bewirbt, könnte es noch besser sein, aber es sieht schon ziemlich gut aus. Ein noch besseres und realistischeres Bild wird jedoch mit einer vollständigen Berechnung der globalen Schattierung mithilfe von Raytracing erzielt. Schauen Sie sich die Vergleichsbilder an, der Unterschied ist eklatant:



Wenn SSAO nur den Anschein von globalen Schatten gibt und nur die offensichtlichsten Ecken schattiert, dann macht die vollwertige Verfolgung alles perfekt und gibt einen tiefen Schatten, wo es sein sollte, basierend auf den Gesetzen der Lichtausbreitung.

Bei Schatten durch direkte Strahlen von Lichtquellen ist bei harten Schatten beim Nachzeichnen alles ganz einfach - Strahlen werden in Richtung Lichtquellen geschossen und Treffer werden überprüft. Für weiche Schatten ist der Algorithmus ähnlich, aber das Ergebnis mit einem Sample pro Pixel ist zu „verrauscht“ und es muss zusätzlich gefiltert werden, danach wird das Bild realistischer:


Harte Schatten, weiche ungefilterte und weiche gefilterte Schatten

Die Entwickler von SEED weisen ausdrücklich darauf hin, dass ihre Forschung zum Hybrid-Rendering zwar noch in den Kinderschuhen steckt, dieser Ansatz jedoch zahlreiche fehlerhafte Hacks durch einen einheitlichen Raytracing-Ansatz ersetzt, der eine bessere Rendering-Qualität liefert. Es ist besonders wichtig, dass Softwareentwickler jetzt über eine einzige allgemein anerkannte API für Raytracing verfügen und nur noch eine weitere Verfeinerung der Algorithmen erforderlich ist, um sowohl die Qualität des Renderings zu verbessern als auch die Leistung zu optimieren, da Raytracing weiterhin ziemlich anspruchsvoll für die Hardware ist.

Im Moment berechnet die Pica-Pica-Demo nur 2,25 Strahlen pro Pixel (insgesamt, einschließlich aller Effekte), und das Ergebnis ist ein fotorealistisches Bild mit einer Qualität, die einer vollwertigen Durchzeichnung nahe kommt, wenn auch mit einigen Einschränkungen. Und jetzt - Wermutstropfen: Wie im Fall der Epic-Games-Demo müssen Sie zur Beschleunigung des Rendering-Prozesses immer noch die Fähigkeiten mehrerer Top-End-GPUs gleichzeitig und mit der Übertragung einer minimalen Datenmenge nutzen über einen relativ langsamen PCI-Express-Bus. Aber die Weiterentwicklung der Hardwarebeschleunigung auf GPUs sollte uns in Zukunft vor solchen Systemanforderungen bewahren.

Experimente mit DirectX Raytracing in Northlight (Remedy Entertainment)

Ein weiteres Demoprogramm zur Förderung von DXR und RTX, das auf der GDC 2018 vorgestellt wurde, waren Experimente mit der Spiel-Engine Northlight-Engine Finnisches Unternehmen Abhilfe Unterhaltung, der Öffentlichkeit bekannt für solche Spiele wie Max Payne, Alan Wake und Quantum Break. Die Northlight Engine wird intensiv von einem Unternehmen entwickelt, das für sein Interesse an den neuesten Grafiktechnologien bekannt ist. Kein Wunder also, dass sie sich auch für hardwarebeschleunigtes Raytracing interessieren.

Auf der GDC zeigte das Unternehmen Entwicklungen, an denen es mit Nvidia und Microsoft gearbeitet hat. Unter mehreren Entwicklern erhielt Remedy frühen Zugriff auf die Nvidia RTX- und DXR-API-Funktionen, die in einer speziellen Version der Northlight-Engine enthalten waren. Der leitende Grafikprogrammierer von Tatu Aalto hielt auf der Konferenz eine Rede "Experimente mit DirectX Raytracing in Remedys Northlight Engine", in dem er über die Merkmale des von ihnen verfolgten hybriden Ansatzes sprach.


Diese Demo verwendet traditionell Rasterisierung für Geschwindigkeit und Raytracing für einige der Effekte, die sonst schwer zu realisieren wären. Zu den Qualitätsverbesserungen gehören physikalisch korrekte weiche Schatten, hochwertige globale Schattierung und Beleuchtung sowie realistische Reflexionen. Das Video zeigt das Ergebnis der Northlight Engine mit allen aktivierten Effekten berechnet per Raytracing mit einer erhöhten Anzahl gerenderter Strahlen:

Um Experimente zur Implementierung von Raytracing durchzuführen, hat Remedy eine neue Szene erstellt, die nichts mit den Spielen des Unternehmens zu tun hat. Wie wir bereits gesagt haben, unterstützt die DXR-API zwei Ebenen von Beschleunigungsstrukturen: untere und obere. Die Idee ist, dass die Struktur der unteren Ebene zum Speichern der Geometrie dient und die obere Ebene die Strukturen der unteren Ebene enthält. Das heißt, jedes Polygonnetz ist eine Struktur auf niedrigerer Ebene, und jede obere Ebene enthält mehrere Strukturen auf niedrigerer Ebene mit möglichen geometrischen Transformationen (Rotationen usw.).


Die Struktur der unteren Ebene wird für die statischen Teile der Szene benötigt, die roten Quadrate im Diagramm sind die Grenzen des Baums der unteren Ebene. In der Szene gibt es beispielsweise vier Instanzen eines kleinen Stuhls (kleine rote Quadrate), die dieselbe Geometrie, aber ihre eigenen geometrischen Transformationen haben. Mittlere Quadrate sind kleine Sofas, große Quadrate sind große runde Sofas. Um eine Raytracing-Szene zu erstellen, müssen Sie diese untergeordneten Strukturen in die übergeordnete Struktur einfügen, wofür die DXR-API über eine spezielle Funktion verfügt, die mehrere Instanzen der untergeordneten Struktur mit Transformationen akzeptiert.

Der Umgang mit sich dynamisch ändernder Geometrie ist etwas schwierig, da der Low-Level-Builder nur statische Puffer akzeptiert. Aber auch hier ist eine Deformation möglich – mit Hilfe eines Computational Shaders, der Geometrie und Skinning-Matrizen nimmt und die bereits veränderte Geometrie schreibt. Dann können Sie mit der Berechnung der Strahlen beginnen.

Beginnen wir mit Ambient Occlusion, einem sichtbarkeitsbasierten Algorithmus, der einfach mit Raytracing durchgeführt werden kann. Das folgende Bild wird erhalten, indem vier Strahlen pro Pixel gerendert werden, die auf eine maximale Länge von vier Metern eingestellt sind, und das Ergebnis sieht definitiv besser aus als die SSAO-Methode, bei der nur der Bildschirmbereich verwendet wird.


Die linke Hälfte zeigt die herkömmliche Methode zur Berechnung der globalen Schattierung und die mit Raytracing auf der rechten Seite. Obwohl die SSAO-Technik einige Kanten anständig bewältigt, fehlen eindeutig geometrische Informationen über die Szene – solche Algorithmen wissen nicht, was sich außerhalb des Bildschirms oder hinter den für die Kamera sichtbaren Oberflächen befindet. Daher wird ein deutlich unvollkommenes Ergebnis erzielt, obwohl es deutlich besser ist als ohne jegliche Schattierung.

Leider ist die Leistung von Raytracing relativ schlecht und viel teurer als Screen-Space-Methoden. Laut Remedy dauert in ihrer Demo das Rendern eines Strahls pro Pixel für ein globales Shading mit einer maximalen Länge von 4 Metern bei Full-HD-Auflösung etwa 5 ms und die Leistung skaliert fast linear, sodass das Rendern von 16 Strahlen bereits unter 80 ms dauert. Natürlich mit ständiger Qualitätsverbesserung:


Diese Screenshots wurden mit dem üblichen Vollbild-Anti-Aliasing unter Berücksichtigung der Zeitkomponente (Daten aus vorherigen Frames) und ohne knifflige Filterung aufgenommen, wie dies in den meisten anderen auf der GDC gezeigten Demos der Fall ist. Mit einer cleveren Rauschunterdrückung können Sie bei 1-2 Strahlen pro Pixel eine akzeptable Qualität erzielen.

Zusätzlich zur globalen Schattierung verwendet die Remedy-Demo auch Raytracing, um reguläre Schatten zu rendern, die jetzt am häufigsten kaskadierende Schattenkarten verwenden, wenn sie gerastert werden ( Kaskadierte Schattenkarten - CSM). Die Entwickler stellen fest, dass es sehr einfach ist, den kaskadierenden Schattenkarten-Shader durch Code zu ersetzen, der Tracing verwendet, wenn die Engine Schatten von Richtungslichtern ausfüllt, bevor sie die Beleuchtung rendert, wodurch die berechneten Daten in denselben Puffer geschrieben werden.


In diesem Fall spricht der Qualitätsunterschied eindeutig für das Tracing (rechts dargestellt). Das Raytracing-Bild verwendet 8 Strahlen pro Pixel ohne zusätzliche Filterung, während die CSM-Technik 16 verwendet Prozentsatz engere Filterung (PCM) Proben mit einem speziellen Filter, der auf den Puffer angewendet wird. Allerdings muss man berücksichtigen, dass die Entwickler das CSM in diesem Fall offensichtlich nicht optimiert haben, denn man könnte die Auflösung der Schattenkarten und deren Filterung anpassen, um bessere Schatten zu bekommen, und das ist mit der Vorgabe nur ein Schatten Einstellungen ihres Motors.

Aber selbst mit diesem Rabatt ist der Unterschied offensichtlich - mit Raytracing sind die Schatten viel realistischer, sie haben glatte Kanten ohne gezackte Kanten, eine bessere Unschärfe an den Rändern und selbst kleine Details (Stuhlbeine) werfen einen physikalisch korrekten Schatten. Das Ergebnis sind dezente Schatten mit weichen und harten Schattenkanten genau dort, wo sie sein sollten. Sie können auch problemlos Schatten von Flächenlichtern zeichnen, was mit der Rasterung äußerst schwierig ist.

In Bezug auf die Leistung rendert diese Demo einen einzelnen Beam für Full-HD-Auflösung in weniger als 4 ms, was etwas schneller ist als Global Shading, obwohl die Beams länger sind. Das Einfügen von Raytracing in eine bestehende DX12-Shadow-Rendering-Engine würde mehrere Tage Programmierarbeit erfordern, aber das Ergebnis lohnt sich, wenn die Leistung am Ende ausreicht.

Es scheint, dass Remedy seiner Engine fast alle Effekte hinzugefügt hat, die mit Tracing in der Anfangsphase der DXR-Entwicklung möglich sind. Einschließlich Reflexionen, die mit Raytracing gerendert wurden. Allerdings gibt es keine so offensichtliche Verwendung in Form von rein spiegelnden Oberflächen, sondern eher einen subtileren Ansatz mit Reflexionen auf allen Objekten, aber weniger offensichtlich. Der folgende Screenshot zeigt einen Vergleich von Techniken, die Raytracing (rechts) und Bildschirmraum (links) verwenden:


Das verfolgte Bild wurde mit nur einem Reflexionsstrahl pro Pixel ohne Filterung erhalten. Reflexionen im Bildschirmbereich sind deutlich weniger realistisch und berücksichtigen nur Objekte, die für die Hauptkamera sichtbar sind, und Raytracing ermöglicht es Ihnen, sie auch anzuzeigen, obwohl es seine Nachteile in Form von erheblichem Pixelrauschen hat. Aber es ist im Prinzip machbar, wie andere Demos zeigen, und die Version des finnischen Unternehmens wendet einfach noch keine Rauschunterdrückung an, außer der Verwendung von Pixelwerten aus vorherigen Frames im Vollbild-Anti-Aliasing.

Die Northlight Engine verwendet bereits die Berechnung der globalen Beleuchtung ( GI) - insbesondere im Spiel Quantum Break, und dieser Effekt ist standardmäßig in der Engine aktiviert. Zur Berechnung des GI werden Voxel mit einer Größe von etwa 25 cm verwendet, die mit dem Ergebnis der SSAO-Global-Shading-Technik unter Verwendung des Bildschirmraums kombiniert werden. Als Experiment hat Remedy SSAO durch einen ähnlichen Effekt mit Raytracing ersetzt und das Ergebnis ist besser.


Es ist zu sehen, dass die Oberflächen nicht so schattiert sind, wie sie sein sollten, und dass etwas mit ihnen eindeutig nicht stimmt. Das Problem wird gelöst, indem die Methode zur Verwendung von GI-Volumendaten modifiziert wird, mit deren Hilfe die meisten Artefakte entfernt werden:


Warum muss man überhaupt globale Illumination / Shading berechnen und kann man auf diesen extrem ressourcenintensiven Schritt verzichten? Sehen Sie sich ein anschauliches Beispiel an, wie das Ergebnis der Berechnung der direkten Beleuchtung allein aussieht:


Es sieht aus wie Doom mit seiner konstanten Dunkelheit und den harten Schablonenschatten. Aber im nächsten Screenshot wird indirekte Beleuchtung zur direkten Beleuchtung hinzugefügt – das heißt Lichtstrahlen, die von anderen Objekten in der Szene reflektiert werden:


Es ist viel besser geworden, trotz des Rauschens hat die Szene an Volumen gewonnen und sieht nicht so aus, als ob sich alle Objekte im Weltraum befinden, wenn die einzige helle Lichtquelle (die Sonne) vorhanden ist. Und so sieht das endgültige Bild aus, mit überlagerten Farbinformationen, vollständiger Beleuchtung und Nachbearbeitung:


Spiegelungen und Schattierungen in der Szene sehen unserer Meinung nach sehr realistisch aus. Insbesondere reflektiert die Lampe alle Objekte, einschließlich eines hellen Fensters, das für die Hauptkamera unsichtbar ist. Und der Becher auf der rechten Seite spiegelt seinen eigenen Stift wider, was Sie mit der Rasterung ohne einige clevere Hacks nicht erreichen können. Das einzige offensichtliche Tracing-Problem hier ist viel Pixelrauschen, an dessen Beseitigung Remedy noch nicht viel getan hat. Aber der gleiche Algorithmus von Nvidia GameWorks könnte viel helfen, ganz zu schweigen von der Rauschunterdrückung durch künstliche Intelligenz.

Natürlich wäre es sehr schön, wo immer möglich Raytracing zu verwenden, aber die optimale Lösung für Hybrid-Rendering ist die Optimierung mit Schattenkarten, die in der Remedy-Demo für die meisten Lichter außer der Sonne verwendet werden. Und so werden sie es zunächst in jeder Anwendung per Raytracing tun, weil der direkte Einsatz überall zu teuer wird und dies noch nicht in Echtzeit möglich ist, selbst wenn mehrere GPUs gleichzeitig verwendet werden.

Wichtig ist, dass die Integration der DXR- und RTX-Unterstützung in die Northlight-Engine ziemlich schnell und schmerzlos war. Die finnischen Entwickler waren erstaunt, wie schnell sie in der Lage waren, verbesserte Beleuchtung, Schattierung und Reflexionen mithilfe von Raytracing zu prototypisieren, und das alles mit viel besserer Qualität als die üblichen Rasterisierungs-Hacks. Während sich die gezeigten Technologien derzeit in der frühen Entwicklung befinden und noch lange nicht in Spiele integriert sind, ist dies ein guter Anfang für ihre zukünftige Implementierung.

Echtzeit-Raytracing in Metro Exodus (4A-Spiele)

Es ist wahrscheinlich, dass wir in den kommenden Jahren mehr als ein Spiel sehen werden, das Hybrid-Rendering mit Raytracing verwendet, um einen Teil der Effekte zu rendern. Insbesondere sollte das erste (na ja, oder zumindest eines der ersten) das Spiel sein Metro-Exodus, in dem DXR-Raytracing mit Nvidia RTX-Technologie verwendet wird, um globale Beleuchtung und Schattierung zu berechnen.

Es wird davon ausgegangen, dass diese Berechnungsmethode GI werden im Spiel als Alternative zu bekannteren Algorithmen verfügbar sein SSAO und IBL(bildbasierte Beleuchtung, Beleuchtung basierend auf der Textur der Umgebung). Natürlich ist dies bisher eine sehr begrenzte Verwendung von Tracing, aber die Qualität der globalen Beleuchtung/Schattierung mit Raytrace ist viel besser als gleichmäßig bei VXAO, nicht zu erwähnen SSAO. Hier ist ein visueller Videovergleich von Bildschirmraummethoden mit Durchpausen, gefilmt von unseren deutschen Kollegen vom Bildschirm des Ausstellungssystems (daher entschuldigen wir uns im Voraus für die Qualität):

Die Texturen wurden während der Demo ausgeschaltet, damit der Unterschied in der Beleuchtung der Szene deutlich sichtbar war. Und das ist wahr, Bildschirmrastermethoden ergeben ein flaches Bild und simulieren nur aus der Ferne Schattierungen in den Ecken zwischen den Flächen von Objekten, und Raytracing liefert physikalisch korrekte globale Schattierungen und Beleuchtungen mit dunklen Schatten genau dort, wo sie sein sollten - zum Beispiel nach innen schauen das Fass beim Betreten des Hauses durch Risse - mit SSAO schattiert es innen überhaupt nicht, und mit Raytracing ist es in seiner Tiefe dunkel, wie es sein sollte.

Hier stellt sich nur eine Frage: Wenn das Video eine statische Szene ohne dynamische Objekte und deren Einfluss auf die globale Beleuchtung zeigt, was hindert Sie dann daran, alles offline vorzuberechnen und diese Daten in statische Lichtkarten einzugeben? Uns scheint, dass bei der dynamischen Berechnung der globalen Illumination in Echtzeit die Szene zur Demonstration der Möglichkeiten irgendwie lebendiger gewählt werden musste, zumindest bei bewegten Lichtquellen, geschweige denn bewegten Objekten. Ansonsten bleibt zu fragen, warum die Spieler überhaupt nicht verstanden haben, was genau ihnen gezeigt wurde und warum dies mit dem Einsatz von Rasterisierung derzeit nicht möglich ist.

Schlussfolgerungen

Raytracing liefert eine viel bessere Bildqualität als Rasterung und wird seit langem dort eingesetzt, wo es möglich ist - in der Filmindustrie, Werbung, Design usw. Aber lange Zeit war es aufgrund seiner riesigen Ressourcen einfach nicht für das Echtzeit-Rendering geeignet Intensität - Schließlich müssen für jedes Pixel mehrere Strahlen berechnet werden, die von Objekten in der Szene reflektiert und in ihnen gebrochen werden. Beim Offline-Rendering, das keine schnellen Ergebnisse erfordert, war dieser Ansatz immer von höchster Qualität, und bei der Echtzeitgrafik mussten wir uns mit der Rasterung begnügen – der einfachsten und schnellsten Möglichkeit, eine 3D-Szene auf einen 2D-Bildschirm zu projizieren. Natürlich hat die hohe Leistung der Rasterung den Nachteil, dass nur ungefähre Berechnungen der Farbe der Pixel in der Szene erfolgen, die viele Faktoren nicht berücksichtigen: Reflexionen von Lichtstrahlen, einige Materialeigenschaften usw. Rasterung, auch mit a ein Haufen kniffliger Hacks, reproduziert die Szene nur ungefähr, und selbst die komplexesten Pixel- und Compute-Shader liefern nicht die volle Raytracing-Qualität, nur weil sie funktionieren.

Die Ankündigung der DXR-API und der Nvidia RTX-Technologie gab Entwicklern die Möglichkeit, mit der Erforschung von Algorithmen zu beginnen, die Hochleistungs-Raytracing verwenden, die vielleicht bedeutendste Änderung bei Echtzeitgrafiken seit der Einführung programmierbarer Shader. Interessierte Entwickler haben der Öffentlichkeit bereits einige sehr beeindruckende technologische Demonstrationen gezeigt, bei denen nur eine kleine Anzahl von Samples pro Pixel beim Tracing verwendet wurde, und die Zukunft der Spiele liegt in ihren Händen. Und die Hände von GPU-Herstellern, die neue Lösungen veröffentlichen sollten, die Hardware-Tracing unterstützen, werden in mehreren Spieleprojekten Ende dieses Jahres und Anfang nächsten Jahres erwartet.

Natürlich werden die ersten Versuche, das Tracing zu verwenden, hybrid und in Bezug auf die Anzahl und Qualität der Effekte stark begrenzt sein, und ein vollwertiges Tracing wird mehr als ein Dutzend Jahre warten müssen. Alle gezeigten Demoprogramme verwenden 1-2 Strahlen pro Pixel oder noch weniger, während es in professionellen Anwendungen Hunderte davon gibt! Und um die Qualität von Offline-Renderings in Echtzeit zu erhalten, müssen Sie sehr lange warten. Aber jetzt ist es an der Zeit, an der Implementierung von Tracing in bestehende Engines zu arbeiten, und wer als erster die Fähigkeiten von DXR beherrscht, kann sich in Zukunft einige Vorteile verschaffen. Darüber hinaus kann Raytracing die Entwicklung virtueller Welten erleichtern, da es viele der kleinen Aufgaben der manuellen Verfeinerung von Schatten, Lightmaps und Reflexionen eliminiert, die mit nicht idealen Rasterisierungsalgorithmen erledigt werden müssen. Bereits jetzt kann hardwarebeschleunigtes Tracing im Entwicklungsprozess selbst verwendet werden, um Dinge wie das Vorab-Rendering von Lightmaps, Reflexionen und statischen Shadow-Maps zu beschleunigen.

Es gibt viele Möglichkeiten, das Hybrid-Rendering zu optimieren, und eines der beeindruckendsten Merkmale in den oben gezeigten Beispielen scheint die Effizienz der Rauschunterdrückung zu sein, die beim Raytracing mit einer geringen Anzahl von Samples pro Pixel äußerst wichtig ist - das ist bekannt Jeder, der jemals die Arbeit von Offline-Tracern gesehen hat, die das Bild allmählich zeichnen und ganz am Anfang extrem laut sind. Der Ansatz mit einer geringen Anzahl berechneter Strahlen und zusätzlicher Rauschunterdrückung ermöglicht es Ihnen, eine akzeptable Endqualität in einem Bruchteil der Zeit zu erhalten, die für eine vollwertige Szenenverfolgung erforderlich ist. Und das, obwohl die Möglichkeiten der künstlichen Intelligenz bei der Geräuschreduzierung noch nicht genutzt wurden, obwohl dies möglich ist.

Die globalen Möglichkeiten von Raytracing sollten nicht nur anhand von voreilig veröffentlichten Demoprogrammen beurteilt werden. Sie rücken absichtlich die Haupteffekte in den Vordergrund, da es sich um technologische Demos handelt, die für den alleinigen Zweck erstellt wurden. Das Raytrace-Bild wird im Allgemeinen viel realistischer, aber die Benutzer verstehen nicht immer, wo genau sie hinschauen müssen, auch wenn sie das Gefühl haben, dass es im Allgemeinen glaubwürdiger geworden ist. Vor allem, wenn der Unterschied zunächst nicht so groß ist und die Massen bereit sind, Artefakte in Kauf zu nehmen, die den Algorithmen zur Berechnung von Reflexionen und globaler Schattierung im Bildschirmraum sowie anderen Rasterisierungs-Hacks innewohnen.

Aber mit physikalisch korrekter globaler Beleuchtung, Schattierung und Reflexionen, die mit Raytracing berechnet werden, wird das gerenderte Bild realistischer, auch ohne das Vorhandensein von spektakulären Spiegeln und anderen deutlich reflektierenden Oberflächen. Moderne Spiele verwenden fast immer physikbasiertes Rendering, bei dem Materialien Rauheits- und Reflexionseigenschaften sowie Umgebungs-Cubemaps aufweisen, sodass Reflexionen immer vorhanden sind, auch wenn sie mit bloßem Auge nicht sichtbar sind. In einem solchen Spiel können Sie die Umgebungs-Cubemaps recht schnell durch Tracing ersetzen und bieten Besitzern von Hochleistungssystemen diese Möglichkeit. Nachgezeichnete Schatten sehen auch besser aus und lösen die grundlegenden Probleme der Schattenabbildung, obwohl einige von ihnen in kniffligen fortgeschrittenen Algorithmen gelöst werden, wie z Nvidia Hybrid Frustum Traced Shadows (HFTS), die in gewisser Weise auch die Ablaufverfolgung verwenden, aber ein einheitlicher Ansatz ist immer noch am besten. Und das Rendern sehr weicher Schatten von Flächenlichtern kann in den meisten Fällen perfekte superrealistische Schatten erzeugen.

Die Hauptschwierigkeit beim Nachzeichnen besteht darin, dass nicht alle Erstimplementierungen sofort merklich besser aussehen als knifflige Screen-Space-Methoden, aber wir können mit Sicherheit sagen, dass dies genau die Richtung ist, in die Sie sich bewegen müssen, um Fotorealismus zu erzielen. Denn Algorithmen im Bildschirmbereich haben grundsätzliche Grenzen, die nicht übersprungen werden können. Das Bild selbst der vorhandenen Demoprogramme ist in vielerlei Hinsicht recht gut, auch wenn es von mehreren leistungsstarken GPUs gerendert wird und geschickte Rauschunterdrückung verwendet. Bisher müssen wir eine kleine Anzahl von Strahlen pro Pixel verwenden und Rauschen unterdrücken, aber in Zukunft wird dies mit Hilfe primitiver umfangreicher Entwicklung gelöst. Schließlich sind dies erst die allerersten Tests mit Echtzeit-Raytracing, zukünftig wird die Bildqualität mit der Leistung steigen.

In den nächsten Jahren werden wir vorerst in der Lage sein, ein oder zwei neue Techniken einzubauen, die Raytracing zusätzlich zur Rasterung verwenden oder nur einen Teil ihrer Arbeit ersetzen. Dies geschieht immer zu Beginn des Lebensweges neuer Technologien, wenn es möglich ist, neue Algorithmen zu deaktivieren, die für den durchschnittlichen Gaming-PC zu schwerfällig sind. Aber wenn Sie sich nur auf sie konzentrieren, dann wird es einfach keinen Fortschritt geben. Und die Unterstützung für Hardware-Tracing von Nvidia ist wichtig, weil sie wissen, wie man Entwicklern hilft, neue Technologien zu implementieren. Und es besteht Zuversicht, dass Metro Exodus bei weitem nicht das einzige Spiel ist, in dem Nvidia das Tracing fördert, da sie mit Spieleentwicklern an mehreren Projekten gleichzeitig zusammenarbeiten. Berüchtigt Tim Sweeney von Epic Games prognostiziert, dass die GPU in zwei Jahren bereits genügend Leistung für den massiven Einsatz von Raytracing in Spielen erreichen wird, und Sie können es glauben.

Entwickler, die Microsoft am nächsten stehen, haben vor fast einem Jahr damit begonnen, DXR zu erkunden, und dies ist erst der Anfang der neuen API. Darüber hinaus gibt es auf dem Markt einfach keine verfügbaren Grafiklösungen, die hardwarebeschleunigtes Tracing unterstützen. Die Ankündigung von DXR soll Hardware- und Softwareentwickler dazu bringen, Raytracing zu erforschen und zu optimieren und die frühe Phase der Einführung neuer Technologien in Spiele einzuleiten. Interessierte Entwickler haben bereits damit begonnen, mit DXR und modernen GPUs zu experimentieren, und Unternehmen wie Epic Games, Futuremark, DICE, Unity und Electronic Arts haben sogar Pläne angekündigt, DXR-Fähigkeiten in zukünftigen Versionen von Spiele-Engines und Spielen einzusetzen.

Es ist wahrscheinlich, dass Enthusiasten (das ist unser Los) auf erschwingliche hardwarebeschleunigte GPUs warten müssen, um auch nur die allerersten Raytracing-Effekte zu sehen, da die Basisunterstützung durch Compute-Shader selbst für einfache Algorithmen zu langsam sein kann. Spiele, die DXR mit Bedacht einsetzen, benötigen Hardwareunterstützung für das Tracing, die zunächst auf Nvidia Volta beschränkt sein wird, sich aber mit der Zeit zu verbessern droht. Es ist auch möglich, dass relativ einfache Spiele mit stilisierter Grafik erscheinen, die ausschließlich Raytracing verwenden.

Ein weiterer wichtiger Punkt ist, dass die aktuelle Generation von Spielkonsolen keine Unterstützung für hardwarebeschleunigtes Raytracing hat, Microsoft hat sich nicht zu DXR in Xbox One geäußert. Höchstwahrscheinlich wird es einfach keine solche Unterstützung geben, was zu einer weiteren Bremse für die aktive Nutzung von Raytracing-Funktionen in Spielen werden kann. Obwohl die Xbox One DirectX 12 fast vollständig unterstützt, enthält es keine Hardwareblöcke zum Beschleunigen des Tracings, sodass die Chancen gut stehen, dass es zumindest bis zur nächsten Konsolengeneration auf ein paar Raytracing-Effekte beschränkt bleibt Mehrere Spieleprojekte, die von Nvidia unterstützt werden und für seine RTX-Technologie werben. Ich würde mich gerne irren, denn Computergrafik-Enthusiasten haben auf solche globalen Verbesserungen beim Echtzeit-Rendering gewartet.

Kürzlich bin ich im Internet auf einen Raytracer auf der Visitenkarte von Paul Huckbert gestoßen. Für diejenigen, die es nicht wissen, dies ist ein sehr berühmtes Problem, das ursprünglich von Paul Heckbert am 4. Mai 1984 auf comp.graphics vorgeschlagen wurde. Seine Essenz besteht darin, eine Demonstration der Ray-Casting-Methode zu schreiben, die ... auf eine Visitenkarte passen würde (lesen Sie mehr darüber im Abschnitt Ray Tracing des Buches Graphic Jewels IV)!

Die Version von Andrew Kensler ist eine der erstaunlichsten und schönsten Implementierungen dieser Aufgabe, die ich je gesehen habe. Aus Neugier habe ich mich entschieden, mich damit zu beschäftigen. In diesem Artikel werde ich alles schreiben, was ich selbst verstehen konnte.

Rückseite der Visitenkarte

So sieht der Code selbst aus:

#enthalten // Karte > aek.ppm #include #enthalten typedef int i;typedef float f;struct v( f x,y,z;v operator+(v r)(return v(x+r.x ,y+r.y,z+r.z);)v operator*(f r)(return v( x*r,y*r,z*r);)f operator%(v r)(return x*r.x+y*r.y+z*r.z;)v()()v operator^(v r) (return v(y*r.z-z*r.y,z*r.x-x*r.z,x*r. y-y*r.x);)v(f a,f b,f c)(x=a;y=b;z=c ;)v operator!()(return*this*(1/sqrt(*this%* this));));i G=(247570,280596,280600, 249748,18578,18577,231184,16,16) ;f R()( return(f)rand()/RAND_MAX;)i T(v o,v d,f &t,v&n)(t=1e9;i m=0;f p=-o.z/d.z;if(.01 0)(f s=-b-sqrt(q);if(s .01)t=s,n=!(p+d*t),m=2;))return m;)v S(v o,v d)(f t ;v n;i m=T(o,d,t, n);if(!m)return v(.7, .6,1)*pow(1-d.z,4);v h=o+d*t,l=!(v(9+R(),9 +R(),16)+h*-1),r=d+n*(n%d*-2);f b=l% n;if(b<0||T(h,l,t,n))b=0;f p=pow(l%r*(b >0),99);if(m&1)(h=h*.2;return((i)(ceil(h.x)+ceil(h.y))&1?v(3,1,1):v(3,3 ,3))*(b *.2+.1);)return v(p,p,p)+S(h,r)*.5;)i main()(printf("P6 512 512 255 " );v g=!v (-6,-16,0),a=!(v(0,0,1)^g)*.002,b=!(g^a)*.002,c=( a+b)*-256+g;for(i y=512;y--;) for(i x=512;x--;)(v p(13,13,13);for(i r =64;r- -;)(v t=a*(R()-.5)*99+b*(R()-.5)* 99;p=S(v(17,16,8)+t,!(t *-1+(a*(R()+x)+b *(y+R())+c)*16))*3.5+p;)printf("%c%c%c" ,(i )p.x,(i)p.y,(i)p.z);))

Der obige Code sieht … einschüchternd aus, aber er lässt sich ohne Probleme kompilieren und ausführen! Sie können es als card.cpp auf Ihrem Desktop speichern, eine Konsole öffnen und Folgendes eingeben:

C++ -O3 -o card card.cpp ./card > card.ppm

Nach 27 Sekunden erscheint das folgende Bild auf dem Bildschirm:

Funktionen des Visitenkarten-Raytracers

Die Möglichkeiten sind einfach unglaublich!

  • eine Welt, die aus streng organisierten Sphären besteht;
  • strukturierter Boden;
  • Himmel mit Farbverlauf;
  • weiche Schatten;
  • OMG, Schärfentiefe! Sie machen Witze?!

Und das alles auf einer Seite der Visitenkarte! Mal sehen, wie es funktioniert.

Vektorklasse

Betrachten Sie den ersten Teil des Codes:

#enthalten // Karte > aek.ppm #include #enthalten typedef int i;typedef float f;struct v( f x,y,z;v operator+(v r)(return v(x+r.x ,y+r.y,z+r.z);)v operator*(f r)(return v( x*r,y*r,z*r);)f operator%(v r)(return x*r.x+y*r.y+z*r.z;)v()()v operator^(v r) (return v(y*r.z-z*r.y,z*r.x-x*r.z,x*r. y-y*r.x);)v(f a,f b,f c)(x=a;y=b;z=c ;)v operator!()(return*this*(1/sqrt(*this%* this));));

Der Trick besteht darin, die Typschlüsselwörter int und float mit einem typedef auf i und f zu verkürzen. Ein weiterer Schritt, der verwendet werden kann, um die Codemenge zu reduzieren, ist die v-Klasse, die nicht nur als Vektor, sondern auch zur Verarbeitung von Pixeln verwendet wird.

#enthalten // Karte > aek.ppm #include #enthalten typedef int ich; // Platz sparen durch Kürzen von int zu i typedef float f; // Noch mehr Platz sparen mit f statt Float // Vektorklasse mit Konstruktor und Operatoren struct v( f x,y,z; // Drei Koordinaten des Vektors v operator+(v r)(return v(x+r.x,y+r.y ,z +r.z);) // Summe der Vektoren v operator*(f r)(return v(x*r,y*r,z*r);) // Skalierung der Vektoren f operator%(v r)(return x *r.x +y*r.y+z*r.z;) // Skalarprodukt von Vektoren v()() // Leerer Konstruktor v operator^(v r)(return v(y*r.z-z*r.y,z*r.x -x* r.z,x*r.y-y*r.x);) // Vektorprodukt v(f a,f b,f c)(x=a;y=b;z=c;) // Konstruktor v Operator!()( return * this*(1 /sqrt(*this%*this));) // Vektornormalisierung );

Rand() und Weltgenerationsdaten

i G=(247570,280596,280600, 249748,18578,18577,231184,16,16);f R()( return(f)rand()/RAND_MAX;)

Der folgende Code spart auch viel Platz, indem er eine R-Funktion deklariert, die einen zufälligen Gleitkommawert zwischen 0 und 1 zurückgibt. Dies ist nützlich für stochastisches Sampling, das für Unschärfe und weiche Schatten verwendet wird.

Das Array G enthält die Position der Kugeln in der Welt, kodiert durch ganze Zahlen. Die Menge aller Zahlen ist ein Bitvektor aus 9 Zeilen und 19 Spalten.

Hier ist der obige Code, aber formatiert und kommentiert:

// Eine Menge von Kugelpositionen, die die Welt beschreiben // Alle diese Zahlen sind im Wesentlichen ein Bitvektor i G=(247570,280596,280600,249748,18578,18577,231184,16,16); // Zufallszahlengenerator, der eine Fließkommazahl im Bereich 0-1 zurückgibt f R()(return(f)rand()/RAND_MAX;)

Hauptmethode

i main()(printf("P6 512 512 255 ");v g=!v (-6,-16.0),a=!(v(0,0,1)^g)*.002,b= !( g^a)*.002,c=(a+b)*-256+g;for(i y=512;y--;) for(i x=512;x--;)(v p(13, 13.13) ;for(i r =64;r--;)(v t=a*(R()-.5)*99+b*(R()-.5)* 99;p=S(v (17,16 ,8)+t,!(t*-1+(a*(R()+x)+b *(y+R())+c)*16))*3.5+p;) printf("% c%c%c" ,(i)p.x,(i)p.y,(i)p.z);))

Das Hauptverfahren verwendet das einfache bekannte textbasierte PPM-Bildformat. Das Bild besteht aus einer Überschrift wie P6 [Breite] [Höhe] [Max. Wert], gefolgt vom RGB-Wert jedes Pixels.

Für jedes Pixel im Bild tastet (S) das Programm die Farbe von 64 Strahlen ab, akkumuliert das Ergebnis und gibt es an stdout aus.

Außerdem ändert dieser Code leicht jede Koordinate des Anfangs des Strahls und seine Richtung. Dies geschieht, um den Effekt der Schärfentiefe zu erzeugen.

Hier ist der obige Code, aber formatiert und kommentiert:

// Hauptfunktion. Gibt ein Bild aus. // Die Benutzung des Programms ist einfach: ./card > erk.ppm i main()( printf("P6 512 512 255 "); // PPM-Header // Der "!"-Operator normalisiert den Vektor v g=!v(- 6,- 16,0), // Kamerarichtung a=!(v(0,0,1)^g)*.002, // Kamerahöhenvektor... b=!(g^a)*.002 , // Der rechte Vektor, der durch das Kreuzprodukt erhalten wird c=(a+b)*-256+g; // WTF? for(i y=512;y--;) // Für jede Spalte for(i x=512; x--;)( // Für jedes Pixel in der Zeile // Verwenden Sie eine Vektorklasse, um die Farbe in RGB zu speichern v p(13, 13,13); // Die Standardpixelfarbe ist fast schwarz // Wirf 64 Strahlen von jedem Pixel for(i r=64;r--;)( // Tiefenschärfeeffekt links/rechts und oben/unten leicht ändern) v t=a*(R()-.5)*99+b*(R() -.5)*99; // v(17,16,8) als Kamerabrennpunkt zuweisen und Strahl werfen / / Farbe akkumulieren, die in Variable t zurückgegeben wird p=S(v(17,16,8)+t, // Strahl starten a!(t*-1+(a*(R()+x)+b*(y+R())+c)*16) // Strahlrichtung mit geringer Verzerrung // für stochastischen Sampling-Effekt)*3.5 +p; // +p um Farbe zu akkumulieren ) printf("%c%c%c",(i)p.x,(i)p.y,(i)p.z); ) )

Sampler

v S(v o,v d)(f t ;v n;i m=T(o,d,t,n);if(!m)return v(.7, .6,1)*pow(1-d.z,4) ;v h=o+d*t,l=!(v(9+R(),9+R(),16)+h*-1),r=d+n*(n%d*-2) ;f b=l% n;if(b<0||T(h,l,t,n))b=0;f p=pow(l%r*(b >0),99);if(m&1)(h=h*.2;return((i)(ceil(h.x)+ceil(h.y))&1?v(3,1,1):v(3,3 ,3))*(b *.2+.1);)return v(p,p,p)+S(h,r)*.5;)

Der Abtaster S ist eine Funktion, die die Farbe eines Pixels zurückgibt, wenn die Koordinaten des Ursprungs o des Strahls und seine Richtung d gegeben sind. Trifft er auf die Kugel, ruft er sich rekursiv auf, ansonsten (wenn der Strahl keine Hindernisse auf seinem Weg hat) gibt er je nach Richtung entweder die Farbe des Himmels oder die Farbe des Bodens zurück (basierend auf seiner schachbrettartigen Textur). ).

Beachten Sie den Aufruf der R-Funktion bei der Berechnung der Lichtrichtung. Dadurch entsteht der Effekt von „weichen Schatten“.

Hier ist der obige Code, aber formatiert und kommentiert:

// (S) die Welt abtasten und die Farbe des Pixels durch // den Strahl zurückgeben, der bei o (Ursprung) beginnt und die Richtung d (Richtung) hat v S(v o,v d)( f t; v n; // Prüfe, ob der Strahl kollidiert mit etwas i m=T(o,d,t,n); if(!m) // m==0 // Die Kugel wurde nicht gefunden, und der Strahl geht nach oben: erzeuge die Himmelsfarbe return v( .7,. 6,1)*pow(1-d.z,4); // Vielleicht berührt der Strahl die Kugel v h=o+d*t, // h ist die Schnittkoordinate l=!(v(9+R (),9+R (),16)+h*-1), // "l" = Lichtrichtung (mit leichter Verzerrung für weichen Schatteneffekt) r=d+n*(n%d*-2); // r = Halbvektor // Berechnung des Lambert-Faktors f b=l%n; // Berechnung des Beleuchtungsfaktors (Lambert-Faktor > 0 oder im Schatten)? if(b<0||T(h,l,t,n)) b=0; // Рассчитываем цвет p (с учетом диффузии и отражения света) f p=pow(l%r*(b>0),99); if(m&1)( // m == 1 h=h*.2; // Die Kugel wurde nicht getroffen und der Strahl fällt auf den Boden: erzeuge die Farbe des Bodens return((i)(ceil(h.x )+ceil(h.y ))&1?v(3,1,1):v(3,3,3))*(b*.2+.1); ) // m == 2 Eine Kugel wurde getroffen: erzeuge einen Strahl, der von der Oberflächenkugel abprallt return v(p,p,p)+S(h,r)*.5; // Schwächung der Farbe um 50 %, wenn sie von der Oberfläche abprallt (* .5) )

Tracer

i T(v o,v d,f &t,v&n)(t=1e9;i m=0;f p=-o.z/d.z;if(.01 0)(f s=-b-sqrt(q);if(s .01)t=s,n=!(p+d*t),m=2;))return m;)

Die Funktion T (Tracer) ist dafür verantwortlich, einen Strahl von einem bestimmten Punkt (o) in eine bestimmte Richtung (d) zu werfen. Es gibt eine Ganzzahl zurück, die der Code für das Ergebnis des geworfenen Strahls ist. 0 - der Strahl ging in den Himmel, 1 - der Strahl ging auf den Boden, 2 - der Strahl traf die Kugel. Wenn die Kugel getroffen wurde, aktualisiert die Funktion die Variablen t (der Parameter, der zur Berechnung der Überquerungsdistanz verwendet wird) und n (der Halbvektor beim Abprallen von der Kugel).

Hier ist der obige Code, aber formatiert und kommentiert:

// Schnittpunkttest für eine Linie // Gibt 2 zurück, wenn die Kugel getroffen wurde (sowie den Schnittpunktabstand t und den Halbvektor n). // Gib 0 zurück, wenn der Strahl nichts trifft und in den Himmel geht // Gib 1 zurück, wenn der Strahl nichts trifft und auf den Boden geht i T(v o,v d,f& t,v& n)( t=1e9; i m=0; f p=-o.z/d.z; if(.01 0)( // Ja. Berechne den Abstand von der Kamera zur Kugel f s=-b-sqrt(q); if(s .01) // Dies ist der Mindestabstand, speichern Sie ihn. Und auch // den abprallenden Strahlenvektor berechnen und in "n" schreiben t=s, n=!(p+d*t), m=2; ) ) gib m zurück; )

Leet-Nummer

Viele Programmierer haben versucht, den Code noch weiter zu verkürzen. Der Autor selbst hat sich für die in diesem Artikel bereitgestellte Version entschieden. Weißt du, warum?

Fabien$ wc card.cpp 35 95 1337 card.cpp - viel Mathematik, aber alles sehr detailliert und verständlich erklärt.