3D-Programmierung (Mathematik) Teil 04: Skalierung, Translation und Rotation

Die Grundlagen der Vektorrechnung wurden bereits in den vorangegangenen Teilen dieser Artikelserie behandelt. An dieser Stelle werden wir nun anhand praktischer Überlegungen erarbeiten, wie sich die Spieleobjekte in der 3D-Welt transformieren lassen. Als Beispiel diene uns ein kleiner Raumflug.


In der nachfolgenden Abbildung ist das Gittermodell eines einfachen Raumschiffs bestehend aus sechs Eckpunkten (Vertices) in seinem Modellkoordinatensystem (model space) dargestellt.


















Skalierung
Es spricht zwar nichts dagegen, die Größe des Raumschiffs im fertigen Spiel beizubehalten, nichtsdestotrotz lässt sich ein 3D-Modell, wie in der nachfolgenden Abbildung dargestellt wird, beliebig skalieren (vergrößern oder verkleinern). Man beachte, dass die Skalierung im Modellkoordinatensystem erfolgt:

Vertex(model space, skaliert).x = scaleX * Vertex(original).x
Vertex(model space, skaliert).y = scaleY * Vertex(original).y
Vertex(model space, skaliert).z = scaleZ * Vertex(original).z


 

















Translation (Verschiebung)
Bei der Simulation des Raumflugs muss im ersten Schritt für jedes Frame die aktuelle Position des Raumschiffs im Weltkoordinatensystem (world space) – der Spielewelt – ermittelt werden. Mithilfe dieses Positionsvektors lassen sich dann die Positionen der einzelnen Modellvertices in der Spielewelt berechnen:

Vertex(world space).x = Schiff_Weltpos.x + Vertex(model space, skaliert).x
Vertex(world space).y = Schiff_Weltpos.y + Vertex(model space, skaliert).y
Vertex(world space).z = Schiff_Weltpos.z + Vertex(model space, skaliert).z

 


Betrachtet man sich die letzte Abbildung etwas genauer, so fällt auf, dass das Raumschiff nicht korrekt ausgerichtet ist. Bevor man das Schiff in der Spielewelt positionieren kann, muss zunächst die richtige Orientierung der Schiffsvertices berechnet werden, so dass Ausrichtung und Flugrichtung miteinander übereinstimmen.

Vertex(world space).x = Schiff_Weltpos.x + Vertex(model space, skaliert,
                                                  rotiert).x
Vertex(world space).y = Schiff_Weltpos.y + Vertex(model space, skaliert,
                                                  rotiert).y
Vertex(world space).z = Schiff_Weltpos.z + Vertex(model space, skaliert,
                                                  rotiert).z

Rotation
Die Drehung (Rotation) der Schiffsvertices muss genau wie die Skalierung im Modellkoordinatensystem des Schiffes durchgeführt werden. In diesem speziellen Fall erfolgt die Drehung um die z-Achse, die jedoch aus Gründen der Übersichtlichkeit nicht in die nachfolgende Abbildung eingezeichnet wurde.

 
















Es gibt jetzt mehrere Möglichkeiten, die gesuchten Rotationsgleichungen zu finden. Für gewöhnlich ist es am einfachsten, die Lösung anhand verschiedener Spezialfälle abzuleiten und diese dann auf Allgemeingültigkeit hin zu überprüfen. Genau das werden wir hier auch tun.

Spezialfall 1:

Vertex(0, 2) Drehung um 90° => Vertex(-2, 0)

Vertex.x = 0 Drehung um 90° => Vertex.x = -2
Vertex.y = 2 Drehung um 90° => Vertex.y = 0

Spezialfall 2:

Vertex(-2, 0) Drehung um 90° => Vertex(0, -2)

Vertex.x = -2 Drehung um 90° => Vertex.x = 0
Vertex.y = 0  Drehung um 90° => Vertex.y = -2

Wenn man sich die beiden Spezialfälle anschaut, fällt auf, dass bei einer Drehung um 90° die x- und y-Koordinaten miteinander vertauscht werden. Im ersten Beispiel ändert sich zudem das Vorzeichen. Wir versuchen es daher mit dem folgenden Ansatz:

Vertex(rotiert).x = a * Vertex(original).x + b * Vertex(original).y
Vertex(rotiert).y = c * Vertex(original).x + d * Vertex(original).y

Sind wir in der Lage, die vier Koeffizienten a, b, c und d zu bestimmen, dann haben wir die gesuchten Rotationsgleichungen gefunden. Die Mathematik sagt uns jetzt, dass wir hierfür vier unabhängige Bestimmungsgleichungen benötigen bzw. vier Spezialfälle betrachten müssen. Uns fehlen also noch zwei weitere Spezialfälle:

Spezialfall 3:

Vertex(0, 2) Drehung um 0° => Vertex(0, 2)

Vertex.x = 0 Drehung um 0° => Vertex.x = 0
Vertex.y = 2 Drehung um 0° => Vertex.y = 2

Spezialfall 4:

Vertex(2, 0) Drehung um 180° => Vertex(-2, 0)

Vertex.x = 2 Drehung um 180° => Vertex.x = -2
Vertex.y = 0 Drehung um 180° => Vertex.y = 0

