如何在C++中使用glm将四元数转欧拉角并避免角度异常?
解决glm四元数转欧拉角时Y轴角度突变问题
你遇到的是欧拉角万向锁导致的典型问题:当Y轴旋转到90度时,X轴和Z轴的旋转自由度重合,glm的glm::eulerAngles会选择一种等价但不符合你需求的欧拉角表示(比如(180, 89, 180)和(0, 91, 0)是同一个旋转),但你需要Y轴角度持续递增,同时保持X、Z轴为0。
下面提供两种可行的解决方案:
方案一:直接跟踪Y轴旋转角度
既然你的旋转是固定绕Y轴进行的,完全可以绕过四元数转欧拉角的步骤,直接维护一个Y轴角度变量,每次旋转后递增即可,这是最简洁的解决方式。
#include <iostream> #include <glm/glm.hpp> #include <glm/gtc/quaternion.hpp> #include <chrono> #include <thread> int main() { glm::quat quaternion = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); float y_angle = 0.0f; // 直接维护Y轴旋转角度 for (int i = 0; i < 360; ++i) { // 应用Y轴旋转到四元数 glm::quat rotation_quat = glm::angleAxis(glm::radians(1.0f), glm::vec3(0.0f, 1.0f, 0.0f)); quaternion = rotation_quat * quaternion; // 直接递增Y轴角度,保持X、Z轴为0 y_angle += 1.0f; glm::vec3 euler_angles(0.0f, y_angle, 0.0f); std::cout << "Euler Angles: " << euler_angles.x << ", " << euler_angles.y << ", " << euler_angles.z << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(50)); } return 0; }
方案二:修正glm转换后的欧拉角输出
如果必须从四元数反向推导欧拉角,可以自定义转换函数,检测glm输出的特殊情况,将其修正为Y轴持续递增的表示。
#include <iostream> #include <glm/glm.hpp> #include <glm/gtc/quaternion.hpp> #include <chrono> #include <thread> // 自定义四元数转欧拉角函数,确保Y轴角度持续递增 glm::vec3 quat_to_euler_y_increasing(const glm::quat& q) { glm::vec3 euler = glm::degrees(glm::eulerAngles(q)); // 检测是否进入万向锁的等价表示(X和Z接近180度) const float eps = 1e-3; if (std::abs(euler.x - 180.0f) < eps && std::abs(euler.z - 180.0f) < eps) { // 将Y轴转换为递增方向,重置X、Z轴为0 euler.y = 360.0f - euler.y; euler.x = 0.0f; euler.z = 0.0f; } // 确保Y轴角度在0-360范围内 if (euler.y < 0.0f) { euler.y += 360.0f; } return euler; } int main() { glm::quat quaternion = glm::quat(1.0f, 0.0f, 0.0f, 0.0f); for (int i = 0; i < 360; ++i) { glm::quat rotation_quat = glm::angleAxis(glm::radians(1.0f), glm::vec3(0.0f, 1.0f, 0.0f)); quaternion = rotation_quat * quaternion; glm::vec3 euler_angles = quat_to_euler_y_increasing(quaternion); std::cout << "Euler Angles: " << euler_angles.x << ", " << euler_angles.y << ", " << euler_angles.z << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(50)); } return 0; }
原理说明
欧拉角的万向锁本质是旋转自由度丢失,此时同一个旋转可以用多种欧拉角组合表示。glm默认选择的组合会让Y轴角度反向递减,同时翻转X/Z轴,但我们可以通过检测这种特殊组合,强制转换为Y轴持续递增的形式,满足你的需求。
内容的提问来源于stack exchange,提问作者ratm
相关产品推荐
相关产品推荐

