一.四元数(Quaternion)中四个分量的含义
四元数是一种用于表示3D旋转的数学工具,由四个分量组成,通常表示为 (x, y, z, w) 或 (i, j, k, real)。在Three.js中,THREE.Quaternion
类使用(x, y, z, w)的表示形式。
四个分量的详细解释
虚部 (x, y, z):
表示旋转轴的方向向量
这个向量会被自动规范化(长度为1)
分量的比例决定了旋转轴的方向
例如:(1, 0, 0)表示X轴,(0, 1, 0)表示Y轴
实部 (w):
表示旋转的角度(实际上是角度的一半的余弦值)
与虚部共同决定旋转量
当w=1时表示无旋转(单位四元数)
数学关系
四元数可以表示为:
q = [x, y, z, w] = [sin(θ/2)*axis_x, sin(θ/2)*axis_y, sin(θ/2)*axis_z, cos(θ/2)]
其中:
θ 是旋转角度
[axis_x, axis_y, axis_z] 是旋转轴的单位向量
具体示例
无旋转的单位四元数
new THREE.Quaternion(0, 0, 0, 1)
2.绕Y轴旋转90度:
// 旋转轴:(0, 1, 0)
// 旋转角度:π/2 (90度)
// 计算:
// x = sin(π/4)*0 = 0
// y = sin(π/4)*1 ≈ 0.707
// z = sin(π/4)*0 = 0
// w = cos(π/4) ≈ 0.707
new THREE.Quaternion(0, 0.7071067811865475, 0, 0.7071067811865476)
可视化理解
可以把四元数想象为:
(x, y, z) 指向旋转轴的方向
w 的大小决定了旋转的角度
四元数的长度(模)总是1(单位四元数)
在Three.js中的实际应用
// 创建一个绕(1,1,0)轴旋转45度的四元数
const axis = new THREE.Vector3(1, 1, 0).normalize();
const angle = Math.PI / 4; // 45度
const quaternion = new THREE.Quaternion();
quaternion.setFromAxisAngle(axis, angle);
console.log(quaternion);
// 输出类似:Quaternion {x: 0.22, y: 0.22, z: 0, w: 0.92}
// 其中x,y,z是旋转轴分量(已规范化),w是角度分量
重要特性
单位长度:
所有表示旋转的四元数都应该是单位四元数:x² + y² + z² + w² = 1
2.规范化:
如果手动修改了四元数值,应该调用:
quaternion.normalize();
3.逆四元数:
表示相反旋转的四元数可以通过取共轭获得:
const inverse = quaternion.clone().conjugate();
理解四元数的四个分量含义有助于在3D编程中更有效地使用它们,特别是在需要精确控制旋转或进行复杂旋转运算时。
二.欧拉角(Euler Angle)三个分量的含义
在Three.js中,欧拉角使用THREE.Euler
类表示,由三个分量(x, y, z)组成,分别表示绕三个轴的旋转角度。
三个分量的具体含义
x分量:
表示绕X轴的旋转角度(俯仰角/Pitch)
正值:物体向前倾斜(低头)
负值:物体向后倾斜(抬头)
y分量:
表示绕Y轴的旋转角度(偏航角/Yaw)
正值:物体向右转
负值:物体向左转
z分量:
表示绕Z轴的旋转角度(滚转角/Roll)
正值:物体顺时针旋转
负值:物体逆时针旋转
旋转顺序的重要性
欧拉角的旋转顺序非常重要,Three.js默认使用"XYZ"顺序:
const euler = new THREE.Euler(x, y, z, 'XYZ');
其他可能的顺序包括:'YXZ'、'ZXY'、'ZYX'、'YZX'、'XZY'等。不同顺序会导致完全不同的最终旋转结果。
实际示例
// 创建一个绕X轴旋转45度,Y轴旋转30度的欧拉角
const euler = new THREE.Euler(
Math.PI/4, // X轴旋转45度 (π/4弧度)
0.523598776, // Y轴旋转30度 (约0.52弧度)
0, // Z轴不旋转
'XYZ' // 旋转顺序
);
// 应用到物体
mesh.rotation.copy(euler);
万向节锁问题
当绕第二个轴旋转±90度时,会出现万向节锁现象,导致失去一个旋转自由度。例如:
在"XYZ"顺序下,当Y轴旋转90度时,X和Z轴的旋转会变得相同
与四元数的对比
特性 | 欧拉角 | 四元数 |
---|---|---|
表示方式 | 三个角度值 | 四个数值(x,y,z,w) |
直观性 | 高 | 低 |
万向节锁 | 存在 | 不存在 |
插值 | 线性插值不自然 | 球面插值(Slerp)平滑 |
组合旋转 | 顺序敏感,计算复杂 | 简单四元数乘法 |
使用建议
适合使用欧拉角的场景:
简单的单轴旋转
需要直观角度控制的编辑器工具
不需要复杂旋转组合的情况
应避免使用欧拉角的情况:
需要绕任意轴旋转
需要平滑的旋转动画
需要避免万向节锁的复杂旋转
理解欧拉角三个分量的含义对于正确控制3D物体的旋转至关重要,特别是在需要精确控制物体朝向的应用中。
三.四元数(Quaternion)与欧拉角(Euler)的区别
四元数和欧拉角都是表示3D旋转的方式,但它们在实现和特性上有显著差异。以下是两者的主要区别:
1. 数学表示
欧拉角:
使用三个角度值表示旋转(通常为x, y, z)
例如:
new THREE.Euler(0.5, 0.2, 0.1)
表示绕x轴旋转0.5弧度,y轴0.2弧度,z轴0.1弧度
四元数:
使用四个数值(x, y, z, w)表示旋转
例如:
new THREE.Quaternion(x, y, z, w)
2. 万向节锁(Gimbal Lock)问题
欧拉角:
存在万向节锁问题,当某个轴的旋转达到90度时,会失去一个自由度
导致旋转行为不符合预期
四元数:
不存在万向节锁问题
可以表示任意旋转而不丢失自由度
3. 插值效果
欧拉角:
线性插值可能导致旋转路径不自然
旋转动画可能出现抖动或突变
四元数:
可以使用球面线性插值(Slerp)
旋转动画平滑自然
4. 计算复杂度
欧拉角:
计算简单,直观易懂
适合简单的旋转操作
四元数:
计算相对复杂
但组合旋转(四元数乘法)效率更高
5. 使用场景对比
特性 | 欧拉角 | 四元数 |
---|---|---|
直观性 | 高(直接对应轴旋转角度) | 低(数学抽象) |
万向节锁 | 有 | 无 |
插值效果 | 差 | 优 |
组合旋转 | 顺序依赖,计算复杂 | 简单乘法运算 |
存储空间 | 3个值 | 4个值 |
规范化 | 不需要 | 需要保持单位长度 |
实际应用建议
1.使用欧拉角当:
需要直观的角度控制(如编辑器中的旋转工具)
进行简单的单轴旋转
不需要复杂旋转组合或插值
// 欧拉角简单旋转示例 mesh.rotation.x = Math.PI/4; // 绕X轴旋转45度
2.使用四元数当:
理解这两种旋转表示方式的差异,可以帮助你在Three.js开发中选择最适合特定场景的旋转方法
需要避免万向节锁
进行复杂的旋转组合
需要平滑的旋转动画
绕任意轴旋转
// 四元数旋转示例 const axis = new THREE.Vector3(1, 1, 0).normalize(); const angle = Math.PI/3; // 60度 mesh.quaternion.setFromAxisAngle(axis, angle);
转换关系
两者可以相互转换:
// 欧拉角转四元数 const euler = new THREE.Euler(0.5, 0.2, 0.1); const quat = new THREE.Quaternion().setFromEuler(euler); // 四元数转欧拉角 const newEuler = new THREE.Euler().setFromQuaternion(quat);
性能考虑
频繁的欧拉角与四元数转换会影响性能
Three.js内部实际上使用四元数存储旋转,当修改
object.rotation
(欧拉角)时,内部会自动转换为四元数对于频繁更新的旋转操作,直接操作四元数更高效