[OpenGL/Mathe] Rotation um zwei Achsen - again

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
Eisflamme
Establishment
Beiträge: 412
Registriert: 26.05.2002, 17:42
Wohnort: Köln

[OpenGL/Mathe] Rotation um zwei Achsen - again

Beitrag von Eisflamme »

Hi,

Die Diskussion gab es vor kurzem, also vor ein paar Jahren, glaube ich, schon Mal.

Und zwar möchte ich in OpenGL eine Kamera installieren, welche natürlich auch Rotationen kombiniert mit Bewegungen anbietet.

Mir reicht fürs Erste, wenn ich um die y-Achse rotieren kann und um die x-Achse, wobei sich die Laufrichtung bei der Rotation um die x-Achse nicht ändern soll, sprich, man beim Hochgucken nicht fliegt.

Jetzt habe ich das bekannte Problem, dass bei zwei Rotationen auf verschiedene Achsen ausgeführt, auch eine Rotation um die dritte Achse geschieht, weil das Ausgangskoordinatensystem für die zweite Rotation nicht mehr das ursprüngliche Koordinatensystem ist.

Ich würde das gerne mathematisch lösen, weil ich meine Matrizen dann irgendwie besser verwalten und speichern kann etc. Im Prinzip ist die Multiplikation der beiden Rotationsachsen nicht ganz korrekt. Dazu zwei Ideen:

1) Man müsste irgendwie Rotation 1 und Rotation 2 unabhängig voneinander ausführen und zusammenführen, aber nicht mit der üblichen Multiplikation, die ja die Effekte nacheinanderschaltet, sondern durch eine Art Addition. Da aber alle drei Rotationsmatrizen z.T. gemeinsame Matrix-Elemente verwenden, wäre für die Felder das Ergebnis zweifelhaft.
2) Man müsste das, was Rotation 2 zu viel rotiert, wieder in einer Rotation 3 um die 3. Achse (also z-Achse) rückgängig machen. Dafür muss man aber den Teil der unerwünschten Änderung von Rotation 2 wissen... keine Ahnung, wie das gehen soll. ;)
3) Man löst das ganze einfach mathematisch und bastelt sich eigene Matrizen, in welche man x/y/z-Rotation einbauen kann. Kann auch sein, dass es diese ultimative Matrix bereits gibt, müsste man sich ergooglen. Blöd ist, dass man für jede Rotation alle drei Matrizen auf einmal berechnen muss, was zu Performanceeinbußen führen könnte (aber durch sin/cos-Tabellen evtl. vernachlässigbar?)

Hab ein wenig nach dem Thema gegooglet. Ein Tutorial ist hier: http://jerome.jouvie.free.fr/OpenGl/Tut ... rial26.php

Das nutzt gluLookAt und glu nutze ich nicht, daher fehlt mir hier ein Puzzleteil.

Freue mich auf Antworten :)
Eisflamme
Establishment
Beiträge: 412
Registriert: 26.05.2002, 17:42
Wohnort: Köln

Re: [OpenGL/Mathe] Rotation um zwei Achsen - again

Beitrag von Eisflamme »

Ok, hab gerade erst kapiert, dass ich bei meinem Beispiel eigentlich nicht mehr brauche, da ich einfach erst um Y-Achse und dann um X-Achse rotieren kann, da es ja so gedacht ist, dass man "das Ziel aus den Augen verliert", also beim Hoch- und Runterschauen nicht die Blickrichtung gleich bleiben soll.

Trotzdem wüsste ich gerne, wie ich alles Erdenkliche herbasteln könnte. Denkbar wäre für mich auch eine LookAt-Kamera mit ner Position. Ich kann die Winkel berechnen, kein Ding, aber dann bräuchte ich alle drei Rotationen ausgehend vom originalen Koordinatensystem (wie kürzt man das Wort noch gleich ab?) und ich hätte das alte Ding.
joeydee
Establishment
Beiträge: 1042
Registriert: 23.04.2003, 15:29
Kontaktdaten:

Re: [OpenGL/Mathe] Rotation um zwei Achsen - again

Beitrag von joeydee »

Tipps welche mich in Bezug auf Rotationen im Raum (Anwendung Bot-Steuerung bzw. Flugsimulation) immer weitergebracht und eigentlich nie in Sackgassen geführt haben:

- Jedes Objekt in der Welt speichert eine Matrix, worin alle bisherigen Transformationen gespeichert sind. Keine Winkel, das wäre überdefiniert.
- Dass jede Spalte einer 4x4-Matrix als Vektor gesehen werden kann, und zwar der Reihenfolge nach Objekt-X, -Y, -Z sowie -position, während die Richtungen 0 und die Position eine 1 als vierten Wert haben, sollte bekannt sein.
- Zwei Funktionen anlegen: prependRotation() und appendRotation(), damit unterscheidest du sinnvoll ob du in den Weltkoordinaten oder in den Objektkoordinaten drehst. Prepend heißt, drehen BEVOR die bereits vorhandenen Transformationen berücksichtigt werden, append heißt NACH allen vorhandenen Transformationen.
- Dabei ist noch wichtig, ob die aktuelle Position 0 ist oder nicht.
- D.h. willst du aufrecht stehenbleiben: Objektposition auf 0 (Welt-Ursprung) setzen, um Welt-Y drehen (prepend), um Objekt-X drehen (append), Objekt wieder an die alte Position setzen.
- D.h. willst du frei fliegen: nur um die Objektachsen drehen, versetzen nicht notwendig.
- Um in eine bestimmte Objektrichtung zu gehen/fliegen (z.B. "nach vorn"), brauchst du keine komplette Matrix aufzustellen: einfach die Z-Spalte der aktuellen Objektmatrix auslesen, mit dem Speed multiplizieren und zur Positionsspalte addieren. Ist ne abgekürzte Multiplikation (und als solche zu sehen), aber ohne die ganzen Leer-Operationen.
- Kleine Schritte pro Zeiteinheit (Frame) relativieren das Problem der Reihenfolge bei aufeinanderfolgenden Transformationen. Ob du nun einen Bruchteil eines Grades drehst (A) und dich anschließend um einen Bruchteil eines Meters nach vorn bewegst (B) oder umgekehrt ist zwar mathematisch ein anderes Ergebnis, aber in schneller Folge intuitiv dasselbe, denn A*B*A*B*A*B bzw. B*A*B*A*B*A unterscheiden sich bei kleinen Werten nur noch minimal.
- Je nach Rechengenauigkeit des eingesetzten Systems und beabsichtigter Laufzeit des Programms empfiehlt es sich, die Objektmatrix regelmäßig zu normalisieren (Richtungsvektoren normalisieren und per Kreuzprodukt senkrecht zueinander ausrichten).
- Eine Kamera ist auch nur ein Objekt. Zum Rendern wird die invertierte Objektmatrix der Kamera vor die Objektmatrix jedes zu rendernden Objekts multipliziert.
- Zwischen zwei Matrizen interpolieren kann man nicht einfach so, hierzu rechnet man am Besten in Quaternions um. Generell empfehlenswert für jegliche freie Rotationsanwendungen.
Eisflamme
Establishment
Beiträge: 412
Registriert: 26.05.2002, 17:42
Wohnort: Köln

Re: [OpenGL/Mathe] Rotation um zwei Achsen - again

Beitrag von Eisflamme »

Wow, das sind sehr hilfreiche Informationen. Ich glaube, ich bin aber noch einen Schritt zu weit weg, um alles zu verstehen. :) Ich schmeiß Mal um die Ohren, was mir nicht ganz klar ist:

prepend und append fügen die Rotationen anscheinend ja in eine Liste von Operationen ein oder so, richtig? Bisher definiert sich pre oder a bei mir ja einfach durch die Reihenfolge der Aufrufe. Ich habe für meine Kameraklasse gerade etwas wie applyMatrix() und das wird halt vor jedem Objekt aufgerufen, danach folgen die lokalen Matrixaufrufe, sprich, wenn sich ein Objekt gerade am Bewegen ist irgendwo hin o.ä.

Das mit dem auf 0 setzen, drehen und wieder zurücksetzen macht an sich Sinn. Ich hatte nur zuletzt damit zu kämpfen, dass das Zurückbewegen ja mit anderen Werten geschehen muss, da die translate-Methode sich ja auf die gedrehten Achsen bezieht. Aber ich nehme an, dass ich das richtig mache, ist bereits mit eingeschlossen in dem Gedanken?
Um in eine bestimmte Objektrichtung zu gehen/fliegen (z.B. "nach vorn"), brauchst du keine komplette Matrix aufzustellen: einfach die Z-Spalte der aktuellen Objektmatrix auslesen, mit dem Speed multiplizieren und zur Positionsspalte addieren. Ist ne abgekürzte Multiplikation (und als solche zu sehen), aber ohne die ganzen Leer-Operationen.
Aber vorne definiert sich ja nicht ausschließlich um die z-Achse, sondern kann ja auch gewisse x- oder y-Anteile haben, je nachdem, wo ich hinschaue, da dürfte die Begrenzung auf z doch nicht zielführend sein.

Ich glaube, was ich noch anders mache, als du gerade meinst, ist, dass ich gerade nicht relativ bewege, sondern in jedem Bewegungsschritt die Matrizen zur Identity-Matrix zurücksetze und dann erneut berechne mit den absoluten Koordinaten. Ist das performancefressender? Wenn ich von der aktuellen Objektmatrix sowieso zurücksetzen müsste, rotieren und wieder hinsetzen müsste, kann ich das doch auch gleich jedes Mal neu machen.
Eine Kamera ist auch nur ein Objekt. Zum Rendern wird die invertierte Objektmatrix der Kamera vor die Objektmatrix jedes zu rendernden Objekts multipliziert.
Genau. Ich habe eigentlich gar keine Kameramatrix mehr, sondern lagere alle Rotationen und Transformationen gerade direkt in die umgekehrte Richtung für alle Objekte.

Quaternions klingen interessant, jetzt sagt aber Wiki, dass OpenGL das mit glRotatef bereits nutzt. Da habe ich aber das Gefühl, einen Schritt zurückzugehen. Aber ich habe mich damit noch nicht eingehend beschäftigt, werde ich wohl noch tun müssen.

Danke erstmal so weit für die vielen Punkte! Das hilft sicher schon Mal etwas weiter!
joeydee
Establishment
Beiträge: 1042
Registriert: 23.04.2003, 15:29
Kontaktdaten:

Re: [OpenGL/Mathe] Rotation um zwei Achsen - again

Beitrag von joeydee »

Ich versuch noch ein wenig Licht reinzubringen.

Zunächst, unterscheide zwischen Welt- und lokalem Koordinatensystem und seinen Achsen. In der Welt ist z.B. "rechts" immer +x, lokal kann es ein beliebiger Vektor sein.

pre- und a- bestimmt natürlich die Reihenfolge, also wenn O deine Objektmatrix ist und T die Transformation, würde O.appendTransformation(T) bedeuten O*T, und O.prependTransformation(T) wäre T*O. Ich finds nur so besser zu merken was man eigentlich macht, weniger Verwechslungsgefahr, gerade auch mit anderen Systemen, da kann multiply() mal so, mal so definiert sein.

O sei also die lokale 4x4-Matrix eines jeden Objekts in der Welt, was du z.B. als Array mitspeicherst. Das Array kannst du einfach in OpenGL als Matrix laden und auch zurückspeichern, musst mal in der Referenz schauen wie das abgelegt wird.
Eine (Transformations-)Matrix wird von der Idee her spaltenweise interpretiert. Also sind es quasi 4 Vektoren mit je 4 Werten. Die ersten drei Vektoren zeigen genau die Richtung der lokalen Achsen an, wie auch immer ein Objekt in der Welt gedreht ist. Das heißt z.B., die 3. Spalte zeigt immer genau nach "vorn", entlang der lokalen Z-Achse des Objekts. Lies diesen Vektor aus, und du hast die Bewegungsrichtung mit der Länge 1. Der vierte Wert jedes Richtungsvektors ist in der Matrix übrigens immer 0.
Die 4. Spalte speichert die Position. Lies diesen Vektor aus, und du hast den aktuellen Nullpunkt des Objekts in der Welt. Oder setze neue Werte in diese Spalte, dann hast du explizit die Position verändert, egal wie das Objekt gedreht ist. Der vierte Wert in dieser Spalte ist übrigens immer 1.
Probier es aus: lade die Einheitsmatrix, mache ein translate(100,200,300) und lies die Matrix als Array aus. Du siehst sofort wo die 100,200 und 300 steht, also wie die 4. Spalte im Array liegt.

Wenn du jetzt, egal wie das Objekt gedreht ist, die dritte Spalte ausliest (lokale Z-Richtung) und die Werte einzeln zur vierten Spalte hinzuaddierst, hast du dich um 1 Einheit der Nase nach bewegt. Die Anteile der anderen Achsen stecken in der 3. Spalte schon mit drin.

Das zurück- und wieder hinsetzen betrifft dann auch nur die Position. Das geschieht nicht mit translate(), sondern du merkst dir die Werte in der 4. Spalte, setzt sie auf 0, machst deine Rotationen, und schreibst sie wieder zurück. Das verhindert eben, dass dein Objekt sich um irgendeinen Punkt dreht, sondern immer um seinen eigenen lokalen Nullpunkt, obwohl du eine Weltachse (statt einer lokalen) benutzt. Das war ja in dem einen Fall, wo du aufrecht stehenbleiben willst, auch wenn du schräg "nach oben" schaust.

