DarkBASIC Pro

3DMaths Secrets Uncovered


Ja, ich gebe zu, dass ich gemein bin. Bei jedem Beispielcode in jedem Kapitel habe ich mindestens einen Befehl verwendet, den ich bis dahin noch nicht erläutert hatte und frech kommentiert, dass die Erklärung "in den nächsten Kapiteln" folgen würde.

So auch im letzten Code, einmal in der Orbitfunktion und dann noch die Kamerafahrt, aber keine Sorge, die Reihe ist bald beendet, so dass ich nicht weiter vertrösten können werde ;).

Es ging im letzten Kapitel ausschließlich um Matrizen und ich habe bis jetzt noch keine Möglichkeit angegeben, wie man die Daten aus den Dingern wieder herausbekommt, nachdem man sie ja nun nicht einfach lesen kann.

Natürlich gibt es (einfache) Möglichkeiten, um die Matrizen hinterher auch wieder zu lesen; man kopiert die relevanten Daten einfach in einen Vektor, mittels der Befehle

transform coords vector2 Resultat, Quelle, 4x4Matrix

transform coords vector3 Resultat, Quelle, 4x4Matrix

transform coords vector4 Resultat, Quelle, 4x4Matrix

je nachdem, wieviele Dimensionen man bearbeitet.

Der Befehl tut eigentlich nichts weiter, als die Rechnung aus Abbildung 1 auszuführen und das Ergebnis in den resultierenden Vektor zu kopieren.

Abhängig davon, welche Dimension man beim Befehl angibt, wird der resultierende Vektor dann unten abgeschnitten (sprich, ein 2D-Vektor gibt nur die ersten beiden Komponenten aus).

Das hat den Vorteil, dass man die 3D-Maths-Befehle auch im 2 dimensionalen Raum prima nutzen kann.

Multiplikation einer 4x4Matrix mit einem Vektor

Kommen wir nun zum großen Abschnitt der Interpolationsbefehle in DarkBASIC Pro, aber was bedeutet Interpolation überhaupt?

Stellen wir uns dazu folgendes Szenario vor...

Wir möchten eine Kamerafahrt für irgendeine Art von Programm erstellen; Wie könnte man so etwas angehen?

Die primitivste Lösung ist sicherlich, alle Punkte einzeln in einem Array zu speichern und die Kamera einfach an alle Positionen im Array zu setzen, sprich, das Array abzufahren.

Das funktioniert, ist aber unter Umständen eine Mordsarbeit und wenn noch etwas Dynamisches dazukommen sollte, sogar unmöglich zu realisieren (es gibt da natürlich Möglichkeiten, passende Tools zu schreiben, wie Stephan und Kulle sicher wissen, aber wir sind faul).

Besser ist da die Idee, nur bestimmte Punkte auszuwählen, bei denen man sicherstellen will, dass sie durchlaufen werden, und den Weg dazwischen einfach berechnen zu lassen (wozu hat man schließlich einen Computer?).

Die Funktion, die den Weg zwischen den Punkten berechnet, nennt man Interpolante und den gesamten Vorgang Interpolation.

Auch hier gibt es wieder Differenzierungsmöglichkeiten.
Will man beispielsweise die Punkte einfach durch gerade Strecken miteinander verbinden, dann stellt einem DarkBASIC im Rahmen der 3D-Maths-Befehle die sogenannte lineare Interpolation zur Verfügung.

Die Befehle dafür lauten:

linear interpolate vector2 Resultat, Vektor1, Vektor2, SWert

linear interpolate vector3 Resultat, Vektor1, Vektor2, SWert

linear interpolate vector4 Resultat, Vektor1, Vektor2, SWert

Vektor 1 und Vektor 2 stehen stellvertretend für die Stützstellen, d.h. für die Punkte zwischen denen interpoliert wird, SWert ist eine Kommazahl zwischen 0 und 1 und gibt an, wieviel Prozent dazwischen interpoliert wird und Resultat ist der resultierende Vektor des berechneten Punktes. Vektor 1 und Vektor 2 müssen natürlich auf jeden Fall von der selben Dimension sein, wie das Resultat!

Der Befehl tut genau das, was in Abbildung 2 steht.

Eine Visualisierungsmöglichkeit im 2D und 3D Raum befindet sich im Beispielcode dieses Kapitels.

Formel für lineare Interpolation
Beispiel für lineare Interpolation
Darstellung der linearen Interpolation im 2D-Raum

Offensichtlich ist das eine Möglichkeit, eine primitive Kamerafahrt zwischen festen Punkten zu ermöglichen, aber mal ehrlich; so eine starre Fahrt bringt doch nichts und sieht auch äußerst kantig aus.

Es gibt selbstverständlich wesentlich bessere Lösungsansätze, die nicht nur die Punkte betrachten, die verbunden werden sollen, sondern auch so eine Art von Geschichte des Systems, also davorliegende und nachfolgende Punkte (sonst kann man keine runden Verbindungen erzeugen, ist ja klar).

Eine weitere, wesentlich bessere Variante ist hier die sogenannte Catmullrom-Interpolation.

Sie funktioniert schon etwas komplizierter und wir wollen uns nicht mit der Herleitung dieses Polynoms quälen, aber das müssen wir zum Glück ja auch nicht, da DBPro alles bereitstellt. Die folgenden Befehle lösen unser Problem:

catmullrom vector2 Resultat, Vektor1, Vektor2, Vektor3, Vektor4, SWert

catmullrom vector3 Resultat, Vektor1, Vektor2, Vektor3, Vektor4, SWert

catmullrom vector4 Resultat, Vektor1, Vektor2, Vektor3, Vektor4, SWert

