Unity基础-欧拉角和四元数
Unity基础-欧拉角和四元数
三、欧拉角和四元数
1. 为什么要使用四元数
Unity使用左手坐标系,X轴向右为正,Z轴向前为正,Y轴向上为正。
在Unity中,物体的旋转通常有两种表示方法:
欧拉角
- 通过三个角度值(X, Y, Z)表示旋转
- 优点:简单易懂,便于理解
- 缺点:
- 多个旋转顺序不同可能导致不同结果
- 同一结果可能有不同种旋转角度
- 存在万向节死锁(Gimbal Lock)问题
【补充说明】
- 欧拉角的旋转顺序(如XYZ、ZYX等)会影响最终的旋转结果。Unity默认采用"ZXY"顺序。
- 常见陷阱:多次叠加欧拉角旋转时,容易出现意外的旋转效果,建议尽量用四元数做插值和叠加。
四元数
- 使用四个分量(x, y, z, w)表示旋转
- 优点:
- 可以避免万向节死锁问题
- 计算效率高,旋转组合更稳定
- 缺点:不够直观,难以理解
【补充说明】
- 四元数的数学性质:
- 单位四元数长度为1,表示纯旋转。
- 四元数的共轭(Conjugate)可用于求逆旋转。
- 四元数的逆(Inverse)常用于"从B到A的旋转"。
2. 什么是万向节死锁
当使用欧拉角进行旋转,第二个旋转轴(通常是Y)旋转到90度时,第一个旋转轴和第三个旋转轴会重合,导致失去一个维度的旋转轴,从而无法实现某些旋转效果。
3. 什么是四元数
轴-角对:在3D空间内,任意旋转都可以表示为绕一个轴旋转一个角度,这里的轴可以是任意方向(不局限于X、Y、Z)。
四元数的定义
对于给定旋转,假设为绕着n轴,旋转β度,n轴为(x, y, z),那么可以构成四元数:
- 四元数Q = [cos(β/2), sin(β/2) * n]
- 展开为:Q = [cos(β/2), sin(β/2)x, sin(β/2)y, sin(β/2)z]
在Unity中,rotation属性就是四元数类型。
// 在Unity中定义一个四元数
Quaternion q = new Quaternion(Mathf.Sin(30 * Mathf.Deg2Rad), 0, 0, Mathf.Cos(30 * Mathf.Deg2Rad));
// 利用轴角对定义一个四元数
// 注意:应为 Quaternion.AngleAxis 而不是 AngleAixs
Quaternion q = Quaternion.AngleAxis(60, Vector3.right);
4. 欧拉角和四元数的相互转化
// 欧拉角转化为四元数
Quaternion quaternion = Quaternion.Euler(0, 60, 0);
// 使用四元数旋转,完美解决了万向节死锁问题
this.transform.rotation *= quaternion;
// 四元数转化为欧拉角
Vector3 rot = (this.transform.rotation).eulerAngles;
rot += Vector3.up;
// 使用欧拉角旋转,存在万向节死锁问题
this.transform.rotation = Quaternion.Euler(rot);
5. 单位四元数
单位四元数表示没有旋转量(角位移)。
当角度为0或者360度时,对于给定轴都会得到单位四元数。
[1, (0,0,0)] 和 [-1, (0,0,0)] 都是单位四元数,表示没有旋转量。
// 使用单位四元数进行对象实例化
Instantiate(target, Vector3.zero, Quaternion.identity);
6. 四元数差值运算
四元数中同样提供如同Vector3中的差值运算Lerp和Slerp。
- Lerp:线性插值,速度快,但大角度旋转时效果不如Slerp
- Slerp:球形插值,旋转更自然,常用于动画、摄像机等平滑过渡
// 使用差值先快后慢过渡
a.transform.rotation = Quaternion.Slerp(a.transform.rotation, target.transform.rotation, Time.deltaTime);
// 使用差值匀速过渡
float time = 0;
time += Time.deltaTime;
b.transform.rotation = Quaternion.Slerp(startQua, target.transform.rotation, time);
【补充说明】
- Slerp(球形插值)适合大角度、需要平滑过渡的旋转,动画和摄像机常用。
- Lerp(线性插值)速度快,适合小角度、对精度要求不高的场景。
- 实际开发中,推荐优先使用Slerp进行旋转插值。
7. LookRotation(朝向旋转)
Quaternion.LookRotation
可以将传入的面朝向量转换为对应的四元数角度信息。
应用示例:
当需要改变人物的面朝方向时,只需将目标面朝向向量传入该函数,即可得到目标四元数角度信息。随后,将人物的四元数角度信息更新为获得的结果,即可实现转向效果。
// 向量指向转四元数LookRotation
lookA.rotation = Quaternion.LookRotation(lookB.position - lookA.position);
关键点:
- 输入:面朝向量(表示目标方向)
- 输出:对应的四元数角度信息
- 用途:快速实现物体或角色的方向调整
【补充说明】
- LookRotation的输入向量不能为零向量,否则会报错。
- 可以指定第二个参数(up方向),如
Quaternion.LookRotation(forward, up)
,以控制物体的"头顶"朝向。
【补充:四元数在Unity常见应用场景】
- 角色和摄像机的平滑旋转
- 物体朝向目标(如导弹追踪、角色面向)
- 动画骨骼插值
- 3D物理模拟中的旋转
8. 四元数的计算
8.1 四元数相乘
q3 = q1 * q2
- 两个四元数相乘得到一个新的四元数
- 代表两个旋转量的叠加,相当于旋转
- 注意:旋转相对的坐标系是物体的局部坐标系,不是世界的全局坐标系
8.2 四元数乘向量
v2 = q1 * v1
- 四元数乘向量返回一个新向量
- 可以将指定向量旋转对应四元数的旋转量,相当于旋转向量
- 注意:q1和v1不可以调换顺序!
8.3 实际应用示例
// 创建一个绕Z轴旋转45度的四元数
Quaternion q = Quaternion.AngleAxis(45, Vector3.forward);
// 应用到物体的旋转上
this.transform.rotation *= q; // 旋转向量
Vector3 v = Vector3.up;
print(v); // 输出原始向量
v = q * v; // 将向量v旋转45度
print(v); // 输出旋转后的向量
【补充说明】
- 四元数乘法不满足交换律,即 q1 * q2 ≠ q2 * q1
- 四元数乘向量时,顺序必须是四元数在前,向量在后
- 在Unity中,transform.rotation *= q 等价于 transform.rotation = transform.rotation * q