当前位置: 首页 > ops >正文

threejs 四元数(Quaternion)和欧拉角(Euler)学习

一.四元数(Quaternion)中四个分量的含义

四元数是一种用于表示3D旋转的数学工具,由四个分量组成,通常表示为 (x, y, z, w) 或 (i, j, k, real)。在Three.js中,THREE.Quaternion类使用(x, y, z, w)的表示形式。

四个分量的详细解释

  1. 虚部 (x, y, z)

    • 表示旋转轴的方向向量

    • 这个向量会被自动规范化(长度为1)

    • 分量的比例决定了旋转轴的方向

    • 例如:(1, 0, 0)表示X轴,(0, 1, 0)表示Y轴

  2. 实部 (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] 是旋转轴的单位向量

具体示例

  1. 无旋转的单位四元数

    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是角度分量

重要特性

  1. 单位长度
    所有表示旋转的四元数都应该是单位四元数:

    x² + y² + z² + w² = 1

    2.规范化
       如果手动修改了四元数值,应该调用: 

quaternion.normalize();

  3.逆四元数
表示相反旋转的四元数可以通过取共轭获得:

const inverse = quaternion.clone().conjugate();

理解四元数的四个分量含义有助于在3D编程中更有效地使用它们,特别是在需要精确控制旋转或进行复杂旋转运算时。

二.欧拉角(Euler Angle)三个分量的含义

在Three.js中,欧拉角使用THREE.Euler类表示,由三个分量(x, y, z)组成,分别表示绕三个轴的旋转角度。

三个分量的具体含义

  1. x分量

    • 表示绕X轴的旋转角度(俯仰角/Pitch)

    • 正值:物体向前倾斜(低头)

    • 负值:物体向后倾斜(抬头)

  2. y分量

    • 表示绕Y轴的旋转角度(偏航角/Yaw)

    • 正值:物体向右转

    • 负值:物体向左转

  3. 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)平滑
组合旋转顺序敏感,计算复杂简单四元数乘法

使用建议

  1. 适合使用欧拉角的场景

    • 简单的单轴旋转

    • 需要直观角度控制的编辑器工具

    • 不需要复杂旋转组合的情况

  2. 应避免使用欧拉角的情况

    • 需要绕任意轴旋转

    • 需要平滑的旋转动画

    • 需要避免万向节锁的复杂旋转

理解欧拉角三个分量的含义对于正确控制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(欧拉角)时,内部会自动转换为四元数

  • 对于频繁更新的旋转操作,直接操作四元数更高效

http://www.xdnf.cn/news/8332.html

相关文章:

  • 学习Raft共识算法基本原理
  • 了解Android studio 初学者零基础推荐(3)
  • Python版scorecardpy库woebin函数使用
  • 安全自动化与AI驱动防御
  • 《数据结构笔记三》:单链表(创建、插入、遍历、删除、释放内存等核心操作)
  • 教育行业课件共享难题:大文件分发效率优化方案
  • 广东省省考备考(第十八天5.23)—言语:语句排序题(听课后强化训练)
  • 对比关系型数据库与NoSQL数据库
  • nlf 2025 部署笔记
  • 利用 Python 爬虫获取唯品会 VIP 商品详情:实战指南
  • microsoft中word如何添加个人签名
  • 时序数据库 TDengine × Superset:一键构建你的可视化分析系统
  • PyQt学习系列10-性能优化与调试技巧
  • Java对象内存分配优化教学
  • 端到端大语言模型微调技术 Demo 全流程详解(附完整模块说明)
  • C语言数据结构
  • 【LaTex】基础语法入门
  • 使用Python在PyCharm中进行交通工程数据分析的完整流程,包括数据清洗、挖掘、关联、可视化和应用整合等各个阶段
  • RK3399 Android13设备插拔无线鼠标键盘设备出现APP或系统界面刷新现象
  • 详解osgb的顶点,纹理,索引,UV读取与存储
  • 注册并创建一个微信小程序
  • 第三章 软件工程模型和方法
  • 免费在线AI聊天工具
  • C# 按行写入txt大量数据
  • AI与.NET技术实操系列(八):使用Catalyst进行自然语言处理
  • 极大似然估计
  • 2025电工杯:光伏电站发电功率日前预测问题 第二问 基于历史功率的光伏电站日前发电功率预测模型构建思路
  • 用 3D 可视化颠覆你的 JSON 数据体验
  • 持续更新 ,GPT-4o 风格提示词案例大全!附使用方式
  • Android 网络全栈攻略(五)—— 从 OkHttp 拦截器来看 HTTP 协议二