Zwei Standardfunktionen, die jeder moderne Renderer unterstützt, sind Subdivision Surfaces und verschiedene Formen des Displacement Mappings. Wie wir später in diesem Beitrag besprechen werden, sind diese beiden Funktionen in der Regel sowohl in der Nutzung als auch in der Implementierung sehr eng miteinander verbunden. Subdivision und Displacement sind entscheidende Werkzeuge für die Darstellung von Details in der Computergrafik, sowohl aus technischer als auch aus autoritärer Sicht ist es von Vorteil, mehr Details darstellen zu können, als tatsächlich in einem Mesh vorhanden sind. Die Anwendung von Details zur Laufzeit ermöglicht es der Geometrie, weniger Speicherplatz zu verbrauchen, als es erforderlich wäre, wenn alle Details in die Geometrie eingebrannt würden und Künstler mögen oft die Möglichkeit, breite Funktionen von hochfrequenten Details zu trennen.
Sowohl die Subdivision als auch das Displacement kamen ursprünglich aus der Welt des Raster-Renderings, wie die fliegende Geometriegenerierung historisch gesehen sowohl einfacher zu implementieren als auch praktischer/plausibler zu verwenden war. Bei der Rasterung wird die Geometrie durch den Renderer gestreamt und auf den Bildschirm gezeichnet, so dass jedes einzelne Geometrieteil unterteilt, tesseliert, verschoben, auf den Framebuffer gelegt und anschließend verworfen werden kann, um Speicherplatz freizugeben. Reyes Renderman war aus genau diesem Grund effizient bei der Darstellung von Subdivision und Displacement Surfaces. Im naiven Raytracing können sich die Strahlen jedoch jederzeit und in beliebiger Reihenfolge in der Geometrie schneiden. Das Unterteilen und Verschieben der Geometrie „on the fly“ für jeden Strahl und das anschließende Verwerfen der Geometrie ist im Vergleich zur Bearbeitungsgeometrie einmal über einen gesamten Framebuffer ungemein teuer. Die einfachste Lösung für dieses Problem besteht darin, alles einfach zu unterteilen, zu verschieben und während des Raytracings im Speicher zu behalten. Historisch gesehen war das einfache Caching von allem jedoch nie eine praktische Lösung, da Computer einfach nicht genug Speicher hatten, um so viele Daten aufzubewahren. Infolgedessen haben frühere Forschungsarbeiten erhebliche Anstrengungen in intelligentere Raytracing-Architekturen unternommen, die eine sofortige Subdivision und Displacement wieder erschwinglich machten.
In den letzten fünf Jahren hat sich die Geschichte der „Raytraced Displacement“ jedoch verändert. Es stehen jetzt viel leistungsfähigere Engines zur Verfügung (in einigen Studios sind Renderfarm-Nodes mit 256 GB Speicher oder mehr nicht mehr ungewöhnlich). Infolgedessen müssen Raytraced-Renderer nicht mehr so clever sein, wenn es um die Verwaltung der verschobenen Geometrie geht. Eine Kombination aus kameradaptiver Tessellierung und einfachem Geometrie-Cache mit einer zuletzt verwendeten Displacementstrategie reichen oft aus, um das Raytraced-Displacement praktisch umzusetzen. Performantes Displacement ist heute in den Workflows für eine Reihe von Pathracern üblich, darunter Arnold, Renderman/RIS, Vray, Corona, Hyperion, Manuka etc. Vor diesem Hintergrund wurde versucht, Subdivision und Displacement in Takua so einfach wie möglich zu gestalten.
Takua hat kein Konzept für eine Displacement Strategie für gecachte Mosaikgeometrie. Die Hoffnung ist, so effizient wie möglich zu sein ohne den Speicher völlig zu überlasten. Da zugegebenermaßen Takua für die meisten Nutzer ein Hobby-Renderer ist und wir persönlich auch Engines mit 48 GB Speicher nutzen, haben wir bisher nicht über Fälle nachgedacht, in den Dinge den Speicher überlasten sollten. Anstatt „on-the-fly“ per Ray oder so etwas zu tessellieren, unterteilen wir einfach vor und verschieben alles im Voraus während der ersten Szenenladung. Die Meshes werden parallel zueinander geladen, unterteilt und verschoben. Wenn Takua feststellt, dass die gesamte unterteilte und verschobene Geometrie nicht in das zugewiesene Speicherbudget passen, stoppt der Renderer von selbst.
Es muss beachtet werden, dass Takuas Szenenformat zwischen einem Mesh und einem Geom unterscheidet: ein Mesh ist eine Kombination aus den Raw Vertex/Face/Primvar Daten, aus dem sich eine Oberfläche zusammensetzt, während ein Geom ein Objekt ist, das eine Referenz auf ein Mesh zusammen mit Transformationsmatrizen, Shader-Bindungen etc. enthält. Diese Trennung zwischen den Meshdaten und dem geometrischen Objekt ermöglicht einige nützliche Funktionen im Subdivision/Displacement-System. Takuas Szene-Dateiformat ermöglicht es, Subdivision und Displacement-Modifikatoren entweder auf Shader-Ebene oder pro Geom zu binden. Bindungen auf Geomebene überschreiben Bindungen auf Shaderebene, was für das Authoring nützlich ist, da eine ganze Reihe von Objekten den gleichen Shader teilen können, aber dann individuelle Spezialisierungen für unterschiedliche Unterteilungsraten und unterschiedliche Subdivision Rates und Displacement Maps haben. Beim Laden von Szenen analysiert Takua, welche Subdivisions/Displacements für welche Meshes durch welche Geome erforderlich sind und entfernt und aggregiert dann alle Fälle, in denen verschiedene Geome die gleiche Subdivision bzw. das gleiche Displacement für dasselbe Mesh wünschen. Diese De-Duplication funktioniert sogar bei Instances.
Sobald Takua eine Liste aller Meshes zusammengestellt hat, die eine Subdivision erfordern, werden die Meshes parallel unterteilt. Für die Catmull-Clark-Subdivision setzen wir auf OpenSubdiv, um Subdivision Schablonen Tabellen zu berechnen, die Schablonen auszuwerten und die endgültige Tesselierung durchzuführen. Soweit wir das beurteilen können, ist die Schablonenberechnung in OpenSubdiv einfädig, so dass sie bei wirklich schweren Meshes recht langsam werden kann. Die Schablonenauswertung und abschließende Tesselierung ist jedoch superschnell, da OpenSubdiv eine Reihe von parallelen Auswertern zur Verfügung stellt, die mit einer Vielzahl von Backends laufen können, die von TBB auf der CPU bis hin zu CUDA– oder OpenGL-Compute-Shadern auf der GPU reichen. Takua setzt derzeit auf den TBB-Evaluator von OpenSubdiv. Eine Besonderheit der Schablonenimplementierung in OpenSubdiv ist, dass die Schablonenberechnung nur von der Topologie des Meshes und nicht von einzelnen Primvars abhängt, so dass eine einzige Schablonenberechnung dann mehrfach verwendet werden kann, um viele verschiedene Primvars wie Positionen, Normals, UVs und mehr zu interpolieren.
Kein Schreiben der Subdivision Surfaces ist vollständig, ohne dass ein Bild eines Würfels in eine Kugel unterteilt ist, so dass die untere Abbildung eine Darstellung eines Würfels mit mehreren Subdivision-Levels anzeigt. Jeder unterteilte Würfel wird mit einer prozeduralen Wireframe-Textur gerendert, die wir umgesetzt haben, um zu veranschaulichen, was mit der Subdivision geschah.
Jedes unterteilte Mesh wird in ein neues Mesh gelegt. Basis-Meshes, die mehrere Subdivision-Level für mehrere verschiedene Geome benötigen, erhalten ein neues unterteiltes Mesh pro Subdivision-Level. Nachdem alle unterteilten Meshes fertig sind, führt Takua anschließend eine Verschiebung durch. Das Displacement wird sowohl durch das Mesh als auch innerhalb jedes Meshes parallelisiert. Takua unterstützt auch sowohl on-the-fly- als auch vollständig zwischengespeicherte Displacements, die pro Shader oder pro Geom angegeben werden können. Wenn ein Mesh für das vollständige Caching markiert ist, wird das Mesh vollständig verschoben, als separates Mesh vom nicht verschobenen Subdivision Mesh gespeichert und anschließend ein BVH für das versetzte Mesh aufgebaut. Wenn ein Mesh für das volle Caching markiert ist, berechnet das Verstellsystem jede versetzte Fläche, berechnet dann die Grenzen für diese Fläche und verwirft dann die Fläche. Die verschobenen Grenzen werden anschließend verwendet, um ein dichtes BVH für das verdrängte Mesh zu bilden, ohne das verdrängte Mesh selbst lagern zu müssen. Stattdessen muss nur ein Hinweis auf das nicht verdrängte Subdivision Mesh beibehalten werden. Wenn ein Strahl den BVH für ein on-the-fly Displacement Mesh durchquert, gibt jeder BVH-Leaf-Node an, welche Dreiecke auf dem nicht verschobenen Mesh verschoben werden müssen, um Endpolys für den Schnittpunkt zu erzeugen und anschließend werden die verdrängten Polys geschnitten und wieder verworfen. Für die Szenen in diesem Beitrag scheint das on-the-fly Displacement etwa doppelt so langsam zu sein wie das vollständig zwischengespeicherte Displacement, was zu erwarten ist, aber wenn das gleiche Mesh auf mehrere verschiedene Arten verschoben wird, dann ergeben sich entsprechend große Speichereinsparungen. Nachdem alle Displacements berechnet wurden, geht Takua zurück und analysiert, welche Basis-Meshes und unversetzten Subdivision Meshes nicht mehr benötigt werden und gibt diese Meshes frei, um Speicher zurückzugewinnen.
Wir haben Unterstützung für sowohl skalare Displacements über normale Graustufen-Textur-Maps als auch Vektor Displacement aus OpenEXR-Texturen umgesetzt. Der Ozean-Render verwendet von Anfang an ein Vektor Displacement, die auf eine einzige Ebene angewendet wird.
Für beide Ozeanrenderer ist die Vektorverschiebung OpenEXR-Textur von Autodesk übernommen worden, der sie großzügig als Teil eines Artikels über Vektor Displacement in Arnold bereitstellt. Die Renderer werden mit einem Skydome unter Verwendung der HDRI Sky 193 Textur beleuchtet.
Sowohl für die skalare als auch auch für das Vektor Displacement kann der Displacement Betrachter aus der Displacement Textur durch einen Einzelwert gesteuert werden. Vektor Displacement Maps werden als in einem lokalen Tangentenraum angenommen, welche Achse als Basis für den Tangentenraum verwendet wird, kann pro Displacement Map festgelegt werden. Die untere Abbildung zeigt drei Tacos mit unterschiedlichen Skalierungswerten. Der linke Taco hat eine Displacement-Skala von 0, die das Displacement effektiv verhindert. Der mittlere Taco hingegen hat einen Displacement-Maßstab von 0,5 der nativen Displacement-Werte in der Vektor Displacement Map. Der ganz rechte Taco hat eine Displacement-Skala von 1,0. d.h. verwenden Sie einfach die nativen Displacement-Werte aus der Vektor Displacement Map.
Die untere Abbildung zeigt eine Nahaufnahme des rechten Tacos aus der obigen Abbildung. Das Basis-Mesh für den Taco ist relativ niedrig aufgelöst, aber durch Subdivision und Displacement kann eine große Menge an geometrischen Details im Rendering hinzugefügt werden. In diesem Fall wird der Taco zu einem Punkt tesselliert, an dem jedes einzelne Mikropolygon eine Subpixelgröße hat. Die Displacement Map und andere Texturen für den Taco stammen aus der Megascans-Bibliothek von Quixel.
Eine große Herausforderung bei dem Displacement Mapping ist die Rissbildung. Rissbildung tritt auf, wenn benachbarte Polygone die gleichen gemeinsamen Nodes auf unterschiedliche Weise für jedes Polygon verschieben. Dies kann pasieren, wenn die Normals über eine Oberfläche nicht kontinuierlich sind oder wenn es eine Diskontinuität gibt, die entweder darin besteht, wie die Displacement-Textur auf die Oberfläche abgebildet wird oder in der Displacement-Textur selbst. Wir haben eine optionale, etwas grobe Lösung für das Displacement Cracking umgesetzt. Wenn die Rissentfernung aktiviert ist, analysiert Takua das Mesh zum Zeitpunkt des Displacements und zeichnet auf, wie viele verschiedene Wege jeder Node im Mesh durch verschiedene Flächen verschoben wurde, zusammen mit welchen Flächen dieser Nodes verschoben werden soll. Nach einem anfänglichen Displacement-Durchlauf geht der Rissentferner anschließend zurück und für jeden Node, der mehr als eine Richtung verschoben wird, werden alle Displacements in ein einziges Displacement gemittelt und alle Flächen, die diesen Node verwenden, werden aktualisiert, um das gleiche gemittelte Ergebnis zu teilen. Dieser Ansatz erfordert ein angemessenes Maß an Buchhaltung und Voranalyse des verlagerten Meshes, aber er scheint gut zu funktionierten.
In den meisten Fällen scheint das Rissentfernungssystem recht gut zu funktionieren. Das System ist jedoch nicht perfekt, manchmal können Dehnungsartefakte auftreten, insbesondere bei Oberflächen mit einer strukturierten Grundfarbe. Diese Drehung geschieht, weil das Rissentfernungssystem Mikropolygone grundsätzlich dehnt, um den Riss abzudecken.
Takua berechnet automatisch Normals für unterteilte/verschobene Polygone neu. Standardmäßig verwendet Takua einfach die geometrischen Normals als Schattierungsnormals für verschobene Polygone. Es gibt jedoch eine Option, glatte Normals auch für die Schattierungsnormals zu berechnen. Wir haben uns dafür entschieden, geometrische Normals als Standard zu verwenden, in der Hoffnung, dass für die Subpixel-Subdivision und -Displacement eine andere Shading-Normalität nicht notwendig wäre.
In Zukunft werden wir vielleicht unsere eigene Subdivision-Library umsetzen und wir sollten wahrscheinlich auch mehr darüber nachdenken, welche Art von kombiniertem Tessellation-Cache und Displacement-Strategie für eine bessere Speichereffizienz geeignet ist. Vorerst scheint jedoch alles gut zu funktionieren und relativ effizient zu rendern. Die nicht-ozeanischen Renderings in diesem Beitrag haben alle eine Subpixel-Subdivision mit Millionen von Pixeln und dauerten jeweils mehrere Stunden, um bei einer Auflösung von 4K (3840×2160) auf einer Engine mit zwei Intel Xeon X5675 CPUs (12 Kerne insgesamt) zu rendern. Die beiden Ozean-Renderings, die wir über Nacht mit einer Auflösung von 1080p laufen lassen haben, brauchten länger, um sich zu konvergieren, hauptsächlich aufgrund der Schärfentiefe. Alle Renderings in diesem Beitrag wurden mit einem neuen, stark verbesserten Shading-System beschattet, so dass Takua jetzt viel mehr Komplexität als bisher darstellen kann.
Vielen Dank für ihren Besuch.