PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : MatrixTransformationen verketten


Elemental
2010-02-06, 17:06:29
Hallo zusammen,
ich versuch mich gerade am verketten von Transformationen um die 3 Achsen.
Dafür hab ich mir folgende Methoden geschrieben:

public static Matrix3D RotateX(Matrix3D oldRotationMatrix, double dAngleInDegrees)
{
Matrix3D newRotationMatrix = new Matrix3D(oldRotationMatrix.M11, oldRotationMatrix.M12, oldRotationMatrix.M13, oldRotationMatrix.M14,
oldRotationMatrix.M21, oldRotationMatrix.M22, oldRotationMatrix.M23, oldRotationMatrix.M24,
oldRotationMatrix.M31, oldRotationMatrix.M32, oldRotationMatrix.M33, oldRotationMatrix.M34,
oldRotationMatrix.OffsetX, oldRotationMatrix.OffsetY, oldRotationMatrix.OffsetZ, oldRotationMatrix.M44);

newRotationMatrix.Rotate(new Quaternion(new Vector3D(1, 0, 0) * oldRotationMatrix, dAngleInDegrees));

return newRotationMatrix;
}

public static Matrix3D RotateY(Matrix3D oldRotationMatrix, double dAngleInDegrees)
{
Matrix3D newRotationMatrix = new Matrix3D(oldRotationMatrix.M11, oldRotationMatrix.M12, oldRotationMatrix.M13, oldRotationMatrix.M14,
oldRotationMatrix.M21, oldRotationMatrix.M22, oldRotationMatrix.M23, oldRotationMatrix.M24,
oldRotationMatrix.M31, oldRotationMatrix.M32, oldRotationMatrix.M33, oldRotationMatrix.M34,
oldRotationMatrix.OffsetX, oldRotationMatrix.OffsetY, oldRotationMatrix.OffsetZ, oldRotationMatrix.M44);

newRotationMatrix.Rotate(new Quaternion(new Vector3D(0, 1, 0) * oldRotationMatrix, dAngleInDegrees));

return newRotationMatrix;
}

public static Matrix3D RotateZ(Matrix3D oldRotationMatrix, double dAngleInDegrees)
{
Matrix3D newRotationMatrix = new Matrix3D(oldRotationMatrix.M11, oldRotationMatrix.M12, oldRotationMatrix.M13, oldRotationMatrix.M14,
oldRotationMatrix.M21, oldRotationMatrix.M22, oldRotationMatrix.M23, oldRotationMatrix.M24,
oldRotationMatrix.M31, oldRotationMatrix.M32, oldRotationMatrix.M33, oldRotationMatrix.M34,
oldRotationMatrix.OffsetX, oldRotationMatrix.OffsetY, oldRotationMatrix.OffsetZ, oldRotationMatrix.M44);

newRotationMatrix.Rotate(new Quaternion(new Vector3D(0, 0, 1) * oldRotationMatrix, dAngleInDegrees));

return newRotationMatrix;
}



In meiner Anwendung hab ich Buttons, um die Rotation zu verändern. So:

private void _ButtonRotateXMinus_Click(object sender, RoutedEventArgs e)
{
_MatrixTransform.Matrix = Helper.RotateX(_MatrixTransform.Matrix, -15.0);
}

private void _ButtonRotateXPlus_Click(object sender, RoutedEventArgs e)
{
_MatrixTransform.Matrix = Helper.RotateX(_MatrixTransform.Matrix, 15.0);
}



Das funktioniert soweit auch, aber nur wenn ich erst um X, dann Y, dann Z drehe.
Wenn ich z.B. erst 90° um Z drehe und dann um X drehen will, wird stattdessen um Y gedreht :confused:

Hab ich irgendwo einen Denkfehler? Oder passt in meinen Rotate-Methoden etwas nicht?

Ich komm gerade einfach nicht weiter...

mfG
Elemental

pest
2010-02-06, 17:14:41
Matrizenmultiplikation ist nicht kommutativ

Neomi
2010-02-06, 19:39:12
Hab ich irgendwo einen Denkfehler? Oder passt in meinen Rotate-Methoden etwas nicht?

