加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

矩阵基础知识

(2012-05-21 11:18:32)
标签:

杂谈

矩阵基础知识  

2009-02-15 15:34:18 分类: ActionScript 3.0  标签: |字号 订阅

8.6  变形矩阵

如果读者完全没有矩阵运算的知识,在使用AS3操作形变时会遇到巨大的困扰。在学习控制动画变形之前,需要先简单了解矩阵运算的相关知识。

8.6.1  矩阵基础知识

矩阵的发明是为了方便对线性方程组求解,其经典定义就是线性方程组的系数和常数组成的数字方阵。其实矩阵并不神秘,完全可以把它看成一组数字的排列记录。矩阵具有行和列,可以使用大写字母标记一个矩阵。如图8-26所示的3个矩阵,记作矩阵A、B、C。其中,矩阵A是一个一列三行的矩阵,矩阵B是一行三列,矩阵C则是一个三行三列的矩阵。

矩阵中的一行数值,叫做横向量,记作(a,b,c,d);矩阵中的一列叫做列向量,记作(a,b,c,d )T。

8.6.2  矩阵的运算

把一组数字记录成矩阵形式是没有意义的,学习矩阵的关键在于掌握矩阵之间的运算。

1.矩阵加法运算

矩阵之间也可以相加。把两个矩阵对应位置的单个元素相加,得到的新矩阵就是矩阵加法的结果。由其运算法则可知,只有行数和列数完全相同的矩阵才能进行加法运算。

矩阵之间相加没有顺序,假设A、B都是矩阵,则A+B=B+A。通常认为矩阵没有减法,若要与一个矩阵相减,在概念上是引入一个该矩阵的负矩阵,然后相加。A-B是A+(-B)的简写。两个三行三列矩阵的加法如图8-27所示。

http://new.51cto.com/files/uploadimg/20080716/213559621.jpg498)this.style.width=498;" border=0<
图8-26  矩阵的行和列
http://new.51cto.com/files/uploadimg/20080716/213622424.jpg498)this.style.width=498;" border=0<
图8-27  矩阵的加法

矩阵加法在图像位移运算时经常用到。

2.矩阵乘法运算

矩阵之间也可以进行乘法运算,但其运算过程相对复杂得多。与算术乘法不同,矩阵乘法并不是多个矩阵之和,它有自己的逻辑。其具体的算法描述为:假设m行n列的矩阵A和r行v列的矩阵B相乘得到矩阵C,则首先矩阵A和矩阵B必须满足n=r,也就是说,第一个矩阵的列数必须和第二个矩阵的行数相同。在运算时,第一个矩阵A的第i行的所有元素同第二个矩阵B第j列的元素对应相乘,并把相乘的结果相加,最终得到的值就是矩阵C的第i行第j列的值。

这个过程用数学公式描述如下。

C(i,j)=A(i1,i2,i3……in)×B(j1,j2,j3……jv)

进而推出。


C(ij)= i1×j1+i2 ×j2+i3×j3……+in×jv

从矩阵的乘法运算过程可以看出,矩阵A和矩阵B相乘的产生的矩阵C,必然是m行v列的。例如,一个5×3的矩阵同一个3×7的矩阵相乘,结果必然是产生一个5×7的矩阵。而一个5×3的矩阵同一个5×7的矩阵,则无法相乘。两组矩阵的乘法运算如图8-28所示。

http://new.51cto.com/files/uploadimg/20080716/213900370.jpg498)this.style.width=498;" border=0<
图8-28  矩阵的乘法

在图形变换时,经常用到多次变换,这会造成多个矩阵相乘。如果多个矩阵相乘,则等价于前两个矩阵相乘的结果再乘以第三个矩阵,以此向后类推。例如,假设A、B、C、D都是矩阵,则A×B×C×D=((A×B)×C)×D。矩阵乘法同数字乘法不同,其运算的先后顺序十分敏感,矩阵A乘矩阵B的结果可能完全不同于矩阵B乘矩阵A的结果,有时甚至根本无法相乘。

矩阵相乘要求矩阵之间列与行前后对应相等。为了方便操作,在图形学的实际应用中变换矩阵都是行数和列数相等的方阵。对于行和列不相等的矩阵,甚至要人为补足。

