OpenGL向量、矩阵的探索

2020-07-16  本文已影响0人  大橘猪猪侠

说到矩阵,很多人第一时间想到的就是数学吧,在大学时,我们应该学过矩阵的一些概念和运算。那么在OpenGL中,矩阵和我们之前学的其实是一样的。
矩阵在现实生活中用的场景非常多,那么今天我们就介绍一下OpenGL中矩阵用在哪些地方。

在OpenGL中有一个GLTools库,这个库中有一个叫Math3d,其中包含了很多OpenGL的3D数学和数据类型,我们不必亲自去计算矩阵和向量的操作。

在开发过程中,一般情况下,涉及到图形变换就会涉及到矩阵/向量的计算。

下面我们介绍一下向量:
在3D笛卡尔坐标系中,一个顶点坐标就是xyz空间上的一个位置,而这个坐标,就是由xyz定义的,这样的xyz就是向量。
既然涉及到向量,那就有单位向量,想必还记得高中知识的同学应该知道单位圆了,这个圆的向量长度为1,那么在3D坐标系当中,也是同样的原理,也就是(x2+y2+z2)0.5 = 1。

那我为什么要介绍这个单位向量呢?
因为在OpenGL中,我们需要按照一个标准去计算,这个标准就叫标准化,也就是将一个不是单位向量的坐标转化为单位向量,将他缩为1,这个过程叫单位化向量。

在OpenGL中的math3d库中有两个数据类型能够表示向量:M3DVector3f表示为一个三维向量(x,y,z),M4DVector4f表示一个四维向量(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:变量名
M3DVector3f vVector;
//声明⼀个四维向量并初始化⼀个四维向量
M3DVector4f vVertex = {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个向量必须为单元向量。计算的是两个三维向量之间进行点乘,得到的结果在[-1,1]之间,夹角的余弦值。

那在上面我们讲了向量需要标准化,那如何单位化向量?
利用公式(x/|xyz|,y/|xyz|,z/|xyz|);
使用一个非零向量除以它的模,就可以的到方向相同的单位向量。

math3d中提供的点乘API

//1.m3dDotProduct3 函数获得2个向量之间的点乘结果;
float m3dDotProduct3(const M3DVector3f u,const M3DVector3f v);
//2.m3dGetAngleBetweenVector3 即可获取2个向量之间夹⻆的弧度值;
 float m3dGetAngleBetweenVector3(const M3DVector3f u,const 
M3DVector3f v);

向量的叉乘

这个叉乘得到的结果其实就是两个向量的法线向量,它不需要为单位向量

在OpenGL也有类似的API

math3d 库中提供了关于叉乘的API
//1.m3dCrossProduct3 函数获得2个向量之间的叉乘结果得到⼀个新的向量
void m3dCrossProduct3(M3DVector3f result,const M3DVector3f u ,const M3DVector3f v); 

下面我们切入主题,讲述一下矩阵(Matrix)

15948859737473.png

上图是一个m行n列的矩阵,记做Amn。
它也可以为一行或者一列,这样的数字可以称为向量,也可以称为矩阵。

那么在OpenGL中,在定义矩阵时,我们通常使用一维数组来定义,原因是OpenGL使用的是Column-Major(以列为主)矩阵排序的约定。在数学中叫做转置矩阵。

代码参考,矩阵的声明

//三维矩阵/四维矩阵的声明
typedef float M3DMatrix33f[9];
typedef float M3DMatrix44f[16];

在一个4*4的矩阵如何表示一个3D空间呢?
答案是在矩阵最后一行都为0,只有最后一个元素为1,

单元矩阵

将一个向量*单元矩阵,这个向量不会发生任何改变

参考代码,单元矩阵的定义。

//单元矩阵初始化⽅式①
GLFloat m[] = {
1,0,0,0, //X Column
0,1,0,0, //Y Column
0,0,1,0, //Z Column
0,0,0,1 // Translation
}
// 单元矩阵初始化⽅式 ②
M3DMatrix44f m = {
1,0,0,0, //X Column
0,1,0,0, //Y Column
0,0,1,0, //Z Column
0,0,0,1 // Translation
}
//单元矩阵初始化⽅式③
void m3dLoadIdentity44f(M3DMatrix44f m); 

注意:在我们用向量乘以一个单元矩阵时,看下面两张图,第二张是不符合规定的,也就是内侧的数要相同,不相同就无法进行运算。得到的结果为两个外侧的数相乘,这个是系统这个定义的,具体的原因,我没有去细究。

15948868458423.png 15948869395912.png

在OpenGL中,我们常用到的矩阵有投影矩阵,视图变换矩阵,模型矩阵。
而在OpenGL维度中:
计算变换顶点向量 = 投影矩阵视图变换矩阵模型矩阵*顶点。
通过将矩阵压入栈中,复制栈顶矩阵,依次取出栈顶矩阵进行左乘,计算完毕后,将结果放回栈顶空间。

视图变换是应用到场景中的第一种变换,用来确定场景中有利位置,默认情况下在原点中间,并沿着z轴负方向进行观察。
而视图变换就是将观察者放在你希望它在的任何位置,并让他指向某一个方向。

从⼤局上考虑, 在应⽤任何其他模型变换之前, 必须先应⽤视图变换. 这样做是因为, 对于视觉坐标系⽽⾔, 视图变换移动了当前的⼯作的坐标系; 后续的变化都会基于新调整的坐标系进⾏.

模型变换:用于操作模型与其中的变换,就是将对象移动到任意位置。可以对图形进行旋转,平移,缩放等。

对OpenGL里的变化进行理解:

视图变换:指定观察者位置
模型变换:在场景中移动物体
模型视图:描述视图/模型变换的二元性
投影:改变视景体大小和设置它的投影方式
视口:窗口大小变化

有了模型变换和视图变换,我们就可以利用两种方式去看待模型变换的方式:
1、移动观察者
2、移动坐标系

模型变换API

//平移
void m3dTranslationMatrix44(M3DMatrix44f m, floata x, float y, float z);
//旋转
m3dRotationMatrix44(m3dDegToRad(45.0), floata x, float y, float z);
//缩放
void m3dScaleMatrix44(M3DMatrix44f m, floata xScale, float yScale, float 
zScale);
//综合变换
void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);

矩阵堆栈的使用,下面列举了一些常用API的方法

//类型
GLMatrixStack::GLMatrixStack(int iStackDepth = 64); 
//在堆栈顶部载⼊⼀个单元矩阵
void GLMatrixStack::LoadIdentity(void); 
//在堆栈顶部载⼊任何矩阵
//参数:4*4矩阵
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m); 
//矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部
void GLMatrixStack::MultMatrix(const M3DMatrix44f); 
//获取矩阵堆栈顶部的值 GetMatrix 函数
//为了适应GLShaderMananger的使⽤,或者获取顶部矩阵的副本
const M3DMatrix44f & GLMatrixStack::GetMatrix(void); 
void GLMatrixStack::GetMatrix(M3DMatrix44f mMatrix);

//将当前矩阵压⼊堆栈(栈顶矩阵copy ⼀份到栈顶) 
void GLMatrixStack::PushMatrix(void); 
//将M3DMatrix44f 矩阵对象压⼊当前矩阵堆栈
void PushMatrix(const M3DMatrix44f mMatrix); 
//将GLFame 对象压⼊矩阵对象
void PushMatrix(GLFame &frame); 
//出栈(出栈指的是移除顶部的矩阵对象)
void GLMatrixStack::PopMatrix(void);

上一篇 下一篇

猜你喜欢

热点阅读