Ausschließlich um die lokalen Achsen drehen (freier Raumflug) ist noch einfacher. Von der Idee her setzt du dein Objekt komplett zurück (Einheitsmatrix E), nun fallen die lokalen mit den Weltachsen zusammen. Nun drehst du z.B. um Welt-Y (also eine sehr einfache Standard-Rotationsmatrix) und setzt das Modell anschließend an die alte Stelle zurück (d.h. multiplizierst mit seiner alten Matrix vor dieser Drehung).
Praktisch heißt das Temp=O; O=E; O*=T; O*=Temp; Kurz: O=E*T*O; Noch kürzer: O=T*O; oder in meiner Schreibweise O.prepend(T).

Natürlich ist es Grundsätzlich ein Problem, nacheinander um mehrere Achsen zu rotieren. Das lässt sich praktisch nicht eliminieren. Aber da mehrmals die Sekunde in kleinen Schritten gedreht wird, passt das schon. Ich machs seit Jahren so und es klappt wunderbar.

Zur Kamera: Kannst du natürlich machen, aber spätestens beim Umschalten auf ein anderes Objekt als Kamera wirst du Probleme bekommen. Die Inverse ist hier dein Freund. Musst du aber glaube ich selbst berechnen, ohne OGL-Hilfe (?). Dann kannst du nämlich jedes Objekt direkt als Kamera umschalten. Außerdem wirst du sie spätestens bei der KI brauchen z.B. um zu beurteilen, wo relativ vom Objekt aus gesehen sich der Spieler befindet.
Benutzeravatar
eXile
Establishment
Beiträge: 1136
Registriert: 28.02.2009, 13:27

Re: [OpenGL/Mathe] Rotation um zwei Achsen - again

Beitrag von eXile »

Eisflamme hat geschrieben:Man löst das ganze einfach mathematisch und bastelt sich eigene Matrizen, in welche man x/y/z-Rotation einbauen kann. Kann auch sein, dass es diese ultimative Matrix bereits gibt, müsste man sich ergooglen.
Gibts schon, nennt sich Quaternionen (naja nicht ganz, aber dazu gleich mehr). Hinter der mathematischen Lösung steht nämlich die Erkenntnis, dass der SO(3) diffeomorph zum PR^3 ist (aber nicht zum R^3, das ist zu viel!). Dummerweise sind die Matrizen aus Eulerwinkeln ja schiefsymmetrische 3x3-Matrizen, d.h. der Parameterraum derselben ist der R^3 (nämlich die drei Eulerwinkel). Somit sind Eulerwinkel und der Raum der Rotationen niemals isomorph, und es gibt mehrere Eulerwinkel für ein und dieselbe Rotation (da ja der R^3 größer ist als der PR^3)! Die Abbildung zwischen Eulerwinkeln und Rotationen kann also niemals bijektiv sein.

Zu Quaterionen: Dort ist dies nicht der Fall, aber man handelt sich dabei den Quaternion-Double-Cover ein, d.h. dass q = -q ist bzw. dass Quaternionen Rotationen bis 720° unterscheiden können. Anschaulich kann man sich das so vorstellen: Eulerwinkel beschreiben einen Punkt auf einer "handelsüblichen" Karte (Mercatorprojektion). Was erkennt man? Stimmt, an Nord- und Südpol gibt es für ein und denselben Punkt (nämlich den Nord-/Südpol) mehrere Beschreibungen (Längen-/Breitengrade aka Eulerwinkeln in unserer einfachen Vorstellung). Würde man die Karte also an den Enden zusammenfalten erhält man einen ... Torus! Eulerwinkel beschreiben einen Punkt auf einem Torus. Dahingegen erhält man bei Quaternionen eine Kugel, welche "doppelt" ist, d.h. zweimal aufeinander liegt. Quaternionen beschrieben somit einen Punkt auf einer Kugel, und das "sogar" mit bis zu 720° Rotation bis zum Ausgangspunkt.

Backrezept: Nur mit Quaterionen hantieren, wenn nötig ein wenig auf den Double-Cover aufpassern, und dann die Quaternionen zum Rendern in Rotationsmatrizen umwandeln. ;)
Eisflamme
Establishment
Beiträge: 412
Registriert: 26.05.2002, 17:42
Wohnort: Köln

Re: [OpenGL/Mathe] Rotation um zwei Achsen - again

Beitrag von Eisflamme »

Guten Morgen,