Wahrscheinlich den gleichen wie schon in deinem letzten Thread zum Thema Matrizen. Du solltest keine vorhandene Matrix inkrementell weiterdrehen, sondern die gewünschten Winkel. Und wenn sich von denen einer ändert, wird die dazu passende Matrix neu aufgebaut. Wenn du irgendwie noch die alte Matrix mit verwurstelst, kann im Endeffekt nur Murks rauskommen.

Ein paar generelle Hinweise noch, damit du nicht auf Dauer unglücklich wirst mit deinen Matrixklassen:

- Benenne die Klasse nicht danach, wofür du sie aktuell brauchst, sondern danach, was sie ist bzw. macht. Deine Matrix3D ist eine Matrix4x4, deshalb sollte sie auch so heißen. Spätestens, wenn du zusätzlich noch Matrix3x3 und/oder Matrix4x3 benötigst (oder eine beliebige andere als 4x4), wirst du um eine Umbenennung nicht herumkommen.

- Aus dem gleichen Grund, daß noch weitere Matrixklassen dazu kommen können (und werden), gehören die statischen Konstruktionsmethoden RotateX/Y/Z in die jeweilige Matrixklasse. Bei einem Matrix3x3.RotateX(...) und Matrix4x4.RotateX(...) ist auf den ersten Blick offensichtlich, was du als Ergebnis bekommst, bei Helper.RotateX(...) nicht.

- Statt OffsetX, OffsetY und OffsetZ solltest du m41, m42 und m43 als Namen wählen. Dann kannst du deine Matrizen auch universell nutzen und stiftest keine Verwirrung, wenn diese Felder mal nicht für den Offset stehen. Ein kleines m statt großem M schont die Shifttaste und tippt sich dadurch auch noch schneller. Dazu würde ich noch 0 als Basis wählen (dann wären das m30, m31 und m32), aber das ist letzendlich Geschmackssache. Ist aber effektiver, wenn du auch indiziert zugreifen können willst, "m[3][0] == m30" ist logischer als "m[3][0] == m41".

- Mach bzw. erstelle in den Methoden nur das, was der Methodenname auch klar aussagt. Dein RotateX ist eher ein RotateXandMultiplyWithOtherMatrix. Einfach nur eine Matrix zu erstellen und nicht noch mit einer anderen zu multiplizieren, ist in dem Fall auch viel praktischer. "Matrix4x4.RotateX(30.0) * otherMatrix" ist zwar nicht kürzer als "Matrix4x4.RotateX(otherMatrix, 30.0)", aber was wenn du andersrum verketten willst? "otherMatrix * Matrix4x4.RotateX(30.0)" ist deutlich kürzer als "otherMatrix * Matrix4x4.RotateX(Matrix4x4.Identity(), 30.0)" und spart auch noch eine (unnötige) Multiplikation.

Auf die Dinge würdest du wohl auch selbst mit der Zeit kommen, aber wozu sich erst quälen? Letztendlich sollte deine Matrixklasse so aussehen, daß du sie in deinen Code in der Form verwenden kannst:

private void _UpdateTransformation()
{
// combine rotations (left handed coordinate system)
_MatrixTransform.Matrix = Matrix4x4.RotateZ(_Roll) * Matrix4x4.RotateX(_Pitch) * Matrix4x4.RotateY(_Yaw);
}

private void _ButtonRotatePitchMinus_Click(object sender, RoutedEventArgs e)
{
_Pitch -= 15.0;
_UpdateTransformation();
}

private void _ButtonRotatePitchPlus_Click(object sender, RoutedEventArgs e)
{
_Pitch += 15.0;
_UpdateTransformation();
}

Der Code hier arbeitet mit einer Matrixklasse, die intuitiv benutzbar ist, weil sie genau das ist und weil ihre Methoden genau das tun, was der Name impliziert. Außerdem wird mit persistenten Winkeln gearbeitet, was dein Problem löst.

pest
2010-02-06, 21:31:06
Außerdem wird mit persistenten Winkeln gearbeitet, was dein Problem löst.

