四元数平滑旋转
四元数不仅可以描述旋转,还可以描述方向,即从初始(零)位置旋转。
我希望模拟从一个方向到另一个方向的平滑旋转。 我计算了起始方向startOrientation
和end orientation endOrientation
并且希望将中间方向描述为startOrientation*(1-argument) + endOrientation*argument
而argument
从0
变为1
。
猴子引擎更新function的代码如下:
@Override public void simpleUpdate(float tpf) { if( endOrientation != null ) { if( !started ) { started = true; } else { fraction += tpf * speed; argument = (float) ((1 - Math.cos(fraction * Math.PI)) / 2); orientation = startOrientation.mult(1-argument).add(endOrientation.mult(argument)); //orientation = startOrientation.mult(1-fraction).add(endOrientation.mult(fraction)); log.debug("tpf = {}, fraction = {}, argument = {}", tpf, fraction, argument); //log.debug("orientation = {}", orientation); rootNode.setLocalRotation(orientation); if( fraction >= 1 ) { rootNode.setLocalRotation(endOrientation); log.debug("Stopped rotating"); startOrientation = endOrientation = null; fraction = 0; started = false; } } } }
预计余弦公式将在开始时模拟平滑加速并在结束时减速。
代码工作但不是预期的:平滑旋转开始并在fraction
和argument
值达到1
之前很长时间完成,我不明白,为什么。
为什么orientation
值到达endOrientation
如此之快?
您已声明在您的情况下正在修改startOrientation
。 然而; 以下仍然是正确的
四元数之间的插值
为此目的,方法slerp
包含在Quaternion类中: 在两个旋转之间进行插值。
假设我们有两个四元数startOrientation
和endOrientation
,我们想要它们之间的点interpolation
,然后我们使用以下代码进行插值:
float interpolation=0.2f; Quaternion result=new Quaternion(); result.slerp(startOrientation, endOrientation, interpolation);
为什么你的方法可能很危险
四元数在内部有些复杂,并且遵循一些不同的数学规则来表示向量。 您已在四元数上调用multiply(float scalar)
方法。 在内部,这看起来像这样
public QuaternionD mult(float scalar) { return new QuaternionD(scalar * x, scalar * y, scalar * z, scalar * w); }
所以它只是对所有元素进行简单的乘法运算。 这显然不会返回scalar
乘以大小的旋转 。 实际上,这样的四元数不再代表有效的旋转,因为它不再是单位四元数。 如果你在这个quaterion上调用normalise
,它会立即撤消缩放。 我敢肯定Quaternion#multiply(float scalar)
有一些用处,但我还没找到它们。
“添加”四元数也不会将它们组合在一起。 事实上,你将它们相乘。 因此,结合q1然后q2然后q3将实现如下:
Quaternion q0 = q1.mult(q2).mult(q3);
备忘单对此非常有用
Formula vs slerp比较
在您的情况下,插值公式几乎但不太正确。 这显示了使用两种方法在2个四元数之间插值的偏航图