游戏引擎开发学习(一)基础系统篇

发布于 2023-04-02  85 次阅读


引言

前面一周一直在写optix光追渲染器,并且渲出来了不错的效果。但是后面还有局部光源、disney pbr材质、mis多重重要性采样、体渲染要写,仍旧有不小的工作量,身心俱疲,所以想换个项目玩玩。

都说图形学无非就是光栅管线渲染和光追渲染两个主流技术,现在我们光追玩腻了,就决定反过来玩玩光栅管线。一开始我想从opengl和directx里选一个,做一个实时光栅渲染器的(本科的课上学了opengl,读研后导师又开了个directx的小课,所以对两个api都略有了解),但是后来想了一下,光栅渲染器最大的优势是啥?是实时,为了凸显实时,我们就要能在场景中漫游,并实现物体交互、各种动态效果,这样才有实时引擎的感觉。所以我们不仅要有渲染模块,还要尽量加入自由镜头、物理交互、动态光源、粒子等等效果,我寻思这再拓展一下编辑功能,不就是游戏引擎了吗?

所以这几天刷了下games104,又看了几节cherno的课,属实受益匪浅,理解了什么才是真正的工程。当然,这种规模的工程必不可能一蹴而就,需要慢慢打基础,然后逐层进行开发、接口对接。当然以我现在对引擎的理解水平,完全不足以自己手撕一个引擎出来,所以我准备参考cherno的系列教程来深入学习。

由于引擎开发的代码量太大,而且很多代码都没有展示并逐行解读的意义,所以这个系列我大概率不会大段大段放代码,只是记录一下每一P中cherno讲的核心理论、开发的架构和流程,当然如果某部分代码特别重要的话,也有可能顺手把代码粘贴进blog里。

P1 教程介绍

cherno:好多人给我打钱,那我就来出个系列教程吧。

P2 什么是游戏引擎

游戏引擎的定义很广泛,比如“能够快速制作出游戏的工具”,“能够给世界场景建模”,“能够堆砌所有游戏元素”。实际上游戏引擎是一种多功能开发平台,它既可以面向游戏开发人员去做游戏,也可以面向建筑公司做建筑可视化、面向产品公司做VR项目、面向实验室做仿真模拟等等。因此广义上讲,游戏引擎是可以根据各种输入,将一系列数据通过自定义的运算或交互,将结果可视化到屏幕上的应用程序平台。

所以游戏引擎的核心任务简言之就是,将一段数据处理成资产后,以另一种数据的形式展示出来,即“我不创造数据,我只是数据的搬运工”。(这也是为什么游戏引擎一般不会预置建模、绘图功能的原因)只不过要处理的数据种类有很多,例如三维模型、图像纹理、音频、序列化场景等等,所以引擎自上而下还要分很多很多的平台层来各司其职。

游戏引擎的意义和重要性就不用提了,如果没有游戏引擎,每次游戏开发人员都要从游戏的底层写起,尽管不考虑这样的成本有多高,每次做新游戏都重复相同的工作才是最不能接受的。因此游戏引擎极大化地把一些机械的、泛化的内容封装起来,从而提供游戏开发人员一个较高的开发起点。

P3 设计游戏引擎

首先先要规划一下引擎的各个模块:

1、Entry Point,即入口点,就是当我们启动游戏引擎的时候,我们需要让引擎做什么;

2、Application Layout,即应用层,用来管理或处理各种程序的生命周期、运行循环,以unity举例就比如控制每帧调用update函数,每帧进行frame渲染刷新;此外还可以管理各种事件系统、用户输入;

3、Window Layout,即窗口层,首要任务就是渲染图像。同时,他还要管理+传递在这个窗口上发生的消息和事件,从事件管理器中处理传播用户在窗口中的输入等。以unity举例就比如其中的广播系统;

4、Renderer,即渲染器,顾名思义,实现实时渲染的层。该系列教程不打算讲opengl3D渲染器的实现;

5、Render API abstract,即渲染api抽象层,因为我们的引擎可能会用到各种api,因此我们要为渲染器系统设计一个抽象层,能够对接不同的渲染api,从而使得顶层的Renderer变得api无关化。(一个漂亮的解耦)

6、Debugging system,与VS自带的调试系统不同,这里我们要自定义一个调试系统,来针对自己设计的事件系统进行监测和打印,并可以分析系统的性能;

7、Scripting layout,即脚本层,就是提供给用户来编写游戏逻辑的层,以unity举例就是它的C#脚本;

8、Memory system,即内存管理系统,越大的工程越需要良好的内存管理;

9、Entity-Component system,即ECS架构,将游戏对象的属性模块化,分离成不同的组件,用过unity的人肯定对这种架构再熟悉不过了;

10、Physics solution,即物理解算,顾名思义,要对场景中的物理行为和交互进行计算;

11、File Input/Output&VFS,即文件读写和虚拟文件系统;

12、Build system,即资产构建系统,将模型、纹理等数据转换成一种低数据冗余的格式保存在工程内,并且要易于更新、替换,资产要能够直接被游戏高效使用;

P4 建立项目

这一节就是在github上创建一个仓库,然后在vs开一个新的空白c++项目。

由于引擎需要依赖于大量的lib静态库,因此按照Cherno的规划,引擎将会编译一个dll,将全部静态依赖都链接到dll中。在用游戏引擎做出游戏后,游戏本身也会直接调用这个dll,从而链接到那成百上千个lib静态库上。这样子设计,就可以把引擎或引擎做出游戏的exe和那些乱七八糟的静态库解耦,可以自由调用外部的dll来链接那些静态库。

现在我们把当前开启的新项目作为上述的dll工程(我自己起名叫Tibbie,大家按自己的引擎名字来给dll工程起名即可)。首先我们配置一下vs项目的属性,禁用32位支持,然后把配置类型从exe改成dll;将输出目录改为$(SolutionDir)bin\$(Configuration)-$(Platform)\$(ProjectName);将中间件目录改为$(SolutionDir)bin-int\$(Configuration)-$(Platform)\$(ProjectName)

第二步我们要再创建一个exe工程SandBox。同理,先禁用32位支持,配置类型保持exe,然后输出目录和中间件目录都使用上一步的字符串即可。