ich würde eine Konvention (http://de.wikipedia.org/wiki/Eulersche_Winkel#Luftfahrtnorm_.28DIN_9300.29_.28Yaw-Pitch-Roll.2C_Z.2C_Y.E2.80.99.2C_X.E2.80.99.E2.80.99.29), nicht unbedingt Lösung nennen

Pinoccio
2010-02-06, 21:44:53
ich würde eine Konvention (http://de.wikipedia.org/wiki/Eulersche_Winkel#Luftfahrtnorm_.28DIN_9300.29_.28Yaw-Pitch-Roll.2C_Z.2C_Y.E2.80.99.2C_X.E2.80.99.E2.80.99.29), nicht unbedingt Lösung nennenKann es sein, daß das Zitat nichts mit deiner Bemerkung zu tun hat? Irgendwie sehe ich da keinen Zusammenhang.

Außerdem dreht er 4x4. (http://de.wikipedia.org/wiki/Homogene_Koordinaten#Wichtige_elementare_homogene_Transformationsmatrizen)
Kann es sein, daß das meine Bemerkung auch ncihts damit zu tun hat?

mfg

pest
2010-02-06, 21:52:27
Irgendwie sehe ich da keinen Zusammenhang.


Roll, Pitch und Yaw stehen doch groß und breit auch in meinem Link :freak:


Außerdem dreht er 4x4.

ich dachte mir...der Sinn von homogenen Koordinaten besteht doch darin,
Translation und Rotation(en) in eine Matrix zu packen.
Da das hier anscheinend nicht passiert, bin ich stillschweigend von einem Tippfehler in den Methodennamen ausgegangen.
X,Y und Z sind natürlich auch falsch beschriftet.

edit: ;)

Pinoccio
2010-02-06, 22:07:20
Roll, Pitch und Yaw stehen doch groß und breit auch in meinem Link :freak:Ich verstehe immernochnciht,w as das mit persistenten Winkeln usw. zu tun hat, aber das ist eigentlich auch egal.
ich dachte mir...der Sinn von homogenen Koordinaten besteht doch darin,
Translation und Rotation(en) in eine Matrix zu packen.
Da das hier anscheinend nicht passiert, bin ich stillschweigend von einem Tippfehler in den Methodennamen ausgegangen.
X,Y und Z sind natürlich auch falsch beschriftet.Sowohl Elemental als auch Neomi arbeiten mit 4x4-Matritzen.

Der Sinn ist eher, alle Bewegungen, also auch Translationen, überhaupt als Matrix darstellen zu können. Daß man dann Translation und Rotation (usw.) in eine Matrix packen kann, ist eine schöne Folge.

mfg

pest
2010-02-06, 22:12:33
Ich verstehe immernochnciht,w as das mit persistenten Winkeln usw. zu tun hat


das verstehe ich allerdings auch nicht, ich habe erstmal persistent gegoogelt :redface:


Der Sinn ist eher, alle Bewegungen, also auch Translationen, überhaupt als Matrix darstellen zu können. Daß man dann Translation und Rotation (usw.) in eine Matrix packen kann, ist eine schöne Folge.


aber hier wird doch nur rotiert? nunja meine erste antwort beantwortet die frage des ts ja schon hinreichend imo, und der spass wird wohl auf eine andere api wie opengl zugreifen, wo dies dann wieder sinn ergibt

ich habe mich nur an dem wort lösung aufgegeilt

Neomi
2010-02-06, 22:25:12
Jede Konvention, die ein Problem löst, ist für dieses Problem eine Lösung. ;)
Natürlich nicht zwangsweise die einzige.

X,Y und Z sind natürlich auch falsch beschriftet

Von wem? In meinem Beispiel oben? Da Elemental ein linkshändiges Koordinatensystem nutzt, bin ich einfach mal vom Direct3D-Standardfall ausgegangen. Also X nach rechts, Y nach oben und Z nach hinten in den Monitor hinein. Ist selbstverständlich eine reine Definitionssache. Natürlich ist mein Beispiel oben unvollständig (z.B. fehlt die Translation) und ich würde es generell auch anders schreiben, aber es sollte ja auch nur ein kräftiger Schubs in die richtige Richtung sein.

Pinoccio
2010-02-06, 22:34:00
aber hier wird doch nur rotiert?Ja. Trotzdem wird in der 3D-Computergrafik praktisch immer mit 4x4 gerechnet, weil man damit alles (naja: mindestends vieles) , z. B. auch die perspektivische Ansicht, handhaben kann.


ich habe mich nur an dem wort lösung aufgegeilt;D Und ich dachte schon, du bist so ein Freak, der sich an Zahlen aufgeilt.

SCNR:
http://666kb.com/i/bgh8hvo72pogoud8z.jpg

Ist selbstverständlich eine reine Definitionssache.*fapfapfap* Ist es doch immer ...



mfg

pest
2010-02-06, 22:43:31
:D@OT ... auch, auch an Zahlen

Elemental
2010-02-07, 08:39:48
Erstmal danke für die vielen Antworten. Ich sehe schon, dass ich noch viel zu lernen hab :D

...
Ein paar generelle Hinweise noch, damit du nicht auf Dauer unglücklich wirst mit deinen Matrixklassen...

Ist nicht meine Matrix-Klasse, sondern die von Microsoft:
http://msdn.microsoft.com/de-de/library/system.windows.media.media3d.matrix3d%28VS.95%29.aspx


private void _UpdateTransformation()
{
// combine rotations (left handed coordinate system)
_MatrixTransform.Matrix = Matrix4x4.RotateZ(_Roll) * Matrix4x4.RotateX(_Pitch) * Matrix4x4.RotateY(_Yaw);
}


Hmm, jetzt bin ich verwirrt. Was wird denn nun als Roll, Pitch und Yaw bezeichnet?
Hier:
Roll: Rotation um Z-Achse
Pitch: Rotation um X-Achse
Yaw: Rotation um Y-Achse

Bei dem Wikipedia-Link steht doch aber:
Luftfahrtnorm (DIN 9300) (Yaw-Pitch-Roll, Z, Y’, X’’) :confused:

Was mir auch nicht klar ist, wieso man bei einem linksdrehenden Koordinatensystem erst um Z, dann X und dann Y drehen soll. :confused:


Bei meinem ersten Control hatte ich keine Buttons um die Winkel zu ändern, sondern Slider, siehe hier (http://www.forum-3dcenter.org/vbulletin/showthread.php?t=476512).
Dort hatte ich statt einer MatrixTransformation drei einfache Rotations-Transformationen, bei denen die Winkel jeweils beim ziehen der Slider upgedated wurden, so in etwa

Transform3DGroup transforms = new Transform3DGroup();

RotateTransform3D xRotateTransform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(1, 0, 0), dAngleX));
transforms.Children.Add(xRotateTransform);

