Kommen wir zum ersten Kapitel mit einem gewissen Anspruch :). Hier wirst du lernen, wie man auf seeehr komplizierte Weise ein Objekt bewegen kann, allerdings wird schnell klar werden, dass gerade die Grundlagen der Matrizenrechnung Basis der gesamten 3D-Engine DirectX sind und dementsprechend viel Macht besitzen, wie man auch an der Funktion zur Visualisierung eines 3D Vektors aus dem letzten Codebeispiel erkennen kann.Wir schlagen uns hier lediglich mit den sinnlosen, da auch anders möglichen, Operationen herum, weil diese einfacher zu verstehen sind. Los gehts und nicht aufgeben! |
Die erste Frage ist natürlich, was ist eigentlich eine Matrix? Nun, eine Matrix unterscheidet sich eigentlich nicht wesentlich von einem Vektor. Einen kleinen Unterschied gibt es natürlich schon; im Gegensatz zum Vektor ist eine Matrix nicht an eine Zeile/Spalte gebunden, sondern kann beliebig viele Zeilen und Spalten haben (sogar eine 3 dimensionale Anordnung der Elemente ist möglich, aber diese Matrizen würden den Rahmen des Tutorials leicht sprengen).Wir kennen Matrizen bereits, nur nicht unter diesem Namen. In Programmiersprachen werden sie meistens “Arrays” genannt und genauso funktionieren sie auch. Ein n-dimensionaler Vektor (=Matrix mit einer Zeile oder Spalte) ist im Prinzip nichts weiter als ein Array mit n Elementen (z.B. “Dim Vektor(n) as Float”).Eine Matrix mit n Zeilen und m Spalten ist demzufolge nichts anderes als ein Array der Dimension 2 (z.B. “Dim Matrix(m,n) as Float”). |
In einer 3D-Engine wie DirectX oder OpenGL werden für alle Operationen, die man an Entitäten durchführen kann, üblicherweise Matrizen mit 4 Reihen und 4 Spalten verwendet (sogenannte 4x4-Matrizen). Der Grund dafür liegt nicht unmittelbar auf der Hand, wird aber im Laufe des Tutorials klar.In DBP lassen sich grundsätzlich nur 4x4 Matrizen erstellen, mit dem Befehl: make matrix4(Nummer) Wie auch bei den Vektoren, hat das Ergebnis hier einen Rückgabewert, die Funktion muss also folgendermaßen aufgerufen werden:r = make matrix4(Nummer) Auch hier gilt, entweder r ist ungleich 0, dann hat alles geklappt oder r ist gleich 0, dann ist ein Fehler aufgetreten. |
Wie bei Vektoren auch, kann man Matrizen addieren und subtrahieren (siehe Abbildung 3 und 4). Es ist ersichtlich, dass hier dieselben Rechenregeln gelten, wie bei ganzen Zahlen (Kommutativität, Assoziativität, aber keine Distributivität, wie später gezeigt wird). Es gibt in DarkBASIC Pro keine direkte Möglichkeit eine 4x4 Matrix zu setzen oder auszulesen und das ist auch nicht notwendig, aber addieren und subtrahieren kann man sie natürlich. Die Befehle dafür lauten: add matrix4 Resultat, Matrix1, Matrix2 und subtract matrix4 Resultat, Matrix1, Matrix2 Sie tun genau das, was in Abb. 3 und 4 passiert. |
|
Auch die Multiplikation ist für Matrizen definiert, allerdings nicht ganz so einfach, wie die Addition und Subtraktion. Abbildung 5 zeigt, wie 2 Matrizen multipliziert werden. Wichtig zu erwähnen ist auf jeden Fall, dass nur 2 Matrizen bei denen die Zeile der ersten Matrix genau gleich viele Elemente hat, wie die Spalten der zweiten Matrix, multipliziert werden können.Als Faustregel gilt: Matrix1 * Matrix2 = ZEILE * SPALTE Am Beispiel aus Abbildung 5 kann man auch gut sehen, dass die Multiplikation nicht kommutativ ist, d.h. es spielt eine Rolle in welcher Reihenfolge zwei Matrizen multipliziert werden; eine wichtige Erkenntnis für die Benutzung in einer 3D-Engine. Da es in DBP sowieso nur 4x4 Matrizen gibt, lassen sich alle Matrizen unbedenklich multiplizieren (es gibt also keine Definitionslücken). DarkBASIC Pro macht das Ganze vollautomatisch, mittels des Befehlsmultiply matrix4 Resultat, Matrix1, Matrix2 Wichtig, wenn auch nicht ganz so einfach zu verstehen, ist auch, dass die Matrix-Matrix-Multiplikation für Matrizen mit identischer Zeilen- und Spaltenanzahl (z.B. 4x4- oder 3x3-Matrizen) ein neutrales Element bezüglich der Multiplikation besitzen.Als ein neutrales Element bezüglich einer gewählten Verknüpfung (hier Multiplikation) bezeichnet man ein Element, das verknüpft mit jedem beliebigen Element aus dem Definitionsbereich (hier alle 4x4-Matrizen) das Element selbst ergibt. Das ist jetzt harte Kost, ich weiß, aber ich will zum einfacheren Verständnis ein paar Beispiele angeben:Im Zahlenbereich der natürlichen Zahlen (das sind alle ganzen, positiven Zahlen inklusive 0) ist das neutrale Element bezüglich der Multiplikation “1”, denn (sprich “mit n Element der natürlichen Zahlen”). Im selben Zahlenbereich ist das neutrale Element bezüglich der Addition “0”, denn.Genau so funktioniert das im “Zahlenbereich” aller 4x4-Matrizen, in dem die Elemente reelle Zahlen (Float) sind, auch. Hier ist das neutrale Element der Multiplikation die sogenannten Identitätsmatrix. Abbildung 6 zeigt, wie sie aussieht und in Abbildung 7 wird sie mit einer beliebigen 4x4-Matrix multipliziert.Wer mir nicht glaubt (und das kann ich gut verstehen), rechnet einfach mal das Beispiel schriftlich durch :). Auch diese lässt sich in eine beliebige Matrix via DBP reinkopieren und zwar mit dem Befehlset identity matrix4 Resultat Die Anwendung dieses Befehls erzeugt die Matrix aus Abbildung 6.ACHTUNG: Die Matrix der Variablen “Resultat” muss erst mittels make matrix4 erstellt werden. Auch die Überprüfung, ob eine gewählte Matrix die Identitätsmatrix ist, lässt sich mit einem DBP-Befehl durchführen.Resultat = is identity matrix4(Matrix) Resultat wird genau dann 1, wenn “Matrix” die Identitätsmatrix ist, ansonsten 0. |
|
Wie bei Vektoren auch, können auch zwei Matrizen auf Gleichheit überprüft werden, indem der Befehl Resultat = is equal matrix4(MatrixA, MatrixB) verwendet wird.Auch hier ist das Resultat entweder 1 oder 0 und es werden alle 16 Komponenten auf Gleichheit geprüft. |
So, jetzt wissen wir, wie man zwei Matrizen addiert, subtrahiert, multipliziert und vergleicht und haben auch schon etwas von der Identität gehört. Eine wichtige Frage bleibt aber schon noch... Was soll ich mit dem Krempel?Wie ich schon angedeutet hatte, kann man mithilfe von Matrizen Sachen im Raum verschieben. Wenn man jetzt zum Beispiel möchte, dass ein Objekt im 3D-Raum erst bewegt und danach rotiert wird, kann man effektiv diese Bewegung aufsplitten, indem man eine Matrix für die Bewegung erstellt und danach eine für die Rotation. Dann werden einfach beide Matrizen multipliziert und wir erhalten die Matrix, die die gewünschte Reaktion beim Objekt hervorruft. Auf diese Art und Weise, kann man alle Bewegungen in einer Matrix simulieren ohne sie real durchzuführen (man multipliziert nur Matrizen, kein großer Rechenaufwand).Danach wendet man einfach die entstandene Matrix auf das Objekt an und dieses befindet sich an der erwarteten Position. Nun wirft sich eine interessante Frage auf... Wenn man Matrizen nicht manuell setzen kann, was nützt es einem dann, dass sie mit DBP addiert, subtrahiert und multipliziert werden können?Nun, man kann sie natürlich manuell setzen, allerdings nur über Zwischenbefehle, die jetzt besprochen werden. Es ist nicht notwendig die theoretischen Ansätze zu verstehen, um die Befehle später zu nutzen, aber schaden tut es definitiv nicht :). |
Wie man die Identitätsmatrix in die aktuelle 4x4-Matrix kopiert, haben wir ja schon gesehen. Es gibt daneben auch noch andere Matrizen, die sich in die Matrix kopieren lassen, zum Beispiel die sogenannte Translationsmatrix. Eine Translationsmatrix, ist eine Matrix der Form von Abbildung 8 und dafür gedacht, ein Objekt zu bewegen (wie der Name schon sagt: 'Translation').Wenn man diese Matrix jetzt mit einem beliebigen Punkt im Raum (repräsentiert durch einen Spaltenvektor) multipliziert, wird dieser automatisch verschoben (Abb. 9); an sich jetzt nichts spektakuläres, da man ihn auch durch Addition der Koordinaten verschieben könnte. Wir werden später sehen, dass das trotzdem sehr nützlich ist! Die DBP-Syntax dafür lautet:translate matrix4 Resultat, X, Y, Z Dieser Befehl produziert die Matrix aus Abbildung 8.Hier kann man übrigens auch sehr gut erkennen, dass das Matrix-Matrix-Produkt nicht kommutativ ist (die Reihenfolge der Faktoren ist ausschlaggebend), da die Multiplikation in umgekehrte Richtung gar nicht definiert ist! |
|
Wesentlich spektakulärer als die simple Verschiebung von Objekten, die man auch einfach mit den normalen DBP-Befehlen realisieren könnte, ist da natürlich zweifelsfrei die Rotation um eine gewählte Achse (X, Y oder Z). Jetzt denkt sich sicher der eine oder andere, naja, also ein Objekt drehen kann ich doch auch einfach mit “Rotate Object”, diese Art von Rotation meine ich jetzt aber nicht, sondern ich spreche über eine Rotation um eine feste Achse, irgendwo im Raum, nicht über eine Rotation um die körpereigene Achse (siehe Beispielprogramm).Abbildung 10, 11 und 12 zeigen, wie die speziellen Rotationsmatrizen für die drei Achsen aussehen und in Abbildung 13 wird eine Multiplikation mit einem Vektor durchgeführt, der zeigt, dass das System funktioniert. Der Winkel Alpha steht in allen Fällen für den Winkel, um den gedreht werden soll. Wie diese Matrizen zustande kommen wird auf anderen Websites ausführlich erklärt und soll für dieses Tutorial nicht weiter von Bedeutung sein, wer jedoch irgendwann einmal eine eigene 3D-Engine entwickeln will, sollte sich ausführlich darüber informieren.In DBP braucht man sich diese komplizierten Konstrukte nicht zu merken, hier kann man die Rotationsmatrizen direkt mit einem Befehl erzeugen. Für die einzelnen Achsen: rotate x matrix4 Resultat, Wert# rotate y matrix4 Resultat, Wert# rotate z matrix4 Resultat, Wert# oder für alle Achsen auf einmal:rotate ypr matrix4 Resultat, X#, Y#, Z# Wichtig bei der Benutzung der Befehle ist, dass die einzelnen Drehwerte nicht in Grad, sondern in Bogenmaß angegeben werden müssen. Es muss also zuerst die Umrechnung von Grad in Bogenmaß erfolgen, einfach indem 3.1415/180.0 mit der Gradzahl multipliziert wird.Ansonsten erfolgt alles so, wie in den Abbildungen 10, 11, 12 und 13 gezeigt. |
|
Alleine mit der Matrix können wir allerdings noch nicht viel anfangen, erst in Verbindung mit anderen Transformationen entwickeln sie ihre volle Power. Dem einen oder anderen ist es sicher schon aufgefallen... ich sagte im letzten Abschnitt man könne die Rotationsachse frei im Raum positionieren, aber nirgendwo ist ein Parameter für diese Position angegeben!Die Rotationsachse bei der “reinen” Rotationsmatrix befindet sich im Ursprung, also in der Nullstelle der Achsen. Diese Position kann man jetzt ganz einfach verschieben, also transformieren; mit der Translationsmatrix von oben. Wir erstellen also ganz einfach eine Translationsmatrix mit translate matrix4 Resultat, X, Y, Z multiplizieren diese mit der Rotationsmatrix vom letzten Abschnitt und voilá, fertig ist eine echte Transformationsmatrix!Abbildung 14 zeigt, wie das von statten geht. Abbildung 15 demonstriert ganz deutlich, dass die Reihenfolge der Transformationen wichtig ist.Die entstandene Transformationsmatrix lässt sich übrigens
jetzt noch weiterbearbeiten, also weiterverschieben und rotieren. |
|
Zusätzlich zu diesen Modifikationen lassen sich außerdem noch Skalierungen mit speziellen Matrizen durchführen. Diese Matrizen sind von der selben Form, wie die aus der Abbildung 16 und skalieren im Prinzip nur die jeweiligen Achsen um die angegebenen Werte (d.h. man sollte sich immer darüber im klaren sein, wo der Ursprung gerade ist, wenn man davor oder danach noch andere Transformationen via Matrixmultiplikation anwendet). In DBP macht man so etwas mit dem Befehlscale matrix4 Resultat, ScaleX#, ScaleY#, ScaleZ# der die Skalierungsmatrix in die angegebene 4x4-Matrix kopiert.Die Skalierungswerte sind dabei nicht in Prozent, sondern als Floatwerte anzugeben, wobei 1.0 genau 100% entsprechen (keine Änderung). Selbstverständlich kann man diese Zahl beliebig groß/klein gewählt werden und die Matrix lässt sich natürlich auch in andere Transformationen, wie Rotation und Bewegung mittels Multiplikation einbinden. |
Mit diesen Funktionen lassen sich schon gute Orbitfunktionen schreiben, wie die im Beispielprogramm, aber so spektakulär ist das alles noch nicht. Wer bis hier her gekommen ist, hat aber das Gröbste überstanden und meinen Respekt. Ab jetzt folgt der interessante Teil, vorausgesetzt man hat dieses Kapitel verstanden. |