8.6.3  矩阵与空间变换

依据能否便捷的被数学函数公式精确描述,空间变换可以分为两大类:简单变换和复杂变换。位移、反射、旋转、斜切等变换都可以通过简单的函数来描述属于简单变换;在Photoshop中对一幅图像应用涂抹工具,是复杂变换,需要根据具体情况具体处理。

简单空间变换是计算机图形学的重要研究内容,其运算是依靠矩阵来描述的。使用脚本控制动画形变,主要是进行简单空间变换。AS3中的Matrix类提供了处理简单空间变换的能力,其本质就是矩阵操作。

假设舞台平面内存在一个点S(x,y),可以将其看成一个一行两列的矩阵A[x,y],如果企图使之水平方向放大两倍,则目标点T的坐标为(2x,y),依然可以看成一个矩阵C [2x,y]。假设有一个矩阵B,满足A×B=C,那么矩阵B就是一个变换矩阵,其功能为令对象水平拉伸2倍。数学已经证明B是存在的,而且可以断言,矩阵B是一个2×2的矩阵。假设矩阵B内的元素为[(a,b),(c,d)],如图8-29所示:

根据矩阵乘法,可以写出以下方程组。


x×a+y×c=2x
x×b+y×d=y

为使这个方程组成立,只需要令a=2,b=0,c=0,d=1即可。于是得到了矩阵B中元素的具体数值,可以明确写出矩阵B(如图8-30所示):

http://new.51cto.com/files/uploadimg/20080716/214931818.jpg498)this.style.width=498;" border=0<
图8-29  水平拉伸2倍的变换矩阵
http://new.51cto.com/files/uploadimg/20080716/214944511.jpg498)this.style.width=498;" border=0<
图8-30  矩阵B的值

显然,对于任意点进行水平放大两倍的变形,都可以应用矩阵B实现。而通过修改矩阵B的值,实现对任意点垂直放缩也不困难。这就体现了矩阵变形操作灵活直观的优势。

8.6.4  旋转变换矩阵

利用矩阵能完成旋转。对于图8-31中的点S ( j,0),可以看做矩阵A[ j,0],令点S旋转a度得到点T(m,n),可以看做矩阵C [m,n],显然可以存在变换矩阵B,令A×B=C成立。由简单的几何知识就能得出变换矩阵B的构造。

http://new.51cto.com/files/uploadimg/20080716/215416992.jpg498)this.style.width=498;" border=0<
图8-31  旋转矩阵

因为点S位于坐标轴上,使得计算矩阵B的过程极大的简化了。如果要对于位置不在坐标轴上的点进行旋转,其数学计算要复杂一些,其推导已经超出了本书的内容范围。不过最终确实可以证明,对于坐标空间中的任意点(x,y)经过旋转s度得到的点(m,n),存在以下公式。


m=a×cos(s)-b×sin(s)
n=a×sin(s)+b×cos(s)

于是可以得到通用的旋转变换矩阵如图8-32所示。

显然,这个通用矩阵对于坐标轴上的点也是适用的。

在AS3中使用变换矩阵操作显示对象时,总是套用显示对象上一级容器的坐标系。如果显示对象直接处在舞台根级别上,则坐标系原点就是舞台的左上角。

http://new.51cto.com/files/uploadimg/20080716/215753746.jpg498)this.style.width=498;" border=0<
图8-32  通用旋转变换矩阵

8.6.5  仿射变形

简单变形也包括许多具体的变形方式,例如球面变形、透视变形、复杂拓扑变形等,其算法各有差别。在AS3中,运用Matrix类进行的简单变形都属于仿射变形(Affine Transformations)。

所谓仿射变形,其特征就是一切变形都不会破坏线条的线性。变形后水平和垂直方向上的长度比例可以发生变化。但直线永远不会变成曲线。坐标系内各点的变换都是均匀的,不存在局部扭曲和象限的塌缩。一对平行线,无论经过多少次仿射变形,都将保持平行,不会有交集。

