(gelöst) Quaternion-Wahnsinn

Design Patterns, Erklärungen zu Algorithmen, Optimierung, Softwarearchitektur
Forumsregeln
Wenn das Problem mit einer Programmiersprache direkt zusammenhängt, bitte HIER posten.
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

(gelöst) Quaternion-Wahnsinn

Beitrag von Krishty »

Kurze Frage an die Experten: Was hat man falsch gemacht, wenn man zwei Einheitsquaternionen a und b in dieser Reihenfolge multipliziert und das Ergebnis eine Rotation erst um a, dann um b repräsentiert? Laut Wikipedia müsste das ja umgekehrt sein …
Zuletzt geändert von Krishty am 16.09.2012, 17:28, insgesamt 1-mal geändert.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Artificial Mind
Establishment
Beiträge: 802
Registriert: 17.12.2007, 17:51
Wohnort: Aachen

Re: Jammer-Thread

Beitrag von Artificial Mind »

Krishty hat geschrieben:Kurze Frage an die Experten: Was hat man falsch gemacht, wenn man zwei Einheitsquaternionen a und b in dieser Reihenfolge multipliziert und das Ergebnis eine Rotation erst um a, dann um b repräsentiert? Laut Wikipedia müsste das ja umgekehrt sein …
Also angenommen a und b sind richtig miteinander multipliziert, dann gibt es immer noch die Frage, wie man es mit einem Punkt multipliziert.
Methode 1: Sei \($\mathbf{q}$\) das Quaternion. Punkt \($\mathbf{p} = (x,y,z)$\) wird als \($0 + xi +yj + zk$\) aufgefasst, dann ist der Imaginärteil von \($\mathbf{qxq}^{-1}$\) der gedrehte Punkt.
Methode 2: Man kann Quaternione in eine Matrixdarstellung umrechen und damit multiplizieren. Hier gibt es allerdings relativ ähnlich aussehende Formeln, um die Matrix für die Multiplikation von links oder von rechts zu bekommen.

Bei Methode 2 kann relativ schnell was schiefgehen, bei Methode 1 bin ich mir nicht ganz so sicher.
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Jammer-Thread

Beitrag von Krishty »

Dann ist es Methode 2.

Ich habe bereits vier verschiedene Matrixdarstellungen ausprobiert. Im Augenblick habe ich die von Wikipedia, eine, die offensichtlich eine Multiplikation von links nach rechts erzeugt.

Ich habe mir gedacht, ich könne die ja einfach invertieren, indem ich sie transponiere. Aber dann sind jedes Mal wechselnd ein paar Vorzeichen falsch, obwohl alle Beträge stimmen. Spezifisch äußert sich das darin, dass die Matrix dann alle Rotationen in der richtigen Reihenfolge darstellt, aber mit linkshändigen Winkeln.

Da die Matrix selber rechtshändig zu sein scheint (richtig so), denke ich, dass es an row major vs. column major liegt. Aber wie komme ich vom einen in das andere, wenn Transponieren falsche Ergebnisse liefert?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Artificial Mind
Establishment
Beiträge: 802
Registriert: 17.12.2007, 17:51
Wohnort: Aachen

Re: Jammer-Thread

Beitrag von Artificial Mind »

Vielleicht können wir das in einen neuen Thread auslagern und du könntest etwas Code zeigen?
Mich interessiert das Thema, da ich demnächst in einem Uni-Projekt Active Ragdolls implementieren will und da werd ich weder um Dual-Quaternion-Skinning noch um Quaternionen im Allgemeinen herumkommen.
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

Abgetrennt.

Mein Problem klingt diesem hier sehr ähnlich: Der OP nutzt ebenfalls völlig konsistente Berechnungen, die aber miteinander kombiniert nur Schrott ergeben.

Er dachte, transponieren könnte das Problem lösen, aber dann hat er falsche Vorzeichen gekriegt.

Seine Lösung war:
This is an update to explain the solution to the problem. Basically it boils down to how I ordered the elements in the matrices. For my case I did not need to transpose the original quaternion to matrix formula; some websites do perform the transpose because they have ordered it differently.
Ja danke, und das heißt?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