Weiterhin nehmen wir an, dass es sich bei den gesuchten Koeffizienten um Sinus- und Kosinusfunktionen handeln könnte. Probieren wir es aus:

Vertex(rotiert).x = a * Vertex(original).x –
                    sin(90°) * Vertex(original).y

Vertex(rotiert).y = c * Vertex(original).x +/-
                    cos(90°) * Vertex(original).y

Vertex(rotiert).z = Vertex(original).z

Mit sin(90°) = 1 und cos(90°) = 0 liefert unsere Annahme für Spezialfall 1 ein korrektes Ergebnis. Bei der Bestimmung der Koeffizienten a und c ist Spezialfall 2 ganz hilfreich:

Vertex(rotiert).x = +/- cos(90°) * Vertex(original).x –
                        sin(90°) * Vertex(original).y

Vertex(rotiert).y = sin(90°) * Vertex(original).x +/-
                    cos(90°) * Vertex(original).y

Vertex(rotiert).z = Vertex(original).z

Spezialfall 3 hilft uns die Vorzeichenfrage (+/-) in der zweiten Gleichung zu lösen. Unter Berücksichtigung von sin(0°) = 0 und cos(0°) = 1 findet man:

Vertex(rotiert).x = +/- cos(0°) * Vertex(original).x –
                        sin(0°) * Vertex(original).y

Vertex(rotiert).y = sin(0°) * Vertex(original).x +
                    cos(0°) * Vertex(original).y

Vertex(rotiert).z = Vertex(original).z

Die Vorzeichenfrage in der ersten Gleichung wird schließlich mithilfe von Spezialfall 4 geklärt:

Vertex(rotiert).x = cos(0°) * Vertex(original).x –
                    sin(0°) * Vertex(original).y

Vertex(rotiert).y = sin(0°) * Vertex(original).x +
                    cos(0°) * Vertex(original).y

Vertex(rotiert).z = Vertex(original).z

Die gesuchten Gleichungen für eine Beschreibung der Rotation um die z-Achse lauten also:

Vertex(rotiert).x = cos(Drehwinkel) * Vertex(original).x –
                    sin(Drehwinkel) * Vertex(original).y

Vertex(rotiert).y = sin(Drehwinkel) * Vertex(original).x +
                    cos(Drehwinkel) * Vertex(original).y

Vertex(rotiert).z = Vertex(original).z

Im dreidimensionalen Raum kann ein Objekt um eine beliebig orientierte Achse gedreht werden. Die Gleichungen für die Rotation um die z-Achse haben wir ja gerade hergeleitet.

Kommen wir nun zur Beschreibung der Rotation um die x-Achse:

Vertex(rotiert).x = Vertex(original).x

Vertex(rotiert).y = cos(Drehwinkel) * Vertex(original).y -
                    sin(Drehwinkel) * Vertex(original).z

Vertex(rotiert).z = sin(Drehwinkel) * Vertex(original).y +
                    cos(Drehwinkel) * Vertex(original).z

Anhand dreier ausgezeichneter Winkel (0°, 90°, 180°) können diese Gleichungen veranschaulicht werden:

Gleichung 1:
Zur ersten Gleichung gibt es nichts zu sagen. Die Rotation um die x-Achse lässt natürlich die x-Komponente eines Vektors unbeeinflusst.

Gleichung 2:
Eine Drehung um 0° überführt die alte y-Komponente in die neue y-Komponente. Eine Drehung um 90° überführt die negative z-Komponente in die neue y-Komponente und eine Drehung um 180° überführt die y-Komponente in die negative y-Komponente.

Gleichung 3:
Eine Drehung um 0° überführt die alte z-Komponente in die neue z-Komponente. Eine Drehung um 90° überführt die y-Komponente in die neue z-Komponente und eine Drehung um 180° überführt die z-Komponente in die negative z-Komponente.

Die Gleichungen zur Beschreibung der Rotation um die y-Achse lauten folgendermaßen:

Vertex(rotiert).x = cos(Drehwinkel) * Vertex(original).x +
                    sin(Drehwinkel) * Vertex(original).z

Vertex(rotiert).y = Vertex(original).y

Vertex(rotiert).z = -sin(Drehwinkel) * Vertex(original).x +
                    cos(Drehwinkel) * Vertex(original).z

Gleichung 1:
Eine Drehung um 0° überführt die alte x-Komponente in die neue x-Komponente. Eine Drehung um 90° überführt die z-Komponente in die neue x-Komponente und eine Drehung um 180° überführt die x-Komponente in die negative x-Komponente.

Gleichung 2:
Zur zweiten Gleichung gibt es nichts zu sagen. Die Rotation um die y-Achse lässt natürlich die y-Komponente des Vektors unbeeinflusst.

Gleichung 3:
Eine Drehung um 0° überführt die alte z-Komponente in die neue z-Komponente. Eine Drehung um 90° überführt die negative x-Komponente in die neue z-Komponente und eine Drehung um 180° überführt die z-Komponente in die negative z-Komponente.

Die Beschreibung der Rotation um eine beliebige Achse ist um einiges anspruchsvoller und erfordert darüber hinaus Kenntnisse in Matrizenrechnung. Aus diesem Grund werden wir dieses Thema erst im nächsten Teil dieser Artikelreihe besprechen.