【问题标题】:Slerp interpolation of angle results in -nan(ind)角度的 Slerp 插值导致 -nan(ind)
【发布时间】:2016-12-12 14:27:39
【问题描述】:

我正在尝试插入一个 2D 角度,它在 99.9% 的时间里都有效。出于某种原因,我得到 -nan(ind) 的某些值,例如:

lastAngle = -0.0613451
currentAngle = -0.061421
alpha = 0.218813

这是代码:

inline float slerpRotation(const float& angle1, const float& angle2, const float& alpha)
{
    auto v1 = b2Vec2{std::cos(angle1), std::sin(angle1)};
    auto v2 = b2Vec2{std::cos(angle2), std::sin(angle2)};
    auto v = this->slerp(v1, v2, alpha);
    return std::atan2(v.y, v.x);
}

inline b2Vec2 slerp(const b2Vec2& v1, const b2Vec2& v2, const float& alpha)
{
    auto cosAngle = v1.x * v2.x + v1.y * v2.y;
    auto angle = std::acos(cosAngle);
    auto angleAlpha = angle * alpha;
    auto v3 = (v2 - (cosAngle * v1)).Normalize();
    auto x = v1.x * std::cos(angleAlpha) + v3 * std::sin(angleAlpha);
    auto y = v1.y * std::cos(angleAlpha) + v3 * std::sin(angleAlpha);
    return b2Vec2{x, y};
}

所有这些示例都会导致 inf num:

slerpRotation(-0.0613451f, -0.061421f, 0.218813f);
slerpRotation(-1.63139f, -1.63139f, 0.723703f);
slerpRotation(-0.0614404f, -0.0614034f, 0.199831f);
slerpRotation(0.0194162f, 0.0194164f, 0.259074f);

我已经尝试解决这个问题一段时间了,但不知道是什么原因导致这个问题,你们碰巧知道如何解决这个问题吗?

【问题讨论】:

  • b2Vec2::Normalize 对 {0,0} 向量有什么作用?
  • @1201ProgramAlarm 它返回 0
  • 嘿,这条线似乎是问题所在:auto angle = std::acos(cosAngle);取回一个 nan 值
  • 问题是 cosAngle 似乎一直为 1,即使我将它打印到控制台但检查它是否大于 1.0f 时它返回 true,这使得 acos 返回一个 nan。所以为了解决这个问题,如果它更大,我必须将它设置为 1.0f,如果它更小,我必须将它设置为 -1.0f。
  • 在最后两行计算中,您肯定想使用v3 的坐标,而不是导致标量和向量之和的完整向量。或者将它们组合成一个向量组合return cos(angleAlpha)*v1+sin(angleAlpha)*v3;

标签: c++ math interpolation


【解决方案1】:

你到底是在计算

angle1+alpha*(angle2-angle1)

或者如果你想排除一些边缘情况,

angle1+alpha*reduce2pi(angle2-angle1)

在哪里

 reduce2pi(phi) = fmod( 3*pi + fmod(phi, 2*pi), 2*pi)-pi

请注意,这些公式完全没有奇异性,因为没有除法。角度和它们在单位圆上的点之间不需要来回切换。

在代码中,那将是

inline float slerpRotation(const float& angle1, const float& angle2, const float& alpha)
{
    auto angleDiff = angle2-angle1;
    angleDiff = std::fmod(angleDiff, 2*std::M_PI);
    angleDiff = std::fmod(angleDiff + 3*std::M_PI, 2*std::M_PI)-std::M_PI;
    return angle1+alpha*angleDiff;
}

(12/13/2016) 结合几个 cmets: 如果你坚持完全使用这个接口结构,那么你可以得到一个无奇点的方法如下:

inline b2Vec2 slerp(const b2Vec2& v1, const b2Vec2& v2, const float& alpha)
{
    auto angle = std::atan2(v1.x*v2.y - v1.y*v2.x, v1.x*v2.x + v1.y*v2.y);
    auto angleAlpha = angle * alpha;
    auto v3=b2Vec2{-v1.y, v1.x}; // rotation by 90°
    return std::cos(angleAlpha)*v1 + std::sin(angleAlpha)*v3;
}

【讨论】:

  • 对不起,我不明白。我错了哪一部分?
  • 您正在执行大量复杂的几何计算来获得角度的简单线性插值的结果。对于实际错误,首先检查cosAlpha 永远不会大于1.0。或者使用atan2函数从cosAlphasinAlpha=v1.x*v2.y-v1.y*v2.x获取角度。
  • 感谢更新伙伴。看起来这个与我的示例完全一样,代码更少。当你说坚持使用这个接口时,你有没有更好的解决方法来插值角度?
  • 是的,我的第二个公式应该给出完全相同的结果。
  • 该死的,这真是个好伙伴,非常感谢。也许我应该发布我的 Hermite 插值,这样你就可以看看它,也许也可以优化它:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-14
  • 1970-01-01
  • 2021-06-23
  • 1970-01-01
  • 2012-11-07
相关资源
最近更新 更多