代码之家  ›  专栏  ›  技术社区  ›  Theo Walton

将2个欧拉角与GLM结合的问题

  •  1
  • Theo Walton  · 技术社区  · 8 年前

    我有以下代码:

    #define GLM_ENABLE_EXPERIMENTAL
    
    #include <iostream>
    #include <glm/glm.hpp>
    #include <glm/ext.hpp>
    
    // combines 2 XYZ euler angles given in degrees
    glm::vec3   EulerCombine(const glm::vec3& first, const glm::vec3& second)
    {
            glm::vec3 output;
            glm::mat4 m1 = glm::eulerAngleXYZ(glm::radians(first.x), glm::radians(first.y), glm::radians(first.z));
            glm::mat4 m2 = glm::eulerAngleXYZ(glm::radians(second.x), glm::radians(second.y), glm::radians(second.z));
            glm::extractEulerAngleXYZ(m2 * m1, output.x, output.y,  output.z);
            return glm::degrees(output);
    }
    
    // applies the XYZ euler rotation on p
    glm::vec3   EulerRotate(const glm::vec3& p, const glm::vec3& euler)
    {
            glm::vec3 output;
            output = glm::rotateX(p, glm::radians(euler.x));
            output = glm::rotateY(output, glm::radians(euler.y));
            output = glm::rotateZ(output, glm::radians(euler.z));
            return output;
    }
    
    int     main(void)
    {
            glm::vec3 euler1(30, 20, 90); // euler angles in degrees
            glm::vec3 euler2(20, 30, 10);
            glm::vec3 euler3 = EulerCombine(euler1, euler2);
            glm::vec3 p(-10, 7, 23);
    
            glm::vec3 result1 = EulerRotate(EulerRotate(p, euler1), euler2);
            glm::vec3 result2 = EulerRotate(p, euler3);
    
            std::cout << result1.x << " " << result1.y << " " << result1.z << std::endl;
            std::cout << result2.x << " " << result2.y << " " << result2.z << std::endl;
    }
    

    但它印着:

    17.9056 -6.99702 17.5622
    17.2369 4.15094 19.0699
    

    这是个问题,因为 result1 和 result2 (正在打印的矢量)应该相同。

    我觉得我的 EulerCombine 函数必须是正确的,因为它几乎完全是glm,但那意味着 EulerRotate 是不正确的-但我认为XYZ欧拉角意味着你在X轴上旋转,接着是Y轴,然后是Z轴。

    怎么回事?

    1 回复  |  直到 8 年前
        1
  •  3
  •   Scheff's Cat    8 年前

    正如OP已经怀疑的那样:执行 EulerRotate() 是错误的相反,应该是:

    glm::vec3 EulerRotate(const glm::vec3& p, const glm::vec3& euler)
    {
      return glm::rotateX(glm::rotateY(glm::rotateZ(p, glm::radians(euler.z)),
        glm::radians(euler.y)), glm::radians(euler.x));
    }
    

    也可以写成(更可读)如下:

    glm::vec3 EulerRotate(const glm::vec3& p, const glm::vec3& euler)
    {
      const glm::vec3 p1 = glm::rotateZ(p, glm::radians(euler.z));
      const glm::vec3 p2 = glm::rotateY(p1, glm::radians(euler.y));
      const glm::vec3 p3 = glm::rotateX(p2, glm::radians(euler.x));
      return p3;
    }
    

    对应于 Euler angles 可以这样拆分:

    米 rXYZ公司 = 米 接收 · 米 里亚尔 · 米 rZ公司

    因此:

    P'= 米 rXYZ公司 ·第页= 米 接收 · 米 里亚尔 · 米 rZ公司 ·第页

    这是

    P’=P绕Z旋转,绕Y旋转,绕X旋转。

    听起来很简单——在实际工作中正确地做这件事让我时不时地发疯三维空间中的旋转不是可交换的→顺序非常重要。

    我准备了一个样品来证明这一点因为我没有 glm 在手边(也不愿意安装在我这边),我使用了自己的代码(我曾经为另一个示例做过准备),并试图尽可能接近OPs代码:

    #include <iostream>
    #include "linMath.h"
    
    double radians(double);
    Vec3 degrees(Vec3 angles);
    Mat4x4 eulerAngleXYZ(double rX, double rY, double rZ);
    void extractEulerAngles(const Mat4x4 &mat, double &rX, double &rY, double &rZ);
    Vec3 rotateX(const Vec3 &p, double angle);
    Vec3 rotateY(const Vec3 &p, double angle);
    Vec3 rotateZ(const Vec3 &p, double angle);
    
    // combines 2 XYZ euler angles given in degrees
    Vec3 eulerCombine(const Vec3 &first, const Vec3 &second)
    {
      const Mat4x4 mat1 = eulerAngleXYZ(radians(first.x), radians(first.y), radians(first.z));
      const Mat4x4 mat2 = eulerAngleXYZ(radians(second.x), radians(second.y), radians(second.z));
      Vec3 output;
      extractEulerAngles(mat2 * mat1, output.x, output.y, output.z);
      return degrees(output);
    }
    
    // applies the XYZ euler rotation on p
    Vec3 eulerRotate(const Vec3 &p, const Vec3 &euler)
    {
    #ifndef FIX // Theo:
      Vec3 output;
      output = rotateX(p, radians(euler.x));
      output = rotateY(output, radians(euler.y));
      output = rotateZ(output, radians(euler.z));
      return output;
    #else // Dirk:
      return rotateX(rotateY(rotateZ(p, radians(euler.z)), radians(euler.y)), radians(euler.x));
    #endif // FIX
    }
    
    int main()
    {
      Vec3 euler1(30, 20, 90); // euler angles in degrees
      Vec3 euler2(20, 30, 10);
      Vec3 euler3 = eulerCombine(euler1, euler2);
      Vec3 p(-10, 7, 23);
    
      Vec3 result1 = eulerRotate(eulerRotate(p, euler1), euler2);
      Vec3 result2 = eulerRotate(p, euler3);
    
      std::cout << result1.x << " " << result1.y << " " << result1.z << std::endl;
      std::cout << result2.x << " " << result2.y << " " << result2.z << std::endl;
      // done
      return 0;
    }
    
    double radians(double angle) { return degToRad(angle); }
    
    Vec3 degrees(Vec3 angles)
    {
      return Vec3(radToDeg(angles.x), radToDeg(angles.y), radToDeg(angles.z));
    }
    
    Mat4x4 eulerAngleXYZ(double rX, double rY, double rZ)
    {
      return Mat4x4(InitRotX, rX) * Mat4x4(InitRotY, rY) * Mat4x4(InitRotZ, rZ);
    }
    
    void extractEulerAngles(const Mat4x4 &mat, double &rX, double &rY, double &rZ)
    {
      decompose(mat, RotX, RotY, RotZ, rX, rY, rZ);
    }
    
    Vec3 rotateX(const Vec3 &p, double angle)
    {
      const Vec4 p_ = Mat4x4(InitRotX, angle) * Vec4(p, 1.0);
      return Vec3(p_.x, p_.y, p_.z);
    }
    
    Vec3 rotateY(const Vec3 &p, double angle)
    {
      const Vec4 p_ = Mat4x4(InitRotY, angle) * Vec4(p, 1.0);
      return Vec3(p_.x, p_.y, p_.z);
    }
    
    Vec3 rotateZ(const Vec3 &p, double angle)
    {
      const Vec4 p_ = Mat4x4(InitRotZ, angle) * Vec4(p, 1.0);
      return Vec3(p_.x, p_.y, p_.z);
    }
    

    首先我用 eulerRotate() 类似于OPs转换顺序的函数,得到以下输出:

    17.9056 -6.99702 17.5622
    17.2369 4.15094 19.0698
    

    看来我正确地再现了OP的问题现在,我定义 FIX 要使用正确的转换顺序并获得以下输出:

    12.1019 -22.7589 3.68476
    12.1019 -22.7589 3.68476
    

    Live Demo on Wandbox

    现在,这两个计算提供了与预期相同的结果。

    我认为XYZ欧拉角意味着你在X轴上旋转,接着是Y轴,然后是Z轴。

    不完全是如上图所示,必须从右到左进行解释。