二、 矩阵运算
1. 什么是矩阵
矩阵就是由多组数据按方形排列的阵列,在3D运算中一般为方阵,即M*N,且M=N,使用矩阵可使计算坐标3D坐标变得很方便快捷。下面就是一个矩阵的实例:
看似没什么特殊的,可是后面你可以看到矩阵的魅力,为什么矩阵这么有效,我也不知道,这个由数学家去论述,我们只要可以用就是了。
2. 向量的点乘和叉乘
向量的点乘和叉乘与矩阵一样是数学定义,点乘在矩阵运算中起到很重要的作用,称为内积,叉乘称为外积,通过叉乘运算可以计算出一个向量,该向量垂直于由两个向量构成的平面,该向量也称为该平面的法线。这两个计算方法在3D运算中的作用就是向量计算工具。
l 点乘公式
其实就是两个向量的各分量相乘后形成新的向量
l 叉乘公式
Uc=U1* U2
两个向量进行叉乘的矩阵如下:
其中x1,y1,z1以及x2,y2,z2分别为向量U1和U2的分量,设UC为叉乘的向量积,其计算公式如下:
3. 三维几何变换矩阵
几何绘图中,常常需要将一个模型从一个位置移动到另一个位置,或者将模型进行缩放旋转,称为几何变换。每个模型都存在一个局部的坐标系,在制作模型的时候是不考虑模型在场景中的具体位置的,模型中的所有顶点的坐标值都相对于局部坐标系,而模型在应用中会发生很多变化,其中大部分情况都是由多种变化复合的结果,这些变化涉及很多复杂的运算,因此你必须逐个计算每个顶点因模型旋转、缩放、位移后相对于世界坐标系的值,这个计算如果仅仅采用一般的三角函数去逐个点处理那简直会使你望而生畏,计算量无法估量,就算你费好大劲把公式写好,也许模型因为你某个小错误已经面目全非,更别说动画效果了。
这时候就必须使用矩阵来解决这些问题。在3D计算中采用的是4元坐标系,因此在计算模型变换的时候采用的是4*4的方阵,矩阵结构中,元素编号按先行后列排列,在编程语言中可以用数组储存,使用循环计算,为便于坐标的批量处理,在绘制和计算一个三维模型前,先计算好所要某种变换所需要的元素填写入矩阵,然后逐一将模型的所有顶点和矩阵相乘就可以将模型的所有顶点按所希望的变换为新的坐标(除非矩阵元素设置错误),这里可以看出,矩阵中的每个数据(元素)是至关重要的,如何产生这些元素将在第三章介绍。
矩阵结构如下。(二维平面则使用3X3的矩阵,原理相同)。
该结构图中的每个元素都给定了一个编号,编号的代码分别代表行和列。
4. 变换计算公式
向量和点的变换运算都可以使用矩阵,一个坐标或向量与一个4X4的矩阵进行点乘运算而进行转换。矩阵中的数据排列可以使用列矩阵,也可以使用行矩阵,但在做乘法时必须要行列交叉做乘积,OPENGL中使用列矩阵。下面表示一个矩阵数组的排列方式以及一个点或向量是如何与矩阵相乘以获得新坐标的计算公式的。
其中P是原顶点坐标或向量,变换时将当前顶点点P的四个分量分别与矩阵的每个行进行点乘运算:
x’=x*M00 y*M01 z*M02 w*M03
y’=x*M10 y*M11 z*M12 w*M13
z’=x*M20 y*M21 z*M22 w*M23
w=x*M30 y*M31 z*M32 w*M33
由于在3D运算的矩阵中最后一行前3列始终为0,所以w’的结果取决于w,因此可以看出向量与矩阵相乘得到的也是向量。
5. 单位矩阵
有一种特殊的矩阵,由左上右下的元素组成的对角线,如果之上的所有元素都为1,且其它为0,该矩阵则称为单位矩阵,任何顶点与单位矩阵相乘的结果等于该顶点的原始坐标,即不发生任何变换。
在OPENGL中常使用glLoadIdentity()函数来设置单位矩阵,由于OPENGL是状态机,所以每次绘制场景前都用来重置之前可能被修改过的矩阵,但是有时绘制一个模型必须在之前的计算结果上进行坐标变换,比如先画了一辆汽车的车身,然后根据汽车的当前位置绘制车轮,就必须保持原先的矩阵,相对汽车的位置进行变换,而有时却要从原点开始计算,所以矩阵的管理是通过一系列的矩阵函数操作的,最常用的是矩阵堆栈的操作,但是由于大多数情况,OPENGL的矩阵堆栈深度为32,即只能保存32个矩阵,虽然矩阵堆栈其效率比较快,但是在某些场合32个矩阵是不够的,为了减少计算机运算负载,必须事先安排好绘制顺序,这也是进行类封装的必要性,不过类的封装会增加一定的调用开销。
6. 矩阵相乘
有时候需要对一个模型进行连续多种变换,而每次变换都要将模型的前次所有顶点与矩阵一一相乘,如果对于一个比较复杂的场景进行处理时,其计算量是很可观的,为了减少计算量,加快场景绘制,采取事先将多种变换矩阵合并,然后对模型所有顶点和矩阵相乘进行一次性矩阵变换。比如要对模型进行先缩放后旋转。
合并方法是将多个矩阵相乘来计算出复合矩阵。三维变换中参与乘法运算的两个矩阵都必须是4X4矩阵,相乘时,每个新元素也通过点乘运算后获得,所得的新矩阵也是4X4的方阵。
矩阵的乘积不可逆的,即MN不等于NM,因此在安排变换时要注意顺序,另外,在顶点与复合矩阵相乘的结果是与矩阵合并顺序相反的。比如,T是一个平移矩阵,R是一个旋转矩阵,假如要进行先旋转后移动的变换顺序,合并矩阵的顺序必须是M=TR,然后进行p’=MP的 变换,其效果等同于p’=T(RP)。
矩阵相乘的计算公式分解:
复合矩阵计算方式为,将左边的矩阵M的每个行元素与右边矩阵N的每列元素进行点乘运算就是新矩阵C的对应的元素。计算顺序为,M由上边第一行开始,提取每行的4个元素,分别与N中左边第一列开始,提取的4个元素进行点乘运算,运算结果放在C中,并从上到下,从左到右排列,编程时采用双重循环。
C00=m00*n00 m01*n10 m02*n20 m03*n30
C01=m00*n01 m01*n11 m02*n21 m03*n31
C02=m00*n02 m01*n12 m02*n22 m03*n32
C03=m00*n03 m01*n13 m02*n23 m03*n33
……..
C30=m30*n00 m31*n10 m32*n20 m33*n30
C31=m30*n01 m31*n11 m32*n21 m33*n31
C32=m30*n02 m31*n12 m32*n22 m33*n32
C33=m30*n03 m31*n13 m32*n23 m33*n33
如上所述,经过一系列的计算,分别把两个矩阵合并起来了,形成了新的复合矩阵,编写这样的代码是很容易的。现在我们已经有了几个基本矩阵计算工具,只要填入适当的参数,我们就可以用循环的方式变换模型的所有顶点,最终实现模型的任意复杂的变换,基本上都是很机械的操作。而剩下的就属最核心的东西,就是基本矩阵中元素的生成了。
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/168078.html原文链接:https://javaforall.cn