另一个 Blog 地址:http://insaneguy.me
原文链接:http://insaneguy.me/2015/03/25/rotation_matrix_and_quaternions/
在计算机图形学的学习中,几何变换(Transformations)是一块重要的内容,我们使用齐次坐标(Homogeneous coordinates)描述点和向量,使用变换矩阵描述平移、旋转等变换。
而在平移、旋转、缩放这几种变换中,又以旋转的情况最为复杂。实际上,计算机图形学中三维空间的旋转不仅仅有旋转矩阵一种表达形式,欧拉角(Euler angles)和四元数(Quaternions)也是常用的方法。
三维空间中的一个点 P ,我们用齐次坐标表示:
我们首先考虑分别绕 X 轴、Y 轴、Z 轴旋转一定角度的情况。
设 P 绕 X 轴、Y 轴、Z 轴的旋转角度分别为 α 、 β 和 θ 。
我们使用右手坐标系,旋转角度的正向由右手定则确定。
点绕坐标轴旋转可以考虑点在相应坐标平面上投影的旋转。比如绕 Y 轴旋转,那么考虑点 P 在 X-Z 平面上的投影的旋转,如下图所示:
假设点 P 在 X-Z 平面上的投影点与坐标原点连成的向量长度为 L ,那么根据简单的平面几何知识,我们可以得到:
用齐次坐标表示绕Y轴的旋转为:
同理可分别得到绕X轴与绕Y轴的情况。
绕X轴旋转:
绕Z轴旋转:
我们可以将绕X、Y和Z坐标轴的旋转矩阵分别记为 Rx(α),Ry(β),Rz(θ) ,则有:
旋转矩阵可以通过其他旋转矩阵复合得到(矩阵乘法)。
上面讨论了绕三条坐标轴旋转的旋转矩阵,旋转矩阵的一般形式(这里没有用齐次坐标)为:
物体在三维空间中的旋转可以从坐标系的旋转来考虑(三维空间中坐标轴,即三维线性空间中基的变换)。那么矩阵 C 的三个列向量实际对应着原坐标系三个坐标轴方向的单位向量在旋转后的新坐标系下的坐标。
我们知道直角坐标系的三个坐标轴方向的单位向量实际上是一组标准正交基,于是矩阵 C 是一个正交矩阵。所以旋转矩阵表面上看起来依赖于 9 个参数,实际上只有三个是独立的。
为了更直接地指出这三个独立参数,欧拉(Euler)证明了如下事实:任何一个旋转都可以由连续施行的三次绕轴旋转来实现,这三次绕轴旋转的旋转角就是三个独立参数,称为欧拉角。
根据绕轴旋转的顺序不同,欧拉角的表示也不同。常见的欧拉角表示有 Yaw-Pitch-Roll (Y-X-Z顺序),通过下面的图片可以形象地进行理解。
偏航(Yaw):
仰俯(Pitch):
侧偏(Roll):
设 Yaw 、Pitch 、Roll 三个角度分别为 θ,φ,ψ ,那么利用欧拉角进行旋转对应的旋转变换矩阵为:
实际上 Yaw 、Pitch 、Roll 的旋转就分别对应着前面我们给出的旋转矩阵 Rx(θ),Ry(φ),Rz(ψ) ,上面的矩阵就是这三个矩阵的复合。
欧拉角的好处是简单、容易理解,但使用它作为旋转的工具有严重的缺陷–万向节死锁(Gimbal Lock)。
万向节死锁是指物体的两个旋转轴指向同一个方向。实际上,当两个旋转轴平行时,我们就说万向节锁现象发生了,换句话说,绕一个轴旋转可能会覆盖住另一个轴的旋转,从而失去一维自由度。
例如,三维空间中有一个平行于 X 轴的向量,我们将它绕 Y 轴旋转直到它平行于 Z 轴,这时,我们会发现任何绕 Z 轴的旋转都改变不了该向量的方向,即出现了万向节死锁。
由于万向节死锁的存在,使用欧拉角也无法很好地处理旋转的插值(以实现“平滑”旋转)。
从前面的讨论我们发现三角度系统(three-angle system)无法很好地处理旋转的插值。下面介绍四元数(Quaternions)以及如何利用四元数描述旋转。
四元数是由数学家 William Rowan Hamilton 于1843年所发明的数学概念,是复数的推广,可以说是“三维的复数”,形式为 w+x i+y j+z k ,其中 i,j,k 的关系如下:
假设有两个四元数:
四元数的加法定义如下:
四元数的乘法定义,利用简单的分配律定义如下:
为了方便表示,我们将四元数记为:
注意,这里四元数的表示形式和“齐次坐标”长得一样,但是它们之间没什么关系!
四元数常常用来表示旋转,很多人将其理解为“w表示旋转角度,v表示旋转轴”,也是错误的!
正确的理解应为:“w与旋转角度有关,v与旋转轴有关”。
四元数的模(norm)定义为
模为1的四元数称为单位四元数(Unit quaternions)。
四元数的共轭(conjugate)定义为:
四元数的倒数定义为:
这里直接给出结论:如果把单位四元数表示为:
的形式,那么该单位四元数可以表示绕轴 n⃗
进行 θ 角的旋转。
该单位四元数对应的旋转矩阵为
这里的推导用到了轴-角旋转表示中的Rodrigues’ rotation formula,具体证明这里不展开了,有兴趣的可以查阅相关资料。
我们发现用四元数描述旋转需要的存储空间很小,更为关键的是可以使用被称为球面线性插值(Slerp Algorithm)的方法对四元数进行插值运算,从而解决了平滑旋转的插值问题。
在 OpenGL 或者 DirectX 中我们通常使用模型视图矩阵来进行几何变换,当我们希望实现光滑旋转、对旋转进行插值时,就可以利用四元数这一工具。处理过程为: