1

我正在尝试在我的游戏引擎中实现骨骼动画,但插值有问题。

每一帧,我想在帧之间进行插值,因此我有类似这样的代码:

// This is the soon-to-be-interpolated joint
Joint &finalJoint = finalSkeleton.joints[i];

// These are our existing joints
const Joint &joint0 = skeleton0.joints[i];
const Joint &joint1 = skeleton1.joints[i];

// Interpolate
finalJoint.position = glm::lerp(joint0.position, joint1.position, interpolate);
finalJoint.orientation = glm::mix(joint0.orientation, joint1.orientation, interpolate);

最后一行是问题所在。的方向finalJoint有时会是(-1.#IND, -1.#IND, -1.#IND, -1.#IND)

这些是一些示例值及其结果(请注意,interpolation0.4所有三种情况下):

  • joint0.orientation = (0.707107, 0.000242, 0.707107, 0.0)
    joint1.orientation = (0.707107, 0.000242, 0.707107, 0.0)
    finalJoint = (-1.#IND, -1.#IND, -1.#IND, -1.#IND) (不正确)

  • joint0.orientation = (-0.451596, -0.61858, -0.262811, -0.586814)
    joint1.orientation = (-0.451596, -0.61858, -0.262811, -0.586814)
    finalJoint = (-0.451596, -0.61858, -0.262811, -0.586814) (正确的)

  • joint0.orientation = (0.449636, 0.6195, 0.26294, 0.58729)
    joint1.orientation = (0.449636, 0.6195, 0.26294, 0.58729)
    finalJoint = (-1.#IND, -1.#IND, -1.#IND, -1.#IND) (不正确)

(是的,我知道它在相同的值之间进行插值。)

我还没有完全掌握四元数是如何工作的,但这对我来说似乎很奇怪。

4

1 回答 1

2

如果你看一下典型的 Slerp 方程,你会发现它Sin(Ω)在分数的分母中有一个,其中 Ω 是两个四元数之间的角度:即,Ω = acos(dot_product(q1,q1)); 现在,Sin(0)==0;当你除以零时,就会出现问题。正如维基百科文章中提到的,这是一个可移动的不连续性,但这需要一些额外的检查。

我只是拉下代码来看看。这是它的总和:

T angle = acos(dot(x, y));
return (glm::sin((T(1)-a)*angle)*x+glm::sin(a*angle)*y)/glm::sin(angle);

没有特别检查。当角度接近零时,这很容易出现数值问题。对于某些插值,它也可能在旋转球体周围花费很长的路要走。glm:::mix 有两个其他版本被注释掉,它们可以更好地处理这些问题。看起来当前版本及其伴随的问题已于2011 年 5 月提交到存储库

如果角度小于某个阈值量,我建议恢复到使用线性插值的版本。只需注释掉当前的并取消注释旧的。

于 2012-07-24T16:54:58.667 回答