上一篇我们实现了一个基本的BMP文件解析器。事实上,对于游戏制作来说,BMP文件并不是一种好的文件形式,这主要因为:
- BMP文件并不是跨平台的。很多平台都有BMP文件,但是其格式其实是有微妙的不同的;
- BMP文件一般不采用压缩,所以比较大;
- BMP文件在图片制作元数据及色彩管理等方面功能有限;
因此,我们还需要加入几种其它的图片文件格式支持,如PNG、JPG等。这些文件相对于BMP都要复杂一些,同时涉及到一些较为复杂的压缩算法,也正好是给我们测试我们的(并行计算)数学库的一个机会。
不过因为我们急切地想看到我们的第一个场景渲染结果,所以这里暂时把这些事情往后面推推。既然我们已经有了一个基础的数学库,一个简单的文件资源加载器,和一个BMP格式的贴图文件解析器,那么如果我再实现一个3D场景描述文件的解析器,结合我们之前开发的渲染器,那么我们就应该可以进行一些较为复杂的场景的渲染工作了。所以让我们先进行这些工作。
3D模型及/或场景描述(矢量)文件也有很多格式。比如较早的obj格式,VRML格式;专有的3ds格式,maya格式,unity格式,ue格式,FBX格式;开源的blender格式,OpenGEX格式;元SCEA员工创造的现在由Khronos Group(就是维护OpenGL和Vulkan的那个非盈利组织)维护的COLLADA格式,等等。
其中,抛开专有格式不谈,COLLADA和OpenGEX这两种格式是比较适合游戏引擎用途,也比较完整的。其它的一些格式要么更多的是面向网页浏览器的渲染为主要对象设计的,要么是主要面向CAD等设计领域,对于游戏当中扮演重要角色的一些功能,如骨骼动画、物理碰撞等的支持十分有限。
COLLADA的优势主要在于推出的年代较早,所以目前支持这个格式的DCC软件以及商业游戏引擎很多。但是也正因为年代早所以其对一些近代的功能支持有限或者不支持,比如LOD,PBR渲染。当然因为它是基于XML的我们可以较为容易地对其进行拓展,只不过这样的拓展会面临很多的兼容互换性问题。
OpenGEX的设计是基于OpenDDL这个同样是开放的通用可读的数据结构定义语法,相对使用XML的COLLADA来说同样的场景定义要更为简洁,文件尺寸也比较小。但是其第一版标准至今也才3年多的时间,目前被支持的范围相对来说小很多。
这两种文件格式均是采用了可读的文本文件格式。一般来说,文本文件格式更加适合作为不同软件之间的互换格式,因为它没有诸如Endian或者32/64 bit,字节对齐等诸多平台兼容性问题。而且文本文件容易阅读,容易发现错误,容易编辑。
但是文本文件通常需要经过多次读取转换才能形成内存上的数据结构,相对来说效率是比较低的。一般来说,在游戏引擎运行时当中,至少是最后的发行版本当中,还是会以二进制形式为主。
抛开文件的格式不谈,这两种文件格式都为我们展现了一个比较近代的场景图(Scene Graph)结构。以OpenGEX为例,其至少包括以下这些数据结构:
- 场景的层叠式组织结构 (节点树 node trees)
节点或者场景物体的坐标变换 (4×4 矩阵、 平移、 旋转、缩放) - 场景几何体对象, 光照对象, 摄像机对象
包含多个LOD级别的顶点数据和索引数据的网格
蒙皮网格 (骨骼,骨骼绑定,骨骼影响权重因子).
网格的多个变形目的对象,以及动画化了的变形权重 - 关键帧动画以及线性、贝塞尔、TCB动画曲线
材质与贴图 (漫反射,高光,法线,切线,自发光, 透明度,凹凸,位移或者PBR类型的金属度,粗糙度,AO等)
关于坐标变换所需要的各种矩阵我们之前已经在数学库当中建立。所以接下来我们主要需要定义以下一些数据结构∶
- 场景图(其实就是由节点与节点关系组成的图(树))
- 节点关系
- 依存关系(表示节点之间有依赖关系需要同步加载/卸载)
- 包含关系(表示节点之间有从属关系,源节点拥有目的节点的所有权,源节点管理目的节点的生命周期)
- 聚集关系(表示节点之间有从属关系,但是源节点不拥有目的节点的所有权,源节点不管理目的节点的生命周期)
- 扩展关系(表示目的节点对于源节点进行扩展,但两者不是从属关系,而是服务和服务对象的关系)
- 参考引用关系(表示源节点是目的节点的一个占位符,一个实例)
- 节点(代表场景当中的层级和位置)
- 普通节点(起到概念上分组和链接其它节点的作用,并对从属节点施加统一的影响,如坐标变换,参数动画,特效范围。或者是定义一些和空间位置有关的处理,如反射材质球,volume)
- 几何体节点(指向几何体对象)
- 骨骼节点(指向带有骨骼的几何体对象)
- 摄像机节点(指向摄像机对象)
- 光照节点(指向光照对象)
- 场景对象(指与场景结构无关的对象)
- 场景几何体对象
- 网格
- 顶点
- 索引
- 蒙皮
- 骨骼
- 变形
- 材质对象
- 贴图
- 颜色
- 参数
- 摄像机对象
- 参数(FOV,裁剪平面等)
- 光照对象
- 贴图
- 颜色
- 参数(类型,亮度等)
- 传递函数
如果画成一张图的话,大概是下面这个样子:(图中只包含了从属关系)
* 图片引用自(参考引用2)
可以看到还是比较复杂的。接下来我们先从静态场景开始,逐渐扩展到包括动画等在内的完整的结构。
顺便说一下,因为接下来我们会开始往代码树当中加入比较多的二进制文件,比如各种资源文件。为了更为有效地管理这些文件,我在GitHub的这个项目当中开启了LFS功能,并且对master的历史进行了改写。(参考引用6)
导入LFS的另外一个考虑是为后面的编辑器的美术资源生产线管理做准备,这个后面相关的地方再细谈。
因为改写了git repo的历史,在昨天之前fork了的朋友抱歉了,你们可能需要重新fork,或者手动merge一下。(在真实项目当中记得不到万不得已千万别这样做。。。)m(_ _)m
参考引用