系列推荐文章:
OpenGL/OpenGL ES入门:图形API以及专业名词解析
OpenGL/OpenGL ES入门:渲染流程以及固定存储着色器
OpenGL/OpenGL ES入门:图像渲染实现以及渲染问题
OpenGL/OpenGL ES入门:基础变换 - 初识向量/矩阵
OpenGL/OpenGL ES入门:纹理初探 - 经常使用API解析
OpenGL/OpenGL ES入门: 纹理应用 - 纹理坐标及案例解析(金字塔)
OpenGL/OpenGL ES入门: 顶点着色器与片元着色器(OpenGL过渡OpenGL ES)
OpenGL/OpenGL ES入门: GLKit以及API简介
OpenGL/OpenGL ES入门: GLKit使用以及案例程序员
肯定对象位置和方向的能力对于任何3D图形编程人员来讲都是很是重要的,正如咱们将要看到的,围绕着原点来描述对象的维度,再将对象变换到须要的位置其实是很是方便的。编程
为空间中(任意选择)的一个点,以及空间中从坐标系原点到这个点坐标的一条带箭头的线段,这个带箭头的线段能够视为一个向量数组
向量可以表明的第一个量就是方向,第二个量就是数量。 方向:好比X轴就是向量(1,0,0)。在X方向为+1,而在Y方向和Z方向则为0; 数量:一个向量的数量就是这个向量的长度。对于上面X轴的向量(1,0,0)来讲,向量的长度为1。咱们把长度为1的向量称为单位向量bash
在math3d
库有两个数据类型,可以表示一个三维或四维向量:M3DVertor3f
能够表示一个三维向量(X,Y,Z),而M3DVertor4f
则能够表示一个四维向量(X,Y,Z,W),其中W
为缩放因子,通常状况下为1.函数
// 简单的声明
M3DVector3f vVector;
M3DVector4f vVertex = { 0.0f, 0.0f, 1.0f, 1.0f};
// 声明一个三份量顶点数组
M3DVector3f vVerts[] = {
-0.5f, 0.0f, 0.0f,
0.5f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f };
复制代码
两个单位向量之间的点乘运算将获得一个标量(只有一个值),它表示两个向量之间的夹角。要进行这种运算,这两个向量必须为单位向量,返回的结果将在-1~1之间,实际上就是这两个向量之间夹角的余弦值工具
math3d
库中也包含一些有用的函数使用点乘操做。post
m3dDotProduct3
函数来实际得到两个向量之间点乘结果ui// 两个单位向量夹角的余弦值 float m3dDotProduct3(const M3DVector3f u, const M3DVector3f v);spa
m3dGetAngleBetweenVectors3
函数返回夹角的弧度值 // 返回这个夹角的弧度值 float m3dGetAngleBetweenVectors3(const M3DVector3f u, const M3DVector3f v);设计
两个向量之间叉乘所得的结果是另一个向量,这个新向量与原来两个向量定义的平面垂直。要进行叉乘,这两个向量都没必要为单位向量。 与点乘还有一个不一样之处是叉乘不符合交换定律即 V1 X V2 != V2 X V1.
math3d
库中也有一个函数m3dCrossProduct3
对两个向量进行叉乘并返回运算获得的结果向量
void m3dCrossProduct3(M3DVector3f result, const M3DVector3f u, const M3DVector3f v);
它是一种功能很是强大的数学工具,大大简化了求解变量之间有复杂关系的方程或方程组的过程。其中一个具备广泛性的例子和图形程序设计人员密切相关,就是坐标变换。 例如,若是在空间中有一个点,由x,y和z坐标定义,将它围绕任意点沿任意方向选择必定角度后,我门须要知道这个点如今的位置,就要用到矩阵。为何呢?由于新的x坐标不只与原来的x坐标和其余旋转参数有关,还与原y和z坐标值有关。这种变量与解之间的相关性就是矩阵最擅长解决的问题。
在咱们进行3D程序设计工做是,咱们将使用的几乎所有是两种维度的矩阵,即3 x 3 和 4 x 4矩阵。在math3d
库中,也有这两种维度的矩阵数据类型。
typedef float M3DMatrix33f[9]; typedef float M3DMatrix44f[16];
在咱们指定顶点和这些顶点出如今屏幕上之间的这段时间里,可能会发生3中类型的几何变换: 视图变换、模型变换和投影变换
视觉坐标是相对于观察者的视角而言的,不管可能进行何种变换,咱们均可以将它们视为“绝对的”屏幕坐标。
从观察者的角度来看,x轴和y轴的正方形分别指向右方和上方。z轴的正方形从原点指向使用者,而z轴的负方向则从观察者只想屏幕内部。
视图变换是应用到场景中的第一种变换。他用来肯定场景中的游离位置。在默认状况下,透视投影中的观察点位于原点(0,0,0),并沿着z轴的负方向进行观察(向显示器内部看)。观察点相对于视觉坐标系进行移动,来提供特定的有利位置。当观察点位于原点(0,0,0)时,就像在透视投影中同样,绘制在z坐标为正的位置的对象则位于观察者背后。 在正投影中,观察者被认为是在z轴正方向无穷远的位置,可以看到视景体中的任何东西。 视图变换容许咱们把观察点放在所但愿的任何位置,并容许在任何方向上观察场景,肯定视图变换就想在场景中放置照相机并让它指向某个方向。
下图展现了3种最广泛的模型变换:
平移: 对象沿着给定的轴进行移动
旋转: 对象围绕着一条坐标轴进行旋转
缩放: 对象的大小进行了指定数量的放大或缩小。缩放能够是不均匀的,即不一样维度能够进行不一样程度的缩放。
场景或对象的最终外观可能在很大程度上取决于应用的模型变换顺序。 以下图,模型变换先旋转后平移与先平移后旋转,结果是不一样的。
实际上,视图和模型变换按照它们内部效果和对场景的最终外观来讲是同样的。将这二者分开是为了咱们(程序员)方便。好比将对象向后移动和将参考坐标系向前移动在视觉上没有区别,以下图:
模型视图是指这两种变换在变换管线中进行组合,成为一个单独的矩阵。即模型视图矩阵。
投影变换将在模型视图变换以后应用到顶点上。这种投影实际上定义了视景体并建立了裁剪平面。 更具体的说,投影变换指定一个完成的场景(全部模型变换都已完成)是如何投影到屏幕上的最终图像。
当上述都完成后,就获得一个场景的二维投影,它将被映射到屏幕上某处的窗口上。这种到物理窗口的映射是咱们最后要作的变换,即视口变换。
这个过程图形硬件会为咱们作好,因此没必要太操心这个过程。
将一个向量乘以一个单位矩阵,就至关于用这个向量乘以1,不会发生任何变化。 单位矩阵中除了对角线上的一组元素为1以外,其余元素均为0.
能够在OpenGL
中这样生成一个单位矩阵:
GLFloat m[] = { 1.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,
0.0f.0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f };
或者使用`math3d`的`M3DMatrix44f`类型:
M3DMatrix44f m = { 1.0f,0.0f,0.0f,0.0f,
0.0f,1.0f,0.0f,0.0f,
0.0f.0.0f,1.0f,0.0f,
0.0f,0.0f,0.0f,1.0f };
复制代码
在math3d
库中,还有一个快捷函数m3dLoadIdentity44
,这个函数初始化一个单位矩阵。
void m3dLoadIdentity44(M3DMatrix44f m);
一个平移矩阵仅仅是将咱们的顶点沿着3个坐标轴重的一个或多个进行平移。
能够调用math3d
库中的m3dTranslationMatrix44
函数来使用变换矩阵
void m3dTranslationMatrix44(M3DMatrix44f m, float x, float y, float z);
将一个对象沿着3个坐标轴中的一个或任意向量进行旋转,须要一个旋转矩阵。
void m3dRotationMatrix44(M3DMatrix44f m, float x, float y, float z);
缩放矩阵能够沿着3个坐标轴方向按照指定因子放大或缩小全部顶点,以改变对象的大小。 缩放不必定是一致的,咱们能够在不一样的方向同时使用它来进行伸展和压缩。
M3DMatrix44f m;
void m3dScaleMatrix44(M3DMatrix44f m, float xScale, float yScale, float zScale);
为了将对象移动道想要的位置,咱们可能须要先将它平移到指定位置,而后在旋转获得想要的结果。又由于4 x 4变换矩阵包含一个位置和一个方向,那么一个矩阵就能够完成这两种转换。只需将两个矩阵相乘,结果获得的矩阵包含结合道一块儿的转换,都在一个矩阵中。 在math3d
库中,m3dMatrixMultiply44
用来将两个矩阵相乘并返回运算结果。
void m3dMatrixMultiply44(M3DMatrix44f product, const M3DMatrix44f a, const M3DMatrix44f b);
// GLMatrixStack类 这个类的构造函数容许指定堆栈的最大深度,默认的堆栈深度为64.
// 同时这个矩阵堆栈在初始化时,已经在堆栈中包含了单位矩阵。
GLMatrixStack::GLMatrixStack(int iStackDepth = 64);
// 在堆栈顶部载入一个单元矩阵
void GLMatrixStack::LoadIdentity(void);
// 在堆栈顶部载入任何矩阵
// 参数:4x4矩阵
void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
// 矩阵乘以矩阵堆栈顶部矩阵,相乘结果存储到堆栈的顶部
void GLMatrixStack::MultMatrix(const M3DMatrix44f);
// 获取矩阵堆栈顶部的值 GetMatrix 函数
// 为了适应GLShaderManager的使用,或者获取顶部矩阵的副本
const M3DMatrix44f & GLMatrixStack::GetMatrix(void);
void GLMatrixStack::GetMatrix(M3Datrix44f mMatrix);
复制代码
一个矩阵的真正价值在于经过压栈操做存储一个状态,而后经过出栈操做恢复这个状态。 经过GLMatrixStack
类,咱们可使用PushMatrix
函数将矩阵压入堆栈来存储当前矩阵的值。而PopMatrix
将移除顶部矩阵,并恢复它下面的值。
// 将当前矩阵压入堆栈(栈顶矩阵copy一份到栈顶)
void GLMatrixStack::PushMatrix(void);
// 将M3DMatrix44f矩阵对象压入当前矩阵堆栈
void PushMatrix(const M3DMatrix44f mMatrix);
// 将GLFrame对象压入矩阵对象
void PushMatrix(GLFrame &frame);
// 出栈(出栈指的是移除顶部的矩阵对象)
void GLMatrixStack::PopMatrix(void);
复制代码
具体过程参照下面流程图
GLMatrixStack
类也内建了对建立旋转、平移和缩放矩阵的支持。以下函数:
// 平移
void MatrixStack::Translate(GLfloat x, GLfloat y, GLfloat z);
// 旋转 参数angle是传递的度数, 而不是弧度
void MatrixStack::Rotate(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
// 缩放
void MatrixStack::Scale(GLfloat x, GLfloat y, GLfloat z);
复制代码