রূপান্তর ম্যাট্রিক্স থেকে ইউলার কোণগুলি কীভাবে বের করবেন?


12

আমার সত্তা / উপাদান গেম ইঞ্জিনের একটি সহজ উপলব্ধি আছে।
রূপান্তর উপাদানটির স্থানীয় অবস্থান, স্থানীয় ঘূর্ণন, গ্লোবাল অবস্থান এবং বৈশ্বিক রোটেশন সেট করার পদ্ধতি রয়েছে।

যদি রূপান্তরটি নতুন বিশ্বব্যাপী অবস্থান নির্ধারণ করা হয়, তবে স্থানীয় অবস্থানও পরিবর্তিত হয়, স্থানীয় অবস্থার আপডেটের ক্ষেত্রে আমি কেবল প্যারেন্টস ট্রান্সফর্ম ওয়ার্ল্ড ম্যাট্রিক্সে স্থানীয় রূপান্তর স্থানীয় ম্যাট্রিক্স প্রয়োগ করছি।

ততক্ষণে আমার কোনও সমস্যা নেই, আমি আপডেট স্থানীয় ট্রান্সফর্ম ম্যাট্রিক্স পেতে পারি।
তবে আমি কীভাবে স্থানীয় অবস্থান এবং রোটেশনের মানকে রূপান্তরিত করতে আপডেট করব তা নিয়ে লড়াই করছি। আমার মনে কেবলমাত্র সমাধানটি হ'ল অনুবাদ এবং রোটেশন মানগুলি ট্রান্সফর্মের স্থানীয় ম্যাট্রিক্স থেকে আহরণ করা।

অনুবাদের জন্য এটি বেশ সহজ - আমি কেবল চতুর্থ কলামের মান নিই। কিন্তু আবর্তন সম্পর্কে কি?
রূপান্তর ম্যাট্রিক্স থেকে ইউলার কোণগুলি কীভাবে বের করবেন?

এই জাতীয় সমাধানটি কি ঠিক ?:
আমরা স্থানীয় ট্রান্সফর্মের এক্স অক্ষ ভেক্টর এবং প্যারেন্ট.লোকাল ট্রান্সফর্মের এক্স অক্ষ ভেক্টর এবং ডেল্টার স্টোর ফলাফলের মধ্যে পার্থক্য খুঁজে পেতে পারি, তারপরে: লোকালরোটেশন.জেড = আতান 2 (ডেল্টা.ই, ডেল্টা) .এক্স);

এক্স ও ওয়াইয়ের চারদিকে ঘোরানোর জন্য একই অক্ষটি অদলবদল করা দরকার।

উত্তর:


10

সাধারণত আমি 4x4 ম্যাট্রিক্স হিসাবে সমস্ত বস্তু সংরক্ষণ করি (আপনি 3x3 করতে পারেন তবে আমার পক্ষে কেবল 1 ক্লাস থাকা সহজ ছিল) পরিবর্তে 4x4 এবং 3 ভেক্টর 3 সেটের (অনুবাদ, রোটেশন, স্কেল) এর মধ্যে পিছনে পিছনে অনুবাদ করার পরিবর্তে। ইউলারের অ্যাঙ্গেলগুলি নির্দিষ্ট পরিস্থিতিতে মোকাবেলা করা কুখ্যাতভাবে কাজ করা হয় তাই আমি যদি কোটার্ট্রিয়েনগুলি ব্যবহার করার পরামর্শ দিই তবে আপনি যদি ম্যাট্রিক্সের পরিবর্তে উপাদানগুলি সংরক্ষণ করতে চান।

