假设,在空间中有一个点,使用XYZ描述它的位置。此时让它进行旋转或者平移,我们需要知道这个点的新位置,那么此时,我们就可以用矩阵进行计算得出新点的位置。
定义
矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合 [1] ,最早来自于方程组的系数及常数所构成的方阵。
表达式
假如我们定义一个n行m列的矩阵A,那么我们可以用如下的公式进行表达:
矩阵.jpg
上图就是一个n行m列的矩阵,其中a为矩阵中的元素,a的第一个下标表示所处行的位置,第二个下标表示所处列的位置。
矩阵只有一行或者一列的时候,这个矩阵可以称之为向量。
在OpenGL中,矩阵可以进行以下定义:
//3*3的矩阵
M3DMatrix33f transform
//4*4的矩阵
M3DMatrix44f transform
矩阵的运算
-
加法
矩阵加法.jpg 矩阵的加法满足下列运算律:
矩阵加法就是矩阵里对应的元素进行加法,公式如下:
A+B = B+A;
(A+B)+C = A+(B+C)
矩阵加法必须是同型矩阵,即A,B矩阵行列相同 -
数乘
矩阵数乘.jpg
矩阵的数乘等于数乘以矩阵里的每一个元素,公式如下:
-
转置
矩阵转置.jpg
把矩阵A的行和列互相交换所产生的矩阵称为A的转置矩阵 ,这一过程称为矩阵的转置。公式如下:
-
矩阵相乘
矩阵相乘.jpg 注意:两个矩阵的乘法仅当第一个矩阵A的列数和另一个矩阵B的行数相等时才能定义。例如,当A矩阵为mn矩阵时,B矩阵必须为nx才能进行矩阵相乘,切得到的结果为m**x。
矩阵相乘公式如下:
故矩阵相乘不满足交换律。
单元矩阵
单元矩阵是一个对角线为非零元素,其它元素为零的方形矩阵。
单元矩阵.jpg
上图就是一个单元矩阵,对角线元素为1,其他元素为0。
单元矩阵用m3d库定义方法:
//3*3的单元矩阵(float)
void m3dLoadIdentity33(M3DMatrix33f m);
//3*3的单元矩阵(double)
void m3dLoadIdentity33(M3DMatrix33d m);
//4*4的单元矩阵(float)
void m3dLoadIdentity44(M3DMatrix44f m);
//4*4的单元矩阵(double)
void m3dLoadIdentity44(M3DMatrix44d m);
行优先矩阵和列优先矩阵
行优先矩阵:指的是矩阵元素以行的顺序进行排列。
列优先矩阵:指的是矩阵元素以列的顺序进行排列。
OpenGL中的变化
变换 | 解释 |
---|---|
视图变换 | 观察者的位置变化 |
模型变换 | 在场景中物体的变换(平移、缩放、旋转等) |
模型视图变换 | 模型、视图变换相乘后的结果 |
投影 | 改变视景体的大小和设置投影方式(正投影、透视投影) |
视口 | 对窗口进行缩放 |
视图变换
一般来说,视图变换是引用场景中的第一个变换,它用来确定观察者的位置,默认情况中,在透视投影下位于原点(0,0,0),并沿着Z轴往负方向观察。(如同从屏幕上往屏幕里面看)。
为什么说,视图变换要放在最开始呢?
这是因为对于坐标系而言,进行试图变换,就会更换坐标系,接下来的变换都会基于新的坐标系进行。(就像我们看电视,会想用一个更舒服、更好的姿势进行观看)
模型变换
模型变换指的是针对物体进行的一系列操作变换,例如平移、旋转、缩放等。
-
平移
平移顾名思义指的是一个物体大小、弧度不变,按照一个方向进行移动。 平移
如图所示,该正方形沿着Z轴进行了平移,公式如下:
//m是一个指针,存储了变换后的结果
//x表示x轴移动的距离
//y表示y轴移动的距离
//z表示z轴移动的距离
inline void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z)
-
旋转
*平移值得是一个物体 在某个点进行旋转
image.png
如图所示,该正方形在原点进行旋转,公式如下:
//m是一个指针,存储了变换后的结果
//angle表示旋转的角度
//x表示x轴移动的距离
//y表示y轴移动的距离
//z表示z轴移动的距离
void m3dRotationMatrix44(M3DMatrix44d m, double angle, double x, double y, double z);
-
缩放
缩放指的是一个物体按一定比例进行大小的变换。
image.png 公式如下:
//m是一个指针,存储了变换后的结果
//x表示x轴缩放的比例
//y表示y轴缩放的比例
//z表示z轴缩放的比例
inline void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale)
-
投影变换
投影变换分为正投影和透视投影。 投影投影和正投影.jpg
上图中,第一个投影是透视投影,模型距离屏幕越近时,物体看起来越大,越远时越小。
第二个投影是正投影,不论物体的远近,大小看起来都是一样的。 -
综合变换
当我们需要对模型先进行平移火旋转时怎么处理呢?我们使用矩阵相乘。公式如下:
//product存放矩阵相乘后的结果
//a表示第一个矩阵
//b表示第二个矩阵
void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
矩阵堆栈
在之前的用OpenGL绘制金字塔、圆环、扇形案例中,我们已经提到过矩阵堆栈。
矩阵堆栈.png如上图所示,在栈的流程中,后来的矩阵都是通过push的方式往里面加入,而且新假如的矩阵都是存放在栈顶。当要离开栈时,需要用到pop方法,pop时是将栈顶的元素从栈中拿出来,所以栈遵循先进后出的规则(FILO)。
用法
- 矩阵定义
//矩阵的深度为64
GLMatrixStack(int iStackDepth = 64);
- 单元矩阵堆栈
//加载单元矩阵
inline void LoadIdentity(void) {
m3dLoadIdentity44(pStack[stackPointer]);
}
正如我们所说的矩阵堆栈流程,加载单元矩阵其实就是往栈顶加载一个元素,但是并没有告诉我们是什么元素,那就是默认添加一个单元矩阵。
- 添加矩阵
//加载矩阵
inline void LoadMatrix(const M3DMatrix44f mMatrix) {
m3dCopyMatrix44(pStack[stackPointer], mMatrix);
}
加载矩阵就是往栈顶添加了一个元素。
- 矩阵相乘
inline void MultMatrix(const M3DMatrix44f mMatrix) {
M3DMatrix44f mTemp;
m3dCopyMatrix44(mTemp, pStack[stackPointer]);
m3dMatrixMultiply44(pStack[stackPointer], mTemp, mMatrix);
}
由代码可以看出,首先从栈顶复制一个矩阵存放在mTemp矩阵里,然后和传入的mMatrix相乘后存入栈顶。
- 入栈(无参数)
inline void PushMatrix(void) {
if(stackPointer < stackDepth) {
stackPointer++;
m3dCopyMatrix44(pStack[stackPointer], pStack[stackPointer-1]);
}
else
lastError = GLT_STACK_OVERFLOW;
}
可以看到,判断stackPointer是否大于栈的深度,如果小于,那么stackPointer加一,然后把栈里的第二个元素复制到栈顶
- 入栈(有参数)
void PushMatrix(const M3DMatrix44f mMatrix) {
if(stackPointer < stackDepth) {
stackPointer++;
m3dCopyMatrix44(pStack[stackPointer], mMatrix);
}
else
lastError = GLT_STACK_OVERFLOW;
}
可以看到,判断stackPointer是否大于栈的深度,如果小于,那么stackPointer加一,然后把传入的参数放到栈顶
- 出栈
inline void PopMatrix(void) {
if(stackPointer > 0)
stackPointer--;
else
lastError = GLT_STACK_UNDERFLOW;
}
把stackPointer--从而移除最顶上的元素。
网友评论