(一)齐次坐标:
“齐次坐标表示是计算机图形学的重要手段之一,它既能够用来明确区分向量和点,同时也更易用于进行仿射(线性)几何变换。”—— F.S. Hill, JR。:
对于一个向量v以及基oabc,可以找到一组坐标(v1,v2,v3),使得v = v1 a + v2 b + v3c (1)
对于一个点p,则可以找到一组坐标(p1,p2,p3),使得 p – o = p1 a + p2 b + p3c (2)
从上面对向量和点的表达,我们可以看出为了在坐标系中表示一个点(如p),我们把点的位置看作是对这个基的原点o所进行的一个位移,即一个向量——p–> o(有的书中把这样的向量叫做位置向量——起始于坐标原点的特殊向量),我们在表达这个向量的同时用等价的方式表达出了点p:p = o + p1 a + p2 b +p3 c (3)
(1)(3)是坐标系下表达一个向量和点的不同表达方式。这里可以看出,虽然都是用代数分量的形式表达向量和点,但表达一个点比一个向量需要额外的信息。如果我写出一个代数分量表达(1, 4, 7),谁知道它是个向量还是个点!
我们现在把(1)(3)写成矩阵的形式:v = (v1 v2 v3 0) X (a bc o)
p = (p1 p2p3 1) X (a b c o),这里(a,b,c,o)是坐标基矩阵,右边的列向量分别是向量v和点p在基下的坐标。这样,向量和点在同一个基下就有了不同的表达:
l 3D向量的第4个代数分量是0
l 3D点的第4个代数分量是1。
像这种这种用4个代数分量表示3D几何概念的方式是一种齐次坐标表示。
这样,上面的(1, 4, 7)如果写成(1,4,7,0),它就是个向量;如果是(1,4,7,1),它就是个点。
下面是如何在普通坐标(Ordinary Coordinate)和齐次坐标(HomogeneousCoordinate)之间进行转换:
(1)从普通坐标转换成齐次坐标时
如果(x,y,z)是个点,则变为(x,y,z,1);
如果(x,y,z)是个向量,则变为(x,y,z,0)
(2)从齐次坐标转换成普通坐标时
如果是(x,y,z,1),则知道它是个点,变成(x,y,z);
如果是(x,y,z,0),则知道它是个向量,仍然变成(x,y,z)
以上是通过齐次坐标来区分向量和点的方式。从中可以思考得知,对于平移T、旋转R、缩放S这3个最常见的仿射变换,平移变换只对于点才有意义,因为普通向量没有位置概念,只有大小和方向.;而旋转和缩放对于向量和点都有意义,你可以用类似上面齐次表示来检测。从中可以看出,齐次坐标用于仿射变换非常方便。
此外,对于一个普通坐标的点P=(Px, Py, Pz),有对应的一族齐次坐标(wPx, wPy, wPz, w),其中w不等于零。比如,P(1, 4, 7)的齐次坐标有(1, 4, 7, 1)、(2, 8, 14, 2)、(-0.1, -0.4, -0.7, -0.1)等等。因此,如果把一个点从普通坐标变成齐次坐标,给x,y,z乘上同一个非零数w,然后增加第4个分量w;如果把一个齐次坐标转换成普通坐标,把前三个坐标同时除以第4个坐标,然后去掉第4个分量。
由于齐次坐标使用了4个分量来表达3D概念,使得平移变换可以使用矩阵进行,从而如F.S. Hill,JR所说,仿射(线性)变换的进行更加方便。由于图形硬件已经普遍地支持齐次坐标与矩阵乘法,因此更加促进了齐次坐标使用,使得它似乎成为图形学中的一个标准。
(二)四元数:
旋转,是三种坐标变换——缩放、旋转和平移,中最复杂的一种了。
旋转的表示方法——矩阵旋转和欧拉旋转和四元数旋转。
1、矩阵旋转使用了一个4*4大小的矩阵来表示绕任意轴旋转的变换矩阵
2、欧拉选择则是按照一定的坐标轴顺序(例如先x、再y、最后z)、每个轴旋转一定角度来变换坐标或向量,它实际上是一系列坐标轴旋转的组合。
3、四元数本质上是一种高阶复数,是一个四维空间,相对于复数的二维空间。
举个栗子:我们高中的时候应该都学过复数,一个复数由实部和虚部组成,即x = a+ bi,i是虚数单位,如果你还记得的话应该知道i^2 =-1。而四元数其实和我们学到的这种是类似的,不同的是,它的虚部包含了三个虚数单位,i、j、k,即一个四元数可以表示为x = a+ bi + cj + dk。那么,它和旋转为什么会有关系呢?
在Unity里,tranform组件有一个变量名为rotation,它的类型就是四元数。很多初学者会直接取rotation的x、y、z,认为它们分别对应了Transform面板里R的各个分量。当然很快我们就会发现这是完全不对的。实际上,四元数的x、y、z和R的那三个值从直观上来讲没什么关系,当然会存在一个表达式可以转换。
一个四元数可以表示为q = w + xi + yj + zk,现在就来回答这样一个简单的式子是怎么和三维旋转结合在一起的。为了方便,我们下面使用
q = ((x, y, z),w)= (v, w),其中v是向量,w是实数,这样的式子来表示一个四元数。
我们可以使用一个四元数q=((x,y,z)sinθ2, cosθ2) 来执行一个旋转。具体来说,如果我们想要把空间的一个点P绕着单位向量轴u = (x, y, z)表示的旋转轴旋转θ角度,我们首先把点P扩展到四元数空间,即四元数p = (P, 0)。那么,旋转后新的点对应的四元数(当然这个计算而得的四元数的实部为0,虚部系数就是新的坐标)为:
p′=qpq−1
其中,q=(cosθ2, (x,y,z)sinθ2) ,q−1=q∗N(q),由于u是单位向量,因此
N(q)=1,即q−1=q∗。右边表达式包含了四元数乘法。
我们举个最简单的例子:把点P(1, 0, 1)绕旋转轴u = (0, 1, 0)旋转90°,求旋转后的顶点坐标。首先将P扩充到四元数,即p = (P, 0)。而q = (u*sin45°, cos45°)。求p′=qpq−1的值。建议大家一定要在纸上计算一边,这样才能加深印象,连笔都懒得动的人还是不要往下看了。最后的结果p` = ((1, 0, -1), 0),即旋转后的顶点位置是(1, 0, -1)。
如果想要得到复合旋转,只需类似复合矩阵那样左乘新的四元数,再进行运算即可。
最后我们可以了解这三种旋转方式的优缺点:
- 矩阵旋转
- 优点:
- 旋转轴可以是任意向量;
- 缺点:
- 旋转其实只需要知道一个向量+一个角度,一共4个值的信息,但矩阵法却使用了16个元素;
- 而且在做乘法操作时也会增加计算量,造成了空间和时间上的一些浪费;
- 优点:
- 欧拉旋转
- 优点:
- 很容易理解,形象直观;
- 表示更方便,只需要3个值(分别对应x、y、z轴的旋转角度);但按我的理解,它还是转换到了3个3*3的矩阵做变换,效率不如四元数;
- 缺点:
- 优点:
- 四元数旋转
- 优点:
- 可以避免万向节锁现象;
- 只需要一个4维的四元数就可以执行绕任意过原点的向量的旋转,方便快捷,在某些实现下比旋转矩阵效率更高;
- 可以提供平滑插值;
- 缺点:
- 比欧拉旋转稍微复杂了一点点,因为多了一个维度;
- 理解更困难,不直观;
- 优点:
说下四元数旋转的几个需要注意的地方:
- 用于旋转的四元数,每个分量的范围都在(-1,1);
- 每一次旋转实际上需要两个四元数的参与,即q和q*;
- 所有用于旋转的四元数都是单位四元数,即它们的模是1;
下面是几点建议:
- 实际上,在Unity里即便你不知道上述公式和变换也丝毫不妨碍我们使用四元数,但是有一点要提醒你,除非你对四元数非常了解,那么不要直接对它们进行赋值。
- 如果你不想知道原理,只想在Unity里找到对应的函数来进行四元数变换,那么你可以使用这两个函数:Quaternion.Euler和Quaternion.eulerAngles。它们基本可以满足绝大多数的四元数旋转变换。