3D四元数旋转不符合预期的实现问题咨询
三维旋转与四元数实践(基于GLM/Vulkan)
核心结论:用单个四元数维护对象最终旋转状态
不需要拆分多个四元数分别管理旋转,单个四元数完全可以承载对象的最终朝向。你当前的核心问题是没有区分旋转的空间(世界空间/对象局部空间),而非四元数的存储方式。
你的代码问题分析
先看你现有的rn_object_rotate方法:
void rn_object_rotate(void* o, float x, float y, float z, float degrees) { Object object = (Object) o; versor q; glm_quat(q, glm_rad(degrees), x, y, z); glm_quat_mul(object->node->rotation, q, object->node->rotation); }
GLM的glm_quat_mul(a, b, c)等价于c = a * b,四元数乘法顺序代表变换的执行顺序:
- 左乘(
new_q = q * current_q):新旋转是世界空间下的变换 - 右乘(
new_q = current_q * q):新旋转是对象局部空间下的变换
你当前的代码用了左乘,当你传入世界Y轴[0,1,0]时,实际是让对象在世界空间下绕Y轴旋转——但如果对象已经有局部旋转,你误以为的"世界Y轴旋转"会和原有旋转叠加,导致看起来像绕倾斜轴转动。
另外,你的rn_object_set_rotation方法可以简化:先设单位四元数再乘q,等价于直接赋值q,没必要多做一次乘法。
修正后的旋转实现
设置旋转(直接覆盖当前状态,轴为世界空间)
void rn_object_set_rotation(void* o, float x, float y, float z, float degrees) { Object object = (Object) o; versor q; // 直接构造目标四元数并覆盖当前旋转 glm_quat(q, glm_rad(degrees), x, y, z); glm_quat_copy(object->node->rotation, q); }
叠加世界空间旋转
用于绕固定的世界轴旋转(比如鼠标左右拖动时绕世界Y轴转):
void rn_object_rotate_world(void* o, float x, float y, float z, float degrees) { Object object = (Object) o; versor q, temp; glm_quat(q, glm_rad(degrees), x, y, z); // 左乘:先应用对象原有旋转,再叠加世界空间的新旋转 glm_quat_mul(temp, q, object->node->rotation); glm_quat_copy(object->node->rotation, temp); }
叠加局部空间旋转
用于绕随对象转动的局部轴旋转(比如鼠标上下拖动时绕对象自身X轴转):
void rn_object_rotate_local(void* o, float x, float y, float z, float degrees) { Object object = (Object) o; versor q, temp; glm_quat(q, glm_rad(degrees), x, y, z); // 右乘:先应用对象原有旋转,再叠加局部空间的新旋转 glm_quat_mul(temp, object->node->rotation, q); glm_quat_copy(object->node->rotation, temp); }
针对你的需求的使用方式
- 鼠标左右拖动:调用
rn_object_rotate_world(o, 0.0f, 1.0f, 0.0f, delta_deg),实现绕世界Y轴旋转 - 鼠标上下拖动:调用
rn_object_rotate_local(o, 1.0f, 0.0f, 0.0f, delta_deg),实现绕对象局部X轴旋转(该轴会随对象绕世界Y轴的旋转同步转动,且始终垂直于世界Y轴)
额外注意事项
- 确保传入的旋转轴是归一化的单位向量,GLM的
glm_quat要求轴向量单位化,否则会导致旋转异常 - 多次四元数乘法后可能出现精度损失,可定期调用
glm_quat_normalize(object->node->rotation, object->node->rotation)做归一化处理
内容的提问来源于stack exchange,提问作者Plegeus
相关产品推荐
相关产品推荐

