【问题标题】:Logical issue in interpolation between two angles?两个角度之间插值的逻辑问题?
【发布时间】:2017-09-04 22:59:41
【问题描述】:

我正在尝试做的是制作一个旋转一定度数的二维对象,例如 A 度,旋转以面对鼠标,其从该对象的方向是 B 度。这是在 openGl 中。

我已经能够使用 glRotatef 函数立即旋转对象,但我不想做的是能够在一定秒数内控制旋转。

我使用了两种增加或减少旋转的方法:

    void GameObject::increaseRot(int millis) {
        rotation += getRotDeg(millis);
    }

    void GameObject::decreaseRot(int millis) {
        rotation -= getRotDeg(millis);
    }

    double GameObject::getRotDeg(int millis) {
        double rot = 360 / this->rotSpeed;
        rot = rot * millis / 1000.0;
        return rot;
    }

Millis 来自一个正常工作的计时器,因此我可以让一个物体以每 rotSpeed 秒 360 度的速度旋转。

编辑:我在互联网上找到了一个解决方案,似乎大部分都有效。用我自己的代码使用那个解决方案的公式,代码是

    shortest_angle=((((end - start) % 360) + 540) % 360) - 180;

    /*  check which way to rotate
     *  this part of the code appears to work fine, all it does is
     *    rotate a certain number of degrees, it's my code that I've been
     *   using the whole time
     */
    if(rotateA < 0)
        game.getPlayer().decreaseRot(deltaT);
    else if(rotateA > 0)
        game.getPlayer().increaseRot(deltaT);

但是,在某些值下,代码仍然需要更长的路径,我不知道为什么。 . .

我注意到这种情况的价值是:

    45 trying for 135
    225 trying for 315
    315 trying for 45

当然,这些是近似值,这些区域周围的任何值都会搞砸。我一直认为这与限制 90、180、270 和 360/0 有关,但我无法弄清楚实际问题是什么。

【问题讨论】:

  • 我无法引导您完成它,但如果您还没有找到它,您可能想查看slerp - 球面线性插值。
  • 我考虑过看 slerp,但像这样的东西,以及四元数,现在对我来说有点难以理解。我尝试在统一上使用它们,但成功有限,但我在网上找到的解决方案(上图)非常接近正常工作,我希望我能以某种方式修复它

标签: c++ rotation interpolation angle


【解决方案1】:

可以在不使用任何条件分支的情况下插入角度,如下所示:

template <typename T>
T lerp_fixed_degrees(T a, T b, T x) {
    T d = wrap_degrees(b - a);
    return wrap_degrees(a + std::copysign(std::fmin(std::abs(x), std::abs(d)), d));
}
  • 使用wrap_degrees(b - a)[-180, 180) 范围内计算ab 之间的差异。函数wrap_degrees 计算[-180, 180) 范围内的等效角度(例如wrap_degrees(190) == -170wrap_degrees(-190) == 170)。
  • 使用std::fmin(std::abs(x), std::abs(d)) 确定步长和差值中的较小者。这可以防止我们超出目标(如果您知道x 是肯定的,您可以使用x 而不是std::abs(x),但我在这里选择了稳健性)。
  • 使用std::copysign 以正确的方向进行插值。
  • 将结果添加到起始角度,并返回[-180, 180)范围内的等效角度。

函数wrap_degrees是这样实现的:

template <typename T>
T wrap_degrees(T x) {
    return fmod_floor(x + T(180), T(360)) - T(180);
}
  • 180 添加到输入值。
  • 使用fmod_floor 包装[0, 360) 范围内的值。 fmod_floor 函数类似于 std::fmod,除了它产生 地板除法 的余数,它始终与 divisor 具有相同的符号。这是我们想要的行为(例如fmod(-10, 360) == -10fmod_floor(-10, 360) == 350)。
  • 通过减去180 将包装后的值移回[-180, 180) 范围(这与之前添加的180 相平衡)。

fmod_floor 函数是这样实现的:

template <typename T>
T fmod_floor(T a, T n) {
    return std::fmod(std::fmod(a, n) + n, n);
}
  • 确保值在(-n, n)std::fmod 的范围内。
  • 将值移到(0, 2n)范围内。
  • 再次确保值在[0, n)std::fmod 的范围内。

这是一个快速演示:

double a = 90.;
double prev;
do {
    std::cout << a << "\n";
    prev = a;
    a = lerp_fixed_degrees(a, -100., 45.);
} while (a != prev);

输出:

90
135
-180
-135
-100

您还可以使用从01 的值执行简单的线性插值,如下所示:

template <typename T>
T lerp_degrees(T a, T b, T t) {
    return wrap_degrees(a + wrap_degrees(b - a) * t);
}

我认为这个是不言自明的。

【讨论】:

    【解决方案2】:

    我已经找到并制定了这个问题的解决方案,供其他可能遇到同样问题的人使用。

    使用来自“user151496”的代码,Rotation Interpolation

          shortest_angle=((((end - start) % 360) + 540) % 360) - 180;
          return shortest_angle * amount;
    

    您可以更改它以应用您自己的自定义旋转速度,就像我使用诸如

    之类的功能一样
        if(rotateA < 0 && rotateA >= -180)
            game.getPlayer().decreaseRot(deltaT);
        else if (rotateA < 0 && rotateA <= -180)    //for the crossing of the boundary
            game.getPlayer().increaseRot(deltaT);
        else if(rotateA > 0 && rotateA <= 180)
            game.getPlayer().increaseRot(deltaT);
        else if(rotateA > 0 && rotateA >= 180)
            game.getPlayer().decreaseRot(deltaT);   //for the crossing of the boundary
    

    这个 ^ 所做的就是检查 shortest_angle 是正的还是负的 (rotateA)。然后它相应地顺时针旋转(decreaseRot)或逆时针旋转(increaseRot)。 但是,请注意两条额外的线,检查 -180 度和 180 度条件。这些是必要的——我发现很难用数字来解释,但这与穿越 0/360 度线有关。

    如果你没有这些额外的条件,只要你必须越过这样的边界,旋转就会以更大的角度旋转。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-04
      • 2017-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多