Categories: Sonstiges

Einsteigerguide: Was sind Bounding Volumes?

Die einfachste Methode zur Beschleunigung des Raytracing kann nicht unbedingt als Beschleunigungsstruktur betrachtet werden, aber dies ist sicherlich der einfachste Weg, Renderzeiten zu verkürzen. Jedes der Grids aus der Teekanne hat potentiell mehr als hundert Dreiecke: Bei 8 Teilungen enthält das Gitter 128 Dreiecke, was eine ähnliche Anzahl von Schnittests für jeden in die Szene geworfenen Strahl erfordert. Was wir stattdessen tun können, ist, dass Ray Trace eine Box mit allen Nodes des Grids enthält. Eine solche Box wird als Begrenzungsband bezeichnet: Es ist das kleinstmögliche Volumen (in diesem Fall eine Box, aber es könnte auch eine Kugel sein), das das Grid umgibt.

Wir können zuerst testen, ob der Strahl diese Box schneidet und wenn nicht, wissen wir sicher, dass dieser die in diesem Begrenzungsband enthaltene Geometrie nicht schneiden kann. Wenn wir die Dreiecke aus diesem Begrenzungsrahmen ignorieren können, speichern wir 128 Ray-Triangle Schnitttests. In Anbetracht der Tatsache, dass viele der Bounding Boxen aus der Szene wahrscheinlich auch diesen Test nicht bestehen werden, können wir insgesamt eine beträchtliche Anzahl von Aufrufen der Ray-Triangle-Routine speichern. Wenn der Strahl jedoch den Begrenzungsrahmen schneidet, müssen alle im Volumen enthaltenen Dreiecke auf einen Schnittpunkt mit diesen Strahl getestet werden. Nur farbige Kästen werden vom Strahl durchschnitten und nur die in diesen Kästen enthaltenen Grids werden auf einen Schnittpunkt mit dem Strahl getestet. Patches, die von den nicht gekreuzten Kästen enthalten sind, können sicher abgelehnt werden.

Der folgende Code veranschaulicht diesen Prozess:

Copy to Clipboard

Die Berechnung eines Begrenzungsrahmens für ein Objekt ist sehr einfach. Wir können über alle Nodes, aus denen sich das Mesh zusammensetzt, loopen, um den Minimal- und Maximalwert für jede der Koordinaten des Punktes (die xyz-Punktposition) zu finden. Diese Werte bilden den so genannten minimalen und maximalen Umfang des Objekts und definieren die beiden Ecken des Begrenzungsrahmens. Der Begrenzungsrahmen eines Objekts wird normalerweise berechnet, wenn das Objekt gebaut wird (z.B. im Konstruktor der Polygonmeshklasse) und in einer Elementvariablen der Objektklasse gespeichert (jeder primitive Typ kann eine andere Methode erfordern, um diesen Begrenzungsrahmen zu berechnen. Aus seinem Radius kann beispielsweise das Begrenzungsvolumen einer quadratischen Kugel berechnet werden). Der folgende Code-Ausschnitt aus dem Raytracer zeigt, wie wir den Begrenzungsrahmen eines Polygonobjekts berechnen. Die Variable bbox ist eine Member-Variable der Basisobjektklasse.

Copy to Clipboard

Der Rest des Codes ist sehr einfach. Wir werden die sogenannte Raybox-Intersection Routine verwenden.

Copy to Clipboard

Wir verwenden das gleiche wie bei der Strahl-Dreieck-Schnittmethode, um die Anzahl der Aufrufe der Strahlbox-Schnittmethode zu zählen. Eine globale Variable numRayBoxTests, die in der Datei box.cpp deklariert ist, wird bei jedem Aufruf der Box-Schnittmethode mit einer Atomic Add-Operation inkrementiert. Der Endwert wird am Ende des Renderings mit den anderen Statistiken ausgedruckt.

Wir haben auch eine AccelarationStructure Basisklasse erstellt, die sich sehr ähnlich wie ein Objekt verhält. Es beinhaltet eine Schnittmethode, die über alle Objekte der Szene läuft. Wir testen zunächst auf einen Schnittpunkt zwischen dem Strahl und dem Begrenzungsrahmen des aktuellen Objekts. Wenn der Test erfolgreich war, wird der Strahl dann gegen das in der Box enthaltene Objekt getestet. Der Rest des Codes ist sehr ähnlich wie die Implementierung der Trace-Funktion. Jedes Mal, wenn ein Dreieck geschnitten wird, müssen wir den nächstgelegenen Abstand zum Schnittpunkt aktualisieren und einen Zeiger auf das geschnittene Objekt halten:

Copy to Clipboard

Schließlich müssen wir noch die Trace-Funktion aktualisieren. Anstatt alle Objekte der Szene zu durchlaufen, müssen wir nur die Schnittmethode aus der Klasse AccelerationStructure aufrufen. Wenn diese Methode einen gültigen Zeiger auf ein Objekt aus der Szene zurückgibt, ist ein Schnittpunkt aufgetreten und wir können die Farbe des Pixels mit der Farbe des geschnittenen Objekts einstellen:

Copy to Clipboard

Wenn wir die Szene rendern, erhalten wir die folgenden Statistiken:

Copy to Clipboard

Die Renderzeit wurde um den Faktor 34 reduziert. Logischerweise ist das Verhältnis zwischen der Anzahl der Ray-Triangle-Tests, die mit und ohne diese grundlegenden Beschleunigungsstruktur durchgeführt werden, etwa gleich 38. Die beiden Zahlen sind nicht exakt gleich, da die Berechnung der Ray-Box Intersections nur länger dauert (9,83 Millionen Mal). Der Nutzen, den wir aus der Verwendung dieses Systems ziehen, überwiegt jedoch bei weitem den zusätzlichen Aufwand, den diese Tests erfordern.

Die in diesem Beitrag vorgestellte Technik funktioniert im Falle der Teekanne gut, da das Modell viele Grids enthält, deren Auflösung (in Bezug auf die Polygonzahl) relativ gering ist. Das Affenmodell hingegen ist sehr komplex und besteht aus einem Stück. Wenn wir dieses Objekt rendern würden, würden wir nur dann Zeit sparen, wenn die Primärstrahlen ihren Begrenzungsrahmen nicht schneiden (was bei einem tyischen Bild vielleicht weniger als ein Drittel der Gesamtzahl der Pixel entspricht). Für alle Überstrahlen müssten wir noch einen Schnitttest gegen die hintertausend Dreiecke des Modells durchführen. In diesem speziellen Fall würde die Bounding-Box-Methode nur einen geringen Nutzen gegenüber dem Brute-Force-Ansatz bringen. Sie ist einfach und schnell zu implementieren, aber eindeutig nicht an alle Situationen angepasst und gilt daher nicht als eine sehr gute Beschleunigungsmethode.

Wir hoffen, dass wir ihnen einen ersten Überblick über diese Thematik verschaffen konnten. Falls Sie noch Fragen haben sollten, hinterlassen Sie uns unten einen Kommentar.

Vielen Dank für ihren Besuch.

3DMaster