Und hier Quelltext:

    struct Quaternion {
        float w, i, j, k;
    };

    quaternionRotationAboutXAxis(
        float const angle
    ) {
        auto const cs = cosineAndSineOf(angle / 2.0f);

        Quaternion const result = {
            cs.cosine,
            cs.sine,
            0.0f,
            0.0f
        };
        return result;
    }
    // Rotation um Y und Z ist genauso aufgebaut, nur dass 'cs.sine' von 'i' nach 'j' oder 'k' wandert

    Quaternion const operator * (
        Quaternion const & a,
        Quaternion const & b
    ) {
        Quaternion const result = {
            (a.w * b.w) - (a.i * b.i) - (a.j * b.j) - (a.k * b.k),
            (a.w * b.i) + (a.i * b.w) + (a.j * b.k) - (a.k * b.j),
            (a.w * b.j) - (a.i * b.k) + (a.j * b.w) + (a.k * b.i),
            (a.w * b.k) + (a.i * b.j) - (a.j * b.i) + (a.k * b.w)
        };
        return result;
    }

    Matrix3x3 const matrix3x3From(
        Quaternion const & q
    ) {
        Matrix4x3 const result = { {
            {
                1.0f - doubled(squared(q.j) + squared(q.k)),
                doubled(q.i * q.j - q.w * q.k),
                doubled(q.w * q.j + q.i * q.k)
            }, {
                doubled(q.w * q.k + q.i * q.j),
                1.0f - doubled(squared(q.i) + squared(q.k)),
                doubled(q.j * q.k - q.w * q.i)
            }, {
                doubled(q.i * q.k - q.w * q.j),
                doubled(q.w * q.i + q.j * q.k),
                1.0f - doubled(squared(q.i) + squared(q.j))
            }
        } };
        return result;
    }


Wenn ich nun folgendes durchführe:

    auto const rotation = quaternionRotationAboutXAxis(0.2f)
                        * quaternionRotationAboutYAxis(0.4f)
                        * quaternionRotationAboutZAxis(0.6f);
    auto const qMat = matrix3x3From(rotation);


Dann kommt das gleiche raus wie bei Verkettung der entsprechenden 3×3-Rotationsmatrizen in identischer Reihenfolge.
Dort, wo man diese Matrix eigentlich erwarten würde, nämlich bei umgekehrter Multiplikation

    auto const rotation = quaternionRotationAboutZAxis(0.6f)
                        * quaternionRotationAboutYAxis(0.4f)
                        * quaternionRotationAboutXAxis(0.2f);


kommt eine komplett andere Matrix raus.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Artificial Mind
Establishment
Beiträge: 802
Registriert: 17.12.2007, 17:51
Wohnort: Aachen

Re: Quaternion-Wahnsinn

Beitrag von Artificial Mind »

Krishty hat geschrieben:     auto const rotation = quaternionRotationAboutXAxis(0.2f)
                        * quaternionRotationAboutYAxis(0.4f)
                        * quaternionRotationAboutZAxis(0.6f);
    auto const qMat = matrix3x3From(rotation);


Dann kommt das gleiche raus wie bei Verkettung der entsprechenden 3×3-Rotationsmatrizen in identischer Reihenfolge.
Wie genau meinst du "identische Reihenfolge"?

ist qMat nun eine Rotation erst um Z, dann um Y und dann um X? (das wäre ja MatrixX * MatrixY * MatrixZ - also identische Matrixreihenfolge)
Wenn ja, dann ist doch alles richtig.
\($rotation = q_X \cdot q_Y \cdot q_Z$\). Dann ist \($rotation(p) = rotation \cdot p \cdot rotation^{-1} = rotation = (q_X \cdot q_Y \cdot q_Z) \cdot p \cdot (q_X \cdot q_Y \cdot q_Z)^{-1} = q_X \cdot q_Y \cdot q_Z \cdot p \cdot q_Z^{-1} \cdot q_Y^{-1} \cdot q_X^{-1} = q_X \cdot (q_Y \cdot (q_Z \cdot p \cdot q_Z^{-1}) \cdot q_Y^{-1}) \cdot q_X^{-1} = q_X(q_Y(q_Z(p)))$\), also erst um Z, dann um Y, dann um X, was ja im Endeffekt \($M_X \cdot M_Y \cdot M_Z \cdot p$\) entspricht (Oder habe ich mich irgendwo vertan?).
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

Artificial Mind hat geschrieben:ist qMat nun eine Rotation erst um Z, dann um Y und dann um X? (das wäre ja MatrixX * MatrixY * MatrixZ - also identische Matrixreihenfolge)
Wenn ja, dann ist doch alles richtig.
Bedenk, dass Matrizen von rechts nach links aufmultiplizieren, wenn sie column-major sind, und von links nach rechts, wenn sie row-major sind (oder umgekehrt? Ist jedenfalls ein Unterschied). Meine Matrizen haben links-nach-rechts-Reihenfolge.

qMat ist identisch der Matrix, die entsteht, wenn ich die drei Rotationmatrizen MatrixX * MatrixY * MatrixZ von links nach rechts aufmultipliziere, also auch die Rotationen in der Reihenfolge von links nach rechts durchführe.

Daher ist mein Verständnis der Situation:

Oben habe ich qX * qY * qZ geschrieben. Bei Quaternions ist die Multiplikation q0 * q1 eine Rotation erst um q1, dann um q0. Das Quaternion sollte also erst um Z rotieren, dann um Y, dann um X. (Oder?)