তবে এখানে আমি কিছু কোড পেয়েছি যা কিছুক্ষণ আগে কাজ করে। আমি আশা করি এটি সাহায্য করে, দুর্ভাগ্যক্রমে আমার কাছে যেখানে এটি পাওয়া গেছে তার মূল উত্স আমার কাছে নেই। কোন অদ্ভুত পরিস্থিতিতে এটি কাজ না করতে পারে সে সম্পর্কে আমার কোনও ধারণা নেই Y আমি ইয়াওপিচরল ঘোরানো, বাম হাতে 4x4 ম্যাট্রিকের ঘূর্ণন পেতে এটি ব্যবহার করছি।

   union {
        struct 
        {
            float        _11, _12, _13, _14;
            float        _21, _22, _23, _24;
            float        _31, _32, _33, _34;
            float        _41, _42, _43, _44;
        };
        float m[4][4];
        float m2[16];
    };

    inline void GetRotation(float& Yaw, float& Pitch, float& Roll) const
    {
        if (_11 == 1.0f)
        {
            Yaw = atan2f(_13, _34);
            Pitch = 0;
            Roll = 0;

        }else if (_11 == -1.0f)
        {
            Yaw = atan2f(_13, _34);
            Pitch = 0;
            Roll = 0;
        }else 
        {

            Yaw = atan2(-_31,_11);
            Pitch = asin(_21);
            Roll = atan2(-_23,_22);
        }
    }

আপনার প্রশ্নের উত্তর দেওয়ার চেষ্টা করার সময় আমি খুঁজে পেয়েছি এমন অন্য থ্রেড যা আমার কাছে অনুরূপ ফলাফলের মতো দেখায়।

/programming/1996957/conversion-euler-to-matrix-and-matrix-to-euler


দেখে মনে হচ্ছে যে আমার প্রস্তাবিত সমাধানটি প্রায় সঠিক, কেবল জানে না কেন পিচ জন্য অ্যাটান 2 আসিনের স্থিতি নেই।

এছাড়াও, এটি কীভাবে আমাকে সহায়তা করবে, যদি আমি প্রতিটি উপাদান আলাদা আলাদা mat4x4 এ সঞ্চয় করি? তাহলে আমি কীভাবে পেতে পারি এবং উদাহরণস্বরূপ কয়েকটি অক্ষের চারপাশে আবর্তনের কোণ

আপনার মূল প্রশ্নটি আমাকে বিশ্বাস করতে পরিচালিত করে যে আপনি আপনার বস্তুগুলিকে 3 ভেক্টর 3 হিসাবে সংরক্ষণ করছেন: অনুবাদ, ঘোরানো এবং স্কেল। তারপরে যখন আপনি কিছু কাজ করছেন এবং পরে (লোকাল ট্রান্সফর্ম * গ্লোবাল ট্রান্সফর্ম) 3 টি ভেক্টর 3 তে রূপান্তরিত করার চেষ্টা করছেন তাদের মধ্যে একটি স্থানীয় ট্রান্সফর্ম তৈরি করার সময়। আমি পুরোপুরি ভুল হতে পারি আমি কেবল এই ধারণাটি পেয়েছিলাম।
এনটিএসকোবাল্ট

হ্যাঁ আমি ASIN এর সাথে পিচ কেন করা হয় তার পক্ষে গণিতটি খুব ভাল জানি না তবে লিঙ্কযুক্ত প্রশ্নটি একই গণিতটি ব্যবহার করে তাই আমি বিশ্বাস করি এটি সঠিক। আমি কোনও সমস্যা ছাড়াই কিছুক্ষণ এই ফাংশনটি ব্যবহার করছি।
এনটিএসকোবাল্ট

প্রথম দুটি ক্ষেত্রে যদি অ্যাটান 2f ব্যবহার এবং তৃতীয়টিতে আতান 2 ব্যবহারের কোনও বিশেষ কারণ আছে, বা এটি টাইপো?
ম্যাটিয়াস এফ

10

মাইক ডে দ্বারা এই প্রক্রিয়াটিতে একটি দুর্দান্ত রচনাআপ রয়েছে: https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2012/07/euler-angles1.pdf

0.9.7.0, 02/08/2015 সংস্করণ অনুসারে এটি এখন গ্ল্যামেও প্রয়োগ করা হয়েছে। বাস্তবায়ন পরীক্ষা করে দেখুন

গণিতটি বুঝতে, আপনার ঘূর্ণন ম্যাট্রিক্সের মানগুলি দেখতে হবে। এছাড়াও, মানগুলি যথাযথভাবে নিষ্কাশনের জন্য আপনার ম্যাট্রিক্স তৈরি করতে আবর্তনগুলি প্রয়োগ করা হয়েছিল এমন ক্রমটি আপনাকে জানতে হবে।

