在三维空间中旋转向量

78

我正在使用OpenGL ES制作一个Android项目,该项目利用加速度计计算特定轴向的变化量,我的目标是旋转类似宇宙飞船对象的运动向量。问题在于我无法理解旋转矩阵背后的数学原理。默认运动向量为0,1,0,表示+y方向,因此对象一开始朝上。我正在尝试旋转其运动向量,以便将对象移动到指向的位置。我可以在手机中获取旋转变化量。X轴:rotate[0],Y轴:rotate[1],Z轴:rotate[2]。如何使用旋转矩阵旋转我的运动向量?


这个关于变换的HTML5演示(http://github.com/legends2k/2d-transforms-101/)通过动画更详细地解释了变换,还解释了如何处理复杂的变换(多个基本变换的连接),以及有关变换坐标系的内容。 - legends2k
2个回答

243
如果您想旋转一个向量,您应该构造一个被称为旋转矩阵的东西。

二维旋转

假设您想将一个向量或点旋转 θ,那么三角函数指出新坐标为

    x' = x cos θ − y sin θ
    y' = x sin θ + y cos θ

为了演示这一点,让我们以基准轴 X 和 Y 为例;当我们将 X 轴逆时针旋转 90° 时,应该得到将 X 轴转换为 Y 轴的结果。考虑:
    Unit vector along X axis = <1, 0>
    x' = 1 cos 90 − 0 sin 90 = 0
    y' = 1 sin 90 + 0 cos 90 = 1
    New coordinates of the vector, <x', y'> = <0, 1>  ⟹  Y-axis

当你理解这一点,创建一个矩阵来实现这个变换就变得简单了。矩阵只是一种数学工具,可以以一种舒适、通用的方式执行各种变换,如旋转缩放平移 (移动),并使用一种共同的方法在单个步骤中组合和执行。从线性代数角度来看,在二维空间中旋转一个点或向量时,需要构建以下矩阵:
    |cos θ   −sin θ| |x| = |x cos θ − y sin θ| = |x'|
    |sin θ    cos θ| |y|   |x sin θ + y cos θ|   |y'|

三维旋转

在三维中,我们需要考虑第三个轴。在二维中,将向量围绕原点(一个点)旋转意味着在三维中围绕 Z 轴(一条线)旋转;因为我们正在围绕 Z 轴旋转,所以它的坐标应该保持不变,即 0°(在三维中旋转发生在 XY 平面上)。在三维中围绕 Z 轴旋转应该是这样的:

    |cos θ   −sin θ   0| |x|   |x cos θ − y sin θ|   |x'|
    |sin θ    cos θ   0| |y| = |x sin θ + y cos θ| = |y'|
    |  0       0      1| |z|   |        z        |   |z'|

绕Y轴旋转

    | cos θ    0   sin θ| |x|   | x cos θ + z sin θ|   |x'|
    |   0      1       0| |y| = |         y        | = |y'|
    |−sin θ    0   cos θ| |z|   |−x sin θ + z cos θ|   |z'|

绕X轴旋转

    |1     0           0| |x|   |        x        |   |x'|
    |0   cos θ    −sin θ| |y| = |y cos θ − z sin θ| = |y'|
    |0   sin θ     cos θ| |z|   |y sin θ + z cos θ|   |z'|

注1:旋转轴在矩阵中没有正弦或余弦元素。

注2:这种旋转方法遵循欧拉角旋转系统,该系统易于教授和理解。对于2D和简单的3D情况,这种方法完全有效;但当需要同时围绕三个轴进行旋转时,欧拉角可能不足以应对由万向锁引起的固有缺陷。在这种情况下,人们会使用四元数,这比欧拉角更先进,但如果正确使用,则不会受到万向锁的影响。

希望这澄清了基本的旋转知识。

旋转和革命

前述的矩阵将一个距离原点距离为r = √(x² + y²)的物体沿半径为r的圆旋转;可以查看 极坐标 了解原因。这种旋转是相对于世界空间原点即 公转绕轨道运动。通常,我们需要围绕物体自身的框架/支点而不是世界的旋转,即本地原点。这也可以看作是一个特殊情况,其中 r = 0。由于并非所有对象都在世界原点处,因此仅使用这些矩阵进行旋转将无法得到围绕对象自身框架旋转的期望结果。相反,您应该:
  1. 将物体平移到世界原点
    • 物体的原点将与世界原点对齐,使r = 0
  2. 使用一个或多个前面提到的旋转矩阵进行旋转
  3. 将物体再次平移到其以前(原始)位置。
变换的应用顺序 很重要

变换的合成/连结

由于矩阵不仅可以表示旋转,还可以表示平移、缩放等操作,因此“旋转和革命”部分的所有三个操作均可写为矩阵。当它们相乘时,我们得到一个单独的矩阵,按顺序执行所有三个操作。与对象的点相乘后,所得到的矩阵将使对象围绕其轴(在其本地空间中)旋转。将多个变换组合在一起称为“连接”或“组合”。
我敦促您先阅读有关线性和仿射变换及其组合的内容,以便在代码中进行多个变换。如果不了解其基本数学知识,则调试变换将是一场噩梦。我发现这个讲座视频是一个非常好的资源。另一个资源是这个变换教程,旨在直观地解释,并通过动画来说明其思想(注意:由我编写!)。

围绕任意向量旋转

如果您只需要绕基本轴(X、Y或Z)旋转,那么上述矩阵的乘积就足够了,如所发布问题中所述。然而,在许多情况下,您可能希望绕任意轴/向量旋转。Rodrigues' formula(又称轴角公式)是一个常用的解决方案。但是,仅当您只使用向量和矩阵时才会使用它。如果您正在使用Quaternions,则只需构建具有所需向量和角度的四元数即可。四元数是存储和操作3D旋转的优选替代方法;它紧凑且速度快,例如在轴角表示中连接两个旋转非常昂贵,在矩阵中适中,但在四元数中则很便宜。通常,所有旋转操作都使用四元数完成,并在上传到渲染管道时作为最后一步转换为矩阵。请参见Understanding Quaternions以了解四元数的入门知识。

9
请记住,四元数并不能解决万向节锁问题,并且与其无关。四元数和矩阵只是用于实际旋转/定向的编码方式。造成万向节锁的原因不是编码方式,而是顺序旋转。使用四元数表示三个欧拉角将导致万向节锁。 - concept3d
4
Gimbal lock是因为将旋转变换表示为绕不同轴的多个组成部分旋转(也称欧拉角)而引起的。这种情况允许将一个轴旋转到另一个轴上,导致失去一个自由度和可怕的万向节锁定。需要用_1_个且仅一个四元数来表示3D旋转,然后用它进行插值和合成,有效避免了万向节锁定问题。更多详细信息请参考Quaterions and their Applications to Rotation in 3D Space - legends2k
4
这正是我想表达的。最终四元数通常会转换为矩阵(用于渲染器)。这意味着再次编码并不是问题,而实际上是每次旋转后失去了一个自由度。 - concept3d
2
实际上,原始措辞来自OpenGL论坛的一位大师成员,功劳归于 :) 感谢您在这里提出此问题。 - legends2k
4
+1 感谢您在接受我的批评时保持积极的态度(很多人并不会以开放的心态接受批评,尤其是在我缺乏信誉时)。 - concept3d
显示剩余13条评论

0
我推断你向量的X分量应该是M*cos(o)*cos(t)+x,Y分量应该是M*cos(t)*sin(o)+y,Z分量应该是M*cos(o)*sin(t)+z,其中M是向量的大小,o是垂直平面旋转的角度,t是水平平面旋转的角度,x是旋转中心的x值,y是旋转中心的y值,z是旋转中心的z值。请告诉我这是否适用于你。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接