接下来学习这些步骤的每一个过程到底是什么意义。
我们来设身处地的思考一下,假如我正要开发一个3D射击游戏,首先我需要一些模型用于构建场景,于是我打开3dmax建模工具,迎面而来的是一个带标识网格的三维空间,这个空间也就是所谓的建模空间,我随便拖动一个基本立体模型到建模空间的任意位置(随便拖动的嘛,也就拖动到屏幕中央方便观察就行),然后对基本立体模型进行顶点添加和拖拽编辑,在操作的过程中,为了方便我是随便旋转和移动模型的,模型做好以后,我就将模型导出成fbx文件,这个fbx文件就断开了和建模空间的任何联系,包括其旋转平移缩放的值,也就是说在模型空间中创建模型的过程和空间本身的关系并不太大,至少那些模型基本变换属性(旋转平移缩放)都不重要,方便自己的操作就行,导出后就没有任何关系了。
接下来,我打开unity引擎(ue或者ce引擎),迎面而来的又是一个带标识网格的三维空间,这个空间也就是世界空间,世界空间的目的是为了给游戏场景中所有物体提供一个绝对参考系,因为我接下来所有工作就是在这个空间中进行。接下来我将刚才的fbx模型当作资源文件拷贝到引擎的资源文件夹中,这时候fbx模型文件和世界空间是没有任何联系的,紧接着我就要使用这个fbx模型文件了,我把fbx模型拖拽到世界空间中的任意位置(因为我就是随便拖拽的),当然随便拖的位置肯定不满足要求啦!我需要构建的场景需要将这个模型首先移动到世界坐标系X轴10刻度位置(这个所谓的”刻度“也是一个虚拟单位,比如说一刻度就是一米),然后我需要将模型沿着世界坐标系Z轴旋转45°(也就是斜朝上),最后我还需要将模型缩放0.5,这样才能达到我构建场景的目的。这个时候我们就需要使用世界坐标系下的平移旋转缩放矩阵进行变换(前面线性代数有推导过程),这个矩阵变换的过程就是建模变换,建模变换的目的也就是使用平移旋转缩放矩阵将任意模型变换到特定的位置。
场景摆放完毕后,我继续往下开发,我要做的是3D射击游戏,当然就要添加一个character角色控制器,这个character也就是我需要写代码控制的主角,主角通过第一人称视角开枪射击击杀敌人,这个第一人称视角就需要我添加一个camera摄像机到character的头部眼睛位置,这个camera摄像机的视椎体照射到的空间区域也就是character能看到区域(当然也就是玩家能看到区域),顺便说一下前面(三维空间中摄像机的视域形成的一个平截体)和此时的视椎体是一个东西,形象来说就是模拟的人眼能看到的广角视野范围,示意图如下:
前一个铅笔平面也就是视域的最近平面,后一个铅笔平面就是视域的最远平面了,好比人眼看不到距离瞳孔很近的物体或者远在天边的物体一个道理,当然程序中,这两个near和far的值是个变量可以随便更改。
问题来了,之前的fbx模型是以世界空间做参考系的,这时候又要用character上眼睛空间当作参考系,这就需要一个矩阵变换来将fbx模型从世界空间变换到眼睛空间了,前面我们学习线性代数可以认为这就是两个仿射空间进行一次矩阵变换的过程,我们可以就将这个变换叫做视口变换。
视口变换的详细过程也好理解,假设我的眼睛在世界空间的原点位置并且眼睛视线方向沿着世界坐标系的X轴方向,那么我(假设我是character)到处运动和观察,也就相当于我的眼睛在世界空间中做了平移和旋转的矩阵变换,同理我们反过来思考,假设我的眼睛就固定在世界空间中某个位置观察,那么如果我把世界坐标系平移到我的眼睛位置同时将世界坐标系的X轴旋转到我的视线轴方向,那么就完成了视口变换,模型也就合理的变换到了我的眼睛空间内。
so,继续探讨,前面我们已经把模型变换到了我的眼睛空间内,但是实际上还需要一个裁剪过程,因为前面的视椎体可以看出,人眼观察物体还是有一个区域范围的,比如我站在一栋摩天大楼面前,一眼是望不到顶的,只能看到大楼的正门口一块区域而已。这个时候就出现了一个裁剪空间的概念,裁剪空间其实就是视椎体可观察到的区域空间的一个规范化空间,裁剪空间的作用就是将处于该空间范围内的图元(多边形、线段和点)进行保留,该空间范围外的图元(多边形、线段和点)则丢弃,同时进行坐标标准化(归一化)。从眼睛空间到裁剪空间的变换叫做裁剪变换(也可以叫透视投影变换),这里以unity为例,裁剪变换的细节过程包括:
①.建立规范化的裁剪空间(规则化观察体),这里可以认为是一个边长为2的立方体。
②.将不规则视椎体空间坐标系中模型变换到规范化观察立方体的空间坐标系。
下图可以简略形象的观察一下这个过程:
这里解释一下为什么变换后的裁切空间坐标系的Z轴是反向的,因为裁切空间规定z值越大,越靠近眼睛,也就是near面的z值为1,far面的z值为-1。
裁切(透视投影)变换就是将左边的眼睛空间的变换到右边的规范化裁切空间,这个数学上详细推导过程我会另外开篇讲解。
到了这一步了,我们已经将模型处理到一个规范化裁剪空间中了,并且处于裁切空间中的模型坐标也进行了标准化(可以理解为在裁切空间坐标系下的xyz的归一化),此时物体的顶点坐标形式为齐次坐标<x,y,z,w>其中w为xyz的归一化分母,这里不就是再次体现出了齐次坐标的作用!
接下来我们要做的就是将这个模型从裁剪空间变换到标准设备空间,此时所有可见的几何图元信息都位于裁切空间立方体中,这是只需要做一个w分量为分母的除法,也就是说将裁切空间中顶点坐标<x,y,z,w>除以w得到<x/w,y/w,z/w,1>,这就是将所有坐标全部归一化了,此除法生成的结果坐标叫做标准化设备坐标,这个过程也叫透视除法。
最后一个步骤了,我们已经得到标准设备空间中的归一化图元坐标信息,但是我们日常使用中的显示器分辨率各不相同,常见的有4k 2k 1080p 720p和各种异型的分辨率(比如魅族手机),他们的屏幕像素矩阵行列是各不相同的(假设为m行n列),此时我们将标准设备空间中顶点的归一化坐标<x/w,y/w,z/w,1>中x/w,y/w分别乘以屏幕像素矩阵的n,m就能得到新的屏幕<x',y'>坐标,这个过程叫做视图变换,同时z/w分量经过插值到深度缓存的范围(假如[0-256])内并储存起来,这个深度depth信息可以起到渲染层级判断,图形混合等作用,这个叫深度范围变换。
进过上面一系列空间变换后,算是完成了“顶点变换”的机器把顶点位置变换到屏幕位置的骚操作了,接着进行图形流水线流程,我的3D射击游戏场景将最终呈现到游戏设备屏幕上了。