Vektor 1 bis Vektor 4 sind hierbei die Ortsvektoren der Stützstellen und die Funktion interpoliert zwischen Vektor 2 und Vektor 3, wobei SWert wieder einen Prozentwert zwischen den beiden angibt, also zwischen 0 und 1.

Vektor 1 und Vektor 4 sind nur dafür da, damit eine echte Kurve herauskommt und nicht so etwas wie bei der linearen Interpolation.

In Abbildung 4 steht noch, wie die Befehle funktionieren (die Vektoren werden in den jeweiligen Dimensionen skaliert).

Formel für CatmullromInterpolation
Beispiel für CatmullromInterpolation
Darstellung der CatmullromInterpolation im 2D-Raum

Na das sieht doch schon nach was aus, aber dem einen oder anderen, mich eingeschlossen, ist es jetzt viel zu rund. So eine richtig gute Kamerafahrt braucht einfach mehr Action, also auch vor allem geschwungenere Kurven!

Und auch hier enttäuscht uns DarkBASIC Pro nicht, es gibt nämlich noch eine andere Interpolationsmethode, genannt Hermite-Interpolation.

Auch diese Form ist ein Polynom und auch hier wollen wir uns nicht mit der Herleitung plagen, denn die folgenden Befehle nehmen uns die Arbeit ab:

hermite vector2 Resultat, Vektor1, Tangente1, Vektor2, Tangente2, SWert

hermite vector3 Resultat, Vektor1, Tangente1, Vektor2, Tangente2, SWert

hermite vector4 Resultat, Vektor1, Tangente1, Vektor2, Tangente2, SWert

Das sieht doch fast so aus, wie oben, ist es aber nicht!

Hermite vector interpoliert standardmäßig zwischen Vektor 1 und Vektor 2 und nicht, wie oben, zwischen Vektor 2 und Vektor 3. Die Startrichtung der Kurve wird dabei durch die erste Tangente angegeben, die Endrichtung durch die zweite.

Durch diese Herangehensweise wird die Kurve natürlich um einiges geschwungener, da die Start-/Endrichtungen praktisch frei wählbar sind und man erhält zusätzliche Flexibilität.

Abbildung 6 zeigt nochmal eine mögliche Implementationsform, die DBP aber offensichtlich nicht so verwendet (davon gibts für Hermite viele, da man auch dort nochmal differenzieren kann; tun wir aber nicht ;) ).

Formel für HermiteInterpolation
Beispiel für HermiteInterpolation
Darstellung der HermiteInterpolation im 2D-Raum

Na also, da haben wir doch unsere Kamerafahrt schön dynamisch und billig (ich meine den Rechenaufwand ;) ).

Die Bilder sind übrigens alle mit DBPro generiert, mit zufälligem Seedwert unter Benutzung der besprochenen Funktionen.

Damit sind wir jetzt fertig mit den Interpolationsmöglichkeiten in DBPro.

Puh, jetzt bleiben wirklich nicht mehr viele Befehle, die zu erklären sind :). Zeit für eine Verschnaufpause und für schön leichte Befehle, wie zum Beispiel:

bcc vector2 Resultat, Ortsvektor, Vektor1, Vektor2, ScaleO1, ScaleO2

bcc vector3 Resultat, Ortsvektor, Vektor1, Vektor2, ScaleO1, ScaleO2

bcc vector4 Resultat, Ortsvektor, Vektor1, Vektor2, ScaleO1, ScaleO2

Ich hatte zugegebenermaßen beim Lesen der DBPro-internen Hilfe keine Ahnung, was ein "BaryCentricCoordinates Vector" sein sollte, aber nach einigem Experimentieren kam ich dann auf die Lösung.

Dieser Befehl berechnet lediglich die Ebenengleichung aus Abbildung 8 in 2, 3 und 4 dimensionalen Räumen, d.h. man gibt einen festen Punkt im Raum in Form eines Ortsvektors an (ein Offset), zwei linear unabhängige Vektoren sowie die Prozentwerte der Vektoren 1 und 2 und bekommt den Punkt in Form eines Vektors heraus, der sich im Referenzkoordinatensystem ergibt (also das mit den Achsenvektoren, im 3D-Raum z.B. [1,0,0], [0,1,0] und [0,0,1]).

Dieser Punkt liegt dann natürlich in der Ebene, die diese zwei Vektoren mit dem dritten Ortsvektor aufspannen.

Funktionsweise von BCC

Es kommt ab und zu vor, dass man sich auch für das Kreuzprodukt von zwei 2-dimensionalen Vektoren interessiert, obwohl diesem nicht die Bedeutung zu kommt, wie im 3D-Raum, in dem das Kreuzprodukt eine Normale auf der von den 2 Vektoren aufgespannten Ebene ist.

Da man wie im 3D-Raum über Kreuz multipliziert, kommt bei der Multiplikation von 2D-Vektoren kein Vektor, sondern ein Skalar (eine Zahl) heraus.

Abbildung 9 zeigt warum.

In DBP gibt es extra dafür den Befehl

Resultat = ccw vector2(Vektor1, Vektor2)

der genau das tut, was das 3D-Äquivalent cross product vector3 auch macht, nur in einer anderen Dimension (2).

Auch hier ist das Ergebnis eine Projektion, d.h. es kommt genau dann 0 heraus, wenn V1 und V2 linear abhängig zueinander sind.

Funktionsweise vom kartesischen Produkt in der zweiten Dimension

Damit haben wir auch dieses Kapitel erfolgreich abgeschlossen und der Leser kann sich noch auf ein letztes freuen, das den Kern der Engine selbst etwas erleuchten wird!

Alles, was bisher kam, war schon nicht schlecht, aber die richtige Power kommt erst mit dem direkten Draht zur Engine. Man darf gespannt sein!


Nächstes Kapitel: Enginekern
© D-Man