【问题标题】:Quaternion rotation in three.js going haywire when rotating past about 90°当旋转超过大约 90° 时,three.js 中的四元数旋转变得混乱
【发布时间】:2016-04-06 14:13:22
【问题描述】:

我正在使用两指触摸事件来捏-旋转-缩放THREE.Mesh 对象,使用四元数。这是我第一次使用这种旋转方法,由于我确信四元数的某些属性我似乎无法理解,当总触摸拖动将对象旋转超过大约 90 时,旋转逐渐开始在整个地方抖动°。然后,当再次被拖回 90° 以下时,它逐渐恢复到原来的平滑旋转,尽管明显是非线性的。 (90° 只是猜测)。

我完全被难住了。这是我用来旋转对象obj的代码:

  // global state
var 
      // keeps track of original object scale
    pinchScale = new THREE.Vector3(),

      // current first touch
    touch1 = new THREE.Vector2(),

      // current second touch
    touch2 = new THREE.Vector2(),

      // first touch at touch start
    touch1OnHold = new THREE.Vector2(),

      // second touch at touch start
    touch2OnHold = new THREE.Vector2(),

      // keeps track of total held rotation at last fired touchmove event
    angularHoldPrev = new THREE.Quaternion();

  ⋮

  // key-value pairs inside an addEventListener utility
touchstart: function (event) {
  event.preventDefault();
  if ( event.touches.length === 1 ) {
    …
  } else if ( event.touches.length === 2 ) {
    touch1OnHold.set(event.touches[0].pageX, event.touches[0].pageY);
    touch2OnHold.set(event.touches[1].pageX, event.touches[1].pageY);
    angularHoldPrev.set(0, 0, 0, 1)
  }
},
touchmove: function (event) {
  event.preventDefault();
  if ( event.touches.length === 1 ) {
    …
  } else if ( event.touches.length === 2 ) {
    touch1.set(event.touches[0].pageX, event.touches[0].pageY);
    touch2.set(event.touches[1].pageX, event.touches[1].pageY);
    var 
          // get touch spread at present event firing, and at the start of current hold
        touchDiff = touch2.clone().sub(touch1),
        touchDiffOnHold = touch2OnHold.clone().sub(touch1OnHold),

          // camera is on z-axis; get this axis regardless of obj orientation
        axis1 = new THREE.Vector3(0, 0, 1).applyQuaternion(obj.quaternion.clone().inverse()),

          // get a touch rotation around this axis
        rot1 = new THREE.Quaternion().setFromAxisAngle(axis1, (Math.atan2(touchDiffOnHold.y, touchDiffOnHold.x) - Math.atan2(touchDiff.y, touchDiff.x))).normalize(),

          // get touch barycentre at present event firing, and at the start of current hold
        touchCentre = touch1.clone().add(touch2).multiplyScalar(.5),
        touchCentreOnHold = touch1OnHold.clone().add(touch2OnHold).multiplyScalar(.5),

          // get axis of touch barycentre movement on the xy plane, regardless of obj orientation
        axis2 = new THREE.Vector3(touchCentre.y - touchCentreOnHold.y, touchCentre.x - touchCentreOnHold.x, 0).applyQuaternion(obj.quaternion.clone().inverse()),

          // get a rotation proportional to magnitude of touch movement
        rot2 = new THREE.Quaternion().setFromAxisAngle(axis2, axis2.length() * rotationSensitivity).normalize(),

          // combine the two rotations
        rot = rot1.multiply(rot2);

      // undo last rotation if not the empty quaternion
    if (!angularHoldPrev.equals(new THREE.Quaternion())) obj.quaternion.multiply(angularHoldPrev.inverse());

      // perform the currently calculated rotation
    obj.quaternion.multiply(rot);

      // save this rotation for next event firing
    angularHoldPrev.copy(rot);

      // resize object according to change in touch spread
    obj.scale.copy(pinchScale.clone().multiplyScalar(touchDiff.length() / touchDiffOnHold.length()))
  }
},
touchend: function (event) {
  event.preventDefault();

    // reset original object scale
  pinchScale.copy(obj.scale)
}

任何线索我如何保持所有两点触控输入的比例旋转将不胜感激。干杯。

【问题讨论】:

  • 不深入研究的快速猜测可能是您的atan2。 tan 函数在 90 度处拉到无穷远。
  • 我很担心。但是 atan2 没有任何无穷大,只有正负 180° 之间的不连续性。