既然属于简单变形,所以仿射变形的过程可以写为数学函数表达式。仿射变形主要是通过变量乘以变换矩阵实现的。考虑到位移难以用矩阵乘法获得,所以需要引入了一个位移矢量加权。其通用数学表达式如下。

f(x)=Ax+b

其中,A是一个变换矩阵,b表示平移矢量。通过这个数学公式,可以计算诸如平移、旋转、拉伸等仿射变形。

在计算机语言中,一般都会将位移矢量与变形矩阵合并在一个矩阵之中。这个矩阵为三行三列,左上角的两行两列是变形矩阵,第三列为平移矢量,并将余下的位置用数值补足。如图8-33所示。

http://new.51cto.com/files/uploadimg/20080716/222703377.jpg498)this.style.width=498;" border=0<

图8-33  变换矩阵同位移矢量的合并

这种记法只是为了计算机程序构造的方便。虽然表面上是一个三行三列的矩阵,但在实际运算时,遵循的依然是f(x)=Ax+b的公式。

因为AS3中的Matrix类是处理仿射变形的,所以其功能有先天的限制。当处理复杂的变形时,必须另寻它途,在代码中自行创建变形函数去实现。如果不考虑这一点,在代码中尝试用Matrix类获得非仿射变形效果,则无异于南辕北辙,难以成功。

8.6.6  仿射变形的次序

在公式f(x)=Ax+b中,表面看来只能进行一次变形,其实不然:这种形变并没有次数的限制。数学上可以证明,无论对矩阵A经过多少次变换方才得到矩阵C,最终都可以通过一个变换矩阵B同A相乘得到C。而矩阵B就是中间多次变换的矩阵乘积。

例如要对舞台上的某点进行旋转+拉伸+映射变形,并不需要依次将公式中的矩阵设为相应变换矩阵,分成三次应用公式获得结果。此时,可以把多次仿射变形累积在一个矩阵上。具体就是将多次变形的各变换矩阵依次相乘,并把得到的最终结果,一个新的变形矩阵,代入公式f(x)=Ax+b即可。

这种变形的次序在二维空间是较为敏感的。同样的变形矩阵,如果应用的次序不同,产生的结果也不相同。这种现象的数学根源在于矩阵乘法的秩序性。

图8-34展示了因为变换顺序不同而产生不同结果的例子。

http://new.51cto.com/files/uploadimg/20080811/153655417.jpg498)this.style.width=498;" border=0<
图8-34  变换矩阵的累积次序

8.6.7  Matrix类的本质

在AS3中,对一个显示对象进行变形操作,需要使用Matrix类。Matrix类的变形操作是基于一个三行三列的方型矩阵。当在代码中创建一个Matrix类时,AS3的内部机制就创建了一个这样的变换矩阵作为代码操作的数理基础。

使用以下代码可以在AS3中创建一个矩阵,并输出矩阵的值。

var myMatrix:Matrix=new Matrix;
trace(myMatrix.toString())

其输出结果如下。

(a=1, b=0, c=0, d=1, tx=0, ty=0)

从这个结果上看,AS3中的矩阵似乎是两行三列的矩阵,其实不然。充分的设置矩阵内的值,理论上可以进行三维的操作。但Flash不支持三维,其中的u、v和w被自动忽略,运算时总是令u=0,v=0,以及w=1。在使用“toString()”方法时,对于永远保持不变的u、v、w就不再输出了。所以真实的Matrix类的变换矩阵应该形如图8-35所示。

http://new.51cto.com/files/uploadimg/20080811/154449141.gif498)this.style.width=498;" border=0<

图8-35  Flash中Matrix类的矩阵实现

矩阵操作描述的是如何将坐标系内的点映射到另一个位置。代码可以读取和写入此矩阵内的数值,来改变和控制形变。在影片运行时,矩阵内相应的值会同显示对象的信息相乘相加,计算出变形后的对象信息,并绘制在舞台上。

AS3中矩阵类变形的具体的数学公式同前文所述的仿射变形公式相同,并不复杂。假设一个点A,其坐标值为(x,y),则等价于三维坐标系中的(x,y,z),其中z为任意值,表示该二维坐标平面是三维空间中垂直于z轴的一个平面。该平面内所有点在z坐标上都具有相同的值。