Es kommt aber die gleiche Matrix raus, die erst um X, dann um Y, dann um Z rotiert.

————

Ich habe mal die beiden Konvertierungsmethoden dieser Frage ausprobiert. Die erste ist totaler Schrott und bringt mich nicht mal in die Nähe des gewollten Ergebnisses, auch nicht mit invertieren oder negieren oder sonstwas. Die zweite Methode hat das gleich Ergebnis wie die Funktion, die ich oben gepostet habe, also links-nach-rechts statt dem gewollten rechts-nach-links.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von dot »

Wie genau sieht Matrix3x3 denn aus? Ist das Absicht, dass du in der Matrix3x3From Funktion eine Matrix4x3 statt einer Matrix3x3 initialisierst? Wieso verwendest du eigentlich überhaupt Quaternionen, wenn du am Ende alles in Matritzen umrechnest? Die Multiplikationsreihenfolge hat nichts mit column major vs row major zu tun, sondern einzig und allein mit der Konvention ob Vektoren als Zeilen oder Spalten aufgefasst werden.
Benutzeravatar
Artificial Mind
Establishment
Beiträge: 802
Registriert: 17.12.2007, 17:51
Wohnort: Aachen

Re: Quaternion-Wahnsinn

Beitrag von Artificial Mind »

Dein Link zur Quaternion->Matrix-Umwandlung ( http://de.wikipedia.org/wiki/Quaternion ... n_Matrizen ) \(sagt aber, dass man einen Quaternion $q$ in eine Matrix $D_q$ umwandelt und dann $D_q\cdot p$ den Punkt $p$ dreht.

Sprich: Deine Quaternion-Matrix wird immer von Links ranmultipliziert:
$D_{q_X q_Y q_Z} \cdot p = (D_{q_X} \cdot D_{q_Y} \cdot D_{q_Z}) \cdot p = D_{q_X} \cdot (D_{q_Y} \cdot (D_{q_Z} \cdot p))$

Jedenfalls verstehe ich das so, dass man die Matrizen "von rechts nach links aufmultipliziert", wenn $D_q\cdot p$ die Drehung beschreibt - im Gegensatz zu $p \cdot D_q$.\)
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

