- Um die interne Funktionsweise von OpenGL, Vulkan, Metal und DirectX zu verstehen, wird ein Software-Renderer ohne externe Grafikbibliotheken von Grund auf implementiert
- Ein 3D-Modell wird aus Dreiecks-Meshes und Texturen in ein Bild umgewandelt; die Implementierung von GUI- oder GPU-Anwendungen wird dabei nicht behandelt
- Der fertige Code umfasst etwa 500 Zeilen, und Studierende benötigen typischerweise 10 bis 20 Stunden, bis sie beginnen, einen funktionierenden Renderer zu bauen
- Es werden nur eine TGA-Verarbeitungsklasse mit Unterstützung für RGB, RGBA und Graustufen sowie eine Funktion zum Setzen einzelner Pixel bereitgestellt; das Zeichnen von Linien und Dreiecken muss selbst implementiert werden
- Statt den fertigen Code zu kopieren, sollte man ihn selbst schreiben, um Rendering-Konzepte zu verstehen und auch die interne Arbeitsweise von 3D-Bibliotheken nachzuvollziehen
Den Rendering-Workflow selbst aufbauen
- Die Struktur moderner 3D-Grafikbibliotheken wird in lockerer Form nachvollzogen, um die Funktionsweise der Rendering-Pipeline zu erlernen
- Statt zu zeigen, wie man GPU-Anwendungen schreibt, wird die interne Arbeitsweise mit einem Software-Renderer nachgebildet
- Die Eingabe ist ein 3D-Modell aus Dreiecks-Meshes und Texturen, die Ausgabe ein gerendertes Bild
- Ohne grafische Oberfläche erzeugt das Programm eine Bilddatei
- Um externe Abhängigkeiten zu reduzieren, wird das einfache Bildformat TGA verwendet
- Zu Beginn stehen nur das Laden und Speichern von Bildern sowie das Setzen der Farbe eines einzelnen Pixels zur Verfügung
- Da es keine eingebaute Funktion zum Zeichnen von Linien oder Dreiecken gibt, muss alles selbst geschrieben werden
- Das Startbeispiel erzeugt einen
64x64-RGB-Framebuffer, setzt die Pixel an drei Koordinaten auf Weiß und speichert ihn als framebuffer.tga
- Farbwerte werden in der BGRA-Reihenfolge angegeben
Code bauen und ausführen
git clone https://github.com/ssloy/tinyrenderer.git &&
cd tinyrenderer &&
cmake -Bbuild &&
cmake --build build -j &&
build/tinyrenderer obj/diablo3_pose/diablo3_pose.obj obj/floor.obj
- Das Ausführungsergebnis wird in
framebuffer.tga gespeichert
- Der fertige Code ist zwar nur etwa 500 Zeilen lang, doch der eigene Implementierungsprozess ist entscheidend für das Verständnis der Konzepte; deshalb wird nicht empfohlen, den bereitgestellten Code unverändert zu verwenden
1 Kommentare
Hacker-News-Kommentare
https://github.com/kshitijl/tinyrenderer-rs
Das Repository enthält viele Screenshots des Entwicklungsprozesses und lustiger visueller Bugs. Ich habe nicht nur die Rendering-Prinzipien gelernt, sondern vor allem auch, wie schnell moderne CPUs sind und dass man selbst mit einem Single-Thread-CPU-Renderer ein interaktives 3D-Spiel mit aufwendigen Spezialeffekten ausführen kann.
Das war noch vor LLMs, deshalb hat es mindestens zwei Monate gedauert, und die meiste Zeit ging dafür drauf, die Mathematik der Computergrafik zu verstehen und C-Segmentation-Fehler zu verfolgen.
Diese Vorlesungsnotizen auf GitHub waren besser, um die Konzepte aufzufrischen. Den Code-Stil des Repositories mag ich nicht, und der altmodische Rasterizer ist auch zu simpel und ineffizient, aber trotzdem finde ich es lesbarer als Foleys Buch.
Mit den Auflagen haben sich auch die verwendeten Sprachen von Pascal zu C sowie C und C++ weiterentwickelt, und in der neuesten Ausgabe ist auch etwas C# enthalten. Einige neuere Konzepte fehlen, aber ich denke, es gibt immer noch viel wertvollen Inhalt darin.
Frustum-Clipping kann über Punktauswahl in lokalen Tiles behandelt werden, und beim Zusammensetzen von Primitives ist es einfacher, mit dem invers transformierten Clipping-Rechteck in baryzentrischen Koordinaten zu arbeiten. Mit Double Precision oder Fixed-Point lassen sich Rundungsfehler kontrollieren; die zentrale Schwierigkeit ist, die Z- und 1/Z-Werte neuer Vertices neu zu erzeugen. Bei einem Rasterizer mit verzögerter Attributzusammensetzung läuft der Rest dann von selbst durch die Pipeline, und in der Open-Source-Implementierung von OpenSWR.org gibt es Beispiele.
Die größte psychologische Hürde ist die Unvertrautheit mit dem projektiven Raum und homogenen Koordinaten. Die sechs Ebenen des Clip-Space sind einfach
x = ±w,y = ±w,z = ±w; man geht einfach jede Polygonkante durch, bestimmt für beide Endpunkte, ob sie innen oder außen liegen, und interpoliert die Position des Grenzschnittpunkts und die Vertex-Attribute linear. Wendet man das nacheinander auf alle Ebenen an, wird das Dreieck zu einem konvexen Polygon mit höchstens 9 Vertices, das sich leicht wieder triangulieren lässt. Wenn man Outcodes im Voraus berechnet, kann man das Clipping für Dreiecke, die komplett innen oder außen liegen, überspringen.Paper: https://dl.acm.org/doi/10.1145/360767.360802
Wenn man die Form einer Fixed-Function-Pipeline beibehält, kann man mit ziemlich einfachen Zeichenfunktionen überraschend viele Dreiecke verarbeiten.