什么是矩阵?
mn个数排成m行n列形成的矩形表称作一个mxn矩阵。
行向量与列向量
由于向量我们可以看做是一种特殊的矩阵,即只有一行或者只有一列的特殊矩阵。因此我们把只有一行的向量称为行向量,只有一列的向量称为列向量。
注意我们的程序在存储矩阵的时候其实是将mn个数存放在一个线性表中,因此获取一个矩阵中的第几行第几列的元素有一个优先原则。这里特别提出,Unity在Matrix4x4这个类的手册中有这样一句话:
Matrices in unity are column major. Data is accessed as: row+ (column*4). Matrices can be indexed like 2D arrays but in an expression likemat[a, b], a refers to the row index, while b refers to the column index (notethat this is the opposite way round to Cartesian coordinates).
Unity中的矩阵使用的是列优先。数据通过行+(列*4)的方式获取。矩阵可以被索引为类似二维数组的形式,但是要以mat[a,b]这样的表达式。其中a代表行号,b代表列号(注意这正好与笛卡尔坐标系相反)。
行向量与列向量的数学表达:
零矩阵
所有元素都是0的矩阵。
单位矩阵
形式如下的矩阵称为单位矩阵
其特点明显:所有元素中行号与列号相等处元素为1,否则为0,也就是从左上至右下的一条对角线为1,其他元素都为0的矩阵。关于单位矩阵的特殊性后面会提到。
矩阵相等
维数相同且所有元素相等的两个矩阵我们认为是两个相等的矩阵。
矩阵加法、矩阵减法、矩阵数乘
这3个运算都比较简单,因此写在一起。他们分别表示对应的行列上的数相加、相减得到的矩阵。一个数与矩阵相乘则是所有元素都乘以这个数得到的矩阵。
转置矩阵
矩阵乘法
矩阵乘法比较特殊,我们在让矩阵相乘的时候首先保证这个过程有意义。有意义的前提的矩阵乘法的左乘矩阵的行数与右乘矩阵的列数相等。
数学过程:矩阵a的列数与矩阵b的行数相等,那么相乘得到的矩阵c中的第i行j列的元素
由于只需要a的列数与b的行数相等,因此我们可以想象一个4元行向量有4列,那么他可以左乘一个4x4矩阵。一个4元列向量有4行,因此他可以右乘一个4x4矩阵。由于行向量与列向量看起来只是书写形式上的区别,实际上行向量与列向量存在转置关系。
矩阵乘法不满足交换律,但是在乘法有意义的情况下有:
在矩阵运算中,向量与矩阵相乘分为两种:行向量左乘与列向量右乘。
行向量左乘与列向量右成在本质上表达的东西没有太大差别,只是由于行向量与列向量存在转置关系,各个游戏引擎有他的书写习惯,以及美感偏好。DirectX中使用的是行向量左乘,而OpenGL中使用的列向量右乘。
逆矩阵
一个矩阵有逆矩阵首先需要满足一个条件:该矩阵为非奇异矩阵或满秩矩阵,又或者说该矩阵的行列式≠0。
满足该条件的矩阵a存在一个逆矩阵b,使得ab=单位矩阵I。
我们记作
空间变换
了解坐标系、向量、矩阵之后,我们可以开始了解空间变换了,而这部分知识基本上覆盖了目前绝大部分的与图形图像开发有关的领域。
前面我们对向量矩阵的定义、特性以及运算法则作了初步了解,然而线性代数没有告诉我们向量、矩阵运算的几何意义。而本书将带领读者去探索向量矩阵运算的空间几何意义。
Matrices can perform any kind ofliner transform.
一般来讲矩阵能描述任意的线性变换。满足两点的空间变换我们称之为线性变换:原点不发生移动;线段和直线之间的平行关系保持不变。换句话说,线性变换可能拉伸坐标系,但是不会扭曲坐标系。
对于任何图形的旋转(rotation)与缩放(scaling),属于线性变换:线段间的平行关系没有发生变化、原点没有发生移动。而平移(translation)变换则不属于线性变换,无论变换前以哪个点为坐标系原点,变换后的原点一定发生了移动。
因此我们说线性变换是一种可能改变线段的长度、线段间的夹角、图形的面积、体积的变换。
现在我们解开心中的疑问:矩阵是如何完成变换的?nxn个数字如何让我们的坐标系翻天覆地?
我们先从二维空间中展开想象。
假设我们有一个图片在平面直角坐标系中,其4个顶点分别为(0,0),(1,0),(0,1)(1,1)。
然后假设有这样一个2x2矩阵:
我们以这样的2x2矩阵对这个图形进行变换,意味着什么呢?
其实这个矩阵可以看作2个行向量p=[2,1],q=[-1,2]。我们在平面直角坐标系中画出这2个向量:
其实这个2x2矩阵代表的意思是将原x轴变换至p向量坐在的方向;将原y轴变换至q向量所在的方向。由于坐标系原点没有发生移动,因此这是一个线性变换。实质上这是一个旋转变换,我们可以伸出我们的左手,大拇指指向x,食指指向y,然后想象将任意一个形状放在两个手指间,然后大拇指由原方向变换至p方向,食指由原方向变换至q方像,这个过程就是我们这个矩阵所描述之变换过程。原图像逆时针旋转了26°。
-->
不仅仅是旋转,我们的图形的边长由原先的1,变换后变为了。因此这个矩阵还对我们的图形进行了拉伸。
这个例子中我们的p与q恰好垂直切长度相等,于是构成了一个正方形,实际上更普通的情况他们构成的是一个平行四边形。这个平行四边形我们叫做“偏转盒”。二维偏转盒我们可以使用两个手指来比划,三维偏转盒我们可以使用3个手指来比划,因此读者试着自己想象3x3矩阵中的线性变换的偏转盒子。
我们再回头看我们对于线性变换的定义:线段间的平行关系不发生改变;原点不发生移动。可见2x2矩阵与3x3矩阵分别真好可以表达2D与3D空间中的线性变换。
仔细观察上述变换,假设我们把右边的纸旋转让图像重新“回正”,我们顺时针对纸张旋转了26°,于是我们抛出一个概念:
我们对一个图形或者物体按照某个角度旋转,相当于对这个坐标系按照这个角度的负值旋转。
看到这里其实我们可以看到一个非常非常非常明显的规律,也就是变换矩阵中能出现cos的地方必然是在行列号相等的地方(一条对角线),任何行列号不相等的地方都可能出现sin。
缩放矩阵中的3个系数分别对应着对原图像的3个轴向的缩放程度。
一种特殊的线性变换我们称为镜像。例如2维变换矩阵 所代表的变换,其变换后的图像如同镜子一样翻折出现在y轴的另一边。
切变变换
讲完线性变换的最后我们提一下切变变换,这是一种特殊的变换,其变换过程为“扭曲”坐标系。其过程希望读者自行查阅相关资料。
至此我们研究完了线性变换的矩阵表达与几何意义。在空间数学中,我们认为坐标与矩阵的乘法就等于变换本身。
那么读者心中是否有疑问,平移变换不属于线性变换,那么能否用矩阵来表达呢?
答案是可以,但是有一个条件:n维空间中的平移变换无法用nxn或者更小的矩阵来表达,至少需要(n+1)x(n+1)的矩阵来表达。我们来看看为什么?
对于2D 3D中的线性变换矩阵,我们将其拆分为与坐标轴数量相当的向量基p,q/p,q,r。我们可以理解为线性变换的过程是将对应的坐标轴上的单位向量变换至该矩阵表示的向量基的过程。其过程必定不包含平移。
那么我们怎么才能表达平移?
我们前面讲了坐标与矩阵的乘法等于变换本身,因此我们假定2个坐标(1,1)(2,2)构成的线段,将其书写为2个行向量[1 1][2 2],将其左乘一个2x2单位矩阵(我们认为2x2单位矩阵是一个x,y方向上缩放系数都为1的缩放矩阵)完成线性变换:
[1 1] 变换后的结果仍然为[1 1],[2 2]同理。得到的线段(图形)没有发生任何变画
我们对这个变换矩阵进行任意修改:将[1 1]对角线上的值进行修改,发现变换过程产生了等比缩放。将没有在这条对角线上的值进行修改,发现变换过程产生了旋转。
除此之外这个2x2矩阵上面已经没有其他地方可以让我们修改能够产生平移变换的可能性了。于是我们不得不提出一个大胆的设想:我们能否得到一个包含有更多数的矩阵来完成变换,使该矩阵能有地方让我们修改其值使原图发生非线性变换?
答案是肯定的,在认知的过程中思维的变迁与升华是必不可少了。于是天才一般的想法被提出来:我们以3维矩阵、向量来表达2维空间中的变换和点。
我们来看对于点[1 1]扩展一个维度之后[1 1 1],我们对扩展过后的这个点坐标给予以下要求:
这不是一个3维空间中的点,它仍然是2维空间中[1 1]这个点。第三个维度我们称之为w。我们想象不出来这个[1 1 1]的点其意义为何的时候,我们只能看到这个点在2维空间中的影子(投影),我们由这个影子反过来来想象它。对于点[1 1 1],[2 2 2],[3 3 3],他们投影在二维空间中都是[1 1]这个点,投影的过程即将前面2维分别除以第三个维度w。当w=0时,除法无意义,因此我们认为点[x,y,0]在二维空间中的意义为无穷远的点。
这个空间我们称这个2维空间的3维齐次空间。这个[x,y,w]形式的坐标,我们称其为改2维坐标的3维齐次坐标。同样的,我们的2维单位矩阵,当w=1时,对应着一个3维齐次单位矩阵
我们再次以齐次行向量与齐次单位矩阵的左乘表达变换:
[x y w] ,此时左矩阵列数与右矩阵行数仍然相等,乘法仍然有意义。
我们再观察,齐次矩阵中是否有地方可以让我们的坐标系产生平移的可能?
齐次矩阵中原2维矩阵的数据项我们不必尝试了,我们试着修改多出来的abcd。
[x y w]=[x+a,y+b,w ] (结果矩阵中:)
不可思议的事情发生了,由于我们对于齐次空间的定义,任意点[x,y,w],x/w , y/w相等的两个点我们认为是同一个点。于是我们可以看到这个变换矩阵令原坐标系中的任意点[x y]变换后都成为了[x+a y+b]!也就是任何图形、线段都沿x方向平移了a,沿y方向平移了b。
结论:对于n维空间中的图形(坐标系),nxn矩阵可以表达线性变换,引入n+1维齐次空间,即可用n+1维齐次矩阵乘法来表达平移变换。而原线性变换,依然可以在该齐次矩阵中的原对应位置表达线性变换。
于是齐次矩阵将线性变换与平移变换统一到了一起。我们将线性变换与平移变换都发生的变换叫做仿射变换。
换句话讲,一个nxn矩阵,可以表达任意的n-1维空间中的仿射变换。
由此我们得到三维空间中的平移矩阵,显然是一个3+1=4维的矩阵:
我们再回到几何层面上,我们认为对位置有意义的量,进行平移才有意义,本身没有位置概念的量,我们进行平移没有意义。最典型的就是点和向量。虽然我们前面说向量可以由终点来表达,但是狭义上的点是有位置的,而向量只有大小和方向。因此我们不能对一个狭义上的向量进行平移变换。
透视投影变换与投影矩阵
我们简单的提一下投影变换。投影变换通常发生在将一个3维空间中的物体投影至一个平面上(阳光将人照射出来的影子投影在墙上)。而我们表达一个3维平面最简单的3个平面分别是x=d,y=d,z=d。他们各自是与圆点距离d处的平面。
我们假设要将一个三维图形投影至z=d这个平面,那么任意的齐次点[x y z]投影至z=d平面上,得到的点坐标应为:[x y z ]/(z/d)=[x y d]将z消除得到了2维的空间(图形). 因此我们构造一个w为1的齐次空间,另[x y z 1]投影之后变为[x y z z/d],那么矩阵构造出来为:
任意点[x y z 1]与该矩阵相乘得到[x y z z/d],[x y z z/d]回到原空间则为[x y d],该矩阵就是三维空间中向z=d平面投影的投影矩阵。
方位与角位移
在掌握了向量、矩阵的一些基本知识之后,我们问自己一个问题:
一个向量有没有角位移?一个物体有没有角位移?
我们知道向量只有大小和方向,即使你要强迫一个向量绕自己旋转,旋转后向量仍然大小和方向没有改变,我们认为向量还是那个向量。但是如果说我们让一个物体绕自己旋转,比如把一个人翻过来,我们还认为是一样吗?由于地球引力的关系,被倒过来的人能够明显感觉到自己的状态发生了改变。而这种改变不单单是我们把一个人从某个地方弄到另外一个地方这种位移。在物理上我们称这个人发生了角位移。
我们将旋转的程度称为角位移。位移往往用来表达物体的位置,角位移往往用来表达物体的方位。
我们知道了n维空间中的位移可以用n个数字来表示。例如2维空间中的点朝x移动了5,朝y移动了10,那么用5,10 两个数字即可表达这个位移。
那么3维空间中的角位移又如何来表达呢?
矩阵表达角位移
我们可以构造一个3维的旋转变换矩阵来表达3维空间中的角位移(这种矩阵我们前面已经给出)。这种表达方式非常地直接,但是并不简洁。我们了解下矩阵表达角位移的优缺点
优点:立即性;更适合底层图形API直接使用;多个角位移连接时只需要连续相乘即可得到最后的角位移;只需要简单的将这个矩阵转置即可求得这个角位移的逆;
缺点:占用更多的空间(3维空间需要9个数来表达角位移);晦涩难懂(我想读者应该已经有所体会);有冗余数据、有可能发生矩阵蠕变。
欧拉角(Euler Angle)表达角位移
神马?我们单纯的描述一个物体旋转的情况,竟然要搬出一个3x3矩阵?我们不能忍!于是乎本章开头提到的欧拉角登场了。欧拉角由著名的数学家欧拉(Euler)提出,并且以他的名字命名。我们看看欧拉角是个什么角,欧拉角有什么厉害之处:
欧拉角将一个三维物体的旋转分解为分别绕3个两两垂直的轴线的旋转。至于哪3个轴,按照什么顺序分解,欧拉没有告诉我们,但是最有意义的情况是我们按3维坐标系的3个轴按照一定的顺序将旋转分解。这个顺序有很多种约定,我们这里提一下”heading-pitch-bank”约定。
即在左手坐标系中,我们继续伸出左手,3个手指两两垂直,得到我们前面提到的左手坐标系,食指指向即y+方向的旋转量我们称为heading,中指指向的z+方向的旋转量我们称为bank,大拇指指向的z+方向的旋转量我们成为pitch。
由此我们将一个物体任意的旋转分解为3个旋转量,分别得到3个角度。我们可以由3个数组成的序列来描述近乎所有的旋转!但是欧拉角仍然无法表达所有的旋转。最著名的就是万向节死锁现象。即当pitch=+-90°时heading与bank都竖直从而减少了一个旋转度,此问题至今没有什么好的办法解决。
优点:容易使用;占用的数最少;简洁;所有的欧拉角表达出来的角位移都是合法的。
缺点:不唯一性(360°和0°虽然数值变化了但是表达的方位没变);最难插值(很难在两个欧拉角中间进行插值,这对我们加入动画非常恼火);万象锁问题。
接下来我们认识最后一种表达角位移的方式:
四元数(Quaternion)表达角位移
关于四元数是什么留给读者自行延伸阅读,这里只提出四元数实质上是对复数(a,b,c,d)的一种几何解释。
复数(a,b,c,d)有一个实部a,3个虚部分别为b,c,d。复数可以进行加、减、点乘、叉乘,可以求它的共轭复数,也可以计算复数的模和求逆。
四元数如何表达角位移呢,我们让n为旋转轴方向的单位向量,,那么四元数q=[]
表达了这个角位移。其中nx ny nz分别是旋转轴在轴线上的投影向量。
我们特别提出四元数的插值(slerp)。四元数的插值过程我们只需要知道有一个开始状态q,结束状态p,插值参数t(0<=t<=1),那么slerp函数可以非常快速平滑地在q与p两个状态中插入一个旋转状态。其数学、几何解释也请读者自行延伸阅读。
优点:slerp提供了2个方位间最为平滑的插值;角位移的连接和求逆更为迅速(相比矩阵);每个角位移占用4个数的内存开销,中等开销;
缺点:最难于使用(光理解它就已经比较科幻了);可能存在一组四元数表达出不合法的角位移。
我们了解了角位移的3种表达方式,具体在开发中使用哪一种来表达需要理解他们各自的优越点,根据实际情况选择。而这3种方式表达的角位移可以进行互相转换,其数学过程较为复杂,我们可以直接调用现有的API来完成。
几何图元
明天继续….