dot hat geschrieben:Wie genau sieht Matrix3x3 denn aus? Ist das Absicht, dass du in der Matrix3x3From Funktion eine Matrix4x3 statt einer Matrix3x3 initialisierst?
Ups, sind in Wirklichkeit Templates (float, double, usw. als Skalar) mit leicht anderem Namen und ich habe mich vertippt, als ich die Templates rausgezogen habe. (Ganz ehrlich: Wenn zur Mathe auch noch Templates kommen, hat niemand mehr Bock, sich den Text anzugucken …)

    struct Matrix3x3 {struct Similarity<Scalar, 3> {
        Vector3D rows[3]; // x axis; y axis; z axis
    };

Wieso verwendest du eigentlich überhaupt Quaternionen, wenn du am Ende alles in Matritzen umrechnest?
Wegen der leichten Interpolierbarkeit und weil sie sparsamer sind. Zur Matrix wird es nur zum Rendern konvertiert; allerdings komme ich auch in die Situation, wo ich wissen muss, wie z.B. die Y-Achse aussieht, nachdem sie rotiert ist – und weil die Antwort auf diese Frage identisch mit der zweiten Zeile der Matrixrepräsentation des Quaternions ist, steht und fällt auch die Physiksimulation mit einem Weg, die Matrix herauszufinden. (Sonst kommt die Schwerkraft je nach Objektrotation aus völlig anderen Richtungen als von unten und alles explodiert.)
Die Multiplikationsreihenfolge hat nichts mit column major vs row major zu tun, sondern einzig und allein mit der Konvention ob Vektoren als Zeilen oder Spalten aufgefasst werden.
Ist das nicht so ziemlich die Definition von row-major und column-major?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Artificial Mind
Establishment
Beiträge: 802
Registriert: 17.12.2007, 17:51
Wohnort: Aachen

Re: Quaternion-Wahnsinn

Beitrag von Artificial Mind »

Krishty hat geschrieben:
Die Multiplikationsreihenfolge hat nichts mit column major vs row major zu tun, sondern einzig und allein mit der Konvention ob Vektoren als Zeilen oder Spalten aufgefasst werden.
Ist das nicht so ziemlich die Definition von row-major und column-major?
Ist column-major und row-major nicht nur das Speicherformat?
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

Artificial Mind hat geschrieben:Dein Link zur Quaternion->Matrix-Umwandlung ( http://de.wikipedia.org/wiki/Quaternion ... n_Matrizen ) \(sagt aber, dass man einen Quaternion $q$ in eine Matrix $D_q$ umwandelt und dann $D_q\cdot p$ den Punkt $p$ dreht.

Sprich: Deine Quaternion-Matrix wird immer von Links ranmultipliziert:
$D_{q_X q_Y q_Z} \cdot p = (D_{q_X} \cdot D_{q_Y} \cdot D_{q_Z}) \cdot p = D_{q_X} \cdot (D_{q_Y} \cdot (D_{q_Z} \cdot p))$

Jedenfalls verstehe ich das so, dass man die Matrizen "von rechts nach links aufmultipliziert", wenn $D_q\cdot p$ die Drehung beschreibt - im Gegensatz zu $p \cdot D_q$.\)
Das klingt für mich logisch, tatsächlich. Uff. Und, ähm, wie komme ich nun an \($p \cdot D_q$.\)? :-3
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von dot »

Krishty hat geschrieben:
Wieso verwendest du eigentlich überhaupt Quaternionen, wenn du am Ende alles in Matritzen umrechnest?
Wegen der leichten Interpolierbarkeit und weil sie sparsamer sind.
Ok, das sind gute Gründe. Ich wollte nur sichergehen, dass du nicht einer von denen bist, die glauben, dass sie jetzt unbedingt Quaternions verwenden müssen, damit sie plötzlich keinen Gimbal Lock mehr haben...
Krishty hat geschrieben:
Die Multiplikationsreihenfolge hat nichts mit column major vs row major zu tun, sondern einzig und allein mit der Konvention ob Vektoren als Zeilen oder Spalten aufgefasst werden.
Ist das nicht so ziemlich die Definition von row-major und column-major?
Nope, row major und column major bezeichnen nur die Art und Weise, wie Matritzen im Speicher ausgelegt sind (row major -> zeilenweise, column major -> spaltenweise). Die Definition der Matritzenmultiplikation ist davon völlig unabhängig...
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

Artificial Mind hat geschrieben:Ist column-major und row-major nicht nur das Speicherformat?
Krishty hat geschrieben:Nope, row major und column major bezeichnen nur die Art und Weise, wie Matritzen im Speicher ausgelegt sind (row major -> zeilenweise, column major -> spaltenweise). Die Definition der Matritzenmultiplikation ist völlig unabhängig davon...
Stimmt; ihr habt recht. Jetzt lese ich es auch. Wie nennt man das denn dann, wenn man Vektoren als Zeilen oder als Spalten interpretiert? Muss ja auch einen Namen haben …
dot hat geschrieben:Ok, das sind gute Gründe. Ich wollte nur sichergehen, dass du nicht einer von denen bist, die glauben, dass sie jetzt unbedingt Quaternions verwenden müssen, damit sie plötzlich keinen Gimbal Lock mehr haben...
Habe ich mit Matrizen doch auch nicht.

In Fact hatte ich alles schon mit Matrizen ans Laufen gekriegt, nur waren die Interpolationen supereklig und der erzeugte Maschinentext absolut suboptimal. Ich dachte, alles durch Quaternions zu ersetzen und die Multiplikationsreihenfolge zu invertieren müsste ausreichen, aber nun ist alles kaputt und alle Rotationen sind negiert und ich sitze seit zwei Tagen und debugge dieselbe Quaternion-zu-Matrix-Konvertierung immer und immer wieder.
Zuletzt geändert von Krishty am 16.09.2012, 11:10, insgesamt 1-mal geändert.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von dot »

Das alle Rotationen negiert sind, lässt irgendwie vermuten, dass deine Matrizen transponiert sind (Rotationsmatritzen sind orthogonal).
Krishty hat geschrieben:Stimmt; ihr habt recht. Jetzt lese ich es auch. Wie nennt man das denn dann, wenn man Vektoren als Zeilen oder als Spalten interpretiert? Muss ja auch einen Namen haben …
Mathematisch gesehen sind Vektoren (1, 0) Tensoren, also Spalten (ja, man könnte natürlich auch eine komplett neue Mathematik konstruieren in der alles umgedreht ist und es dann Zeilen sind...anyway, Vektoren sind nach heute üblicher Konvention Spalten, die Variante mit den Zeilenvektoren kommt afaik daher, dass das in einigen frühen Computergrafik Büchern falsch gemacht wurde und seitdem als alte Tradition im Brauchtum des ländlichen CG Volkes weiterlebt). Siehe dazu auch z.B. das hier: http://chrishecker.com/Column_vs_Row_Vectors
Krishty hat geschrieben:
dot hat geschrieben:Ok, das sind gute Gründe. Ich wollte nur sichergehen, dass du nicht einer von denen bist, die glauben, dass sie jetzt unbedingt Quaternions verwenden müssen, damit sie plötzlich keinen Gimbal Lock mehr haben...
Habe ich mit Matrizen doch auch nicht.
Richtig bzw. das hast du mit Matritzen und Quaternionen gleichermaßen, weil es ein inhärentes Problem von Eulersequenzen ist und nichts mit der Art und Weise wie Rotationen beschrieben werden zu tun hat. Das hindert viele leider nicht daran, etwas anderes zu glauben... ;)
Zuletzt geändert von dot am 16.09.2012, 11:51, insgesamt 6-mal geändert.
Benutzeravatar
Artificial Mind
Establishment
Beiträge: 802
Registriert: 17.12.2007, 17:51
Wohnort: Aachen

