如何在N维中固定万向节锁

2020-08-29 14:05:28

万向节锁定是三维旋转系统中常见的问题。传统观点认为,您应该将旋转表示为四元数,以避免此问题(正如许多流行的游戏引擎文档和无数YouTube视频中所述)。但是我找不到任何很好的解释来解释四元数到底是如何解决这个问题的。

这对我来说很重要,因为我正在实现一个4D几何查看器,我花了很多时间试图弄清楚如何将四元数推广到更高的维度,结果却发现你仍然可以用四元数来获得万向节锁!

答案是,这不是关于你用什么来表示你的旋转(四元数、矩阵或转子),而是关于你如何应用这些旋转。

万向节锁定是旋转系统中自由度的丧失。这将始终发生在使用Euler角度的任何系统中,在该系统中,您可以通过应用一组固定的连续旋转来旋转对象。

在3D中,当3个旋转轴中的一个变得与任何其他轴平行时(或者换句话说,当“万向架”对齐时),就会发生这种情况。下面的动画显示了这一点。它从面向相机的+Z侧开始。第一个90度旋转会将顶部立方体置于万向架锁定状态:通过蓝色和红色万向架旋转现在会产生相同的旋转。在此状态下,不可能围绕立方体的局部X轴旋转(指向摄影机)。这是我们失去的自由度,由底部视图中的红色万向节表示。

在此演示中亲自尝试会更容易看到这一点(需要键盘):

顶部的两个立方体使用Euler角,因此总有一种方法可以进入该万向节锁定状态。你所要做的就是把蓝色和红色的万向节排成一排。然后将行为与最下面的两个立方体进行比较,这两个立方体显示正确的旋转应该是什么。

RotationAround X=Matrix3.from Axisangle(angle1,X轴);rotationAround Y=Matrix3.from Axisangle(angle2,yAxis);rotationAround Z=Matrix3.from Axisangle(angle3,Z轴);cubeRotation=rotationAround X*rotationAround Y*rotationAround Z;

这是一个使用旋转矩阵表示和应用旋转的Euler角度系统。如果我们把矩阵换成四元数,我们会得到完全相同的行为。

RotationAround X=Quaternion.from Axisangle(angle1,X轴);rotationAround Y=Quaternion.from Axisangle(angle2,yAxis);rotationAround Z=Quaternion.from Axisangle(angle3,Z轴);cubeRotation=rotationAround X*rotationAround Y*rotationAround Z;

这是一种使用四元数表示和应用旋转的欧拉角系统,因此也存在万向节锁定问题。

在3D中,我们将不使用我们相乘以获得最终旋转的3个固定角度,而是:

构造一个四元数,它描述围绕我们想要的任何轴的旋转,以及旋转的角度。

获取结果并将其覆盖回对象当前存储的旋转中。

上面演示中的右下角多维数据集就是这样实现的。伪代码如下所示:

//我们希望在这帧中旋转0.05弧度。RotationThisFrame=Quaternion.from Axisangle(0.05,任意轴);cubeRotation=cubeRotation*rotationThisFrame;

这项技术并不是四元数所特有的。下面是左下角多维数据集如何使用矩阵实现这一点:

“任意轴”是立方体的局部X/Y/Z轴,具体取决于按下的键。如果我们想要相对于固定的全局框架旋转,则可以改为全局X/Y/Z轴。

一种表示旋转的方式。到目前为止,在3D中,我们已经使用了3x3矩阵和四元数。

附注:谈论“平面内旋转”比“绕轴旋转”更具概括性。在2D中,只有一个旋转平面,即XY平面。在3D中有3个旋转平面。在4D中有6个平面。在N-D中有(N选择2)个平面。

我最终选择了4x4矩阵来表示我的旋转,因为GPU支持4x4矩阵运算,所以我可以旋转顶点着色器中的几何体,就像在3D中一样。

现在,棘手的部分是实现Matrix4.fromAxisangle函数。为6个旋转平面中的每个平面创建4x4旋转矩阵并合成它们要容易得多。因此,这可以细分为:

//检查按下了哪些键。在此帧中,只按下了用于在XZ平面中旋转的键,因此rXZ 4x4矩阵获得了非零角度。RXY=Matrix4.from XYRotation(0);rXZ=Matrix4.from XZRotation(0.05);rYZ=Matrix4.from YZRotation(0);rXW=Matrix4.from XWRotation(0);ryw=Matrix4.fromYWRotation(0);rZW=Matrix4.from ZWRotation(0);rotationThisFrame=rXY*rX。

这看起来可疑地像欧拉角,但它不是。这永远不会是万向节锁。无论立方体最终的方向是什么,我们总是可以在6个平面中的任何一个平面上将其旋转0.05弧度。

//检查按下了哪些键。在此帧中,只按下了在XZ平面上旋转的键,因此我们在角度2上添加了0.05。Angle2+=0.05;rXY=Matrix4.from XYRotation(Angle1);rXZ=Matrix4.from XZRotation(Angle2);rYZ=Matrix4.from YZRotation(Angle3);rXW=Matrix4.from XWRotation(Angle4);ryw=Matrix4.from YWRotation(Angle5);rZW=Matrix4.from ZWRotation(Angle5)。

在4D之后,您可以继续使用NxN旋转矩阵,但由于GPU不支持,我将使用几何代数中的旋转器来简化实现,这就是如何将四元数推广到更高维。