Herleitung der perspektivischen (linkshändigen) Projektionsmatrix

Zum Verständnis der Herleitung ist es nötig zu wissen was Homogene Koordinaten sind bzw. wie man mit ihnen arbeitet. Wer das bereits weiß kann den nächsten Abschnitt überspringen und gleich bei der Herleitung weiter lesen. Wer mit diesem Begriff nichts oder wenig anfangen kann sollte den nächsten Abschnitt unbedingt zuerst lesen und verstehen.

Homogene Koordinaten

In der Computergrafik werden häufig dreidimensionale Vektoren mit Hilfe von 4×4 Matrizen transformiert, da gewisse Transformationen wie beispielsweise eine perspektivische Projektion nicht möglich wären. Eine Multiplikation eines drei Komponenten Vektors mit einer 4×4 Matrix ist jedoch direkt nicht möglich, weshalb man den Vektor um eine Komponente erweitern und somit einen 4-dimensionalen Vektor erhält den man mit der Matrix problemlos multiplizieren kann. Diese vierte Koordinate nennt man w-Koordinate und hat vor der Transformation stets den Wert 1. Nach der Multiplikation muss man den erhaltenen 4D Vektor durch die w-Koordinate dividieren um falls sie nicht 1 ist wieder auf 1 zu bringen. Danach darf man die w-Koordinate wieder weglassen und hat somit den 3D Vektor korrekt transformiert.

P = (x,y,z)
(x,y,z,1) * delim{[}{matrix{4}{4}
{ m_11 m_12 m_13 m_14
  m_21 m_22 m_23 m_24
  m_31 m_32 m_33 m_34
  m_41 m_42 m_43 m_44 }}{]}
= (x^t,y^t,z^t,w^t)
P^t = (x^t/w^t,y^t/w^t,z^t/w^t)

Projektionsmatrix

Aufgrund des Strahlensatz ergibt sich folgender Zusammenhang:

P^l_x / near = P_x / {-P_z}

Hinweis: P_z ist deshalb negativ, da die Kamera bei einem Linkshändigem Koordinatensystem in Richtung der negativen z-Achse schaut.

Auch wenn es in der Skizze verdeckt ist gilt analog das selbe auch in y-Richtung:

P^l_y / near = P_y / {-P_z}

Durch Umformen erhalten wir:

P^l_x = P_x {near/{-P_z}}
P^l_y = P_y {near/{-P_z}}

Hier fällt auf das in beiden Formeln durch -P_z dividiert wird. Da wir aber nur eine Matrizenmultiplikation durchführen ist eine Division direkt nicht möglich. Da wir aber mit homogenen Koordinaten arbeiten können wir einfach -P_z in die w Koordinate schreiben und erreichen somit die gewünschte Division. Wir können jetzt also die Division durch -P_z weglassen und müssen die erhaltenen Werte noch auf den Bereich [-1,1] normieren. Wenn wir die Größe des Ausschnitts auf dem Bildschirm durch width und height gegeben haben, dann müssen wir die x bzw. y Koordinate entsprechend durch die halbe Breite bzw. Höhe dividieren und erhalten daraus:

P^l_x = P_x {near/{width/2}} = P_x {{2 * near}/width}
P^l_y = P_y {near/{height/2}} = P_y {{2 * near}/height}

Die x und y-Koordinate hätten wir damit erledigt. Fehlt nur noch die z-Koordinate. Da die w-Koordinate nicht 1 sondern -P_z ist haben wir folgende Gleichung:

P^l_z = {a P_x + b P_y + c * P_z + d * P_w}/{-P_z}

Nachdem P^l_z unabhängig von der x und y Koordinate ist können wir a und b schon null setzen. Außerdem ist P_w stets 1 sodass sich die Gleichung vereinfacht zu:

P^l_z = {c * P_z + d}/{-P_z}

Jetzt können wir ein die bekannte Zusammenhänge an den Grenzen des Viewfrustum verwenden um die Unbekannten zu erhalten. Für P_z = -near gilt P^l_z = -1 und für P_z = -far gilt P^l_z = 1. Daher erhalten wir durch Einsetzen folgendes System von Gleichungen in c und d:

{c * -near + d}/{near} = -1
{c * -far + d}/{far} = 1

Wir drücken in der ersten Gleichung d aus und erhalten somit:

d = c * near - near

Jetzt können wir in die zweite Gleichung einsetzen:

{c * -far + c * near - near}/{far} = 1

Durch Umformen erhalten wir c:

-far * c + near * c = far + near
(near - far) * c = far + near
c = {far + near}/{near - far}

Jetzt setzen wir c in die erste Gleichung ein und erhalten d:

{{{far + near}/{near - far}} * {-near} + d}/{near} = -1
{{far + near}/{near - far}} * {-near} + d = -near
d = -near + near * {{far + near}/{near - far}}
d = ({{far + near}/{near - far}} - 1) * near
d = {{far + near - (near - far)}/{near - far}} * near
d = {{2 * far * near}/{near - far}}

Wir haben jetzt also alle nötigen Elemente der Matrix erhalten und können diese jetzt in eine Matrix eintragen:

delim{[}{matrix{4}{4}
{ {{2*near}/width} 0                 0                               0
  0                {{2*near}/height} 0                               0
  0                0                 {{far + near}/{near - far}}    {-1}
  0                0                 {{2 * far * near}/{near - far}} 0 }}{]}