Re: Quaternion-Wahnsinn

Beitrag von Artificial Mind »

Krishty hat geschrieben:
Artificial Mind hat geschrieben:Ist column-major und row-major nicht nur das Speicherformat?
Krishty hat geschrieben:Nope, row major und column major bezeichnen nur die Art und Weise, wie Matritzen im Speicher ausgelegt sind (row major -> zeilenweise, column major -> spaltenweise). Die Definition der Matritzenmultiplikation ist völlig unabhängig davon...
Stimmt; ihr habt recht. Jetzt lese ich es auch. Wie nennt man das denn dann, wenn man Vektoren als Zeilen oder als Spalten interpretiert? Muss ja auch einen Namen haben …
Zeilenvektoren und Spaltenvektoren :D

\(Wenn man $D_q \cdot p$ schreibt, dann ist $p$ ein Spaltenvektor, damit $D_q \cdot p$ eine gültige Matrixmultiplikation ist.
Andersherum, bei $p \cdot D_q$ ist $p$ ein Zeilenvektor, damit das Ergebnis wohldefiniert ist.

Damit kommen wir auch deiner Frage der Konvertierung relativ nahe:
Ich mache das jetzt mal 2D:
Also der Punkt $p = \bigl(\begin{smallmatrix}
x\\ y
\end{smallmatrix} \bigr)$ ist ein Spaltenvektor. Nehmen wir $p^T = (x,y)$ als Zeilenvektor.

Sei weiterhin $M=\bigl(\begin{smallmatrix}
a&b\\ c&d
\end{smallmatrix} \bigr)$.

Dann ist $M \cdot p = \bigl(\begin{smallmatrix}
a&b\\ c&d
\end{smallmatrix} \bigr) \cdot \bigl(\begin{smallmatrix}
x\\ y
\end{smallmatrix} \bigr) =
\bigl(\begin{smallmatrix}
ax + by\\ cx +dy
\end{smallmatrix} \bigr)$ wohldefiniert.

Andererseits ist $p \cdot M = \bigl(\begin{smallmatrix}
x\\ y
\end{smallmatrix} \bigr) \cdot \bigl(\begin{smallmatrix}
a&b\\ c&d
\end{smallmatrix} \bigr)$ gar keine vernünftige Matrixmultiplikation.

Aber $p^T \cdot M = (x,y) \cdot \bigl(\begin{smallmatrix}
a&b\\ c&d
\end{smallmatrix} \bigr) = (ax +cy, bx+dy)$ ist wieder wohldefiniert, allerdings nicht das gleiche Ergebnis - mal von transponiert abgesehen - wie $p \cdot M$.

Nehmen wir hingegen $p^T \cdot M^T = (p \cdot M)^T = (x,y) \cdot \bigl(\begin{smallmatrix}
a&c\\ b&d
\end{smallmatrix} \bigr) = (ax + by, cx + dy) = { \bigl(\begin{smallmatrix}
ax + by\\ cx +dy
\end{smallmatrix} \bigr)}^T$, dann haben wir das gleiche Ergebnis wie $p \cdot M$, nur transponiert. Und du weißt nun was deine Matrix für "$p \cdot D_q$" ist, nämlich $D_q^T$.
Klingt das soweit schlüssig?\)
(Ich hab mir das gerade so auf nem Zettel aufgeschrieben, deswegen können sich sehr gut Fehler eingeschlichen haben.)
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

Aaalso. Wenn ich meine Quaternionen \($Q_x \cdot Q_y \cdot Q_z$ aufmultipliziere;

und damit mein endgültiges Quaternion $Q$ kriege;

und das gemäß meinem Quelltext in eine Matrix $D_q$ umwandle;

dann ist $D_q$ dafür ausgelegt, den Punkt $p$ als $D_q \cdot p$ zu transformieren, und das entspricht $Q_x \cdot Q_y \cdot Q_z \cdot p$ – ganz egal, wie sich die Multiplikationsreihenfolge der Quaternionen zu jener der Drehungen, die sie repräsentieren, verhält.

Ich muss in meinem Quelltext also keine Reihenfolge ändern sondern kann schlicht jede Matrix durch ein Quaternion ersetzen, und weil die Matrix, zu der sie konvertiert werden, für linksseitiges Anmultiplizieren ausgelegt ist, wird sich nichts ändern?

Und wenn die Matrix für rechtsseitiges Anmultiplizieren ausgelegt wäre, entspräche das $p \cdot Q_z \cdot Q_y \cdot Q_x$, und die Reihenfolge wäre dadurch umgekehrt?\)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Artificial Mind
Establishment
Beiträge: 802
Registriert: 17.12.2007, 17:51
Wohnort: Aachen

