OpenGL--向量、矩阵、基础变换与矩阵堆栈

一、 向量

1.1 什么是向量

在 3D 笛卡尔坐标系,基本上⼀个顶点就是XYZ 坐标空间上的⼀个位置,⽽在空间中给定的⼀个位置恰恰是由⼀个单独的 XYZ 定义的,⽽这样的 XYZ 就是向量。

1.2什么是单位向量

在 X轴上的向量 (1,0,0),向量⻓度为1. 我们称为⻓度为1的向量为单位向量。
向量长度计算公式

如果⼀个向量不是单位向量,⽽我们把它缩放到1,这个过程叫做标准化。将⼀个向量进⾏标准化,也就是将它的长度缩为1,这个过程也叫做单位化向量;

1.3那么在OpenGL中是如何定义向量的呢?
  • 在OpenGL中有很多库已经帮助我封装了相关的方法,那么本篇文章内容主要通过math3d库来描述的。在math3d中有两个数据类型来表示一个三维向量一个四维向量,M3DVector3f表示一个三维向量(x,y,z);M3DVector4f则表示一个四维的向量(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
};
向量的点乘
  • 向量可以进⾏加法,减法计算.,但是向量⾥有⼀个在开发中使⽤价值⾮常⾼的操作,叫做点乘(dot product) 。点乘只能发⽣在2个向量之间进⾏;2个(三维向量)单元向量之间进⾏点乘运算将得到⼀个标量(不是三维向量,是⼀个标量)。它表示两个向量之间的夹⻆;
    前提条件: 2个向量必须为单位向量;
    动作:2个三维向量之间进⾏点乘;
    结构:返回⼀个[-1,1]范围的值。这个值其实就是夹⻆的cos值(余弦值)。
  • 如何单位化向量?
    (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);
向量的叉乘
  • 向量之间的叉乘(cross product) 也是在业务开发⾥⾮常有⽤的⼀个计算⽅式;2个向量之间叉乘就可以得到另外⼀个向量,新的向量会与原来2个向量定义的平⾯垂直。 同时进⾏叉乘,不必为单位向量;
    前提: 2个普通向量;
    动作:向量与向量叉乘;
    结果: 向量(垂直于原来2个向量定义的平⾯的向量);
  • math3d 库中提供了关于叉乘的API
//1.m3dCrossProduct3 函数获得2个向量之间的叉乘结果得到⼀个新的向量
void m3dCrossProduct3(M3DVector3f result,const M3DVector3f u ,const M3DVector3f v); 

二、矩阵Matrix

2.1为什么需要矩阵

假设在空间有⼀个点,这个点使⽤x、y、z 描述它的位置,此时让其围绕任意位置旋转⼀定⻆度后,我们需要知道这个点的新的位置,此时我们就需要通过矩阵进⾏计算了。
为什么需要新的点位置就需要矩阵计算呢?
因为新的位置的x不单纯与原来的x有关系,而且还和旋转的参数有关,甚⾄于y和z坐标有关;矩阵只有⼀⾏或者⼀列都是合理的,只有⼀⾏或者⼀列数字可以称为向量,也可以称为矩阵;
-在其他的编程标准中,其他的矩阵库东哟时候使用的是二维数组,而OpenGL的约定里,更多倾向的是使用一维数组,这样做的主要原因是因为OpenGL使用的是Column-Major(以列为主)矩阵排序的约定(数学上叫做转置矩阵)。


截屏2020-07-17 17.08.26.png
截屏2020-07-17 17.08.30.png
//三维矩阵/四维矩阵的声明
typedef float M3DMatrix33f[9];
typedef float M3DMatrix44f[16];
2.2⼀个4*4矩阵是如何在3D空间中表示⼀个位置和⽅向的
截屏2020-07-17 17.11.18.png
  • 列向量进⾏了特别的标注:矩阵的最后⼀⾏都为0,只有最后⼀个元素为1;
  • 在于这 16 个值表示空间中⼀个特定的位置;这4列中每⼀列都是有4个元素组成的向量;
  • 如果将⼀个对象所有的顶点向量乘以这个矩阵,就能让整个对象变换到空间中给定的位置和⽅向;
2.3单元矩阵 Matrix
  • 单元矩阵初始化三种方式
//单元矩阵初始化⽅式①
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); 
  • 将⼀个向量乘以单元矩阵,就相当于⼀个向量乘以1;原来的向量不会发⽣任何改变
  • 在线性代数数学的维度,为了便于书写,所以坐标计算都是从左往右顺序进⾏计算的 如下列公式
    变换后顶点向量 = V_local * M_model * M_view * M_pro
    变换后顶点向量 = 顶点 x 模型矩阵 x 观察矩阵 x 投影矩阵
  • 在OpenGL的角度
    变换顶点向量 = M_pro * M_view * M_model * V_local
    变换顶点向量 = 投影矩阵 x 视图变换矩阵 x 模型矩阵 x 顶点
  • 矩阵左乘
  1. 从栈顶获取栈顶矩阵复制到 mTemp
  2. 将栈顶矩阵mTemp左乘mMatrix
  3. 将结果放回栈顶空间⾥;
    源代码
    inline void MultMatrix(const M3DMatrix44f mMatrix) {
            M3DMatrix44f mTemp;
            m3dCopyMatrix44(mTemp, pStack[stackPointer]);
            m3dMatrixMultiply44(pStack[stackPointer], mTemp, mMatrix);
            }

三、基础变换

3.1视图变换:指定观察者位置
  • 视图变换是应⽤到场景中的第⼀种变换,它⽤来确定场景中的有利位置,在默认情况下透视投影中位于原点(0,0,0),并沿着 z 轴负⽅向进⾏观察 (向显示器内部”看过去”)。当观察者点位于原点(0,0,0) 时,就像在透视投影中⼀样。视图变换将观察者放在你希望的任何位置并允许在任何⽅向上观察场景确定视图变换就像在场景中放置观察者并让它指向某⼀个⽅向;
  • 从⼤局上考虑,,在应⽤任何其他模型变换之前,必须先应⽤视图变换, 这样做是因为对于视觉坐标系⽽⾔,视图变换移动了当前的⼯作的坐标系;后续的变化都会基于新调整的坐标。
    系进⾏
3.2模型变换:在场景中移动物体
  • ⽤于操纵模型与其中某特定变换,这些变换将对象移动到需要的位置,通过旋转,缩放,平移的方式。
  • 模型变换-平移
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);
3.3投影变换:改变视影体的大小和设置它的投影方式

四、矩阵堆栈

  • 代码
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);
仿射变换
  • 仿射变换,又称仿射映射,是指在几中,一个向量空间进行一次线性变换并接上一个平移,变换为另一个向量空间。

  • 代码

//Rotate 函数angle参数是传递的度数,⽽不是弧度
void MatrixStack::Rotate(GLfloat angle,GLfloat x,GLfloat y,GLfloat z); 

void MatrixStack::Translate(GLfloat x,GLfloat y,GLfloat z); 

void MatrixStack::Scale(GLfloat x,GLfloat y,GLfloat z);

你可能感兴趣的:(OpenGL--向量、矩阵、基础变换与矩阵堆栈)