我知道这有点像死神,抱歉,但我认为这可能对未来的读者有用。
除非我完全弄错了,否则实际上最好不要在归一化时保留角度以考虑舍入误差(在使用四元数表示旋转的情况下,尤其是在游戏中)。让我解释一下原因:
假设您有两个四元数(我将它们称为 Q1 和 Q2 从这里开始)应该表示旋转,但由于舍入误差而不是单位,并且您想要将它们相乘(我将结果称为Q3。我们也希望这是一个单位四元数)。假设t1 是一个变量,当与Q1 的每个分量相乘时,Q1 成为一个单位四元数(这意味着t1 是一个除以Q1 的欧几里得长度,但这确实是在这里不相关。t2 对Q2 做同样的事情。如果我们现在乘以四元数后归一化(即Q3 = (t1*Q1)(t2*Q2)),我们得到以下结果:
Q3.w = t1*Q1.w*t2*Q2.w - t1*Q1.x*t2*Q2.x - t1*Q1.y*t2*Q2.y - t1*Q1.z*t2*Q2.z
Q3.x = t1*Q1.w*t2*Q2.x + t1*Q1.x*t2*Q2.w + t1*Q1.y*t2*Q2.z - t1*Q1.z*t2*Q2.y
Q3.y = t1*Q1.w*t2*Q2.y - t1*Q1.x*t2*Q2.z + t1*Q1.y*t2*Q2.w + t1*Q1.z*t2*Q2.x
Q3.z = t1*Q1.w*t2*Q2.z + t1*Q1.x*t2*Q2.y - t1*Q1.y*t2*Q2.x + t1*Q1.z*t2*Q2.w
可以改写为
Q3.w = (t1*t2)*(Q1.w*Q2.w - Q1.x*Q2.x - Q1.y*Q2.y - Q1.z*Q2.z)
Q3.x = (t1*t2)*(Q1.w*Q2.x + Q1.x*Q2.w + Q1.y*Q2.z - Q1.z*Q2.y)
Q3.y = (t1*t2)*(Q1.w*Q2.y - Q1.x*Q2.z + Q1.y*Q2.w + Q1.z*Q2.x)
Q3.z = (t1*t2)*(Q1.w*Q2.z + Q1.x*Q2.y - Q1.y*Q2.x + Q1.z*Q2.w)
换句话说,Q3=(t1*Q1)(t2*Q2)=(t1*t2)(Q1*Q2)。如您所见,在乘法之后以这种方式归一化会产生与对两个输入预乘法进行归一化相同的四元数。这意味着我们只需在将旋转应用于向量/网格/点之前进行归一化,而不是在每次计算之后进行归一化,因为无论四元数偏离单位多远,它都会产生相同的结果。
现在让我们看一下相同的计算,但使用保留角度的方式制作四元数单元(t 变量现在只与非实数(又名 xyz)部分相乘时产生四元数单元):
Q3.w = Q1.w*Q2.w - t1*Q1.x*t2*Q2.x - t1*Q1.y*t2*Q2.y - t1*Q1.z*t2*Q2.z
Q3.x = Q1.w*Q2.x + t1*Q1.x*Q2.w + t1*Q1.y*t2*Q2.z - t1*Q1.z*t2*Q2.y
Q3.y = Q1.w*Q2.y - t1*Q1.x*t2*Q2.z + t1*Q1.y*Q2.w + t1*Q1.z*t2*Q2.x
Q3.z = Q1.w*Q2.z + t1*Q1.x*t2*Q2.y - t1*Q1.y*t2*Q2.x + t1*Q1.z*Q2.w
注意Q3 的非实部如何不再有一个公因子这意味着以这种方式规范化Q3post-operation 可能会导致与规范化@ 时得到的四元数不同。 987654339@和Q1以这种方式预运行。
对四元数进行归一化并不是一项廉价的操作,因此最好将非角度保持方式用于游戏之类的东西,因为您不需要经常使用它,尤其是在合成大量旋转时。我不确定其他四元数运算是否同样适用,但考虑到您可能会大量乘以四元数,至少在我看来,最好使用非角度保持方式。