threejs 四元数

在 Three.js 中,**四元数(Quaternion)**是一种用来表示和处理三维空间旋转的数学方式

相比于欧拉角(Euler),它是一种更稳定和高效的旋转表示方法,能够避免万向锁(Gimbal Lock)问题。

四元数基础

  1. THREE.Quaternion 类: Three.js 中的 THREE.Quaternion 类用于处理四元数。一个四元数由四个分量 ((x,y,z,w)) 组成,但这些分量不像欧拉角的 x、y、z 那样直观,它们代表的是一个旋转轴和旋转角度的组合。
  2. quaternion 属性: THREE.Object3D 及其子类(例如 THREE.Mesh)都带有一个 .quaternion 属性,它是一个 THREE.Quaternion 的实例。当改变物体的 .rotation (欧拉角) 时,Three.js 会自动同步更新 .quaternion,反之亦然。
  3. 优点
    • 避免万向锁:欧拉角在某些特定角度下会失去一个自由度,导致旋转操作变得不可预测。四元数则没有这个问题。
    • 平滑插值(SLERP):四元数可以进行球形线性插值(SLERP),这使得从一个旋转平滑过渡到另一个旋转变得非常简单,常用于动画。

如何使用四元数进行旋转

最常见的方法是指定一个旋转轴(THREE.Vector3)和一个旋转角度(弧度)

typescript 复制代码
 // 创建一个四元数实例
const quaternion = new THREE.Quaternion();

// 围绕 Y 轴旋转 90 度
const axis = new THREE.Vector3(0, 1, 0).normalize();
const angle = Math.PI / 2; // 90 度

// 使用 setFromAxisAngle 方法设置四元数
quaternion.setFromAxisAngle(axis, angle);

// 将四元数应用到物体
object.quaternion.copy(quaternion);

从欧拉角或矩阵创建四元数

typescript 复制代码
// 从欧拉角创建四元数
const euler = new THREE.Euler(Math.PI / 4, 0, 0); // 绕 X 轴旋转 45 度
const quaternionFromEuler = new THREE.Quaternion().setFromEuler(euler);
object.quaternion.copy(quaternionFromEuler);

// 从一个旋转矩阵创建四元数
const matrix = new THREE.Matrix4().makeRotationX(Math.PI / 4);
const quaternionFromMatrix = new THREE.Quaternion().setFromRotationMatrix(matrix);
object.quaternion.copy(quaternionFromMatrix);

通过两个单位向量创建

ini 复制代码
const startVector = new THREE.Vector3(0, 1, 0); // 初始方向
const endVector = new THREE.Vector3(1, 0, 0);   // 目标方向
const quaternion = new THREE.Quaternion().setFromUnitVectors(startVector, endVector);

object.quaternion.copy(quaternion);

应用四元数旋转

你可以使用 .applyQuaternion() 方法来应用旋转,这对于将一个物体的旋转叠加到另一个物体上非常有用。

csharp 复制代码
  // 创建一个要应用的四元数
const rotationQuaternion = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), 0.1);

// 将旋转应用到物体上
object.applyQuaternion(rotationQuaternion);

平滑插值(SLERP)

scss 复制代码
// 假设这是物体的当前四元数
const startQuaternion = mesh.quaternion.clone();

// 假设这是我们想要达到的目标四元数
const endQuaternion = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI);

// 在渲染循环中逐步插值
function animate() {
    requestAnimationFrame(animate);

    // 每帧都向目标四元数进行插值,t 的范围在 [0, 1]
    const t = 0.05; // 控制插值速度
    mesh.quaternion.slerp(endQuaternion, t);

    // 如果物体已经非常接近目标,可以停止插值
    if (mesh.quaternion.angleTo(endQuaternion) < 0.001) {
        // ... 停止插值或进行其他操作
    }

    renderer.render(scene, camera);
}

欧拉角 vs 四元数

结论

  • 对于简单的、可预测的旋转,或需要直接修改特定轴的旋转角度时,使用欧拉角很方便。
  • 对于复杂的动画、平滑过渡或需要避免万向锁的场景,四元数是更好的选择。
  • 在 Three.js 中,由于 rotationquaternion 属性会自动同步,你可以根据需求在两者之间灵活选择。
相关推荐
玄魂11 分钟前
如何查看、生成 github 开源项目star 图表
前端·开源·echarts
前端一小卒1 小时前
一个看似“送分”的需求为何翻车?——前端状态机实战指南
前端·javascript·面试
syt_10131 小时前
Object.defineProperty和Proxy实现拦截的区别
开发语言·前端·javascript
遝靑1 小时前
Flutter 跨端开发进阶:可复用自定义组件封装与多端适配实战(移动端 + Web + 桌面端)
前端·flutter
cypking1 小时前
Web前端移动端开发常见问题及解决方案(完整版)
前端
老前端的功夫1 小时前
Vue 3 vs Vue 2 深度解析:从架构革新到开发体验全面升级
前端·vue.js·架构
栀秋6661 小时前
深入浅出链表操作:从Dummy节点到快慢指针的实战精要
前端·javascript·算法
狗哥哥2 小时前
Vue 3 动态菜单渲染优化实战:从白屏到“零延迟”体验
前端·vue.js
青青很轻_2 小时前
Vue自定义拖拽指令架构解析:从零到一实现元素自由拖拽
前端·javascript·vue.js
树下水月2 小时前
纯HTML 调用摄像头 获取拍照后的图片的base64
前端·javascript·html