模型从硬盘文件到显示器像素的全流程分析
- 引言
- 一、硬盘中的模型文件
- (1)分离式模型文件
- (2)模型主文件
- 2.1 顶点几何与索引数据构成模型表面
- 2.2 通过材质或贴图为模型表面着色
- 2.3 通过骨骼动画驱动模型
- 二、CPU中的模型数据
- 三、GPU中的模型数据
- 三、Shading
引言
-
艺术家使用Blender、MAYA、3DS MAX等建模软件将设计好的模型导出为硬盘文件,而最终我们看到的模型是显示器上的像素,从硬盘文件到显示器像素,这其中究竟发生了什么?
-
本文主要对 “模型从硬盘文件到显示器像素” 的全过程进行理论分析,硬盘文件即艺术家导出的模型文件,显示器像素即显示器屏幕上显示模型的像素。
一、硬盘中的模型文件
-
当艺术家导出模型时需要选择文件格式,常见的模型文件格式有:FBX、GLTF、OBJ等,不同格式存储模型数据的方法大相径庭,同一格式也可能由于版本和形式不同导致存储的数据不同。例如GLTF具有1.0和2.0两个不同版本,FBX具有分离式和嵌入式两种不同形式。分离式和嵌入式是许多模型格式都具有的不同形式,分离式可以将模型的贴图分离到一个文件夹中存储,而嵌入式则会将贴图直接嵌入到模型文件中。下图展示了广泛使用的GLTF格式。
-
使用怎样的模型格式是一个值得考虑的问题,本文考虑到FBX和GLTF格式使用的广泛性,使用FBX和GLTF格式作为研究对象,为了能够直接查看和处理模型贴图,采用分离式FBX和GLTF。
(1)分离式模型文件
- 对于分离式模型文件,其存储的内容如下图所示。模型文件分为主文件和纹理文件夹,这是因为采用了分离式,所以纹理文件并不包含在模型主文件中。
- 纹理文件中存储了模型的一系列贴图,可能是JEPG、PNG和DSS等格式,纹理文件其实就是一个文件夹,打开后可以直接查看和修改其中的纹理图片。而模型主文件往往是代表整个模型的文件,比如分离式GLTF2.0的模型主文件是xxx.gltf,而FBX的模型主文件是xxx.fbx。
- 经典的GLTF2.0分离式模型文件如下图所示,其中textures文件夹保存了模型的纹理文件,而scene.gltf为模型主文件。当你使用建模软件或游戏引擎读取模型文件时,选择模型主文件即可代表整个模型文件。
(2)模型主文件
- 模型主文件存储的内容如下图所示。接下来将介绍顶点的几何数据、顶点几何数据与索引数据如何构成模型表面、如何通过材质或贴图为模型表面着色、如何使用骨骼动画驱动模型。
2.1 顶点几何与索引数据构成模型表面
- 顶点数据存储了一系列顶点,每个顶点具有自己的几何数据即:位置、法向量、纹理坐标、切线向量。
- 有了顶点要如何组成世界中的实体呢?答案是必须要将顶点连接起来,构成模型表面,这样才能作为世界中的实体被人们看到。游戏中常常使用三角形来构成模型表面,那么如何才能知道哪三个顶点构成一个三角形呢?我们可以将顶点按照构成三角形的集合依次排开,这样顶点数组中每三个顶点即对应一个三角形。
- 虽然上述方法能够解决问题,但它并不好,因为一个顶点可能会构成多个三角形,因此这种方法会造成一个顶点数据在数组中保存多次。考虑使用一个索引数组,每个索引可以获取到顶点数组中的对应顶点,每三个索引即可对应到一个三角形,这样使用相同的顶点时我们仅需指定相同的索引即可,而不用保存重复的顶点数据。当然构成模型表面的也可以是四边形,这称为模型的图元拓扑类型。
- 下图展示了如何由顶点构成模型表面。可以看到石头凸起的地方为顶点,多个顶点连接构成一个多边形面,多个面构成了物体表面。采用类似下图的多边形面数建模人物只能建出方块人,易知多边形的面数越多则其细节越丰富,但多边形数的增大会导致GPU计算量的越大,因此许多模型网站提供“低多边形”的模型分类,以此满足较低性能设备的开发需求。
2.2 通过材质或贴图为模型表面着色
- 由2.1中我们即可构建出模型的表面,我们平常在游戏中看到的模型就是通过许多个三角形构成的表面,那么如何才能为模型的表面上色呢?
- 方案一:采用纹理贴图。每个顶点具有一个纹理坐标,如果模型具有纹理贴图,查找顶点对应的纹理贴图在纹理坐标处的颜色值,将此颜色值作为此顶点的颜色,这样每一个顶点都具有了颜色。当显卡渲染物体表面时,每个顶点处的像素可直接使用顶点颜色,而顶点之间的像素使用数学插值的方法计算出一个颜色值,这样物体表面每一个像素都具有颜色了,即完成了模型表面的着色。当然,对于纹理坐标和纹理贴图等数据,由艺术家负责生成,我们只需使用即可。
- 方案二:采用材质数据。考虑不采用贴图,如果我们为每个顶点附带一个颜色值材质数据,以此指定顶点的颜色,那么也可以完成模型表面的着色。
- 虽然上述方案可以完成物体表面的着色,但是它的效果是非常差劲的,请回忆3A游戏的画面,其中模型表面的颜色并不是一成不变的,它会随着灯光和摄像机的视角发生改变,产生高光、阴影等许多效果。因此使用单一的颜色值直接着色是不可取的,除非如果你只是想画个白色方块。高级的着色效果如下图所示。
- Phong模型是一种来源于经验和直觉的着色模型,使用它计算顶点的最终颜色,即可让物体表面产生高光的效果。它的升级版着色模型是Blinn-Phong。目前实时渲染领域最为流行和逼真的着色模型为PBR模型,它是基于物理的渲染模型。后面我们会介绍PBR着色模型,现在你只需要知道PBR模型以基础颜色、金属度、粗糙度、自发光、环境光遮蔽等属性作为输入即可,通过这些属性,PBR着色模型就能计算出顶点的最终颜色。
- 互联网上许多模型都是使用PBR着色的,即模型的创作者在设计模型时就生成了顶点的基础颜色、金属度、粗糙度等信息,我们直接使用即可。类似于上述的两种着色方案,PBR需要的输入信息也可以保存在纹理贴图或材质中。艺术家可能在纹理文件中放置了基础颜色、金属度、粗糙度等贴图,这样在为顶点着色时根据其纹理坐标和各个贴图,即可获取顶点的基础颜色、金属度、粗糙度等属性,以此作为输入,即可计算出PBR模型下顶点的颜色。
- 艺术家也可以不生成模型贴图,而为每个顶点指定所用材质,不同材质有不同的基础颜色、金属度、粗糙度等属性,这时我们就需要直接根据顶点材质数据获取所需属性。
2.3 通过骨骼动画驱动模型
- 使用PBR着色模型我们能够渲染出类似上图球体的酷炫效果,但是如果整个3D场景一动不动,那么还有什么意思呢?简单的模型移动不能满足用户的需求,我们在游戏中常常会试图使用角色进行攻击、跳跃和躲避,只有实现这些功能才能使模型变得生动,那么该如何实现呢?
- 使用骨骼动画即可实现驱动模型的效果。考虑人体骨架,挥动我们的胳膊小臂,我们发现手会跟着运动,抬起我们的二头肌即胳膊大臂,我们发现小臂和手都跟着运动了。易知人体骨架其实是一种层次关联关系,当父对象运动时子对象也需要运动,以此保持父子的相对位置关系不变。
- 我们通常将人体骨架的盆骨作为根骨骼,一个标准的人体骨架如下图所示。
- 假设有一个骨骼数组int bone[BoneNum],其中bone[i]表示第i块骨骼的父骨骼索引,据此我们就可以构建骨架的树状层次结构。每个骨骼都有一个至父矩阵,当骨骼乘以其至父矩阵后便能变换到其父骨骼空间中,因此根据bone和至父矩阵,我们要将一块骨骼如手变换至顶级坐标系时,就要先将其乘以至父矩阵变换到小臂坐标系中,再乘以小臂坐标系变换到大臂坐标系中,这样子对象就受到了父对象的影响,当大臂运动时,此计算机制将导致小臂、手随之运动。
- 当然骨架的根骨骼即盆骨是没有父对象的,因此其至父矩阵为单位矩阵,其他骨骼都需要一路至父变换到根骨骼。弄清楚了骨架之间如何实现父骨骼影响子骨骼,我们考虑如何变换顶点。模型中的骨骼是看不见的,显示器显示的是物体表面即顶点,那么如何让顶点随着骨骼运动呢?考虑胳膊肘处的顶点,它会随着小臂和大臂的移动而被拉伸即移动,因此顶点可能受到多块骨骼的影响(一般最多为四块),模型文件中记录了影响顶点的骨骼索引和权重数组,比如骨骼索引数组可能为:3 5 7 9,而权重数组可能为:0.1 0.3 0.4 0.2。每块骨骼具有一个偏移矩阵,受其影响的顶点乘以偏移矩阵即可变换到骨骼空间。每个顶点将位置以此乘以每个关联骨骼的最终变换矩阵及权重,再对其求和,即可计算出顶点的位置,以此实现骨骼动画。