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θ ┘
これらの行列を乗算して、3つの回転すべての結果である1つの行列を作成できます。行列の乗算は可換ではないので、これらの行列が一緒に乗算される順序が重要であることに注意することが重要です。つまりRx*Ry*Rz ≠ Rz*Ry*Rx
。可能な回転順序の1つであるzyxについて考えてみましょう。3つの行列を組み合わせると、次のような行列になります。
┌ CyCz -CySz Sy ┐
RxRyRz = │ SxSyCz + CxSz -SxSySz + CxCz -SxCy │
└ -CxSyCz + SxSz CxSySz + SxCz CxCy ┘
どこCx
の余弦れるx
回転の角度は、Sx
のサインであるx
等、回転の角度
さて、課題はオリジナルを抽出することであるx
、y
とz
行列に入った値。
まずはx
角度を出しましょう。私たちが知っている場合sin(x)
と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;
}