我正在尝试将骨骼动画应用到我的游戏引擎中,我对插值有问题。
每一帧,我想在帧之间插值,因此我有类似的代码:
// 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)
。
这些是一些示例值及其结果(请注意,interpolation
在所有三种情况下均为0.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)
(不正确)
(是的,我知道它在相同的值之间进行插值。)
我还没有准确掌握四元数是如何工作的,但这对我来说似乎很奇怪。
答案 0 :(得分:2)
如果查看typical Slerp equation,您会看到它在分数的分母中有一个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月,当前版本及其附带的问题似乎是committed to the repository。
如果角度小于某个阈值量,我建议恢复使用线性插值的版本。只需注释掉当前内容并取消旧内容即可。