【Unity每日一记】Unity三维数学进阶:齐次坐标、万向节锁与四元数详解
📐 一、齐次坐标(Homogeneous Coordinates)
1.1 基本概念
齐次坐标是通过引入n+1维向量来表示n维空间中的点,在三维图形学中具有重要意义。
1.2 坐标表示形式
类型 | 齐次坐标 | 说明 |
---|---|---|
坐标点 | (x, y, z, 1) | 表示三维空间中的一个点 |
向量 | (x, y, z, 0) | 表示三维空间中的一个方向向量 |
1.3 数学表示
标准三维坐标 → 齐次坐标
点:P(x, y, z) → P'(x, y, z, 1)
向量:V(x, y, z) → V'(x, y, z, 0)
1.4 在Unity中的应用
// Unity中的齐次坐标应用
Vector4 point = new Vector4(1, 2, 3, 1); // 点
Vector4 vector = new Vector4(1, 2, 3, 0); // 向量// 矩阵变换(使用齐次坐标)
Matrix4x4 transformationMatrix = Matrix4x4.TRS(new Vector3(1, 2, 3), Quaternion.identity, Vector3.one
);Vector4 transformedPoint = transformationMatrix * point;
1.5 优势与用途
- ✅ 统一变换:平移、旋转、缩放可以用同一个矩阵表示
- ✅ 透视投影:方便实现透视除法
- ✅ 区分点与向量:w分量为0表示向量,1表示点
1.6 实例演示:点和向量的转换
public class HomogeneousExample : MonoBehaviour
{void Start(){// 实例1:点和向量的区别Vector4 pointA = new Vector4(1, 2, 3, 1); // 点AVector4 pointB = new Vector4(4, 5, 6, 1); // 点BVector4 vectorAB = pointB - pointA; // 向量AB:(3, 3, 3, 0)Debug.Log("点A: " + pointA);Debug.Log("点B: " + pointB);Debug.Log("向量AB: " + vectorAB);// 实例2:平移变换Matrix4x4 translation = Matrix4x4.Translate(new Vector3(2, 0, 0));Vector4 translatedPoint = translation * pointA;Debug.Log("平移后的点: " + translatedPoint);}
}
🔄 二、万向节锁(Gimbal Lock)
2.1 问题定义
万向节锁是欧拉角表示旋转时的一个固有缺陷,当第二个旋转轴旋转90°时,会导致第一个和第三个旋转轴重合,失去一个自由度。
2.2 实际示例
// 万向节锁示例
public class GimbalLockExample : MonoBehaviour
{void Update(){// 当Y轴旋转接近90°时可能出现万向节锁float yRotation = 89f; // 接近临界值// 欧拉角旋转(可能产生万向节锁)transform.eulerAngles = new Vector3(30f, yRotation, 45f);// 使用四元数避免万向节锁transform.rotation = Quaternion.Euler(30f, yRotation, 45f);}
}
2.3 影响与解决方案
问题 | 影响 | 解决方案 |
---|---|---|
旋转轴重合 | 失去一个旋转自由度 | 使用四元数 |
插值异常 | 旋转路径不自然 | 四元数球面插值 |
动画抖动 | 关键帧之间跳动 | 使用Quaternion.Lerp |
2.4 实例演示:摄像机控制系统
public class CameraController : MonoBehaviour
{public float rotationSpeed = 2f;private float currentYaw = 0f;private float currentPitch = 0f;void Update(){// 获取鼠标输入float mouseX = Input.GetAxis("Mouse X");float mouseY = Input.GetAxis("Mouse Y");// ❌ 有风险的欧拉角方式(可能产生万向节锁)// Vector3 euler = transform.eulerAngles;// euler.y += mouseX * rotationSpeed;// euler.x -= mouseY * rotationSpeed;// transform.eulerAngles = euler;// ✅ 安全的四元数方式currentYaw += mouseX * rotationSpeed;currentPitch -= mouseY * rotationSpeed;currentPitch = Mathf.Clamp(currentPitch, -80f, 80f);Quaternion rotation = Quaternion.Euler(currentPitch, currentYaw, 0);transform.rotation = rotation;}
}
🧊 三、四元数(Quaternions)
3.1 基本概念
四元数是高阶复数系统,用于表示三维空间中的旋转,形式为:q = w + xi + yj + zk
3.2 数学表示
四元数:q = (w, x, y, z)
其中:i² = j² = k² = ijk = -1
3.3 Unity中的四元数
// 创建四元数
Quaternion rotation = Quaternion.identity; // 单位四元数// 通过欧拉角创建四元数
Quaternion eulerRotation = Quaternion.Euler(30f, 45f, 60f);// 通过轴角创建四元数
Quaternion axisAngle = Quaternion.AngleAxis(90f, Vector3.up);
3.4 四元数运算
// 四元数乘法(旋转组合)
Quaternion combined = rotationA * rotationB;// 四元数插值
Quaternion smoothRotation = Quaternion.Slerp(from, to, t);// 线性插值(性能更好)
Quaternion lerpRotation = Quaternion.Lerp(from, to, t);
3.5 四元数优势
特性 | 优势 | 应用场景 |
---|---|---|
无万向节锁 | 避免旋转自由度丢失 | 连续旋转动画 |
平滑插值 | 球面线性插值更自然 | 摄像机跟随 |
计算高效 | 比矩阵乘法更高效 | 实时图形渲染 |
数值稳定 | 避免浮点数误差累积 | 长期运行应用 |
3.6 实例演示:敌人AI朝向系统
public class EnemyAI : MonoBehaviour
{public Transform player;public float rotationSpeed = 2f;public float attackRange = 5f;void Update(){if (player == null) return;// 计算朝向玩家的方向Vector3 directionToPlayer = player.position - transform.position;// 使用LookRotation创建目标旋转Quaternion targetRotation = Quaternion.LookRotation(directionToPlayer);// 平滑旋转朝向玩家transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);// 检查是否在攻击范围内if (directionToPlayer.magnitude <= attackRange){AttackPlayer();}}void AttackPlayer(){Debug.Log("攻击玩家!");}
}
3.7 实例演示:炮塔追踪系统
public class TurretController : MonoBehaviour
{public Transform target;public Transform turretBase; // 炮塔底座(水平旋转)public Transform turretBarrel; // 炮管(俯仰旋转)public float rotationSpeed = 90f;void Update(){if (target == null) return;// 计算水平方向(Y轴旋转)Vector3 horizontalDirection = target.position - turretBase.position;horizontalDirection.y = 0; // 忽略高度差,只计算水平方向Quaternion targetHorizontalRotation = Quaternion.LookRotation(horizontalDirection);turretBase.rotation = Quaternion.RotateTowards(turretBase.rotation, targetHorizontalRotation, rotationSpeed * Time.deltaTime);// 计算俯仰角度Vector3 barrelDirection = target.position - turretBarrel.position;Quaternion targetVerticalRotation = Quaternion.LookRotation(barrelDirection);turretBarrel.rotation = Quaternion.RotateTowards(turretBarrel.rotation, targetVerticalRotation, rotationSpeed * Time.deltaTime);}
}
🎯 四、实际应用对比
4.1 旋转表示方法比较
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
欧拉角 | 直观易理解 | 万向节锁问题 | 简单的单轴旋转 |
四元数 | 无万向节锁,插值平滑 | 数学复杂,不直观 | 复杂旋转,动画 |
旋转矩阵 | 数学基础扎实,可组合 | 存储空间大,计算复杂 | 底层图形运算 |
4.2 Unity中的最佳实践
public class RotationBestPractices : MonoBehaviour
{public Transform target;public float rotationSpeed = 2f;void Update(){// ❌ 避免:直接修改欧拉角(可能产生万向节锁)// transform.eulerAngles += new Vector3(0, rotationSpeed, 0);// ✅ 推荐:使用四元数进行旋转transform.rotation = Quaternion.AngleAxis(rotationSpeed * Time.deltaTime, Vector3.up) * transform.rotation;// ✅ 平滑朝向目标if (target != null){Quaternion targetRotation = Quaternion.LookRotation(target.position - transform.position);transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, rotationSpeed * Time.deltaTime);}}
}
4.3 实例演示:第三人称摄像机系统
public class ThirdPersonCamera : MonoBehaviour
{public Transform target;public Vector3 offset = new Vector3(0, 2, -5);public float rotationSpeed = 3f;public float zoomSpeed = 2f;private float currentYaw = 0f;private float currentPitch = 30f;private float currentZoom = 5f;void LateUpdate(){if (target == null) return;// 鼠标输入控制旋转if (Input.GetMouseButton(1)) // 右键拖动{currentYaw += Input.GetAxis("Mouse X") * rotationSpeed;currentPitch -= Input.GetAxis("Mouse Y") * rotationSpeed;currentPitch = Mathf.Clamp(currentPitch, -10f, 80f);}// 鼠标滚轮缩放currentZoom -= Input.GetAxis("Mouse ScrollWheel") * zoomSpeed;currentZoom = Mathf.Clamp(currentZoom, 2f, 10f);// 使用四元数计算旋转(避免万向节锁)Quaternion rotation = Quaternion.Euler(currentPitch, currentYaw, 0);// 计算摄像机位置Vector3 desiredPosition = target.position + rotation * (Vector3.forward * -currentZoom + offset);// 应用变换transform.rotation = rotation;transform.position = desiredPosition;}
}
💡 五、关键知识点总结
5.1 齐次坐标核心要点
- 🎯 定义:用n+1维向量表示n维空间
- 🔍 区分:点(w=1) vs 向量(w=0)
- 🚀 应用:统一变换矩阵,透视投影
5.2 万向节锁理解要点
- ⚠️ 成因:欧拉角第二个旋转轴旋转90°
- 🔄 现象:两个旋转轴重合,失去自由度
- 🛠️ 解决:使用四元数替代欧拉角
5.3 四元数掌握要点
- 🧮 形式:q = w + xi + yj + zk
- ✅ 优势:无万向节锁,平滑插值
- 🎮 应用:Unity中所有旋转操作的基础
🧪 六、练习与思考
6.1 动手练习
- 创建四元数旋转:实现物体绕任意轴旋转
- 坐标系转换:使用齐次坐标实现点在不同坐标系间转换
- 万向节锁演示:用欧拉角重现万向节锁现象
6.2 思考问题
- 🤔 为什么四元数能避免万向节锁?
- 🔄 齐次坐标中的w分量在透视投影中起什么作用?
- 🎯 在实际项目中如何选择旋转表示方法?
6.3 实例演示:物体环绕运动
public class OrbitalMotion : MonoBehaviour
{public Transform centerObject;public float radius = 3f;public float revolutionSpeed = 30f; // 公转速度public float rotationSpeed = 90f; // 自转速度private float currentAngle = 0f;void Update(){if (centerObject == null) return;// 更新公转角度currentAngle += revolutionSpeed * Time.deltaTime;// 计算轨道位置(使用四元数)Quaternion orbitRotation = Quaternion.Euler(0, currentAngle, 0);Vector3 orbitPosition = centerObject.position + orbitRotation * Vector3.forward * radius;// 应用位置和旋转transform.position = orbitPosition;transform.Rotate(Vector3.up, rotationSpeed * Time.deltaTime);// 始终朝向中心物体transform.LookAt(centerObject);}
}
💡 提示:在Unity中创建测试场景,亲自体验不同旋转方法的效果差异!
🎯 实战练习项目
项目1:太阳系模拟
public class SolarSystem : MonoBehaviour
{public Transform sun;public Transform earth;public Transform moon;void Update(){// 地球绕太阳公转earth.RotateAround(sun.position, Vector3.up, 30 * Time.deltaTime);// 月球绕地球公转moon.RotateAround(earth.position, Vector3.up, 120 * Time.deltaTime);// 所有天体自转sun.Rotate(Vector3.up, 10 * Time.deltaTime);earth.Rotate(Vector3.up, 50 * Time.deltaTime);moon.Rotate(Vector3.up, 20 * Time.deltaTime);}
}
项目2:FPS武器瞄准系统
public class WeaponAim : MonoBehaviour
{public Transform weapon;public Transform target;public float aimSpeed = 5f;void Update(){if (target == null) return;// 计算瞄准方向Vector3 aimDirection = target.position - weapon.position;// 使用四元数平滑瞄准Quaternion targetRotation = Quaternion.LookRotation(aimDirection);weapon.rotation = Quaternion.Slerp(weapon.rotation, targetRotation, aimSpeed * Time.deltaTime);}
}
通过以上丰富的实例和练习,你应该能够深入理解齐次坐标、万向节锁和四元数在Unity开发中的重要应用!