GLTools库中有一个组件叫Math3d,其中包含了大量好用的OpenGL一致的3D数学和数据类型。虽然我们不必亲自进行所有矩阵和向量的操作,但是我们得知道它们是什么以及如何运用它们。
像我们开发中用到的图形变换,就涉及到了矩阵/向量的计算。例如CAAnimation实现的仿射变换,就用到了OpenGL渲染技术。
向量
在3D笛卡尔坐标系里,一个顶点就是xyz坐标空间里的一个位置。而在空间中给定的一个位置,恰恰是有一个单独的xyz定义的,这就是向量。数学思维里,一个顶点就是一个向量。
单位向量
例如在X轴上的向量(1,0,0),其长度为1,我们称长度为1的向量为单位向量。
向量的长度(模)的计算公式是:
![](https://img.haomeiwen.com/i8488628/e3c845fa3b3378a6.png)
= |xyz|
把一个不是单位向量的向量缩放到1的过程,叫做标准化,也叫单位化向量。
单位化向量的计算公式
(x/|xyz|,y/|xyz|,z/|xyz|)
,使用一个非零向量除以它的模,就得到方向相同的单位向量。
math3d库中,有两个数据类型可以表示一个三维或者四维向量。
M3DVeoctor3f
表示三维向量(x,y,z)
M3DVeoctor4f
表示四维向量(x,y,z,w),典型情况下,w坐标设为1.0。x、y、z值除以w(缩放因子),来进行缩放。而本质上除以1.0不改变x、y、z的值。
声明三维向量/四维向量
typedef float M3DVector3f[3];
typedef float M3DVector4f[4];
声明一个三维向量
M3DVector3f vVector;
声明一个四维向量并初始化一个四维向量
M3DVector4f vVector = {0,0,1,1};
声明一个三分量顶点数组,例如生成一个三角形
M3DVector3f vVerts[] = {
-0.5f,0.0f,0.0f,
0.5f,0.0f,0.0f,
0.0f,0.5f,0.0f
}
点乘
向量直接可以加法、减法,还可以点乘,点乘只发生在两个向量之间。
2个单位向量点乘将得到一个标量,表示两个向量之间的夹角。(也就是夹角的cos值,[-1,1]之间)
math3d提供的点乘API:
-
float m3dDotProduct3(const M3DVector3f u, const M3DVector3f v)
:获得2个向量之间的点乘结果,即余弦值 = cosα -
float m3dGetAngleBetweenVector3(const M3DVector3f u, const M3DVector3f v)
:获取2个向量之间夹⻆的弧度值,即α = arccos(余弦值)
叉乘
向量之间叉乘是业务开发中比较常用的。2个向量叉乘得到另外一个向量,会与原来2个向量定义的平面垂直,也就是平面的法线。
![](https://img.haomeiwen.com/i8488628/0cc2fb94d5bef558.png)
math3d中叉乘API:
-
void m3dCrossProduct3(M3DVector3f result, const M3DVector3f u, const M3DVector3f v)
:2个向量之间的叉乘结果得到一个新的向量。
矩阵Matrix
假设我们想求得空间中一个点(x,y,z),围绕任意位置旋转一定角度后的新位置,可以通过矩阵计算。
新位置x不只与原来的x、旋转参数有关,还和y、z坐标有关。
只有一行或一列的矩阵,也可以称为向量。
在其他编程标准中,许多矩阵库定义一个矩阵时,使用二维数组。而在OpenGL的约定中,更多倾向使用一维数组。原因是:OpenGL使用的是column-Major(以列为主)矩阵排序的约定,这个在数学中叫转置矩阵。
![](https://img.haomeiwen.com/i8488628/8a7e386a26dd6c5f.png)
这两个矩阵的内容是一样的。第一个矩阵读取数据的顺序是{A0,A1,A2,A3,A4...A15},第二个矩阵读取的顺序是{A0,A4,A8,A12,A1...A15}.
这16个值表示空间中一个特定的位置,这4列中每一列都是由4个元素组成的向量。如果将一个对象所有的顶点向量乘以这个矩阵,就能让整个对象变换到空间中给定的位置和方向。
列向量进行了特殊的标注,矩阵的最后一行都是0,只有最后一个元素为1。
![](https://img.haomeiwen.com/i8488628/4d67e4549c552f80.png)
1. 单元矩阵
有三种初始化方式:
①
GLFloat m[] = {
1,0,0,0, //X
0,1,0,0, //Y
0,0,1,0, //Z
0,0,0,1 //Translation
}
②
M3DMatrix44f m = {
1,0,0,0, //X
0,1,0,0, //Y
0,0,1,0, //Z
0,0,0,1 //Translation
}
③
void m3dLoadIdentity44f(M3DMatrix44f m);
叉乘规则
- 将一个向量叉乘单元矩阵,就相当于一个向量 X 1,不会发生任何改变;
-
两个矩阵相乘,前一个矩阵的列数必须等于后一个矩阵的行数,否则不符合矩阵规则。
顶点向量的变换计算
1.线性代数角度
为了便于书写,都是从左到右计算的。从数学角度上由于顶点是行向量,要满足矩阵相乘的规定条件(即 前一个矩阵列数等于后一个矩阵行数),必须将mvp矩阵(模型矩阵、视图矩阵、投影矩阵)放在右边,属于右乘。
公式:
变换后顶点向量 = V_local * M_model * M_view * M_pro
变换后顶点向量 = 顶点 * 模型矩阵 * 观察矩阵 * 投影矩阵
![](https://img.haomeiwen.com/i8488628/7b41f75988710d4f.png)
2.OpenGL角度
OpenGL中的矩阵是以列优先,所以顶点以列向量的方式表示,从OpenGL角度上由于顶点是列向量,要满足矩阵相乘的条件,顶点向量需要向左乘变换矩阵,之后mvp矩阵的顺序颠倒为pvm,才能保证相乘结果一致(因为矩阵叉乘不满足交换律)。称为左乘。
![](https://img.haomeiwen.com/i8488628/f0060a85acce2e06.png)
可以从OpenGL矩阵堆栈中矩阵相乘源码分析
![](https://img.haomeiwen.com/i8488628/999bdb9cde3cf588.png)
- 主要分为3步:
- 从栈顶获取栈顶矩阵,复制到mTemp
- 将栈顶矩阵 mTemp 左乘 mMatrix
- 将结果放回栈顶空间里
-
矩阵堆栈变化代码段:
矩阵堆栈变化代码段
![](https://img.haomeiwen.com/i8488628/f521197788c05b1c.png)
-
在顶点着色器中的代码段:
顶点着色器中的代码段
网友评论