来源:计算机科学与应用
作者:魏新亮,孙伟
单位:中山大学软件学院
目前,三维图形引擎应用最多就是三维游戏。有这么一句话,“游戏引擎就像汽车的引擎,决定着游戏的速度、真实感、吸引力等,玩家所体验到的剧情、关卡、美工、音乐、操作等内容都是由游戏的引擎直接控制的,它扮演着发动机的角色,把游戏中的所有元素捆绑起来,在后台指挥它的同时,有序地进行工作”。
一个优秀的三维图形引擎需要提供动态灯光、曲面细分、体雾、镜面、天空盒、阴影、粒子系统、网格动画等功能。按照三维引擎所具有的功能,可以把三维引擎划分为几个功能模块:系统模块、底层渲染模块、控制台模块、数据存储模块、游戏接口模块与游戏插件模块。这些模块构成了三维引擎所具有的功能[。如图,三维应用程序可以调用三维引擎的函数接口来创建不同的场景。
三维引擎的架构
而三维引擎则封装了底层的图形应用程序接口,如 OpenGL 或 Direct3D [15]。三维引擎实现的是各种高级的图形功能,其中主要由控制模块来实现各个模块之间的调用,包括对场景的渲染,程序的配置,用户的输入,场景的音效等等。OpenGL 和 Direct3D 在工作时,会进一步调用底层的显示硬件,来完成渲染工作。三维引擎中各个模块之间的组织关系,会影响到引擎的性能,所以在引擎开发中要处理好各个模块之间的组织关系。
一、系统模块
系统模块是三维引擎中与计算机,准确来说是机器本身通讯的部分。判断一个引擎是否优秀,则要看其系统模块能否很好地进行不同平台的移植。一个优秀的三维引擎在进行平台移植时,它的系统模块则是唯一需要做主要更改的地方[16]。我们可以进一步将系统模块分为若干个子系统模块,分别是:图形子系统、输入子系统、声音子系统、时间子系统以及配置子系统。主系统模块负责初始化、更新以及关闭所有子系统[14]。
(1) 图形子系统
图形子系统主要负责在屏幕上画点、线等图形操作,表现非常直观。图形子系统一般都是利用 OpenGL、Direct 3D、Java3D、Glide 等图形库来实现。每种图形库都有独自的一套 API 函数,并抽象出一个“图形层”置于实现的 API 函数之上,这样就方便了开发人员,提高了程序的开发效率[17]。
(2) 输入子系统
输入子系统用来把各种不同的输入装置(键盘、鼠标、游戏摇杆等)的输入触发做成统一的控制接收处理,等同于将这些输入统一起来以允许控制的抽象化。通过输入子系统,开发人员不必一一检测每种输入设备,只需要向输入子系统发送请求以获取输入信息即可。玩家也可以方便地切换不同的输入设备[18]。
(3) 声音子系统
声音子系统负责载入和播放声音等操作。目前大部分的游戏都支持 3D 声音,这让实现变得稍微复杂。
(4) 时间子系统
时间子系统负责三维引擎中各部分组件的时间触发。引擎中很多功能包括对象等,都是基于时间进行的,一个优秀合理的时间子系统可以避免在开发过程中撰写许多相似的代码。因此在时间子系统中必须实现一些时间管理功能的代码[19]。
(5) 配置子系统
配置子系统实际位于上述子系统之上。它负责读取配置文件、命令行参数、读取游戏进度、保存游戏进度、设置游戏参数等等。其他子系统在初始化和运行的时候都会向配置子系统查询相应的配置,并一直与配置子系统保持通信。配置子系统让整个引擎变得可配置化,这为调试和测试带来了很大的方便,并让玩家能更简单地按照自己喜欢的方式改变游戏设置,包括改变分辨率、键盘设置、声音设置等等。
二、 底层渲染模块
一款三维游戏真正吸引玩家的正是它逼真虚幻的场景画面和人物造型,以及灯光照明、阴影效果、粒子系统特效等等,这就需要一个高效的渲染引擎来完成。底层渲染模块是三维引擎最重要最核心的部分之一,渲染模块最重要的就是提供基本的成像功能并且简洁干净,这部分一般都是由 OpenGL 或 Direct3D 图形库来实现[14]。
模块按照功能可以进一步细分为:可见裁剪、摄影机、静态几何图形、动态几何图形、粒子系统、布告板、网格、天空盒、光照、雾化、顶点光影、输出等部分。其中每个部分应该各有一个界面,并通过这个界面来改变设置、位置、方向、以及其他可能与系统相关的属性配置[29]。
目前底层渲染模块的各部分都有一些相应的成熟算法帮助开发者实现功能。这些算法都是前人通过实验测试得出来的,运用好相应的算法,可以避免三维引擎中出现大量的冗余代码[21]。
(1)物理系统
三维引擎还有一个重要的功能就是提供基本的物理系统,即“控制物体运动模式的一套规则”。物理系统是渲染模块的一部分,因为涉及到碰撞检测(collision detection)和碰撞反应(collision response)等功能[22]。在 3D 游戏的相关技术中,首先要考虑的就是如何构建物体的实体模型以及如何真实地把它们呈现倒显示器上。一旦将这些技术应用到真实世界的仿真时,我们还需要考虑自然法则的仿真,如碰撞检测和碰撞反应。碰撞检测是物理系统的核心部分,它可以探测游戏中各物体的物理边缘[23]。当两个 3D物体撞在一起的时候,这种技术可以防止他们互相穿过,这就保证了当你撞在墙上的时候不会穿墙而过,也不会把墙撞到。
对于碰撞检测,有很多优秀的算法已经实现出来。如基于 BSP 引擎的碰撞检测、基于 Voronoi 区域的碰撞检测方法等等[24]。目前有很多学者正在完善和研究更多更好的碰撞检测方法。
三、控制台模块
控制台模块负责改变引擎和游戏的设置,方便调试和测试。通过控制台的变量和函数,可以在不重新激活游戏的情况下,改变相应的设置[25]。该模块在游戏调试阶段具有非常重要的意义,很多时间都需要测试一系列变量的值,把这些值输出到控制台要比直接运行一个调试程序要方便快速得多。如果在引擎运行期间发现一个错误,可以不必退出程序,通过控制台即可做出相应控制,并打印出错误信息。另外,在游戏正式运行的时候,也可以通过控制台将调试信息隐藏屏蔽掉,不必让玩家看到。
四、数据存储模块
数据存储模块会被引擎的大部分组件所使用。它定义了游戏中所使用的数据格式以及他们的组织方式。该模块也包含了相应的数据读取和保存功能、以及数学程序代码(向量、平面、矩阵等等)、数据内存管理等等[26]。在游戏中会出现大量的数据结构的类库,这些类库之间必然存在数据的传递和保存,如何处理好这些数据的组织,是该模块需要考虑的。
五、 游戏接口模块
游戏接口模块主要提供一个接口层,方便游戏开发者使用三维引擎的各部分的功能,这使得游戏开发变得简便快捷。游戏引擎中的每一部分都有动态的属性,而该模块则提供了一个接口去修改这些动态的属性值。其中包含了摄像机、模型属性、光照、粒子系统、声音播放、输入处理、关卡变换、碰撞检测和相应、模型贴图的现实等等[27] [28]。一般该模块会以图形界面的形式呈现给游戏开发人员。游戏接口模块让引擎和游戏制作很好的分离开,使代码设计变得更加清晰,应用起来也会更方便,它能使三维引擎具有非常好的重用性。
六、游戏插件模块
游戏插件模块负责提供一些插件供三维引擎与其他应用程序生成的内容进行融合,如模型转换程序或插件等。每一个三维图形引擎都希望能制造出精美的虚拟场景和逼真的人物造型,但三维引擎的主要任务不是场景和对象模型的制作,即使提供了三维模型编辑器也是有限的,这就需要编写一些模型转换程序或插件,将其他专业的三维建模工具(3D Max、Maya 等)设计出的模型转换为该引擎所使用的格式。