RotateTransform3D yRotateTransform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 1, 0), dAngleY))
transforms.Children.Add(yRotateTransform);

RotateTransform3D zRotateTransform = new RotateTransform3D(new AxisAngleRotation3D(new Vector3D(0, 0, 1), dAngleZ))
transforms.Children.Add(zRotateTransform);


Das würde doch deinem Code entsprechen, bis auf die andere Reihenfolge, oder?

mfG
Elemental

Neomi
2010-02-07, 14:47:15
Ist nicht meine Matrix-Klasse, sondern die von Microsoft:

Dafür ist die aber ernsthaft enttäuschend. Naja, vielleicht soll es einfach keine generelle Matrixklasse bzw. Gruppe sein, sondern eine zurechtgestutzte "Auswahl" für den einen Zweck. Vielleicht gibt es ja in einem anderen Namespace was umfassenderes.

Hmm, jetzt bin ich verwirrt. Was wird denn nun als Roll, Pitch und Yaw bezeichnet?

Yaw: die Ausrichtung, z.B. kann man damit die Himmelsrichtung beschreiben, in die geschaut wird
Pitch: die Neigung, also ob man geradeaus, nach oben oder nach unten schaut
Roll: Drehung um die Längsachse, also ohne die Blickrichtung zu ändern

Bei dem Wikipedia-Link steht doch aber:
Luftfahrtnorm (DIN 9300) (Yaw-Pitch-Roll, Z, Y’, X’’) :confused:

Beides richtig, eben je nach Definition des Koordinatensystems. In besagtem Link zeigt Z nach oben, in Direct3D zeigt in der Regel Y nach oben. Die Variante benutze ich meistens auch, aber die sind alle untereinander austauschbar.

Was mir auch nicht klar ist, wieso man bei einem linksdrehenden Koordinatensystem erst um Z, dann X und dann Y drehen soll. :confused:

