Wie stark komprimiert 3Dc?
15. Juni 2005 / von aths / Seite 1 von 7
Einleitung
In diesem Artikel geht es um die Komprimierung vom Normalmaps, welche beim Bumpmapping eine wichtige Rolle spielen. Aber was sind Normalmaps? Zunächst müssen die Grundlagen zur pixelgenauen Beleuchtung klar sein. Da 3DCenter bislang noch nichts tiefergehendes zum Thema Bumpmapping geschrieben hat, wollen wir die Gelegenheit nutzen, das an dieser Stelle zu tun.
Es kommt dabei nicht unbedingt darauf an, dass die vorgestellten Formeln in ihrer ganzen Tiefe verstanden werden, wichtig ist dass das (bebilderte) Prinzip klar wird.
Grundlagen der Beleuchtung
Gibt es keine pixelgenaue Beleuchtung, also nur Vertexshading, wird der Einfluss des Lichtes nur pro Eckpunkt berechnet. Die so gewonnenen Farbwerte werden von der Grafikeinheit über das gesamte Dreieck interpoliert. Um berechnen zu können, wie stark ein Eckpunkt denn nun beleuchtet wird, ist neben der Entfernung und Farbe der Lichtquelle auch der Winkel zu ihr interessant. Was frontal zum Licht steht, ist natürlich heller als etwas im flachen Winkel zum Licht.
Die Ausrichtung der Eckpunkte wird in so genannten Normalenvektoren gespeichert. In der Geometrie ist eine Normale ein Zeiger (im Jargon: ein Vektor), der senkrecht auf der Ebene steht. Um aus dem Lichteinfallswinkel und dem Normalenvektor die Lichtintensität zu berechnen, wird das Skalarprodukt verwendet. Das Skalarprodukt liefert als Ergebnis einen Skalar – also eine normale Zahl, und keinen Vektor mehr.
Wenn man den Lichteinfallswinkel-Vektor L und den Normalenvektor N hat, ergibt sich das Skalarprodukt aus L.x∙N.x+L.y∙N.y+L.z∙N.z. Diese Rechnung erfordert also Multiplikationen und Additionen. Moderne Grafikkarten können das Skalarprodukt – englisch "dot product" – mit drei Komponenten in einem Takt ausführen. Daher auch der Name "Dot3 Bumpmapping" (der Pixelshader-Befehl für das Skalarprodukt von Vektoren mit je drei Komponenten lautet "dp3").
Für Bumpmapping (aber auch für andere Effekte) ist also eine Pixelpipeline, die stark im Skalarprodukt ist, wünschenswert. Aktuelle Grafikchips können bei Vektoren mit drei Komponenten pro Pixelpipe und Takt nur ein Skalarprodukt berechnen. Schön wäre es natürlich, wenn die Berechnung von zwei Skalarprodukten in jedem Takt möglich wären.
Sofern beide Operanden senkrecht zueinander stehende Vektoren sind, also einen 90°-Winkel aufspannen, liefert das Skalarprodukt als Ergebnis eine Null. Wenn der eine Vektor vom anderen "wegzeigt", man also eine Fläche hat, die der Lichtquelle abgeneigt ist, ergibt sich beim Skalarprodukt ein Ergebnis kleiner Null. Das Vorzeichen hängt davon ab, wie man den Winkel der Lichtquelle ausrichtet. Man kann sich auch ein System überlegen, wo das Vorzeichen genau vertauscht ist, doch das ändert am Berechnungs-Prinzip nichts.
Lange Rede, kurzer Sinn: Das Skalarprodukt liefert eine Aussage zum Winkel zwischen beiden Vektoren. Bei normalisierten Vektoren ist das Skalarprodukt = cos (einschließender Winkel).
Pixelgenaue Beleuchtung
Nun kann man ja, anstatt den Lichteinfluss pro Eckpunkt zu berechnen und über die Dreiecksfläche zu interpolieren, die eigentliche Normale über das Dreieck interpolieren – und pro Pixel den Lichteinfluss neu ausrechnen. Damit hätte man Dot3 Shading realisiert. Es gibt auch aufwändigere Verfahren, zum Beispiel Phong Shading, die auf Dot3 Shading aufbauen.
Damit die Lichtberechnung ein "schönes", kantenloses Ergebnis liefert, muss das Dreieck aus beleuchtungstechnischer Sicht gebogen sein. Dies erreicht man, in dem die Normalen der Dreieckspunkte nicht senkrecht zur Dreiecksfläche stehen, sondern für eine konvexe Oberfläche abgespreizt sind, und für eine konkave Rundung zueinander geneigt (konvex heißt in diesem Kontext "nach außen gebogen", konkav "nach innen gebogen").
Das muss klar sein: Die Beleuchtungsberechnung interessiert sich nicht für die tatsächliche Lage des Dreiecks im Raum und deren Ausrichtung zur Lichtquelle, sondern nur dafür, welche Winkelinformation in den Normalen steckt.
In jedem Fall sollte die interpolierte Normale noch normalisiert werden. Das klingt widersinnig, da eine Normale der Wortbedeutung nach ja scheinbar automatisch normalisiert sein sollte. Doch sehen wir uns einmal ein Dreieck, welches eine konvex gebogene Oberfläche repräsentieren soll, "von der Seite" an:
Ohne Normalisierung sind die interpolierten Normalen in ihrer Länge kleiner. |
Anstatt für jedes einzelne Pixel die interpolierte Normale anzuzeigen, haben wir das nur für die Mitte des Dreiecks gemacht. Nach der linearen Interpolation ist die Länge des Pfeils kleiner als die beiden (vom Modellierer vorgegebenen) Eck-Normalen. Das heißt nichts anderes, als dass das Skalarprodukt an dieser Stelle ein kleineres Ergebnis liefert – die Mitte des Dreiecks wird damit dunkler. Deshalb renormalisiert man für volle Qualität:
Normalisierung bringt die Vektoren auf die Einheitslänge. |
Die Renormalisierung macht mehr aus, als man denkt. Für eine MouseOver-Darstellung haben wir den Effekt vom Dot3-Shading in OpenGL mittels ultrafeinem Gouraud-Shading nachgestellt, und zeigen hier die Screenshots von einem Torus im 3DCenter-Orange (MouseOver-Effekt per Javascript, Alternativ-Variante: Klick öffnet beide Screenshots):