问题
如果给定一定的时间增量,如何在两个给定角度之间进行插值,以便当算法以不同的频率运行时,来自旋转A或旋转B的模拟运动将花费相似的时间量(没有固定的时间步长依赖性)。
潜在解决方案
我一直在使用以下C#代码在两点之间进行这种插值。它解决了这种情况的差异:
Vector3 SmoothLerpVector3(Vector3 x0, Vector3 y0, Vector3 yt, double t, float k)
{
// x0 = current position
// y0 = last target position
// yt = current target position
// t = time delta between last and current target positions
// k = damping
Vector3 value = x0;
if (t > 0)
{
Vector3 f = x0 - y0 + (yt - y0) / (k * (float)t);
value = yt - (yt - y0) / (k * (float)t) + f * (float)Math.Exp(-k * t);
}
return value;
}
通过将Z
的{{1}}坐标设置为0,此代码可用于2D坐标。
“最后”和“当前”位置是因为目标可以在插值期间移动。如果不考虑这一因素会导致中等速度下的运动抖动。
我没有编写此代码,它似乎工作。我在修改角度方面遇到了麻烦,因为例如,角度350°和10°之间的插值将采用“长”方式而不是朝向20°角度差的方向。
我已经研究了四元数slerp但是却找不到考虑时间增量的实现。我曾经想过但也无法实现的东西是在两个角度之间插值两次,但第二次在每个角度上相位差为180°并输出两个中较小的一个乘以-1。
感谢任何帮助或指导!
答案 0 :(得分:2)
我以前做过这种方式的方法是测试两个角度之间的差异是否大于180°,如果是,则将360°加到较小的值,然后使用这两个角度值进行测量。因此,在您的示例中,不是在350°和10°之间进行插值,而是在350°和370°之间插值。如果需要显示结果,您可以随时对结果进行模数设计。
答案 1 :(得分:1)
使用Slerp()
并确保用其中一个辅助函数包装-π和π之间的角度
/// <summary>
/// Wraps angle between -π and π
/// </summary>
/// <param name="angle">The angle</param>
/// <returns>A bounded angle value</returns>
public static double WrapBetweenPI(this double angle)
{
return angle+(2*Math.PI)*Math.Floor((Math.PI-angle)/(2*Math.PI));
}
/// <summary>
/// Wraps angle between -180 and 180
/// </summary>
/// <param name="angle">The angle</param>
/// <returns>A bounded angle value</returns>
public static double WrapBetween180(this double angle)
{
return angle+360*Math.Floor((180-angle)/360);
}
的相关帖子
答案 2 :(得分:1)
解决方案
我有一些使用四元数的工作代码。为了考虑时间步骤(以消除对固定步骤更新的依赖),使用amount = 1 - Math.Exp(-k * t)
计算slerp / lerp的量。恒定k
效果阻尼(1 - 非常缓慢,20 - 几乎立即对目标进行捕捉)。
我决定不尝试将其用于3D,因为我正在开发2D游戏。
public static float SlerpAngle(
float currentAngle, float targetAngle, double t, float k)
{
// No time has passed, keep angle at current
if (t == 0)
return currentAngle;
// Avoid unexpected large angles
currentAngle = MathHelper.WrapAngle(currentAngle);
targetAngle = MathHelper.WrapAngle(targetAngle);
// Make sure the shortest path between
// current -> target doesn't overflow from
// -pi -> pi range otherwise the 'long
// way round' will be calculated
float difference = Math.Abs(currentAngle - targetAngle);
if (difference > MathHelper.Pi)
{
if (currentAngle > targetAngle)
{
targetAngle += MathHelper.TwoPi;
}
else
{
currentAngle += MathHelper.TwoPi;
}
}
// Quaternion.Slerp was outputing a close-to-0 value
// when target was in the range (-pi, 0). Ensuring
// positivity, halfing difference between current
// and target then doubling result before output
// solves this.
currentAngle += MathHelper.TwoPi;
targetAngle += MathHelper.TwoPi;
currentAngle /= 2;
targetAngle /= 2;
// Calculate spherical interpolation
Quaternion qCurrent = Quaternion.CreateFromAxisAngle(
Vector3.UnitZ, currentAngle);
Quaternion qTarget = Quaternion.CreateFromAxisAngle(
Vector3.UnitZ, targetAngle);
Quaternion qResult = Quaternion.Slerp(
qCurrent, qTarget, (float)(1 - Math.Exp(-k * t)));
// Double value as above
float value = 2 * 2 * (float)Math.Acos(qResult.W);
return value;
}
旋转速度在5Hz上是一致的 - > 1000Hz范围。我认为这些都是合适的极端。没有任何真正的理由让它高于60Hz。