ইউলার কোণ থেকে একটি ঘূর্ণন ম্যাট্রিক্সটি x-, y-, এবং z- অক্ষগুলির চারপাশে ঘূর্ণনগুলির সমন্বয় করে গঠিত হয়। উদাহরণস্বরূপ, জেডের চারপাশে θ ডিগ্রি ঘোরানো ম্যাট্রিক্সের সাহায্যে করা যেতে পারে

      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। আসুন একটি সম্ভাব্য ঘূর্ণন ক্রম বিবেচনা করুন, জাইএক্স। যখন তিনটি ম্যাট্রিক একত্রিত হয়, তখন এর ফলাফলটি একটি ম্যাট্রিক্সে আসে:

               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]। আমাদের যদি কোস (y) থাকে atan2তবে আমরা আবার ব্যবহার করতে পারতাম , তবে আমাদের ম্যাট্রিক্সে সেই মান নেই। তবে পাইথাগোরিয়ান পরিচয়ের কারণে আমরা তা জানি:

cosY = sqrt(1 - M[0][2])

সুতরাং, আমরা গণনা করতে পারি y:

y = atan2(M[0][2], cosY)

সর্বশেষে, আমাদের গণনা করা দরকার z। মাইক ডে-এর দৃষ্টিভঙ্গি পূর্ববর্তী উত্তর থেকে আলাদা। যেহেতু এই মুহুর্তে আমরা কীভাবে xএবং yঘূর্ণনের পরিমাণ জানি , তাই আমরা একটি এক্সওয়াই রোটেশন ম্যাট্রিক্স তৈরি করতে পারি, এবং zলক্ষ্য ম্যাট্রিক্সের সাথে মিলের জন্য প্রয়োজনীয় ঘূর্ণনের পরিমাণটি খুঁজে পেতে পারি । RxRyম্যাট্রিক্স ভালো দেখায়:

          Cy     0     Sy  
RxRy =   SxSy   Cx   -SxCy 
        -CxSy   Sx    CxCy 

যেহেতু আমরা জানি যে RxRy* Rzআমাদের ইনপুট ম্যাট্রিক্সের সমান M, তাই আমরা ফিরে পেতে এই ম্যাট্রিক্সটি ব্যবহার করতে পারি Rz:

M = RxRy * Rz

inverse(RxRy) * M = Rz

একটি ঘূর্ণন ম্যাট্রিক্সের বিপরীত তার TRANSPOSE হয় , তাই আমরা এই প্রসারিত করতে পারেন:

 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;
}

নোট করুন, তবে, সমস্যাটি যখন y = pi / 2 এবং এভাবে (y) == 0 হয় 0. কারণ অনুপাতটি অনির্ধারিত, এবং atan2 মান পাওয়া যায় না। আমি বিশ্বাস করি যে এটি গিম্বল লক সমস্যার সমতুল্য ।
পিটার জেরকেনস

@ পিটারজির্কেন্স, আপনি ঠিক বলেছেন, এটি জিম্বল লক। বিটিডাব্লু, আপনার মন্তব্যটি প্রকাশ করেছে যে আমার কাছে এই বিভাগে একটি টাইপ ছিল। আমি 0 এ প্রথম এক সঙ্গে ম্যাট্রিক্স সূচকের পড়ুন এবং যেহেতু তারা 3x3 ম্যাট্রিক্স হয়, গত সূচক 2, না 3. আমি সংশোধন করেছি M[1][3]সঙ্গে M[1][2], এবং M[2][3]সঙ্গে M[2][2]
ক্রিস

আমি নিশ্চিত যে মিশ্রিত ম্যাট্রিক্সের উদাহরণের দ্বিতীয় সারির প্রথম কলামটি হ'ল SxSyCz + CxSz, SxSySz + CxSz নয়!
লেক

@ দেখুন, আপনি সঠিক আছেন। সম্পাদনা করা হয়েছে।
ক্রিস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.