Wichtig daran ist, daß Roll innen und Yaw außen ist. Wie die drei Drehungen dann auf die Achsen gemappt werden, hängt von der Definition des Koordinatensystems ab. In der Luftfahrtnorm wird erst (Roll) um global X, dann Y und zuletzt (Yaw) um Z gedreht, wahrscheinlich ist das Koordinatensystem genau aus dem Grund dort so definiert. Aber wie gesagt, das ist Definitionssache und untereinander austauschbar. Solange du drei Achsen hast, die in rechtem Winkel zueinander stehen, kannst du von einem System in ein anderes ohne Verlust umrechnen. Du solltest bloß bei einem System bleiben und Berechnungen, die du z.B. bei Wikipedia findest, für dein System adaptieren, nicht einfach mit anders definierten Achsen dann falsch kopieren.

Das würde doch deinem Code entsprechen, bis auf die andere Reihenfolge, oder?

Die Transformation wird aus drei Rotationen neu generiert, eine ältere Transformation wird nicht weiter beachtet. Ja, das entspricht dem.

Elemental
2010-02-07, 16:13:55
...
Wichtig daran ist, daß Roll innen und Yaw außen ist.
...

Was meinst du mit innen und aussen? :confused:

Wie ich gelesen habe, nutzt WPF3D ein "right handed coordinate system":
klick mich (http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/7ee5c656-111e-4565-9a9b-3b4e71da4ccc/)

edit:
Was mir gerade noch einfällt:
Diese Transformationen wende ich nicht auf die Kamera an, sondern auf mein 3D Objekt, welches angezeigt wird.
Kann man dann das mit der Reihenfolge der Transformationen überhaupt so anwenden?

Pinoccio
2010-02-07, 16:47:54
Was meinst du mit innen und aussen? :confused:Wichtig zur Angabe der Rotation ist auch eine Vereinbarung in welchem Koordinatensystem man redet. Bei Yaw gibt man die Achse im unveränderlichen äußeren Koordinatensystem an, bei Roll im bereits gedrehten inneren. (Oder andersrum ...).
(Denn bezogen auf das Flugzeug macht es natürlich einen Unterschied, ob sich das Flugzeug um seine x-Achse dreht oder um eine globale, die i. A. nicht mit der des Flugzeugs übereinstimmt.)
Was mir gerade noch einfällt:
Diese Transformationen wende ich nicht auf die Kamera an, sondern auf mein 3D Objekt, welches angezeigt wird.
Kann man dann das mit der Reihenfolge der Transformationen überhaupt so anwenden?Brainfuck komplett, was die Wahl der Koordinatensysteme betrifft. :freak:

mfg

Elemental
2010-02-07, 17:06:14
...
Brainfuck komplett, was die Wahl der Koordinatensysteme betrifft. :freak:

mfg

Naja, so konnte ich einfach eine "Maussteuerung" realisieren.
Meine Kamera hat die Position (0,0,Z), LookDirection (0,0,-1) und UpDirection (0,1,0).
Bei MouseWheel events veränderte ich den Z-Wert der Kameraposition und bei MouseMove events ändere ich die X- und Y-Werte der Kameraposition (wenn der linke Mouse-Button dabei gedrückt ist).
Bei dieser Methode kann ich halt keine Transformationen auf die Kamera gebrauchen...

Elemental
2010-02-09, 10:36:05
...

private void _UpdateTransformation()
{
// combine rotations (left handed coordinate system)
_MatrixTransform.Matrix = Matrix4x4.RotateZ(_Roll) * Matrix4x4.RotateX(_Pitch) * Matrix4x4.RotateY(_Yaw);
}

...

Noch eine Frage:
Wie sieht die entsprechende Methode für ein "right handed coordinate system" aus?

So?
Matrix4x4.RotateY(_Yaw) * Matrix4x4.RotateX(_Pitch) * Matrix4x4.RotateZ(_Roll);


mfG
Elemental

Neomi
2010-02-09, 11:05:53
Ja, bei rechtshändigen Systemen ist die innere Transformation auf der rechten Seite. Allerdings ist die Achsenkonfiguration anders, im einfachsten Fall zeigt Z aus dem Bildschirm hinaus statt hinein. Entsprechend der Definition der Rotation (z.B. Drehung im Urzeigersinn entlang der Achse) müßtest du dann _Roll invertieren. Was genau wie angepaßt werden muß, hängt vom Koordinatensystem ab.

Elemental
2010-02-10, 09:50:24
Danke :)