Re: Quaternion-Wahnsinn

Beitrag von Artificial Mind »

Krishty hat geschrieben: Ich muss in meinem Quelltext also keine Reihenfolge ändern sondern kann schlicht jede Matrix durch ein Quaternion ersetzen, und weil die Matrix, zu der sie konvertiert werden, für linksseitiges Anmultiplizieren ausgelegt ist, wird sich nichts ändern?
So würde ich meinen. Ich hab bis jetzt immer mit Spaltenvektoren und "von links anmultiplizieren" gearbeitet und auch Quaternionen so benutzt und es ging gut.

Ich finde "von rechts anmultiplizieren" auch ziemlich konterintuitiv, da es nicht mit der normalen mathematischen Konvention geschrieben wird.
Wenn die Matrix für "von rechts" ausgelegt wäre, würde ich behaupten, dass du die Quaternionen immer noch "von links" anmultiplizieren musst (da du ja nichts an der Quaternionen-Multiplikation geändert hast), allerdings die Matrizen von rechts - super Verwirrung.

\($q_x \cdot q_y \cdot q_z \to Q_x \cdot Q_y \cdot Q_z$, da wir aber bei "von rechts" mit der transponierten Version arbeiten wird es zu $p^T \cdot (Q_x \cdot Q_y \cdot Q_z)^T = p^T\cdot Q_z^T \cdot Q_y^T \cdot Q_x^T$, die Reihenfolge wird ja beim Transponieren gedreht.\)
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

Es gibt aber einen Punkt, an dem das ganze inkosistent wird:

Ich kombiniere jetzt sowohl Matrizen als auch Quaternions je von links nach rechts:

\($Q = Q_x \cdot Q_y \cdot Q_z$
$M = M_x \cdot M_y \cdot M_z$

Soweit, so gut. Jetzt konvertiere ich $Q$ gemäß Wikipedia durch die Funktion, die ich gepostet habe, zu einer Matrix $M_Q$.

Transformiere ich nun einen Punkt $p$, gilt:

$rotated(p, M_Q) = rotated(p, M)$

Ist auch logisch – schließlich sind die Matrizen gleich! So. Jetzt transformiere ich den Punkt aber nicht mit der Matrixrepräsentation $Q$s, sondern direkt mit $Q$. Und peng voll in die Fresse es kommt ein anderer Punkt raus. Ist ja auch klar: Wenn ich $Q_x \cdot Q_y \cdot Q_z$ ausrechne, kommt ein Quaternion raus, das erst um Z rotiert, dann um Y, dann um X. Links-nach-rechts wird ja erst durch die Matrixkonvertierung gemacht.

Und das finde ich absolut katastrophal: Dass etwas anderes rauskommt, wenn ich einen Punkt durch ein Quaternion transformiere, als wenn ich einen Punkt mit der Matrix transformiere, die ich aus demselben Quaternion berechnet habe.\)


(to be continued …)
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Artificial Mind
Establishment
Beiträge: 802
Registriert: 17.12.2007, 17:51
Wohnort: Aachen

Re: Quaternion-Wahnsinn

Beitrag von Artificial Mind »

Krishty hat geschrieben:Ich kombiniere jetzt sowohl Matrizen als auch Quaternions je von links nach rechts:

\($Q = Q_x \cdot Q_y \cdot Q_z$
$M = M_x \cdot M_y \cdot M_z$\)
Also wenn \($M$\) normale mathematische Matrizen sind, dann werden die von rechts-nach-links "angewandt".
Wenn du deine Matrizen von links-nach-rechts benutzt, dann arbeitest du ja quasi mit transponierten Matrizen, ergo musst du auch die Multiplikationsreihenfolgen überall ändern.

Diese Inkonsistenz ist ja genau das was ich vorher sagte. Quaternione verhalten sich wie "normale mathematische" Matrizen: von links anmultiplizieren heißt als neue Transformation auf das Gesamtergebnis anwenden.
Deine Matrizen, die du von rechts anmultipliziert verhalten sich halt genau umgekehrt. Wenn du beides mischts, musst du die Reihenfolgen echt vorsichtig analysieren.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von dot »

Krishty hat geschrieben:Soweit, so gut. Jetzt konvertiere ich $Q$ gemäß Wikipedia durch die Funktion, die ich gepostet habe, zu einer Matrix $M_Q$.
Und genau da liegt wohl dein Problem. Die Formel von Wikipedia gibt dir eine Matrix, die Spaltenvektoren entsprechend transformiert. Nachdem deine Rotationsmatritzen aber für Zeilenvektoren gedacht sind, ist das Ergebnis natürlich genau das Transponierte von dem, was du erwarten würdest...
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

