用外部旋转更新内部旋转

Updating an intrinsic rotation with an extrinsic rotation

我正在尝试为个人项目制作魔方模型,使用 Zdog for lightweight 3d graphics. Zdog uses a {x,y,z} vector to represent rotation - I believe this is essentially a Tait-Bryan angle

为了制作顶部、右侧、前部等的旋转动画,我将 9 个块附加到立方体中心的 anchor,然后将其沿所需方向旋转 90 度。这很好用,但是当动画完成后,我需要“保存”9 个块上的平移和旋转。翻译相对简单,但我坚持旋转。我基本上需要这样的功能:

function updateRotation(xyz, axis, angle) {
  // xyz is a {x,y,z} vector
  // axis is "x", "y", or "z"
  // rotation is the angle of rotation in radians
}

世界坐标中的axis/angle旋转应用于对象[中的xyz矢量=30=]坐标。最初我只有 xyz[axis] += angle,但这仅在其他轴没有任何旋转时才有效。然后我想我可以使用查找 table,我认为这是可能的,因为我只使用四分之一圈,但构建 table 比我想象的要难。

我开始怀疑我需要将 xyz 向量转换为其他表示形式(矩阵?四元数?)并在那里应用旋转,但我不确定从哪里开始。令人沮丧的是,我的块在动画结束时处于正确的位置 - 我只是不确定如何应用父级变换,以便我可以将它们从父级分离而不会丢失旋转。

据我所知,这不能仅用欧拉角来完成(至少不是以任何简单的方式)。我的解决方案是转换为四元数,旋转,然后转换回欧拉:

function updateRotation(obj, axis, rotation) {
  const {x, y, z} = obj.rotate;
  
  const q = new Quaternion()
    .rotateZ(z)
    .rotateY(y)
    .rotateX(x);
  
  const q2 = new Quaternion();
  if (axis === 'x') {
    q2.rotateX(rotation);
  } else if (axis === 'y') {
    q2.rotateY(rotation);
  } else if (axis === 'z') {
    q2.rotateZ(rotation);
  }
  
  q.multiply(q2, null);

  const e = new Euler().fromQuaternion(q);
  
  obj.rotate.x = e.x;
  obj.rotate.y = e.y;
  obj.rotate.z = e.z;
  obj.normalizeRotate();
}

这使用 EulerQuaternion 类 来自 math.gl

(据我所知,Zdog 实际上使用 ZYX 欧拉角,因此创建第一个四元数时的旋转顺序。)