Mike Day의이 프로세스에 대한 훌륭한 글이 있습니다 :
https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2012/07/euler-angles1.pdf
버전 0.9.7.0, 2015 년 2 월 8 일 현재 glm에서도 구현됩니다. 구현을 확인하십시오 .
수학을 이해하려면 회전 행렬에있는 값을 봐야합니다. 또한 값을 올바르게 추출하려면 행렬을 만들기 위해 회전이 적용된 순서를 알아야합니다.
오일러 각으로부터의 회전 매트릭스는 x 축, y 축 및 z 축 주위의 회전을 결합하여 형성된다. 예를 들어, Z를 중심으로 θ도 회전하는 것은 행렬로 수행 할 수 있습니다.
┌ cosθ -sinθ 0 ┐
Rz = │ sinθ cosθ 0 │
└ 0 0 1 ┘
X 축과 Y 축을 중심으로 회전하기위한 유사한 행렬이 있습니다.
┌ 1 0 0 ┐
Rx = │ 0 cosθ -sinθ │
└ 0 sinθ cosθ ┘
┌ cosθ 0 sinθ ┐
Ry = │ 0 1 0 │
└ -sinθ 0 cosθ ┘
이 행렬을 곱하여 세 회전의 결과 인 하나의 행렬을 만들 수 있습니다. 행렬 곱셈은 정식 적이 지 않기 때문에 이러한 행렬을 곱하는 순서가 중요하다는 점에 유의해야합니다 . 이것은 의미합니다 Rx*Ry*Rz ≠ Rz*Ry*Rx
. 가능한 회전 순서 zyx를 고려해 봅시다. 세 개의 행렬이 결합되면 다음과 같은 행렬이 나타납니다.
┌ CyCz -CySz Sy ┐
RxRyRz = │ SxSyCz + CxSz -SxSySz + CxCz -SxCy │
└ -CxSyCz + SxSz CxSySz + SxCz CxCy ┘
여기서 Cx
의 코사인 인 x
회전 각도, Sx
의 사인이다 x
회전 각도 등
이제 과제는 원본을 추출하는 것입니다 x
, y
그리고 z
매트릭스에 들어갔다 값.
먼저 x
각도를 알아 봅시다 . sin(x)
and 를 알고 있으면 cos(x)
역 탄젠트 함수 atan2
를 사용하여 각도를 되돌릴 수 있습니다 . 불행히도, 그 값들은 우리의 매트릭스에 스스로 나타나지 않습니다. 그러나 요소를 자세히 살펴보면 다음 M[1][2]
과 같이 잘 M[2][2]
알고 있음을 알 수 있습니다 . 탄젠트 함수는 삼각형의 반대편과 인접한 변의 비율이므로 두 값을 같은 양 (이 경우 )으로 스케일링 하면 같은 결과가 나타납니다. 그러므로,-sin(x)*cos(y)
cos(x)*cos(y)
cos(y)
x = atan2(-M[1][2], M[2][2])
이제을 시도해 봅시다 y
. 우리는 sin(y)
에서 알고 있습니다 M[0][2]
. cos (y)가 있으면 atan2
다시 사용할 수 있지만 행렬에 해당 값이 없습니다. 그러나 피타고라스의 정체성으로 인해 우리는 다음을 알고 있습니다.
cosY = sqrt(1 - M[0][2])
따라서 다음을 계산할 수 있습니다 y
.
y = atan2(M[0][2], cosY)
마지막으로를 계산해야합니다 z
. Mike Day의 접근 방식이 이전 답변과 다른 곳입니다. 이 시점에서 우리는의 양을 알고 있기 때문에 x
와 y
회전을, 우리는 XY 회전 행렬을 구성, 그리고 양을 찾을 수 있습니다 z
대상 행렬에 맞게 필요한 회전을. RxRy
행렬은 다음과 같습니다 :
┌ Cy 0 Sy ┐
RxRy = │ SxSy Cx -SxCy │
└ -CxSy Sx CxCy ┘
RxRy
* Rz
는 입력 행렬과 같다는 것을 알고 M
있으므로이 행렬을 사용하여 Rz
다음으로 돌아갈 수 있습니다 .
M = RxRy * Rz
inverse(RxRy) * M = Rz
회전 행렬 의 역수는 전치이므로이 값을 다음 과 같이 확장 할 수 있습니다.
┌ 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 ┘
지금 우리가 해결할 수 sinZ
와 cosZ
행렬 곱셈을 수행하여. 우리는 요소를 계산해야 [1][0]
하고 [1][1]
.
sinZ = cosX * M[1][0] + sinX * M[2][0]
cosZ = coxX * M[1][1] + sinX * M[2][1]
z = atan2(sinZ, cosZ)
다음은 참조를위한 전체 구현입니다.
#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;
}