Quat.js 完全指南
Quat.js 是一个高性能的四元数库,专为 3D 数学设计,适用于游戏开发、3D 图形处理和物理模拟等场景。它提供了丰富的 API,支持四元数的基本操作、欧拉角转换、矩阵转换以及插值等功能。
目录
- 简介
- 安装与导入
- 基本概念
- 核心功能
- 创建四元数
- 设置旋转轴和角度
- 四元数插值
- 欧拉角与四元数转换
- 矩阵与四元数转换
- 高级用法
- 随机生成四元数
- 计算旋转轴和角度
- 使用球面线性插值(SLERP)
- 常见问题解答
简介
四元数是一种高效表示三维空间旋转的数学工具,常用于计算机图形学、游戏开发和机器人学等领域。相比于欧拉角和旋转矩阵,四元数具有以下优点:
- 避免万向锁:四元数不会像欧拉角那样遇到万向锁问题。
- 计算效率高:四元数的存储和计算开销较小。
- 插值平滑:通过球面线性插值(SLERP),可以实现平滑的旋转过渡。
Quat.js 提供了一套完整的四元数操作函数,旨在简化 3D 数学中的旋转处理。
安装与导入
安装
通过 npm 安装:
bash
npm install quat
导入
在 TypeScript 或 JavaScript 文件中按需导入:
typescript
import { create, identity, setAxisAngle } from 'quat';
javascript
const { create, identity, setAxisAngle } = quat;
基本概念
四元数是一个长度为 4 的数组 [x, y, z, w]
,其中:
- x,y,z 表示旋转轴的方向。
w
表示旋转的角度。
例如,单位四元数 [0, 0, 0, 1]
表示无旋转。
核心功能
创建四元数
创建一个新的单位四元数:
typescript
import { create, identity } from 'quat';
const q = create();
identity(q); // 将四元数设置为单位四元数
console.log(q); // 输出: [0, 0, 0, 1]
设置旋转轴和角度
根据给定的旋转轴和角度(弧度制)设置四元数:
typescript
import { create, setAxisAngle } from 'quat';
const q = create();
const axis = [0, 1, 0]; // 绕 Y 轴旋转
const angle = Math.PI / 2; // 90 度(弧度制)
setAxisAngle(q, axis, angle);
console.log(q); // 输出: [0, 0.7071, 0, 0.7071]
四元数插值
在两个四元数之间进行球面线性插值(SLERP):
typescript
import { create, setAxisAngle, slerp } from 'quat';
const q1 = create();
const q2 = create();
// 设置初始四元数
setAxisAngle(q1, [1, 0, 0], Math.PI / 2); // 绕 X 轴旋转 90 度
setAxisAngle(q2, [0, 1, 0], Math.PI); // 绕 Y 轴旋转 180 度
const result = create();
slerp(result, q1, q2, 0.5); // 在 q1 和 q2 之间进行插值
console.log(result);
欧拉角与四元数转换
从欧拉角创建四元数,并支持自定义旋转顺序:
typescript
import { create, fromEuler } from 'quat';
const q = create();
fromEuler(q, 90, 45, 0, 'xyz'); // 绕 X 轴旋转 90 度,绕 Y 轴旋转 45 度
console.log(q);
矩阵与四元数转换
从 3x3 旋转矩阵创建四元数:
typescript
import { create, fromMat3 } from 'quat';
const matrix = [
1, 0, 0,
0, 0, -1,
0, 1, 0
];
const q = create();
fromMat3(q, matrix);
console.log(q);
高级用法
随机生成四元数
生成一个随机的单位四元数:
typescript
import { create, random } from 'quat';
const q = create();
random(q);
console.log(q);
计算旋转轴和角度
获取四元数的旋转轴和角度:
typescript
import { create, setAxisAngle, getAxisAngle } from 'quat';
const q = create();
const axis = [0, 1, 0];
const angle = Math.PI / 2;
setAxisAngle(q, axis, angle);
const outAxis = [0, 0, 0];
const outAngle = getAxisAngle(outAxis, q);
console.log('旋转轴:', outAxis);
console.log('旋转角度:', outAngle);
使用球面线性插值(SLERP)
使用两个控制点进行球面线性插值:
typescript
import { create, setAxisAngle, sqlerp } from 'quat';
const q1 = create();
const q2 = create();
const q3 = create();
const q4 = create();
setAxisAngle(q1, [1, 0, 0], Math.PI / 2);
setAxisAngle(q2, [0, 1, 0], Math.PI);
setAxisAngle(q3, [0, 0, 1], Math.PI / 4);
setAxisAngle(q4, [1, 1, 0], Math.PI / 3);
const result = create();
sqlerp(result, q1, q2, q3, q4, 0.5);
console.log(result);
常见问题解答
如何避免万向锁?
四元数本身不会遇到万向锁问题,因此在需要处理复杂旋转时,推荐使用四元数而非欧拉角。
如何检查两个四元数是否相等?
使用 [exactEquals] 函数检查近似相等:
typescript
import { exactEquals, equals } from 'quat';
const q1 = [0, 0, 0, 1];
const q2 = [0, 0, 0, 1];
console.log(exactEquals(q1, q2)); // true
console.log(equals(q1, q2)); // true
如何归一化四元数?
使用 [normalize]函数将四元数归一化:
typescript
import { create, normalize } from 'quat';
const q = create();
q[0] = 1; q[1] = 2; q[2] = 3; q[3] = 4;
normalize(q, q);
console.log(q);