假设变形运算后的位置为(m,n),则根据矩阵乘法和仿射变形公式,其运算结果如下。

m=x*a+y*c+z*u+tx
n= x*b+y*d+z*v+ty

在AS3中,u和v永远是0,所以简化的运算结果如下。

m=x*a+y*c+tx
n= x*b+y*d+ty

对于一个Matrix对象,其内部属性a、b、c、d、tx和ty,就对应了图8-35中矩阵内的值。AS3中DisplayObject类及其子类,内部都包含transform属性,这个属性是一组对象的集合,其中就有一个矩阵类。将这个类复制,并对副本做操作,能得到一个目标矩阵。将目标矩阵替换原有矩阵。Flash会自动套用目标矩阵的信息,改变显示对象。这种改变包括位移、缩放、旋转、斜切、映射等。

变形可以累计。无论对一个矩阵进行多少次变形,它保留的都是个变形依次积累的数值信息。当对显示对象应用这个矩阵的时候,等于所有的变换都被依次应用了。代码中不能使用为矩阵类赋null值的办法使其恢复原始状态。

8.6.8  矩阵变形方法

用户在代码中可以创建矩阵类的实例,并通过手动设置矩阵的内部属性a、b、c、d、tx和ty,纯手工的创造或改变一个变换矩阵。如果用户具备丰富的图形学知识,可以自己算出位移、缩放、旋转、斜切等变形所需要的二次变换矩阵值,那么使用这种方法就足够了。高级用户使用这种方式将会相当的高效。

但是,显然大多数用户并不了解矩阵数学和计算机图形学算法。对它们而言,直接操作Matrix类的属性是很不容易的。所以为了简化用户操作,AS3中的Matrix类绑定了一些内部方法。初级用户完全可以不顾及矩阵数学和图形学算法,通过使用这些内建的方法,修改它们的参数,累加它们的效果,最终得到想要的结果。

AS3中Matrix类内建的变形方法如表8-3所示。

表8-3  Matrix类内建变形方法

   

   

translate(tx ty)

实现显示对象的移动

其中的txty描述的是显示对象移动的差值,而不是目标点的最终坐标

scale(sx sy)

实现显示对象的放大和缩小

sx:倍数,对应x坐标

sy:倍数,对应y坐标

rotate(q)

旋转一个对象

q:对象的旋转量。这是一个弧度值

为了了解矩阵类的实际使用,下面介绍一个最简单的实例。新建AS3项目,在代码编辑器中直接输入以下代码。

var myMatrix:Matrix=new Matrix;
trace(myMatrix.toString());
//使用translate()方法修改矩阵
myMatrix.translate(5,13);
trace(myMatrix.toString());
//新建一个点
var myp:Point=new Point(5,5);
trace(myp.toString());
//新建一个目标点,用于接受位移后的坐标
var mypt:Point;
//应用位移矩阵
mypt=myMatrix.transformPoint(myp);
//输出目标点的坐标
trace(mypt.toString());

以上代码较为简单,注释也很清楚,不再赘述。运行这段代码,可以得到输出。

(a=1, b=0, c=0, d=1, tx=0, ty=0)//初始变换矩阵
(a=1, b=0, c=0, d=1, tx=5, ty=13)//修改后的变形矩阵
(x=5, y=5)//原坐标
(x=10, y=18)//目标坐标

至此,矩阵类的运行流程就很清楚了。声明一个矩阵类就建立了一个初始变换矩阵,初始矩阵如图8-36所示。

使用矩阵类的方法其本质是修改这个初始矩阵,应用矩阵类其实就是用该变形矩阵代入仿射变形公式进行运算的过程。

有的用户觉得这些方法使用起来并不方便,因为它们都是相对值。例如,其中的“translate(tx,ty)”方法,并不能将对象直接移动到目标点,其真实作用是在水平方向上移动tx距离,在垂直方向上移动ty距离。如果想把显示对象移动到某点,用户必须自己进行计算前后的距离差。

http://book.51cto.com/files/uploadimg/20080811/1555490.gif498)this.style.width=498;" border=0<
图8-36  初始矩阵

