Mike Day tarafından bu süreçte harika bir yazı var:
https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2012/07/euler-angles1.pdf
Ayrıca şimdi 0.9.7.0, 02/08/2015 sürümünden itibaren glm'de uygulanmaktadır. Uygulamayı kontrol edin .
Matematiği anlamak için, döndürme matrisinizdeki değerlere bakmalısınız. Ayrıca, değerleri düzgün bir şekilde çıkarmak için matrisinizi oluşturmak için rotasyonların uygulanma sırasını bilmeniz gerekir.
X-, y- ve z eksenleri etrafındaki rotasyonların birleştirilmesiyle Euler açılarından bir rotasyon matrisi oluşturulur. Örneğin, Z'nin etrafında θ derece döndürmek matrisle yapılabilir
┌ cosθ -sinθ 0 ┐
Rz = │ sinθ cosθ 0 │
└ 0 0 1 ┘
X ve Y eksenleri etrafında döndürmek için benzer matrisler vardır:
┌ 1 0 0 ┐
Rx = │ 0 cosθ -sinθ │
└ 0 sinθ cosθ ┘
┌ cosθ 0 sinθ ┐
Ry = │ 0 1 0 │
└ -sinθ 0 cosθ ┘
Üç rotasyonun sonucu olan bir matris oluşturmak için bu matrisleri birlikte çoğaltabiliriz. Matris çarpımı değişmeli olmadığından , bu matrislerin birlikte çarpılma sırasının önemli olduğunu belirtmek önemlidir . Bu demektir Rx*Ry*Rz ≠ Rz*Ry*Rx
. Bir olası dönüş sırasını düşünelim, zyx. Üç matris birleştirildiğinde, şöyle görünen bir matrise neden olur:
┌ CyCz -CySz Sy ┐
RxRyRz = │ SxSyCz + CxSz -SxSySz + CxCz -SxCy │
└ -CxSyCz + SxSz CxSySz + SxCz CxCy ┘
dönme açısının Cx
kosinüsü nerede , x
dönme açısının Sx
sinüsü x
vb.
Şimdi, meydan orijinali çıkarmaktır x
, y
ve z
matrisin girdi değerleri.
Önce x
açıyı çıkaralım . Bildiğimiz ise sin(x)
ve cos(x)
biz ters tanjant fonksiyonunu kullanabilirsiniz atan2
bize açısını geri vermek. Ne yazık ki, bu değerler matrisimizde kendiliğinden görünmüyor. Ama biz unsurları daha yakından bakacak olursak M[1][2]
ve M[2][2]
biz biliyoruz görebilirsiniz -sin(x)*cos(y)
yanı sıra cos(x)*cos(y)
. Teğet fonksiyonu, bir üçgenin zıt ve bitişik kenarlarının oranı olduğundan, her iki değeri de aynı miktarda ölçeklendirmek (bu durumda cos(y)
) aynı sonucu verir. Böylece,
x = atan2(-M[1][2], M[2][2])
Şimdi almaya çalışalım y
. Biz biliyoruz sin(y)
dan M[0][2]
. Eğer cos (y) varsa, atan2
tekrar kullanabiliriz , ama matrisimizde bu değere sahip değiliz. Ancak, Pisagor kimliğinden dolayı şunu biliyoruz:
cosY = sqrt(1 - M[0][2])
Böylece, hesaplayabiliriz y
:
y = atan2(M[0][2], cosY)
Son olarak, hesaplamamız gerekiyor z
. Mike Day'in yaklaşımı önceki yanıttan farklıdır. Bu noktada x
ve y
dönüş miktarını bildiğimizden, bir XY dönüş matrisi oluşturabilir ve z
hedef matrisle eşleştirmek için gerekli dönüş miktarını bulabiliriz . RxRy
Matris şöyle görünür:
┌ Cy 0 Sy ┐
RxRy = │ SxSy Cx -SxCy │
└ -CxSy Sx CxCy ┘
RxRy
* Rz
'Nin giriş matrisimize eşit olduğunu bildiğimizden, M
bu matrisi kullanarak geri dönebiliriz Rz
:
M = RxRy * Rz
inverse(RxRy) * M = Rz
Rotasyon matrisi ters onun devrik olduğunu biz bu genişletmek, böylece:
┌ 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 ┘
Şimdilik çözebilir sinZ
ve cosZ
matris çarpma gerçekleştirerek. Biz sadece unsurları hesaplamak gerekir [1][0]
ve [1][1]
.
sinZ = cosX * M[1][0] + sinX * M[2][0]
cosZ = coxX * M[1][1] + sinX * M[2][1]
z = atan2(sinZ, cosZ)
İşte referans için tam bir uygulama:
#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;
}