joeydee:
Ok, das ist eine super Beschreibung, so habe ich die Matrizen noch nie gesehen, danke. :)
Das erleichtert meine Vorstellung wirklich um einiges.
Noch nicht ganz verstehe ich die mehreren kleinen Rotationen pro Sekunde. Angenommen, ich drehe meine Kamera auf y-Achse nach links. Mein bisheriger Ansatz speichert den rotY, berechnet die Kameramatrix halt andersherum und bei jedem gerenderten Objekt wird die Einheitsmatrix geladen und meine neue Matrix gesetzt. Hier gehe ich also immer von 0 aus.
Dein Ansatz hat dann ja Objektmatrizen, die sich anscheinend direkt bei Kameraaenderung angleichen, sonst haette man ja nicht besagte Rotationen pro Sekunde. Da haette ich schon Angst vor Rundungsfehlern, speicherst Du dann die Position + Rotation zusaetzlich noch irgendwo anders und gleichst das hin und wieder an? Das mit dem Normalisieren macht dann Sinn, aber da frage ich mich, ob es nicht weniger aufwendig ist direkt immer von der Einheitsmatrix auszugehen und jedes Mal neu zu berechnen. Denn wir wenden letztlich sowieso immer Bewegungs- und Rotationsmatrix an und haben dadurch unsere sin/cos-Rechnungen, da sparen wir doch keine Performance, wo liegt da der Vorteil?

Die Kamerainverse muesste ich dann natuerlich fuer jede Bewegung berechnen, ist das nicht etwas performancelastig? Gut, letztlich ist das nur eine Berechnung pro Frame (wenn ueberhaupt), ist also vermutlich vernachlaessigbar. Und andere Objekte als Kamera sind natuerlich ein prima Vorteil. :)

eXile:
Wow. Ok, ich bin mathematisch leider (noch nicht) so firm. Diffeomorph habe ich gegooglet, aber was sind SO und PR? R ist natuerlich klar. Mit etwas Recherche ist mir der erste Abschnitt dann... klar genug geworden, denke ich.

Aber wieso hat man bei Quaternionen 720 grad bzw. wieso ist die Kugel doppelt gefaltet, das ist dann also kein normaler Globus? Den Torus haette ich ja bei einer Karte meistens nicht. Mir ist die Vorstellung noch nicht ganz klar, aber ich werde das spaeter nochmal durchlesen. Warum Quaternionen besser als die Matrizen sind, leuchtet mir jetzt aber ein. Wir sind quasi bis auf das Double-Cover eindeutig, richtig?
joeydee
Establishment
Beiträge: 1042
Registriert: 23.04.2003, 15:29
Kontaktdaten:

Re: [OpenGL/Mathe] Rotation um zwei Achsen - again

Beitrag von joeydee »

Genau, alles wird nur in der Matrix gespeichert, keine zusätzlichen Winkel, weil die ja, wie eXile so schön beschrieben hat, nicht eindeutig sind. Ich schleppe also keine Rotationen mit sondern nur das fertige lokale Koordinatensystem mit seinen 3 Achsen und seiner Position.
Wie gesagt, ich mach das so und finde es einfach praktisch für meine Anwendungen. Was die Performance angeht: der Flaschenhals steckt bei mir in der Regel in der Füllrate, aber ich habe jetzt auch keine Massenszenen mit zig verschachtelten Objekten. Was die Genauigkeit angeht, ich meine ich hätte vor Jahren mal einen vielstündigen Test gefahren und unter OpenGL und C++ mit Double keine Störungen bemerkt. In Flash sind Abweichungen seltsamerweise nach kurzer Zeit zu sehen, hier korrigiere ich per Frame. Aber da auch hier die Füllrate der allergrößte Flaschenhals ist, habe ich massig Ressourcen.

Auch wenn ich zur Zeit Quaternionen mit drin habe, benutze ich sie eigentlich nur zur Interpolation zwischen zwei Koordinatensystemen.

Dir kann ich nur raten: schau dir alles davon an, probiere es aus und versuche die Mathematik dahinter zu verstehen. Vielleicht macht es irgendwo "Klick" und du findest einen Weg wie das für deinen Anwendungsfall noch viel eleganter geht.
Eisflamme
Establishment
Beiträge: 412
Registriert: 26.05.2002, 17:42
Wohnort: Köln

Re: [OpenGL/Mathe] Rotation um zwei Achsen - again

Beitrag von Eisflamme »

Ok, zum dritten Mal (wenn ihr auf gutes Internet angewiesen seid: vergesst Australien, ganz ehrlich): Danke :D Das hilft mir gut weiter und ich werd mich informieren und Mal schauen, was sich machen lässt.
Antworten