माइक डे द्वारा इस प्रक्रिया पर एक शानदार राइटअप है:
https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2012/07/euler-angles1.pdf
यह अब glm में भी लागू हो गया है, संस्करण 0.9.7.0, 02/08/2015 के रूप में। कार्यान्वयन की जाँच करें ।
गणित को समझने के लिए, आपको उन मूल्यों को देखना चाहिए जो आपके रोटेशन मैट्रिक्स में हैं। इसके अलावा, आपको उस क्रम को जानना होगा जिसमें मूल्यों को ठीक से निकालने के लिए आपके मैट्रिक्स को बनाने के लिए घुमाव लागू किए गए थे।
यूलर एंगल्स से एक रोटेशन मैट्रिक्स x-, y- और z- अक्षों के चारों ओर घुमावों को मिलाकर बनता है। उदाहरण के लिए, Z के चारों ओर around डिग्री का घूमना मैट्रिक्स के साथ किया जा सकता है
┌ cosθ -sinθ 0 ┐
Rz = │ sinθ cosθ 0 │
└ 0 0 1 ┘
एक्स और वाई कुल्हाड़ियों के बारे में घूमने के लिए समान मेट्रिसेस मौजूद हैं:
┌ 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)
और 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
। यह वह जगह है जहां माइक डे का दृष्टिकोण पिछले उत्तर से भिन्न होता है। चूंकि इस बिंदु पर हम 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;
}