跳到主要内容
腾讯题库 · Q30

原题 Q30 · 当前收录 29 / 49

学习状态 未开始

Quaternion 旋转

数学与空间 quaternion · rotation 对应课程:数学、变换与动画

面试官问

PDF 第 64 页

请推导四元数(Quaternion)绕任意轴旋转的公式,并说明为何游戏引擎优先使用四元数而非欧拉角?(常问题,理清逻辑)

学习进度

0/9 已勾

只保存在这台浏览器;勾满 9 项即视为本题完成。

先把题目说成人话

先别从“四个数”开始。想象一支箭绕着一根固定铁杆转:沿铁杆方向的那一截没有移动,真正转圈的是垂直铁杆的那一截。无论铁杆朝向哪里,这次三维旋转都能降成一个局部平面里的普通二维转圈。用“一根轴加一个角”描述这次动作,叫轴角(Axis–Angle);把整次旋转压进四个数,叫四元数(Quaternion)。

这题真正要推导的不是凭空出现的四元数公式,而是:三维任意轴旋转为什么可以先拆开、在局部二维平面里转完、再合回去;四元数又怎样把已经看懂的动作编码起来,方便游戏引擎连续组合和插值。

这是虚构教学故事,不是个人项目经历。

场景:先让问题变得看得见

先从最熟悉的坐标轴开始。让向量绕 Y 轴旋转:它在 Y 方向的高度保持不变,只有投影到 XZ 平面里的部分转圈。这里已经藏着任意轴旋转的全部骨架——找出不动的部分,再把会动的部分放进一个垂直于旋转轴的二维平面。

(右脑)画面:把任意旋转轴想成一根穿过原点的黑色铁杆,把待旋转向量想成一支蓝色箭。先从箭尖向铁杆垂直落下一条线:贴在铁杆上的蓝色分量不会动,伸向杆外的蓝色分量才会绕杆画圆。再在圆所在的平面铺两条互相垂直的红色尺子;蓝色分量原本躺在第一条尺上,旋转以后,它在两条尺上的长度分别按余弦和正弦变化。最后把贴在铁杆上、始终没动的那一截加回来,完整箭头就转完了。

(左脑)对表:现在给画面逐一命名。设单位旋转轴为 nn,输入向量为 vv。贴在轴上的不动部分是

v∥=(n⋅v)nv_{\parallel}=(n\cdot v)n

垂直于轴、真正转圈的部分是

v⊥=v−v∥v_{\perp}=v-v_{\parallel}

记 r=∣v⊥∣r=|v_{\perp}|。若 r=0r=0,箭头本来就平行于旋转轴,结果直接等于输入,不必建立局部平面;其余情况才取第一根局部单位尺 e1=v⊥/re_1=v_{\perp}/r,再用 e2=n×e1e_2=n\times e_1 得到第二根尺。于是 e1e_1 与 e2e_2 张成垂直于 nn 的局部二维平面。转过 θ\theta 后,会动部分只是普通二维旋转:v⊥′=rcos⁡θ e1+rsin⁡θ e2v'_{\perp}=r\cos\theta\,e_1+r\sin\theta\,e_2。把 v∥v_{\parallel} 加回并消去临时尺子,得到 Rodrigues 任意轴旋转公式:

v′=vcos⁡θ+(n×v)sin⁡θ+n(n⋅v)(1−cos⁡θ)v'=v\cos\theta+(n\times v)\sin\theta+n(n\cdot v)(1-\cos\theta)

到这里,三维旋转已经完全成立,四元数还没有承担任何神秘工作。现在才把同一轴角编码为 q=(cos⁡(θ/2), nsin⁡(θ/2))q = (\cos(\theta/2),\,n\sin(\theta/2))。把向量 vv 写成标量部为零、向量部为 vv 的纯四元数,再执行 q v q⁻¹;展开得到的正是上面的 Rodrigues 结果。四元数公式不是新的空间故事,而是刚才那次旋转的代数压缩。

小明:先形成一个完整猜测

小明看到四元数有三个看起来像方向的分量,便把它们当成另一套 X、Y、Z 角度,逐项相加。单独绕一根轴时,某些结果碰巧正确;连续绕两根不同轴时,调换顺序却得到不同姿态。

这个失败没有证明四元数失效,而是揭示了旋转本来就不交换:先点头再转身,与先转身再点头,本来就是两条不同动作。四元数的三个虚部也不是三只独立角度旋钮。