Artificial Mind hat geschrieben:Also wenn \($M$\) normale mathematische Matrizen sind, dann werden die von rechts-nach-links "angewandt".
Wenn du deine Matrizen von links-nach-rechts benutzt, dann arbeitest du ja quasi mit transponierten Matrizen, ergo musst du auch die Multiplikationsreihenfolgen überall ändern.
Augenblick: Die Matrixmultiplikation funktioniert ja so, dass man für das Element \($a_ij$ die $i$-te Zeile der linken Matrix mit der $j$-en Spalte der rechten Matrix multipliziert.\) So habe ich das auch implementiert. Soweit ich das beurteilen kann, liegen auch meine Vektoren bei der Transformierung als Spaltenvektoren vor. Woher kommt es dann, dass ich von links nach rechts aufmultiplizieren kann?
dot hat geschrieben:Die Formel von Wikipedia gibt dir eine Matrix, die Spaltenvektoren entsprechend transformiert. Nachdem deine Rotationsmatritzen aber für Zeilenvektoren gedacht sind, ist das Ergebnis natürlich genau das Transponierte von dem, was du erwarten würdest...
Nicht einmal – nach dem Transponieren sind alle Drehwinkel negiert. Warum sind sie für Zeilenvektoren gedacht?

Weil du das Thema ansprichst:

Ich habe in der Zwischenzeit weitergesucht und eine Matrix gefunden, die ein Quaternion tatsächlich zu einer Matrix konvertiert, mit der ich einen Punkt multiplizieren muss, damit er exakt so transformiert wird wie durch das Quaternion. Leider sieht diese Matrix völlig anders aus als die, mit der wir bisher gerechnet haben.

Alles, was ich will ist, dass es egal ist, ob ich einen Punkt durch ein Quaternion transformiere, oder durch eine Matrix, oder durch eine aus einem Quaternion berechnete Matrix:

\($rotated(p, M) = rotated(p, M_Q) = rotated(p, Q)$\)

dafür ist mir auch egal, ob ich von links nach rechts multiplizieren muss oder von rechts nach links oder von meiner Oma zu meiner Katze. Es soll einfach nur bei allen drei Operationen das gleiche rauskommen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von dot »

Zeig doch mal, wie genau du Matritzenmultiplikation, Quaternionmultiplikation, Transformation von Vektor mit Matrix und Transformation von Vektor mit Quaternion implementiert hast. Ohne das zu wissen, können wir, denk ich, nur raten wo genau das Problem liegt...
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

Ja, bin gerade dabei. Ich glaube, dass die Vektor-Matrix-Multiplikation schon ein Volltreffer ist:

    Vector3D const rotated(
        Vector3D const & v,
        Matrix3x3 const & m
    ) {
        Vector3D const result = {
            v.x * m.rows[0].x + v.y * m.rows[1].x + v.z * m.rows[2].x,
            v.x * m.rows[0].y + v.y * m.rows[1].y + v.z * m.rows[2].y,
            v.x * m.rows[0].z + v.y * m.rows[1].z + v.z * m.rows[2].z
        };
        return result;
    }


Da sieht der Vektor nicht sehr spaltig aus.

Die Matrixmultiplikation sieht aber richtig aus, oder?

    Matrix3x3 const operator * (
        Matrix3x3 const & a,
        Matrix3x3 const & b
    ) {
        Matrix3x3 const result = { {
            {
                a.rows[0].x * b.rows[0].x + a.rows[0].y * b.rows[1].x + a.rows[0].z * b.rows[2].x,
                a.rows[0].x * b.rows[0].y + a.rows[0].y * b.rows[1].y + a.rows[0].z * b.rows[2].y,
                a.rows[0].x * b.rows[0].z + a.rows[0].y * b.rows[1].z + a.rows[0].z * b.rows[2].z
            }, {
                a.rows[1].x * b.rows[0].x + a.rows[1].y * b.rows[1].x + a.rows[1].z * b.rows[2].x,
                a.rows[1].x * b.rows[0].y + a.rows[1].y * b.rows[1].y + a.rows[1].z * b.rows[2].y,
                a.rows[1].x * b.rows[0].z + a.rows[1].y * b.rows[1].z + a.rows[1].z * b.rows[2].z
            }, {
                a.rows[2].x * b.rows[0].x + a.rows[2].y * b.rows[1].x + a.rows[2].z * b.rows[2].x,
                a.rows[2].x * b.rows[0].y + a.rows[2].y * b.rows[1].y + a.rows[2].z * b.rows[2].y,
                a.rows[2].x * b.rows[0].z + a.rows[2].y * b.rows[1].z + a.rows[2].z * b.rows[2].z
            }
        } };
        return result;
    }
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von dot »

