多四元数乘法顺序问题:如何实现欧拉角叠加达成目标旋转结果?
问题分析与解决方案
首先明确:你代码里的glm::vec3是欧拉角(角度值),并非四元数。四元数乘法不交换的特性确实会影响旋转组合结果,但你的目标是让三个旋转的欧拉角分量直接叠加(10+1+2, 20+1+2, 30+1+2),这需要先明确旋转的参考系(外旋/内旋)和旋转顺序。
1. 修正代码基础错误
你重复定义了同一个变量VecA,会导致编译失败,正确命名方式如下:
glm::vec3 InZeroRotEuler(10.0f, 20.0f, 30.0f); // 初始欧拉角(角度) glm::vec3 InTrackingRotEuler(1.0f, 1.0f, 1.0f); // 跟踪模块的欧拉角增量 glm::vec3 InJoystickRotEuler(2.0f, 2.0f, 2.0f); // 摇杆的欧拉角增量
2. 实现欧拉角分量叠加的两种方式
方式一:直接叠加欧拉角(仅适用于特定场景)
如果三次旋转都是相对于全局坐标系的外旋,且旋转顺序完全一致(比如GLM默认的YXZ顺序),直接叠加欧拉角分量再转四元数即可:
// 直接叠加三个欧拉角的分量 glm::vec3 totalEuler = InZeroRotEuler + InTrackingRotEuler + InJoystickRotEuler; // 将角度转弧度后生成四元数(GLM默认欧拉角旋转顺序为YXZ) glm::quat OutRotation = glm::quat(glm::radians(totalEuler));
这种方式能直接得到你想要的(13,23,33)欧拉角对应的四元数,但仅当所有旋转都是外旋且顺序一致时成立——若为内旋(旋转后轴随物体同步转动),叠加结果会和实际旋转效果不符。
方式二:用四元数正确组合旋转(通用场景)
如果旋转涉及混合参考系(如部分内旋、部分外旋),或需要严格符合3D旋转数学逻辑,必须用四元数乘法,但要注意乘法顺序:
- 四元数乘法顺序为从右到左应用旋转:
q1 * q2表示先应用q2的旋转(内旋,基于物体本地坐标系),再应用q1的旋转;若为外旋(基于全局坐标系),顺序需反向。
若你需要模拟“先摇杆旋转、再跟踪旋转、最后初始零位旋转”的外旋逻辑,代码如下:
// 将每个欧拉角转换为四元数 glm::quat qZero = glm::quat(glm::radians(InZeroRotEuler)); glm::quat qTracking = glm::quat(glm::radians(InTrackingRotEuler)); glm::quat qJoystick = glm::quat(glm::radians(InJoystickRotEuler)); // 外旋顺序对应四元数乘法顺序:qZero * qTracking * qJoystick glm::quat OutRotation = qZero * qTracking * qJoystick; // 可选:将结果转回欧拉角验证(注意可能出现万向锁问题) glm::vec3 resultEuler = glm::degrees(glm::eulerAngles(OutRotation));
这种情况下,resultEuler的分量不会严格等于(13,23,33)——因为四元数乘法是真正的3D旋转组合,欧拉角分量叠加仅为小角度下的近似。
3. 关于“沿单轴旋转三个四元数”的疑问
你提到的“沿单轴旋转三个四元数”是误解:每个欧拉角本身是绕三个轴的旋转组合,若只想单独绕某一轴叠加旋转,只需修改对应欧拉角分量即可,示例如下:
// 仅叠加X轴旋转分量,保持Y、Z轴旋转不变 float totalXRot = InZeroRotEuler.x + InTrackingRotEuler.x + InJoystickRotEuler.x; glm::vec3 totalEuler(totalXRot, InZeroRotEuler.y, InZeroRotEuler.z); glm::quat OutRotation = glm::quat(glm::radians(totalEuler));
总结
- 若需求仅为简单的欧拉角分量叠加,且旋转均为外旋、顺序一致,直接叠加欧拉角最直接;
- 若需严格的3D旋转逻辑,必须使用四元数乘法,注意乘法顺序对应旋转参考系;
- 四元数不交换的特性决定了旋转顺序直接影响结果,需明确旋转是内旋还是外旋。
内容的提问来源于stack exchange,提问作者iTzBarathon
相关产品推荐
相关产品推荐

