3D-Programmierung (Mathematik) Teil 02: Vektoren

Zentrale Elemente bei der mathematischen Beschreibung von Spieleobjekten sind Vektoren und Matrizen. In Teil 1 dieser 3D-Mathematik-Serie wurde bereits kurz auf Orts- und Geschwindigkeitsvektoren eingegangen. In Teil 2 soll der Umgang mit Vektoren vertieft werden. Zur Wiederholung, grafisch lässt sich ein Vektor als ein Pfeil veranschaulichen. Ein Vektor besitzt zwei Eigenschaften – Länge (Betrag) und Richtung. Besonders anschaulich lässt sich das am Beispiel der Geschwindigkeit erklären. Der Betrag des Geschwindigkeitsvektors kann am Tachometer abgelesen werden. Die Richtung des Geschwindigkeitsvektors entspricht der Fahrtrichtung.

Die einzelnen Komponenten eines Vektors lassen sich entweder in Zeilenform oder in Spaltenform darstellen:
 



Hinweis:
An dieser Stelle erscheint die Unterscheidung zwischen Zeilen- und Spaltenvektoren völlig überflüssig. Aber spätestens dann, wenn man sich mit dem Aufstellen von Transformationsmatrizen (Skalierung, Rotation und Translation) beschäftigt, spielt diese Unterscheidung eine wichtige Rolle.

Die DirectX-Mathebibliothek stellt dem Programmierer Klassen für den Umgang mit zwei-, drei- und vierdimensionalen Vektoren zur Verfügung:
D3DXVECTOR2, D3DXVECTOR3, D3DXVECTOR4.

Die einzelnen Komponenten eines Vektors lassen sich mit einem Skalar (einer Zahl) multiplizieren bzw. durch eine Zahl dividieren, wobei sich die Vektorlänge verändert:
 
Aus der Sicht des Programmierers kann die skalare Multiplikation dank Operatorüberladung sehr leicht durchgeführt werden:

D3DXVECTOR3 Vector1, Vector2;

Vector2 = 5.0f*Vector1;

// bzw.

Vector2 = Vector1;
Vector2 *= 5.0f;

Die Addition und Subtraktion von Vektoren erfolgt komponentenweise:

 
 


Auch hier stehen dem Programmierer für die praktische Umsetzung dank Operatorüberladung mehrere Möglichkeiten zur Auswahl:

D3DXVECTOR3 Vector1, Vector2, Vector3;

Vector3 = Vector1 + Vector2;

// bzw.

Vector3 = Vector1;
Vector3 += Vector2;

Die Rechenoperationen lassen sich wie folgt geometrisch veranschaulichen:

(Vektoraddition)


(Vektorsubtraktion)

Multipliziert man zwei Vektoren komponentenweise und addiert die einzelnen Produkte, dann erhält man das Skalarprodukt:

  
INLINE FLOAT D3DXVec3Dot
       (CONST D3DXVECTOR3 *pV1, CONST D3DXVECTOR3 *pV2)
{
    return pV1->x*pV2->x +
           pV1->y*pV2->y +
           pV1->z*pV2->z;
}


Bildet man das Skalarprodukt eines Vektors mit sich selbst, dann ergibt sich der quadratische Betrag des Vektors:

  
INLINE FLOAT D3DXVec3LengthSq
       (CONST D3DXVECTOR3 *pV)
{
    return pV->x*pV->x +
           pV->y*pV->y +
           pV->z*pV->z;
}

Hinweis:
Das Skalarprodukt zweier senkrechter (linear unabhängiger, orthogonaler) Vektoren ist Null.
Das Skalarprodukt zweier paralleler Vektoren ist gleich dem Produkt der Beträge der beiden Vektoren.
Das Skalarprodukt zweier antiparalleler Vektoren ist gleich dem Produkt der Beträge der Vektoren mal -1.

Das Skalarprodukt lässt sich offensichtlich berechnen, indem man die Beträge der Vektoren miteinander multipliziert und zudem einen Faktor berücksichtigt, der sich aus dem Winkel zwischen den Vektoren ergibt. Dieser Faktor ist gleich 1, wenn der Winkel zwischen den Vektoren 0° ist, gleich 0, wenn der Winkel zwischen den Vektoren 90° ist, und gleich -1 bei einem Winkel von 180°. Die Kosinusfunktion erfüllt genau diese Bedingungen (cos 0° = 1, cos 90° = 0, cos 180° = -1):

 
Durch die Berechnung der Quadratwurzel des Skalarprodukts eines Vektors mit sich selbst erhält man den Betrag des Vektors:

 
Mit Hilfe seines Betrags kann ein Vektor normiert, also in einen Einheitsvektor verwandelt werden. Man muss lediglich alle Komponenten durch den Betrag des Vektors teilen:

  
INLINE D3DXVECTOR3* D3DXVec3Normalize
      (D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV)
{
    float length = sqrtf(pV->x*pV->x +
                         pV->y*pV->y +
                         pV->z*pV->z);

    length = 1.0f/length;

    *pOut = *pV * length;
    return pOut;
}

Im dreidimensionalen Raum lässt sich ein weiteres Produkt zweier Vektoren definieren – das Vektorprodukt oder Kreuzprodukt. Wie der Name schon andeutet, ergibt das Vektorprodukt zweier Vektoren a und b einen Vektor c, der noch dazu senkrecht auf a und b steht. Mathematisch kennzeichnet man dieses Produkt durch ein Kreuz.

 


Der Betrag des Vektors c ist gleich dem Flächeninhalt der Fläche A, die von den Vektoren a und b aufgespannt wird:


(Vektorprodukt)

Die Größe dieser Fläche ist proportional zu den Beträgen dieser Vektoren sowie zum Sinuswert des von ihnen eingeschlossenen Winkels:




Der Abbildung entnimmt man, dass der Flächeninhalt bei Parallelität der Vektoren a und b verschwindet (sin 0° = 0) und bei Orthogonalität (orthogonal: senkrecht) den maximal möglichen Wert annimmt (sin 90° = 1). Weiterhin ist diese Fläche vorzeichenbehaftet, was sich in der Richtung des Vektors c ausdrückt. Ist der Winkel größer als 180°, zeigt c in die entgegengesetzte Richtung und die Fläche erhält entsprechend ein negatives Vorzeichen (der Sinuswert eines Winkels größer als 180° ist immer negativ):
(negatives Vektorprodukt)

Anstatt mit einem Winkel größer als 180° zu hantieren, kann man auch mit dem negativen Winkel arbeiten (z. B. -30° anstelle von 330°).

Die Komponenten des Produktvektors c berechnen sich nun wie folgt:






INLINE D3DXVECTOR3* D3DXVec3Cross
      (D3DXVECTOR3 *pOut, CONST D3DXVECTOR3 *pV1,
       CONST D3DXVECTOR3 *pV2)
{
    D3DXVECTOR3 v;

    v.x = pV1->y*pV2->z - pV1->z*pV2->y;
    v.y = pV1->z*pV2->x - pV1->x*pV2->z;
    v.z = pV1->x*pV2->y - pV1->y*pV2->x;

    *pOut = v;
    return pOut;
}