Es gibt eine großartige Zusammenfassung zu diesem Prozess von Mike Day:
https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2012/07/euler-angles1.pdf
Es ist jetzt auch in glm ab Version 0.9.7.0 vom 02.08.2015 implementiert. Überprüfen Sie die Implementierung .
Um die Mathematik zu verstehen, sollten Sie sich die Werte in Ihrer Rotationsmatrix ansehen. Außerdem müssen Sie die Reihenfolge kennen, in der die Rotationen angewendet wurden, um Ihre Matrix zu erstellen, damit die Werte ordnungsgemäß extrahiert werden können.
Eine Rotationsmatrix aus Eulerwinkeln wird durch Kombinieren von Rotationen um die x-, y- und z-Achse gebildet. Zum Beispiel kann mit der Matrix eine Drehung von θ Grad um Z durchgeführt werden
┌ cosθ -sinθ 0 ┐
Rz = │ sinθ cosθ 0 │
└ 0 0 1 ┘
Ähnliche Matrizen existieren für die Drehung um die X- und Y-Achse:
┌ 1 0 0 ┐
Rx = │ 0 cosθ -sinθ │
└ 0 sinθ cosθ ┘
┌ cosθ 0 sinθ ┐
Ry = │ 0 1 0 │
└ -sinθ 0 cosθ ┘
Wir können diese Matrizen miteinander multiplizieren, um eine Matrix zu erstellen, die das Ergebnis aller drei Rotationen ist. Es ist wichtig zu beachten, dass die Reihenfolge, in der diese Matrizen miteinander multipliziert werden, wichtig ist, da die Matrixmultiplikation nicht kommutativ ist . Das bedeutet das Rx*Ry*Rz ≠ Rz*Ry*Rx
. Betrachten wir eine mögliche Rotationsreihenfolge, zyx. Wenn die drei Matrizen kombiniert werden, ergibt sich eine Matrix, die folgendermaßen aussieht:
┌ CyCz -CySz Sy ┐
RxRyRz = │ SxSyCz + CxSz -SxSySz + CxCz -SxCy │
└ -CxSyCz + SxSz CxSySz + SxCz CxCy ┘
Wo Cx
ist der Cosinus des x
Drehwinkels, Sx
ist der Sinus desx
Drehwinkels usw.
Nun besteht die Herausforderung darin , das Original zu extrahieren x
, y
undz
Werte , die in die Matrix ging.
Lassen Sie uns zuerst den x
Winkel herausholen. Wenn wir das sin(x)
und kennen cos(x)
, können wir die inverse Tangentenfunktion verwenden atan2
, um unseren Winkel zurückzugeben. Leider erscheinen diese Werte nicht von alleine in unserer Matrix. Aber wenn wir uns die Elemente genauer ansehen M[1][2]
und sehen M[2][2]
, dass wir es -sin(x)*cos(y)
genauso gut wissen wie cos(x)*cos(y)
. Da die Tangentenfunktion das Verhältnis der gegenüberliegenden und benachbarten Seiten eines Dreiecks ist, führt die Skalierung beider Werte um den gleichen Betrag (in diesem Fall cos(y)
) zum gleichen Ergebnis. Somit,
x = atan2(-M[1][2], M[2][2])
Jetzt lass uns versuchen zu bekommen y
. Wir wissen sin(y)
von M[0][2]
. Wenn wir cos (y) hätten, könnten wir es atan2
wieder verwenden, aber wir haben diesen Wert nicht in unserer Matrix. Jedoch aufgrund der pythagoreischen Identität , wir wissen , dass:
cosY = sqrt(1 - M[0][2])
So können wir berechnen y
:
y = atan2(M[0][2], cosY)
Zuletzt müssen wir berechnen z
. Hier unterscheidet sich der Ansatz von Mike Day von der vorherigen Antwort. Da wir zu diesem Zeitpunkt den Betrag x
und die y
Rotation kennen, können wir eine XY-Rotationsmatrix erstellen und den z
Rotationsbetrag ermitteln, der zur Anpassung an die Zielmatrix erforderlich ist. Die RxRy
Matrix sieht folgendermaßen aus:
┌ Cy 0 Sy ┐
RxRy = │ SxSy Cx -SxCy │
└ -CxSy Sx CxCy ┘
Da wir wissen, dass RxRy
* Rz
unserer Eingabematrix entspricht M
, können wir diese Matrix verwenden, um zu Folgendem zurückzukehren Rz
:
M = RxRy * Rz
inverse(RxRy) * M = Rz
Die Umkehrung einer Rotationsmatrix ist ihre Transponierung , daher können wir diese erweitern auf:
┌ Cy SxSy -CxSy ┐┌M00 M01 M02┐ ┌ cosZ -sinZ 0 ┐
│ 0 Cx Sx ││M10 M11 M12│ = │ sinZ cosZ 0 │
└ Sy -SxCy CxCy ┘└M20 M21 M22┘ └ 0 0 1 ┘
Wir können jetzt nach sinZ
und cosZ
durch Ausführen der Matrixmultiplikation lösen . Wir müssen nur die Elemente [1][0]
und berechnen [1][1]
.
sinZ = cosX * M[1][0] + sinX * M[2][0]
cosZ = coxX * M[1][1] + sinX * M[2][1]
z = atan2(sinZ, cosZ)
Hier ist eine vollständige Implementierung als Referenz:
#include <iostream>
#include <cmath>
class Vec4 {
public:
Vec4(float x, float y, float z, float w) :
x(x), y(y), z(z), w(w) {}
float dot(const Vec4& other) const {
return x * other.x +
y * other.y +
z * other.z +
w * other.w;
};
float x, y, z, w;
};
class Mat4x4 {
public:
Mat4x4() {}
Mat4x4(float v00, float v01, float v02, float v03,
float v10, float v11, float v12, float v13,
float v20, float v21, float v22, float v23,
float v30, float v31, float v32, float v33) {
values[0] = v00;
values[1] = v01;
values[2] = v02;
values[3] = v03;
values[4] = v10;
values[5] = v11;
values[6] = v12;
values[7] = v13;
values[8] = v20;
values[9] = v21;
values[10] = v22;
values[11] = v23;
values[12] = v30;
values[13] = v31;
values[14] = v32;
values[15] = v33;
}
Vec4 row(const int row) const {
return Vec4(
values[row*4],
values[row*4+1],
values[row*4+2],
values[row*4+3]
);
}
Vec4 column(const int column) const {
return Vec4(
values[column],
values[column + 4],
values[column + 8],
values[column + 12]
);
}
Mat4x4 multiply(const Mat4x4& other) const {
Mat4x4 result;
for (int row = 0; row < 4; ++row) {
for (int column = 0; column < 4; ++column) {
result.values[row*4+column] = this->row(row).dot(other.column(column));
}
}
return result;
}
void extractEulerAngleXYZ(float& rotXangle, float& rotYangle, float& rotZangle) const {
rotXangle = atan2(-row(1).z, row(2).z);
float cosYangle = sqrt(pow(row(0).x, 2) + pow(row(0).y, 2));
rotYangle = atan2(row(0).z, cosYangle);
float sinXangle = sin(rotXangle);
float cosXangle = cos(rotXangle);
rotZangle = atan2(cosXangle * row(1).x + sinXangle * row(2).x, cosXangle * row(1).y + sinXangle * row(2).y);
}
float values[16];
};
float toRadians(float degrees) {
return degrees * (M_PI / 180);
}
float toDegrees(float radians) {
return radians * (180 / M_PI);
}
int main() {
float rotXangle = toRadians(15);
float rotYangle = toRadians(30);
float rotZangle = toRadians(60);
Mat4x4 rotX(
1, 0, 0, 0,
0, cos(rotXangle), -sin(rotXangle), 0,
0, sin(rotXangle), cos(rotXangle), 0,
0, 0, 0, 1
);
Mat4x4 rotY(
cos(rotYangle), 0, sin(rotYangle), 0,
0, 1, 0, 0,
-sin(rotYangle), 0, cos(rotYangle), 0,
0, 0, 0, 1
);
Mat4x4 rotZ(
cos(rotZangle), -sin(rotZangle), 0, 0,
sin(rotZangle), cos(rotZangle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
Mat4x4 concatenatedRotationMatrix =
rotX.multiply(rotY.multiply(rotZ));
float extractedXangle = 0, extractedYangle = 0, extractedZangle = 0;
concatenatedRotationMatrix.extractEulerAngleXYZ(
extractedXangle, extractedYangle, extractedZangle
);
std::cout << toDegrees(extractedXangle) << ' ' <<
toDegrees(extractedYangle) << ' ' <<
toDegrees(extractedZangle) << std::endl;
return 0;
}