标签: javascript three.js rotation multi-touch quaternions


【解决方案1】:

我通过使所有 touchmove 旋转增量而不是相对于 touchstart 来使其工作,它实际上清理了一些东西。 rotV 是我用于动画循环的角速度变量,当你移动它时,它会给对象一个很好的惯性踢。 (感谢@WestLangley 的优化建议)

var touch1 = new THREE.Vector2(),
    touch2 = new THREE.Vector2(),
    touch1Prev = new THREE.Vector2(),
    touch2Prev = new THREE.Vector2(),
    rotV = new THREE.Quaternion(),

    touchDiff = new THREE.Vector2(),
    touchDiffPrev = new THREE.Vector2(),
    touchCentre = new THREE.Vector2(),
    touchCentrePrev = new THREE.Vector2(),
    axis1 = new THREE.Vector3(),
    axis2 = new THREE.Vector3(),
    rot1 = new THREE.Quaternion(),
    rot2 = new THREE.Quaternion(),
    adjq = new THREE.Quaternion();

⋮

touchstart: function (event) {
  event.preventDefault();
  if ( event.touches.length === 1 ) {
    …
  } else if ( event.touches.length === 2 ) {
    touch1Prev.set(event.touches[0].pageX, event.touches[0].pageY);
    touch2Prev.set(event.touches[1].pageX, event.touches[1].pageY)
  }
},
touchmove: function (event) {
  event.preventDefault();
  adjq.copy(obj.quaternion).inverse();
  if ( event.touches.length === 1 ) {
    …
  } else if ( event.touches.length === 2 ) {
    touch1.set(event.touches[0].pageX, event.touches[0].pageY);
    touch2.set(event.touches[1].pageX, event.touches[1].pageY);

    touchDiff.copy(touch2).sub(touch1);
    touchDiffPrev.copy(touch2Prev).sub(touch1Prev);
    axis1.set(0, 0, 1).applyQuaternion(adjq);
    rot1.setFromAxisAngle(axis1, (Math.atan2(touchDiffPrev.y, touchDiffPrev.x) - Math.atan2(touchDiff.y, touchDiff.x))).normalize();

    touchCentre.copy(touch1).add(touch2).multiplyScalar(.5);
    touchCentrePrev.copy(touch1Prev).add(touch2Prev).multiplyScalar(.5);
    axis2.set(touchCentre.y - touchCentrePrev.y, touchCentre.x - touchCentrePrev.x, 0).applyQuaternion(adjq);
    rot2.setFromAxisAngle(axis2, axis2.length() * rotationSensitivity * 10).normalize();

    obj.quaternion.multiply(rot1.multiply(rot2));
    rotV.multiply(rot1.slerp(adjq.set(0, 0, 0, 1), .9));
    obj.scale.multiplyScalar(touchDiff.length() / touchDiffPrev.length());

    touch1Prev.copy(touch1);
    touch2Prev.copy(touch2)
  }
},
touchend: function (event) {
  event.preventDefault()
}

我仍然希望我知道是什么导致了最初的问题:P

【讨论】:

  • 避免在紧密循环中调用newclone()。创建单个实例并重用它。您为每个 touchmove 创建了多少个实例?
  • 感谢@WestLangley,我已经更新了我的答案。另外我注意到您实际上在 three.js 中编写了很多四元数代码。你有没有看到我忽略的任何明显的东西?
  • 我可以回答有关three.js的问题,但我无法评论您的代码。对不起。 :)
  • 虽然我不能告诉你如何“修复”它,但我可以通过我的一个问题告诉你“90°左右”的问题是什么。四元数(以及欧拉表示)的问题是它们具有不连续性。如果我们谈论单位球体,假设您在 (x: 0 y:0 z:1) 附近的“北极”附近有两个点。您想在这两个点之间旋转,即使它们“彼此相邻”,其中一个的“x”旋转为“-178”,另一个旋转为“+178”。 (180 是顶部)在我的情况下,TWEEN 认为(正确地)-178 和 178 之间的最短路线是通过 0。
猜你喜欢
  • 2011-04-23
  • 1970-01-01
  • 2019-07-09
  • 2012-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-17
  • 2014-02-15
相关资源
最近更新 更多