四元数平滑旋转

四元数不仅可以描述旋转,还可以描述方向,即从初始(零)位置旋转。

我希望模拟从一个方向到另一个方向的平滑旋转。 我计算了起始方向startOrientation和end orientation endOrientation并且希望将中间方向描述为startOrientation*(1-argument) + endOrientation*argumentargument0变为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; } } } } 

预计余弦公式将在开始时模拟平滑加速并在结束时减速。

代码工作但不是预期的:平滑旋转开始并在fractionargument值达到1之前很长时间完成,我不明白,为什么。

为什么orientation值到达endOrientation如此之快?

您已声明在您的情况下正在修改startOrientation 。 然而; 以下仍然是正确的

四元数之间的插值

为此目的,方法slerp包含在Quaternion类中: 在两个旋转之间进行插值。

假设我们有两个四元数startOrientationendOrientation ,我们想要它们之间的点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个四元数之间插值的偏航图

在此处输入图像描述