ThreeJS Oribital相机短时旋转

col*_*unk 2 javascript math trigonometry three.js

我有一台轨道相机,它的轨道是一个地球仪。用户可以单击地球上的几个标记,然后相机将移动到该点。

使用TweenMax这样的动画-

 TweenMax.to(currentPos, 3, {
     theta:targetPos.theta, 
     phi:targetPos.phi, 
     radius:targetPos.radius, 
     ease:Circ.easeIn, 
     onComplete:btnZoomComplete,
     onUpdateParams:["{self}"], 
     onComplete:SataliteTweenComplete, 
     onUpdate: function(tween) {
       controls.setThetaPhi(tween.target.theta, tween.target.phi, tween.target.radius);
     }
});
Run Code Online (Sandbox Code Playgroud)

这很好用,但是没有考虑到达那里的最短路径。因此,它很可能经常绕地球转。

ThreeJS似乎是在一个非常奇怪的单位系统中测量角度:0、1.57(相当于90度),3.14(等于180dg),然后在3.14之后跳到-3.14,-1.57(等于270dg),然后又回到0 ...这让我大吃一惊。

例如,假设摄影机处于2.6且需要移至-2.61,此刻摄影机将对CCW进行动画处理(从2.6到-2.16),在视觉上需要对CW进行动画处理,而CW将从2.6变为3.14。 ,则-3.14,然后变为-2.61。

任何帮助,将不胜感激。

我猜有两个问题,如何弄清楚该怎么回事,然后如何从2.6-> 3.14进行动画处理,无缝跳转到-3.14-> -2.61

Mar*_*fuß 5

因此,“奇怪的单位制”只是弧度,因此通常在-180°至180°和-90°至90°的范围内测量theta / phi值(请想想纬度/经度,同一件事)。转换很简单:

angleDegrees = radians / Math.PI * 180;
radians = angleDegrees / 180 * Math.PI;
Run Code Online (Sandbox Code Playgroud)

现在,补间库将仅从一个值插入到另一个值,并且不知道这些值代表什么。因此,当旋转时,它根本不知道如何处理最短路径。但是,您可以在开始补间之前执行此操作。

假设我们从动画2.6-2.6(或149°至-149°)。

var from = 2.6, to = -2.6;
Run Code Online (Sandbox Code Playgroud)

动画的方向和角度距离可以计算为

var distance = to - from; 
// === -5.2
Run Code Online (Sandbox Code Playgroud)

此处的负值表示逆时针方向,并且5.2(〜298°)是相机移动的“距离”。现在请记住,任何正负360°(2 * Math.PI)的角度实际上都会使您落在同一位置。因此,让我们尝试:

var distance = (to + 2 * Math.PI) - from; 
// === 1.083185307179586 (~62°)
Run Code Online (Sandbox Code Playgroud)

因此,如果您从位置旋转2.6-2.6 + 2 * Math.PI(或从149°到-149°+ 360°= 211°),您将获得一条具有较短路径的顺时针动画。

为了确保所有值都在允许的范围内,我们对onUpdate-function进行了一些更改以正确地进行环绕:

controls.setThetaPhi(
    tween.target.theta % Math.PI, 
    tween.target.phi % Math.PI, 
    tween.target.radius);
Run Code Online (Sandbox Code Playgroud)

您可能还希望currentPos在动画开始之前和发生下面的计算之前,用实际值更新值。

剩下要做的就是针对一般情况解决此问题,以便找出何时进行顺时针和逆时针旋转。要查看相反的方向是否更短,我们只需要查看距离是否大于180°:

if (Math.abs(to - from) > Math.PI) {
  if (to > 0) { // if to is positive we remove a full-circle, add it otherwise
    to = to - 2 * Math.PI;
  } else {
    to = to + 2 * Math.PI;
  }
}
Run Code Online (Sandbox Code Playgroud)