three Quaternion 四元数

519 阅读10分钟

四元数(Quaternion)是一种用于表示旋转的数学工具,尤其在三维计算机图形学、机器人学和物理学等领域广泛应用。与欧拉角或旋转矩阵不同,四元数可以避免常见的万向节锁问题,并且在计算旋转时更为高效和稳定。 1729037908962.png

归一化,是指将数值转换为 0 - 1 之间的数字并应用于three

Quaternion 有五个属性二十八个方法

Quaternion( x : Float, y : Float, z : Float, w : Float ) 属性

  • isQuaternion : Boolean 判断是四元数对象
  • x x 坐标
  • y y 坐标
  • z z 坐标
  • w w 坐标 方法
  • angleTo ( q : Quaternion ) : Float 以弧度返回该四元数与四元数 q 之间的夹角。 1729038338952.png
    // 创建两个四元数
    let q1 = new THREE.Quaternion();
    let q2 = new THREE.Quaternion();
    // 假设 q1 表示绕 X 轴旋转 45 度
    q1.setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 4);
    // 假设 q2 表示绕 X 轴旋转 90 度
    q2.setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI / 2);
    // 计算两个四元数之间的旋转角度
    let angle = q1.angleTo(q2);
    console.log("两个四元数之间的夹角(弧度):", angle);// 两个四元数之间的夹角(弧度): 0.7853981633974484
  • clone () : Quaternion 创建一个与该四元数具有相同x、y、z和w 属性的四元数。
  • conjugate () : this 在四元数中,conjugate() 方法返回当前四元数的共轭(conjugate),并更新四元数的值。四元数的共轭是在保持标量部分(w)不变的情况下,将虚数部分(x, y, z)取反,即:
    // 计算方式
    // q=(w,−x,−y,−z)
    // 创建一个四元数
    let quaternion = new THREE.Quaternion( 1, 2, 3, 4 );
    // 输出原始四元数
    console.log('Original Quaternion:', quaternion);// [1,2,3,4]
    // 计算共轭四元数
    let conjugateQuaternion = quaternion.clone().conjugate();
    // 输出共轭四元数
    console.log('Conjugate Quaternion:', conjugateQuaternion);// [-1,-2,-3,4]
  • copy ( q : Quaternion ) : this 复制四元数 q 的 x、y、z 和 w 属性到该四元数中。
  • equals ( v : Quaternion ) : Boolean v - 用于进行比较的四元数。将四元数 v 的 x、 y、 z 和 w 的属性 与当前四元数的对应属性相比较,以确定它们是否表示相同的旋转。
  • dot ( v : Quaternion ) : Float dot(v: Quaternion): Float 方法用于计算当前四元数与另一个四元数 v 的点积(dot product)。它返回一个浮点值,该值表示两个四元数的相似程度,通常用于判断它们之间的旋转角度关系。点积的值范围在 -11 之间。
    点积的含义
        当点积接近 1 时,表示两个四元数的方向非常接近,旋转角度差非常小。
        当点积为 0 时,表示两个四元数正交,它们之间的旋转角度为 90 度。
        当点积接近 -1 时,表示两个四元数的旋转方向完全相反(180 度的旋转差)。
    // 创建两个四元数
    let quaternion1 = new THREE.Quaternion(1, 0, 0, 0);
    let quaternion2 = new THREE.Quaternion(0, 1, 0, 0);
    // 计算两个四元数之间的点积
    let dotProduct = quaternion1.dot(quaternion2);
    console.log('Dot Product:', dotProduct);  // 输出 0
  • fromArray ( array : Array, offset : Integer ) : this array - 用于构造四元数的形如(x, y, z, w)的数组。 offset - (可选)数组的偏移量。(译者注:使用数组中从第offset元素算起的四个元素)从一个数组来设置四元数的 x、 y、z 和 w 的属性。
  • identity () : this 设置该四元数为 identity 四元数,即表示“不旋转”的四元数。重置为 q=(0,0,0,1)
  • invert () : this 翻转该四元数 —— 计算 conjugate 。假定该四元数具有单位长度。
    // 创建一个四元数
    let quaternion = new THREE.Quaternion(1, 2, 3, 4);
    // 计算共轭四元数
    let conjugateQuaternion = quaternion.clone().conjugate();
    console.log('Conjugate:', conjugateQuaternion);  // [-1,-2,-3,4]
    // 计算逆四元数
    let inverseQuaternion = quaternion.clone().invert();
    console.log('Inverse:', inverseQuaternion);  // [-1,-2,-3,4]
  • length () : Float 计算四元数的 Euclidean length (欧几里得长度,直线长度),视为一个四维向量。 1729039434210.png
    // 创建一个四元数
    let quaternion = new THREE.Quaternion(1, 2, 3, 4);
    // 计算四元数的长度
    let length = quaternion.length();
    // 输出长度
    console.log('Quaternion Length:', length);  // 输出 5.477225575051661
    
    
    // 创建一个四元数
    let quaternion = new THREE.Quaternion(1, 2, 3, 4);
    // 计算长度
    let length = quaternion.length();
    // 归一化四元数
    quaternion.x /= length;
    quaternion.y /= length;
    quaternion.z /= length;
    quaternion.w /= length;
    // 输出归一化后的四元数
    console.log('Normalized Quaternion:', quaternion); // x y z w [0.18257418583505536,0.3651483716701107,0.5477225575051661,0.7302967433402214]
  • lengthSq () : Float 方法用于计算四元数的平方长度(模的平方),而不是其实际长度。计算平方长度可以避免计算平方根的开销,从而提高性能,尤其在需要频繁进行这种计算时。
    let quaternion = new THREE.Quaternion(1, 2, 3, 4);
    // 计算平方长度
    let lengthSq = quaternion.lengthSq();
    // 输出平方长度
    console.log('Quaternion Length Squared:', lengthSq);  // 输出 30
  • normalize () : this Normalizes(归一化)四元数 —— 即计算与该四元数具有相同旋转、但长度为1的四元数。
  • multiply ( q : Quaternion ) : this 将该四元数与q相乘。 1729040286671.png
    // 创建两个四元数
    let quaternion1 = new THREE.Quaternion(2, 0, 0, 1);
    let quaternion2 = new THREE.Quaternion(1, 1, 0, 2);
    // 进行四元数乘法
    quaternion1.multiply(quaternion2);
    // 输出结果
    console.log('Resulting Quaternion:', quaternion1);// [5,1,2,0]
  • multiplyQuaternions ( a : Quaternion, b : Quaternion ) : this 将该四元数设为 a x b 。 改编自 here 所概述的方法。
  • premultiply ( q : Quaternion ) : this 使用 q 乘以该四元数。 要注意的是 premultiply 中 在上面公式中 q 指的是 与 p 的顺序相反
  • rotateTowards ( q : Quaternion, step : Float ) : this rotateTowards(q: Quaternion, step: Float): this 方法用于将当前四元数逐渐旋转到目标四元数 q,并根据给定的步长 step 限制旋转的角度。这种方法可以用于平滑过渡旋转,例如在动画或物体移动时。 是的,每次调用 rotateTowards(q: Quaternion, step: Float) 方法时,都会根据传入的步长 step 和当前四元数与目标四元数之间的角度差来进行计算。
    // 创建两个四元数
    let currentQuaternion = new THREE.Quaternion(1, 0, 0, 0); // 当前四元数
    let targetQuaternion = new THREE.Quaternion(0, 1, 0, 0); // 目标四元数
    let step = Math.PI / 4; // 设定步长(45度)
    // 使用 rotateTowards 方法逐渐旋转到目标四元数
    currentQuaternion.rotateTowards(targetQuaternion, step);
    // 输出结果
    console.log('Updated Quaternion:', currentQuaternion);// [0.7071067811865476,0.7071067811865476,0,0]
    // 使用 rotateTowards 方法逐渐旋转到目标四元数
    currentQuaternion.rotateTowards(targetQuaternion, step);
    // 输出结果
    console.log('Updated Quaternion:', currentQuaternion);// [0.7071067811865476,0.7071067811865476,0,0]
  • slerp ( qb : Quaternion, t : Float ) : this 方法用于在当前四元数与另一个四元数之间进行球面线性插值(slerp),以实现平滑的旋转过渡。slerp 是一种常用的技术,可以在两个四元数之间插值,以获得自然的旋转效果。
    // 创建两个四元数
    const quaternionA = new THREE.Quaternion(0, 0, 0, 1); // 初始四元数
    const quaternionB = new THREE.Quaternion(0, Math.sin(Math.PI / 4), 0, Math.cos(Math.PI / 4)); // 目标四元数(旋转 90 度)
    // 插值因子  值 为 0.5 是计算 两个四元数的中四元数
    let t = 0.5; // 取值范围 [0, 1]
    // 使用 slerp 进行插值
    const resultQuaternion = quaternionA.clone().slerp(quaternionB, t);
    // 输出结果 
    console.log('Interpolated Quaternion:', resultQuaternion);
  • slerpQuaternions ( qa : Quaternion, qb : Quaternion, t : Float ) : this 在给定的四元数之间执行球面线性插值,并将结果存储在这个四元数中 qa:起始四元数,表示物体的当前方向。 qb:结束四元数,表示物体的当前方向。 当 0 < t < 1 时,结果为 qa 和 qb 之间的某个中间状态。
  • set ( x : Float, y : Float, z : Float, w : Float ) : this 设置该四元数的 x、y、z和w属性。
  • setFromAxisAngle ( axis : Vector3, angle : Float ) : this 从由 axis(轴) 和 angle(角度)所给定的旋转来设置该四元数。 改编自 here 所述的方法。 假定Axis已被归一化,angle以弧度来表示。
  • setFromEuler ( euler : Euler ) : this 从由 Euler 角所给定的旋转来设置该四元数。
  • setFromRotationMatrix ( m : Matrix4 ) : this 从m的旋转分量中来设置该四元数。 改编自 here 所概述的方法。 通过矩阵设置四元数
  • setFromUnitVectors ( vFrom : Vector3, vTo : Vector3 ) : this 将该四元数设置为从方向向量 vFrom 旋转到方向向量 vTo 所需的旋转。 改编自方法 here。 假设 vFrom 和 vTo 都已归一化。 1729042279785.png
    // 创建两个单位向量
    const vFrom = new THREE.Vector3(1, 0, 0); // 起始方向向量 (x轴)
    const vTo = new THREE.Vector3(0, 1, 0);   // 目标方向向量 (y轴)
    // 创建一个四元数
    const quaternion = new THREE.Quaternion();
    // 使用 setFromUnitVectors 设置四元数
    quaternion.setFromUnitVectors(vFrom, vTo);
    // 计算旋转角度
    const angle = 2 * Math.acos(quaternion.w); // 旋转角度(弧度)
    const angleInDegrees = THREE.MathUtils.radToDeg(angle); // 转换为度
    // 输出结果
    console.log('Quaternion:', quaternion); // [0,0,0.7071067811865475,0.7071067811865475]
    console.log('Rotation Angle (radians):', angle); // 1.5707963267948968
    console.log('Rotation Angle (degrees):', angleInDegrees);// 90.00000000000001
  • toArray ( array : Array, offset : Integer ) : Array array - (可选)存储该四元数的数组。若未指定该参数,则将创建一个新数组。 offset - (可选)若指定了该值,结果将会被拷贝到该 Array。 在形如[x, y, z, w]的数组中,返回四元数中的数字元素。
  • fromBufferAttribute ( attribute : BufferAttribute, index : Integer ) : this attribute - 源 attribute。 index - attribute 中的索引。 从 attribute 中设置该四元数的x、 y、 z、 w属性。 在 attribute 中取四个值对就x y z w
  • slerpFlat ( dst : Array, dstOffset : Integer, src0 : Array, srcOffset0 : Integer, src1 : Array, srcOffset1 : Integer, t : Float ) : undefined slerpFlat(dst: Array, dstOffset: Integer, src0: Array, srcOffset0: Integer, src1: Array, srcOffset1: Integer, t: Float): undefined 方法用于在两个四元数之间执行球面线性插值(SLERP),并将结果存储在一个平面数组(如 Float32Array)中。这种方法非常适合在高性能场景中使用,例如游戏开发,因其直接操作数组,减少了对象的创建和销毁。
### 方法解析
-   **参数**:
    -   `dst`: 目标数组,插值结果将存储在这个数组中。
    -   `dstOffset`: 目标数组中的偏移量,指示从哪个位置开始写入结果。
    -   `src0`: 起始数组,包含第一个四元数的值。
    -   `srcOffset0`: 起始数组中的偏移量,指示第一个四元数在该数组中的位置。
    -   `src1`: 目标数组,包含第二个四元数的值。
    -   `srcOffset1`: 目标数组中的偏移量,指示第二个四元数在该数组中的位置。
    -   `t`: 插值因子,通常在 [0, 1] 之间:
        -   当 `t = 0` 时,结果为 `src0`。
        -   当 `t = 1` 时,结果为 `src1`。
        -   当 `0 < t < 1` 时,结果为 `src0``src1` 之间的某个中间值。
        
        
    // 创建源四元数
    const src0 = new Float32Array([0, 0, 0, 1]); // 第一个四元数(无旋转)
    const src1 = new Float32Array([0, Math.sin(Math.PI / 4), 0, Math.cos(Math.PI / 4)]); // 第二个四元数(旋转 90 度)
    // 目标数组
    const dst = new Float32Array(4);
    // 插值因子
    const t = 0.5; // 中间值
    // 使用 slerpFlat 进行插值
    THREE.Quaternion.slerpFlat(dst, 0, src0, 0, src1, 0, t);
    // 输出结果
    console.log('Interpolated Quaternion (Flat):', dst);// { "0": 0, "1": 0.3826834261417389, "2": 0, "3": 0.9238795042037964 }
  • multiplyQuaternionsFlat ( dst : Array, dstOffset : Integer, src0 : Array, srcOffset0 : Integer, src1 : Array, srcOffset1 : Integer ) : Array multiplyQuaternionsFlat(dst: Array, dstOffset: Integer, src0: Array, srcOffset0: Integer, src1: Array, srcOffset1: Integer): Array 方法用于在两个四元数之间执行乘法运算,并将结果存储在一个平面数组(如 Float32Array)中。这种方法特别适合高性能应用,因为它直接操作数组,避免了额外的对象创建和销毁。
    方法解析
-   **参数**:
    -   `dst`: 目标数组,用于存储结果的四元数。
    -   `dstOffset`: 在目标数组中的偏移量,指示结果从哪个位置开始写入。
    -   `src0`: 第一个源数组,包含第一个四元数的值。
    -   `srcOffset0`: 第一个源数组中的偏移量,指示第一个四元数在该数组中的位置。
    -   `src1`: 第二个源数组,包含第二个四元数的值。
    -   `srcOffset1`: 第二个源数组中的偏移量,指示第二个四元数在该数组中的位置。
    
    // 定义两个四元数
    const q0 = new Float32Array([0, Math.sin(Math.PI / 4), 0, Math.cos(Math.PI / 4)]); // 旋转 90 度
    const q1 = new Float32Array([Math.sin(Math.PI / 4), 0, 0, Math.cos(Math.PI / 4)]); // 旋转 90 度
    // 创建目标数组
    const dst = new Float32Array(4);
    // 使用 multiplyQuaternionsFlat 进行四元数乘法
    THREE.Quaternion.multiplyQuaternionsFlat(dst, 0, q0, 0, q1, 0);
    // 输出结果
    console.log('Resulting Quaternion (Flat):', dst); // { "0": 0.4999999701976776, "1": 0.4999999701976776, "2": -0.4999999701976776, "3": 0.4999999701976776 }