小红:把猜测放回现实检查

小红回到已经看得见的基线:让 (1,0,0)(1,0,0) 绕 Z 轴转九十度,几何上必须落到 (0,1,0)(0,1,0),长度仍为一。她分别用局部二维推导、Rodrigues 公式和四元数共轭作用计算,三条路径得到同一结果;再用逆旋转走回来,必须恢复原向量。

随后她交换两次不同轴旋转的先后顺序,结果确实不同,证明“顺序影响姿态”是三维旋转的性质,不是某种表示法凭空制造的故障。真正需要比较的是:三个欧拉角的参数化会在特定姿态丢失独立控制轴,逐分量插值也未必沿自然路径;单位四元数则把整个姿态作为一个对象组合。

我:用检查结果修正模型

我保留欧拉角作为人类编辑界面,因为“向左转多少、抬头多少”便于调整;进入运行时姿态组合和插值时,再转换成单位四元数。两个四元数点积为负时,可以翻转其中一个符号,让插值选择同一旋转的较近表示;构造、累计和插值后要处理数值漂移。

四元数也不是万能容器:它只表达旋转,不能独自携带平移、非均匀缩放或斜切;乘法不交换,左右手系、分量排列和引擎接口不同,也会改变代码写法。数学上,非零四元数若使用真正的逆,整体尺度会抵消;工程中常用“共轭等于逆”的捷径只对单位四元数成立。

(右脑)画面修正:旋转不再是一地互不相干的四个数。先有一根不动的轴、一个垂直轴的圆形舞台,以及舞台上的二维转圈;四元数只是把这整次动作封进一个可以继续组合的旋转块。欧拉角像依次拨动三只带顺序的转盘,适合人看和编辑;四元数像拿住完整姿态,适合把一段旋转接到下一段旋转。

(左脑)严格对表:给定同一坐标空间中的单位轴 nn、弧度 θ\theta 和向量 vv,先用投影分解与局部二维旋转推得 Rodrigues;再按项目规定的 (w,xyz)(w,xyz) 或 (xyz,w)(xyz,w) 排列构造半角四元数,把 vv 写成纯四元数并计算 qvq−1q v q^{-1}。验证条件是旋转前后 ∣v∣|v| 相同、逆旋转恢复输入、九十度手算与引擎结果一致。随后检查零角、一百八十度、非单位轴、累计误差、左右手系和乘法次序。插值时还要验证 qq 与 −q-q 的等价性和短弧选择;这些证据成立后,才能说四元数适合运行时组合与姿态插值。

老师解惑

  • 为什么先推局部二维平面:它让每一项都有画面。点积取出轴向长度,叉积给出平面内第二个方向,正弦和余弦只负责普通二维转圈。
  • 为什么四元数使用半角:向量通过左右两侧的共轭作用完成整次旋转;展开后两个半角关系合成为完整角度,并还原 Rodrigues 公式。半角不能靠“好记”证明,必须由展开或等价推导得到。
  • 为什么不是所有地方都抛弃欧拉角:欧拉角适合编辑、显示和有明确旋转顺序的控制;问题出在参数化奇异位置以及逐分量插值。四元数主要改善运行时组合与姿态插值,不是更像人类。
  • 为什么需要单位长度:旋转接口、共轭代替逆和球面插值通常都建立在单位四元数上。若不是单位长度,就要先归一化或使用真正的逆,不能把共轭直接当逆。

工具分工

  • Quaternion Transform Lab:同时画旋转轴、轴向分量、垂直分量、局部二维平面和最终向量,帮助核对空间关系。
  • Unity 的轴角构造与球面插值接口:核对手算、分量排列和引擎乘法约定,不代替推导。
  • 场景向量辅助线:画出原向量、旋转轴、圆弧和结果,优先发现轴向或顺序错误。
  • 单元测试:检查长度保持、逆旋转、组合非交换和零度/九十度/一百八十度边界。

记忆钩子

先把三维旋转降成垂直轴的二维转圈,再把这次完整动作压进四元数;公式是画面的压缩,不是画面的替代品。

这是教学场景,不是目标项目的真实捕获或个人经历。

下一步:验证这条因果

看懂轴角如何生成四元数,并验证 q·p·q⁻¹ 为什么保持向量长度。

教学边界:它只验证这一条画面因果,不替代完整答案、项目经历或目标设备数据。

进入教学 Lab:「四元数旋转:轴、角度与逆」