three.js中绕单轴旋转

2,917 阅读3分钟

在webGL中通过右手坐标系去思考其中的旋转

  • 当物体绕z轴,从x轴正半轴向y轴负半轴逆时针旋转,为正向旋转,反之为负向旋转
  • 当物体绕x轴,从y轴正半轴向z轴正半轴逆时针旋转,是正向旋转,反之为负向旋转
  • 当物体绕y轴,从z轴正半轴向x轴正半轴逆时针旋转,是正向旋转,反之为负向旋转

计算旋转量.png

思路过程

举例:计算一个顶点A绕z轴旋转后所在B的位置

  1. 已知点A,以及∠b

  2. ∠XOB = ∠a+∠b

  3. bx=cos(∠XOB)*OA

  4. by=sin(∠XOB)*OA

  5. 利用三角形勾股定理求出OA的长度

     const OA = Math.sqrt(ax*ax + ay*ay) //首先计算OA的长度
    
  6. 利用三角形的和角公式得出

    cos(∠XOB) = cos(∠a+∠b) = cos(a)*cos(b) - sin(a)*cos(b)
    sin(∠XOB) = sin(∠a+∠b) = cos(b)*sin(a) + sin(b)*cos(a)
    
  7. 由此求出bx,by

    bx=(cos(a)*cos(b) - sin(a)*sin(b))*OA
    by=(cos(b)*sin(a) + sin(b)*cos(a))*OA
    
  8. 简化公示 因为:ax = cos(a) * OA, ay = sin(a) * OA

    上式化简为

    bx = cos(a)*cos(b)*OA - sin(a)*sin(b)*OA
    by = cos(b)*sin(a)*OA + sin(b)*cos(a)*OA
    ​
    bx = ax*cos(b) - ay*sin(b)
    by = cos(b)*ay + sin(b)*ax
    ​
    bx = ax*cos(b) - ay*sin(b)
    by = ax*sin(b) + ay*cos(b)
    
  9. 由向量和矩阵乘法计算

    [x, y, z, w] 
       * 
    [
         a, b, c, d,
         e, f, g, h,
         i, j, k, l,
         m, n, o, p,
    ]
      =
    [
        ax + by + cz + dw,
        ex + fy + gz + hw,
        ix + jy + kz + lw,
        mx + ny + oz + pw,
    ]
    // 由以上乘法规则得出
    [bx, by, bz, bw]
      = 
    [
        cos(b), -sin(b), 0, 0,
        sin(b),  cos(b), 0, 0,
        0,       0,      1, 0,
        0,       0,      0, 1
    ]
      *
    [ax, ay, az, aw]
      =
    [
      ax*cos(b) + ay*-sin(b) + az*0 + aw*0,
      ax*sin(b) + ay*cos(b) + az*0 + aw*0,
      ax*0 + ay*0 + az*1 + aw*0,
      ax*0 + ay*0 + az*0 + aw*1,
    ]
      =
    [
      ax*cos(b) - ay*sin(b),
      ax*sin(b) + ay*cos(b),
      az,
      aw
    ]
    

计算过程

const OA = Math.sqrt(ax*ax + ay*ay) //首先计算OA的长度
// 通过三角函数得出bx,by
const [bx, by, bz, bw] 
= 
[
    cos(b)*ax - sin(b)*ay,
    sin(b)*ay + cos(b)*ax,
    1*az,
    1*aw
]
=
[ax, ay, az, aw]
*
[
    cos(b), -sin(b), 0, 0,
    sin(b),  cos(b), 0, 0,
    0,       0,      1, 0,
    0,       0,      0, 1
]

由以上推导计算点A绕z轴旋转后所在B的位置所需的矩阵为:

[
    cos(b), -sin(b), 0, 0,
    sin(b),  cos(b), 0, 0,
    0,       0,      1, 0,
    0,       0,      0, 1
]

源码

以下为three.js中分别绕x、y、z轴旋转的源码

 makeRotationX( theta ) {
    const c = Math.cos( theta ), s = Math.sin( theta );
​
    this.set(
      1, 0, 0, 0,
      0, c, - s, 0,
      0, s, c, 0,
      0, 0, 0, 1
    );
    return this;
  }
​
 makeRotationY( theta ) {
    const c = Math.cos( theta ), s = Math.sin( theta );
​
    this.set(
       c, 0, s, 0,
       0, 1, 0, 0,
      - s, 0, c, 0,
       0, 0, 0, 1
​
    );
    return this;
  }
  makeRotationZ( theta ) {
    const c = Math.cos( theta ), s = Math.sin( theta );
​
    this.set(
      c, - s, 0, 0,
      s, c, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1
    );
    return this;
  }

注意⚠️

通常在数学中矩阵是列主序,在webGL中矩阵是行主序的,以上都是用行主序的写法,其中的this.set()方法就是在将行主序的矩阵转换为列主序的矩阵

下面是set方法的源码

set( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {
​
    const te = this.elements;
​
    te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;
    te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;
    te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;
    te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;
​
    return this;
​
}