Jop, wie die Reihenfolge der Parameter von rotated() schon anzeigt, multiplizierst du da definitiv Zeile mit Matrix. Ich würd generell vorschlagen, die Reihenfolge der Parameter umzudrehen und die Funktion mul() zu nennen oder so, denn eine 3x3 Matrix kann viel mehr als nur rotieren.

Die Matritzenmultiplikation schaut mir richtig aus, zumindest wenn ich die Initialisierungsliste jetzt richtig interpretiere...
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

dot hat geschrieben:Ich würd generell vorschlagen, die Reihenfolge der Parameter umzudrehen und die Funktion mul() zu nennen oder so, denn eine 3x3 Matrix kann viel mehr als nur rotieren.
Ich arbeite sowieso nicht mit Matrizen, sondern habe Templates für bestimmte Arten von Abbildungen, die in diesem Fall zufällig als 3×3-Matrix implementiert sind. Daher auch immer die Umbenennerei, bevor ich hier was poste :)
dot hat geschrieben:Die Matritzenmultiplikation schaut mir richtig aus, zumindest wenn ich die Initialisierungsliste jetzt richtig interpretiere...
Die drei Blöcke sind je eine Zeile.

Also, ich mache genau den Fehler, der in deinem Link beschrieben wurde, und multipliziere Zeilenvektoren. Ich habe eben meine Rotationsmatrizen geprüft, und sie sind richtig. Dass ich mit Zeilenvektoren gearbeitet habe, ist also der Grund, warum ich von links nach rechts multiplizieren konnte und das werde ich nun umstellen.

Während ich jetzt 15k Zeilen schwer debugbaren Text auf Spaltenvektoren umschreibe, möchte ich das Thema gern warmhalten, weil ich nicht glaube, dass es erledigt ist: Da ich ja mit Spaltenvektoren gearbeitet habe, hätte die Wikipedia-Matrix für mich doch funktionieren müssen, oder?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

Z-Rotationen funktionieren nicht. Ich weiß nicht, ob es neu ist oder schon der Auslöser für den früheren Fehler war, aber für Rotationen um die Z-Achse kommen bei mir grundsätzlich verschiedene Matrizen raus, je nachdem, ob ich sie direkt oder über ein Quaternion berechne.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von dot »

Könnte sein, dass das was mit der Handedness von deinem Koordinatensystem zu tun hat, denn je nach Handedness gehen positive Winkel anders rum...
Benutzeravatar
Krishty
Establishment
Beiträge: 8240
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Quaternion-Wahnsinn

Beitrag von Krishty »

Nein, die Matrix ist right-handed. Mir war tatsächlich ein Minus verrutscht. Ursprünglicher Auslöser war das aber nicht; die Version vor den Spaltenvektoren war korrekt.

Proof of concept – Similarity ist durch eine Matrix implementiert, RigidMotion durch eine Quaternion; die überladenen Operatoren gaukeln links-nach-rechts-Reihenfolge vor, obwohl intern von rechts nach links aufmultipliziert wird:
math is so hard.png
Interessant auch die Genauigkeitsunterschiede.

Ich habe zur Umrechnung von Quaternion zur Matrix nun diese Formel eingesetzt.

Tatsächlich hat sich das ganze Tohuwabohu nur an drei Stellen geäußert:
  • Daran, dass sich das Quaternion nicht zur Matrix umrechnen ließ;
  • an einer einzigen negierten Achse in der Physik; und
  • an einer einzigen negierten Zahl in der GUI.
Ziemlich subtile Scheiße. Je schwerer der Fehler, desto schwerer ist er zu finden. Ich bin einfach nur froh, dass es funktioniert.

Danke euch, dot und Artificial Mind – alleine hätte ich da wohl noch Jahre dransitzen können.

————

Also die Lösung des ganzen (falls ich sie überhaupt selber verstehe):

Ich habe bei meinen Matrizen Zeilenvektoren statt Spaltenvektoren benutzt. Dadurch waren alle Matrizen einerseits transponiert und andererseits in der falschen Reihenfolge (sie wurden von links nach rechts aufrotiert statt von rechts nach links, wie es Konvention ist).

Weil das verkehrtherum war, war die übliche Quaternion-zu-Matrix-Konvertierung nicht zu gebrauchen – selbst nach Umkehrung nicht, da sie dabei alle Winkel negiert. Ich bekam also keine Quaternion-Matrix-Interaktion hin.

Als ich eine Quaternion-zu-Matrix-Konvertierung gefunden hatte, die zu gebrauchen war, stand sie im Widerspruch zur Punkt-durch-Quaternion-Transformation, weil die Quaternionen sich unabhängig der Konvertierungsmatrix weiter von rechts nach links aufrotiert haben.

Jetzt, wo ich alles auf Spaltenvektoren und im ganzen Programm die Multiplikationsreihenfolge umgestellt habe, sind alle Rechenwege konsistent und die Standardformeln greifen.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten