World, View und Projection Matrix

OK, fangen wir an :)

Wie funktioniert Projektion?

Fangen wir an mit der Frage, wie man eigentlich einen 3D Punkt auf eine Ebene projiziert. Immerhin ist das ja genau was wir machen müssen, um überhaupt 3D Grafik darstellen zu können. Die trivialste Variante wäre sicher, einfach die z-Koordinate wegzulassen:

Abbildung 1

Das entspricht aber nicht wirklich der Welt, wie wir sie wahrnehmen, oder?
Nehmen wir also Papier und Bleifstift und stellen ein paar Überlegungen an…

Angenommen wir befinden uns in einem linkshändigen Koordinatensystem. Wir blicken vom Koordinatenursprung aus in Richtung der positiven z-Achse:

Abbildung 2

Wir sehen dort den Punkt P, schauen wir uns das mal von der Seite an:

Abbildung 3

Wir wollen nun den Punkt P auf die Ebene ε (grün), die im Abstand f parallel zu xy-Ebene liegt, projizieren. Kennen wir f, so können wir mit Hilfe des Strahlensatzes (ähnliche Dreiecke) die y-Koordinate des projizierten Punktes P' berechnen:

formula(1)

Wenn wir das ganze auch noch von oben betrachten, ergibt sich für die x-Koordinate praktisch das gleiche Bild, sodass wir für die x und y Koordinaten des projizierten Puntkes P' festhalten können:

formula(2)

Was wir eben hergeleitet haben ist eine sog Zentralprojektion und entspricht auch weitestgehend dem menschlichen Sehen (abgesehen davon, dass wir natürlich 2 Augen haben…). Wir erhalten den projizierten Punkt also einfach, indem wir die x und y Koordinate durch z dividieren. Je weiter weg ein Objekt (je größer dessen z-Koordinate), desto kleiner wird es…

Was hat es aber mit diesem Faktor f auf sich!?
Unter f können wir uns sowas wie die Brennweite unserer Linse vorstellen. Wenn wir entlang der z-Achse in den Raum blicken, so sehen wir natürlich nur einen Ausschnitt des ganzen, da wir ja keine 360°-Panorama-Augen haben.
Den Bereich den wir sehen, nennen wir unser “field of view”, kurz fov.
Mal sehen was wir damit anfangen können:

Abbildung 4

Wir beschreiben unser field of view also durch jenen Winkelbereich den wir wahrnehmen können, z.b. 140°. Kennen wir nun die Höhe h des wahrgenommenen Bildausschnittes auf der Projektionsfläche (und das tun wir, denn wir wissen ja wie groß unsere Leinwand sein soll), so können wir uns f ganz einfach berechnen:

formula(3)

Würden wir das ganze von oben betrachten, so würden wir wieder praktisch das gleiche Bild erhalten und bekommen die gleiche Formel für f abhängig von der Breite des Bildausschnittes.

formula(4)

Wobei fovh der horizontale und fovv der vertikale Sichtwinkel ist. w und h sind bekannt, es handelt sich um die Abmessungen unserer “Leinwand” bzw. unseres Viewports.

Was bewirkt nun der Sichtwinkel?
Zunächst einmal können wir aufgrund der Formel sagen, dass ein größerer Sichtwinkel zu einer kleinen Brennweite und umgekehrt führt. Dann können wir den Bereich sinnvoller Werte für den Sichtwinkel beschränken auf 0° < fov < 180°, denn bei 0° wäre die Brennweite unendlich und bei 180° null.
Ein kleiner Sichtwinkel (hohe Brennweite) bedeutet, dass nur ein kleiner Ausschnitt der Szene auf die ganze Leinwand kommt, ein größerer Sichtwinkel (kleinere Brennweite) nimmt einen größeren Ausschnitt auf:

Effekt unterschiedlicher SichtwinkelAbbildung 5: Effekt unterschiedlicher Sichtwinkel

Wenn wir also den Sichtwinkel bei gleichbleibenden Abmessungen der Leinwand immer kleiner werden lassen, zoomen wir immer weiter in die Szene hinein.

Unser horizontales Sichtfeld beträgt etwas mehr als 180° (wer's nicht glaubt kanns gleich im Selbstversuch ausprobieren: schaut einfach gerade aus auf einen fixen Gegenstand und testet wie weit ihr z.b. eure Zeigefinger nach hinten nehmen könnt um sie gerade noch zu bemerken).
Vertikal ist das Sichtfeld mit ca. 150° kleiner. Räumliches Sehen ist uns in den Randzonen aufgrund der fehlenden Überdeckung der Einzelbilder übrigens nicht mehr möglich, dafür haben wir dort eine höhere Empfindlichkeit für Bewegungen.
Wer jetzt voller Enthusiasmus in seinem aktuellen Projekt 190° als fov einstellt, wird schnell von kopfstehenden, seitenverkehrten Bildern enttäuscht werden. Nach unserem Modell ergibt sich dann nämlich eine negative Brennweite.
Um das gesamte menschliche Gesichtsfeld wirklich korrekt abzubilden, müssten wir ein komplexeres Modell verwenden, um das Auge entsprechend nachzubilden…

Üblicherweise verwendet man einen vertikalen Sichtwinkel von 90° um ein für uns “normal” wirkendes Bild zu erzeugen.

OK, das war ja schonmal was.
Mit unseren bisherigen Überlegungen sollten wir eigentlich bereits in der Lage sein einfache 3D Grafik darzustellen!
Im nächsten Abschnitt werden wir uns ein bisschen mit Matritzen beschäftigen.

Matrix?

Matritzen sind ein Konzept aus der linearen Algebra. Im Prinzip handelt es sich bei einer Matrix um nichts weiter als eine tabellarische Anordnung von Objekten (üblicherweise Zahlen), den Elementen der Matrix. Sie dienen als Hilfsmittel beim Arbeiten mit Linearen Gleichungssystemen.
Schauen wir uns einmal eine Matrix an:

formula(5)

Das ist eine 3×4 Matrix. 3 Zeilen und 4 Spalten. Die Benennung der Elemente folgt dem Schema "Erst die Zeile dann die Spalte", wobei wir bei 1 anfangen zu zählen.
Oft arbeitet man nicht nur mit einzelnen Elementen, sondern mit ganzen Zeilen bzw. Spalten einer Matrix. Man spricht dann von den Zeilen- bzw. Spaltenvektoren.
(a11, a12, a13, a14) wäre der erste Zeilenvektor in obigem Beispiel, (a12, a22, a32) der zweite Spaltenvektor.

Für Matritzen sind bestimmte Rechenoperationen wie z.B. Addition und Multiplikation definiert.
Die Addition erfolgt Elementweise. Es kann daher immer nur eine n×m Matrix zu einer n×m Matrix addiert werden:

formula(6)

Die Multiplikation einer Matrix mit einer Zahl sieht genauso aus: Es wird einfach jedes Element mit dieser Zahl multipliziert
Die Multiplikation von zwei Matritzen ist jedoch etwas komplizierter. Das ij Element des Produktes P zweier Matritzen A und B ergibt sich nach:

formula(7)

Wobei m die Anzahl der Zeilen in B und Spalten in A ist.

Anders ausgedrückt: Das ij Element des Produktes zweier Matritzen A×B ist das Punktprodukt des i-ten Zeilenvektors von A mit dem j-ten Spaltenvektor von B (Allen die noch Probleme mit der Vorstellung dazu haben bringt das vielleicht Klarheit):

formula(8)

Daher kann man eine n×m Matrix nur mit einer m×k Matrix multiplizieren. Das Produkt ist dann eine n×k Matrix.
Man erkennt: die “inneren Dimensionen” der beiden Matritzen (m und m) müssen gleich sein, das Ergebnis ist eine Matrix mit den “äußeren Dimensionen” der beiden Matritzen (n und k), die Matrixmultiplikation ist Assoziativ (also A×(B×C) = (A×B)×C = A×B×C...), aber nicht Kommutativ (A×B ≠ B×A).

Soviel einmal zu den Grundlagen zum Thema Matritzen. Für unsere Zwecke hier wird dieses Wissen ausreichend sein, ich empfehle jedoch jedem der in diesem Abschnitt etwas neues gehört hat, sich noch etwas intensiver mit Matritzen zu beschäftigen ;)

Another point of view

Gut, was nützt uns all das Jetzt?
Wir können einen n dimensionalen Vektor grundsätzlich als 1×n (Zeilenvektor) oder n×1 (Spaltenvektor) Matrix auffassen. Schauen wir uns einmal an, was passiert, wenn wir einen solchen Matrix gewordenen 1×3 Vektor mit einer 3×3 Matrix multiplizieren:

formula(9)

Uns fällt natürlich sofort auf, dass das Ergebnis wieder ein 1×3 Vektor ist, aber sonst ist das ganze recht unspektakulär.

Überlegen wir uns nun folgendes: Der Vektor q = (x, y, z) bedeutet ja nichts anderes als: gehe x Einheiten in Richtung der x-Achse, dann y Einheiten in Richtung der y-Achse und dann z Einheiten in Richtung der z-Achse. Wobei unsere Koordinatenachsen, nennen wir sie u (x-Achse), v (y-Achse) und w (z-Achse), ja selbst nur Vektoren (die sog. Basisvektoren unseres Koordinatensystems) sind. Fassen wir das mal in eine Gleichung:

formula(10)

Wir gehen also qx Einheiten in Richtung u (x-Achse), qy Einheiten in Richtung v (y-Achse) und qz Einheiten in Richtung w (z-Achse) und erhalten so den Vektor q'.
Da u, v und w jedoch wieder nur Vektoren sind, befinden sich diese ja selbst wieder in einem Koordinatensystem i, j, k. Unser Vektor q' aus obiger Gleichung ist also nichts anderes, als der Vektor q, vom Koordinatensystem ijk aus “betrachtet”. Wir haben den Vektor q also vom Koordinatensystem uvw in das Koodinatensystem ijk transformiert!

Abbildung 6

Schreiben wir uns mal auf, wie wir zu den Koordinaten von q' kommen:

formula(11)

Oha, das ist ein lineares Gleichungssystem. Sowas können wir also auch in eine Matrix packen (denn dafür wurden Matritzen schließlich erfunden)

Nehmen wir also eine kleine Umbenennung der Elemente unserer Matrix vor und schauen uns die Vektor-Matrix Multiplikation von oben nochmal an:

formula(12)

So ein Zufall aber auch! (oder auch nicht :P)
Die Koordinaten des Ergebnisvektors entsprechen genau denen von q'!

Denkt über diesen Schritt ruhig ausgiebig nach. Meiner Meinung nach steckt genau in dieser Überlegung der Schlüssel zum Verständnis von Matritzen.

Das erlaubt uns eine neue Sichtweise auf 3x3 Matrtizen:
Eine solche Matrix repräsentiert ein Koordinatensystem (Genauer gesagt beschreibt sie, wie sich ein Koordinatensystem zu einem anderen verhält), wobei die Zeilenvektoren der Matrix den Basisvektoren des Koordinatensystems entsprechen. Wenn wir einen Vektor mit dieser Matrix multiplizieren, dann transformieren wir diesen Vektor aus dem durch die Matrix beschriebenen Koordinatensystem in das Koordinatensystem, in dem sich die Basisvektoren befinden.

So betrachtet ist das alles doch total einfach, oder?

Wir können einen Vektor natürlich auch mit mehr als einer Matrix transformieren. Zuerst mit einer Matrix A und das Ergebnis dann mit der Matrix B, das dann mit C usw.:

formula(13)

Aufgrund der Assoziativität der Matritzenmultiplikation können wir das auch anders hinschreiben:

formula(14)

Wir können also erst alle Matritzen zusammenmultiplizieren und den Vektor dann mit dem Produkt transformieren, und erhalten das gleiche Ergebnis. Multiplizieren wir zwei Matritzen, ist das Produkt also eine Matrix, die beide Transformationen vereint. Schauen wir uns eine solche Multiplikation einmal genau an:

formula(15)

Die Zeilenvektoren der Ergebnismatrix entsprechen ja den Basisvektoren des sich ergebenden Koordinatensystems. Der erste Zeilenvektor sieht so aus:

formula(16)

++n

Bis jetzt haben wir die ganze Zeit nur von Vektoren gesprochen. Unsere 3D Modelle bestehen ja aber aus Vertices, also Punkten. Punkte können wir als Vektoren die vom Koordinatenursprung zum Punkt zeigen beschreiben (sog. Ortsvektoren). Das klingt komplizierter als es ist. Nehmen wir zum Beispiel den Punkt (1, 2, 3): Der entsprechende Ortsvektor zu diesem Punkt wäre natürlich (1, 2, 3). Auf den ersten Blick schaut es also so aus als ob Vektor und Punkt das gleiche wären. Dem ist aber nicht so: Ein Vektor beschreibt eine Richtung, also die Differenz von zwei Punkten!

Überlegen wir mal, was wir mit unseren 3x3 Matritzen alles machen können. Wir können durch geeignete Wahl von Basisvektoren Rotieren, Skalieren etc. Was wir aber nicht können ist verschieben. Wir haben keine Möglichkeit unseren Basisvektoren einen anderen Ursprung als den des Koordinatensystem, in dem sie selbst liegen, zu verpassen.

Putting it all together