其实这是矩阵的内部机制造成的。Matrix类的这些方法都是基于矩阵操作的,遵循的是数学思维。当用户使用“translate(tx,ty)”方法时,本质上构建一个产生位移的数学矩阵。矩阵提供了一种仿射变形的映射。AS3执行时,将这个矩阵应用在显示对象内部的每一个点,令它们在水平方向上移动tx距离,在垂直方向上移动ty距离。假设能够指定最终的目标值,则运算后,显示对象的每一个点都会被映射到这同一个点,则显示对象就被扭曲压缩了,这显然是不正确的。

如果用户从人的思维去考虑,觉得这些内建方法不方便,可以自己写一组自定义函数,扩展它们的功能。

8.6.9  矩阵算法原理

Matrix类的变形方法,最终都是根据用户给出的参数修改内部矩阵。这些方法的不同之处,在于修改值的算法,以及修改结果在矩阵中的位置。

当用户在代码中调用translate(5,13)时,AS3修改矩阵类的内建矩阵,将其中的(tx,ty)T与(5,13)T相加,由于在矩阵创建时(tx,ty)被初始化为(0,0),所以这个结果就是向量(tx,ty)与(5,13)的和。

这个过程如图8-37所示:

http://new.51cto.com/files/uploadimg/20080811/160844547.jpg498)this.style.width=498;" border=0<
图8-37  translate方法操作矩阵

当使用非位移的变换时,这个过程有所改变。根据公式f(x)=Ax+b可知,位移变换是矩阵相加运算,而非位移的变换是矩阵相乘运算。这个过程用代码可以很清晰地展现,新建项目并在代码编辑器中写入以下代码。

var myMatrix:Matrix=new Matrix;
trace(myMatrix.toString());
myMatrix.scale(4,6);
trace(myMatrix.toString());
myMatrix.scale(2,1);
trace(myMatrix.toString());

本例的操作过程如图8-38所示。

http://new.51cto.com/files/uploadimg/20080811/161308409.jpg498)this.style.width=498;" border=0<
图8-38  Scale方法多次操作矩阵

运行,可以观察到输出的结果如下。

(a=1, b=0, c=0, d=1, tx=0, ty=0)
(a=4, b=0, c=0, d=6, tx=0, ty=0)
(a=8, b=0, c=0, d=6, tx=0, ty=0)

用这种方法,用户可以研究矩阵类所有方法的内部逻辑。

其实,矩阵类其他内建变形方法的矩阵操作及其图形学算法都大同小异,其通用模式是:

(1)根据方法的类型,对参数进行数学运算

(2)将运算得出的值,赋予变换矩阵中的相应元素

不同的变换有不同的算法和相关矩阵元素,详情如表8-4所示。

表8-4  内建方法的矩阵操作和算法

   

   

translate(tx ty)

 

scale(sx sy)

 

rotate(q)

 

由此可知,Matrix类的所有内建变形方法都不神秘。它们仅仅是简化了操作,避免了烦琐的操作步骤。例如rotate(q)方法,如果使用此方法,用户只需要一个语句就能完成操作。

DisplayObjectMatrix.rotate(Math.PI/4);

如果不使用这个方法,直接根据其内部机制,操作Matrix类的内部属性,也能达到相同的结果,但步骤显然要烦琐一些。

DisplayObjectMatrix.a=Math.cos(Math.PI/4);
DisplayObjectMatrix.b = Math.sin(Math.PI/4);
DisplayObjectMatrix.c = Math.-sin(Math.PI/4);
DisplayObjectMatrix.d = Math.cos(Math.PI/4);

在动画中经常会要求创建一些复杂的动画变形,但使用Matrix类的内建方法的默认行为,在有些场合并不合适。对于初级用户,因为不具备矩阵知识,面对这种需求只能束手无策。通过前面几节的学习,原本看似高深莫测的Matrix类,现在已经一目了然不在话下。有了坚实的知识储备,就能根据Matrix和仿射变换矩阵的原理,手工改变各种内建方法的行为,以及根据所需创建其他变换。下一节,将详细讨论如何利用旋转变形制作复杂的动画。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有