本期为GAMES104《现代游戏引擎:从入门到实践》视频公开课文字实录第13期。本课程由GAMES(图形学与混合现实研讨会)发起,游戏引擎技术专家王希携手游戏引擎一线开发者共同研发。
课程共计22个课时,将介绍现代游戏引擎所涉及的系统架构,技术点,引擎系统相关的知识。为配合学习实践,课程组在 GitHub 上开源了小引擎Piccolo,上线1个月即获得了2900+star, 累计下载量已超过20000+。
以下内容为公开课视频转文字版本,为阅读通顺,有删减
经过上一节课,我们基本上就可以进行渲染了。(关注公众号,回复[课件],即可获取完整版课件)
下面我们介绍另外一个很关键的概念——纹理压缩。渲染的一切基础都是可渲染物体,在可渲染物体中,有一个很重要的组件,叫做纹理。对于照片来说,我们一般会将其存成JPEG或者PNG格式,或者其他一些流行的图片格式。
而在游戏引擎中,我们一般会将纹理压缩存储。大家知道,计算机会将照片进行压缩存储,对于未经压缩的BMP格式和经过压缩的图片格式来说,同一张图片所占用的空间可能会相差十倍以上。而在游戏引擎的绘制系统中,我们无法使用一些流行的、非常优秀的算法对图片进行压缩,因为经过这些算法压缩后的图片无法进行随机访问。举例来说,对于JPEG格式的文件,如果给定一个UV坐标,系统无法快速从JPEG格式的文件中获取到相应坐标的信息,而且这个计算成本非常高。在游戏引擎中,我们一般采用基于块(Block Based)的压缩方法。我们将图片切成一个个小方块,最经典的就是4×4的小方块,然后进行压缩。
这里介绍一个非常经典的算法。对于DXT类型的纹理,在一个4×4的色块中,可以找到最亮的点和最暗的点,即颜色最鲜艳和颜色最暗的点,然后将该方块中的其他点都视为这两个点之间的插值。因为对于很多图片来说,相邻的像素之间都有一定的关联度(Coherence)。所以我们可以存储一个最大值和一个最小值,然后为每个像素存储一个距离最大值和最小值的比例关系,这样就可以近似地表达整个色块中的每个像素的颜色值。在计算机图形学领域中,纹理压缩(Texture Compression)都是基于这个思想,称为块压缩(Block Comppression)。在DirectX中,最经典的就是DXT系列的压缩算法。块压缩系列压缩算法的最新版本已经演进到了BC7。DXT系列压缩算法的优势在于,当我们生成了一个纹理后,就可以在CPU上对纹理进行实时压缩。因为无论是压缩还是解压缩,这一系列算法的效率都非常高。
另外一类压缩算法就是手机上使用的压缩算法,使用较多的就是ASTC算法。ASTC压缩的分块就不再是严格的4×4了,它可以使用任意的形状,而且ASTC的压缩效果是最好的,解压缩的效率也不低。然而,ASTC算法压缩时的性能消耗较大,因此无法在运行中进行压缩。总而言之,对于计算机的渲染系统来说,纹理压缩的基本逻辑都是按照这个思想来进行压缩的。
我们之所以介绍纹理压缩的相关内容,是因为当大家构建游戏引擎的时候,对纹理的压缩和管理是一个非常重要的模块。而且,我们加载到显卡中的图片基本上也是压缩过的数据格式。
我们对于基础绘制需要介绍的知识点到此结束。
下面我们介绍如何制作和生成可渲染物体。我们会在工具链部分中详细介绍。在此,作为前导知识,我们对一些建模工具做一下简单的介绍。
构建3D模型最经典的工具是3DS Max和Maya,近几年来,Blender也变得越来越流行。我个人也非常看好Blender,因为它的功能越来越强大。大家可以在这些软件中构建我们需要的各种各样的模型。在十几年前,流行的做法是在这些软件中卡住一个个关键点,由粗到细地构建模型。近年来,随着ZBrush的出现,传统的制作流程受到了巨大的冲击。ZBrush是一个雕刻性的工具。在真实的世界中,当雕塑家通过雕刻塑造一个形体时,会不断地对雕刻材料进行切削,从而形成想要的形状。而在计算机中,这种雕刻行为可以更加自由,因为我们不仅可以进行切削,还可以进行放大和拉伸。这给了艺术家更大的自由度。基于ZBrush通过雕刻生成素材的工具也越来越受欢迎。还有一种生成素材的方式是3D扫描。手机上有一些APP,我们使用这些APP围绕物体拍摄几张照片,这些APP就可以将这个物体的3D模型建立起来。这也是得益于现在的深度学习、以及图像配准(Registration)算法的提升。在3A游戏行业中,基于实体扫描的模型也越来越多。比如下图中展示的一个非常复杂的古代武士盔甲,我们通过一圈扫描,就能够形成非常高精度的、能够达到十几亿面级别的精细网格,这个精度远远超过了手工构建网格的精度。
最后一个方法就是现在快速发展的程序性生成算法。这类算法可以通过一些规则自动生成网格。Houdini就是这样的一个工具,它能够生成漂亮的地形网格。现在有些前沿的人工智能算法可以生成很多我们需要的网格细节。这也是游戏引擎工业未来一个很重要的发展方向。因为这类方法能够将艺术家们从繁琐的细节工作中解放出来,使艺术家能够真正专注在创意上,而人工智能可以自动将细节部分补足。
这四种方法各有利弊。有的方法更有弹性,而有的方法更难以进行调整。比如对于3D扫描来说,大家会觉得这种方法特别方便,因为我们将一个物体扫描一下就得到了这个物体的3D网格。但这种方式有一个很大的难题,就是我们首先得拥有想要扫描的物体。以前有个很著名大型游戏厂商,他们为了做一个游戏,会先在网上购买所需要的物体,然后将这些物体扫描进电脑,这样才能进入游戏资产库。不同的工艺制作方法会产生不同的数据,而这些数据就是我们计算机绘制所需要的输入。
下面是本节课程的最后一部分,我们会介绍现代游戏引擎的管线在往哪个方向发展。前面我们所介绍的内容,比如网格、子网格、纹理、着色器等,虽然有点复杂,但同学们应该可以理解。然而,现代游戏的工艺变化非常之大。大家如果仔细观察近几年发布的新游戏,就会发现,这些游戏的细节越来越多,而且地图越来越大,这就是开放世界。对于传统的射击游戏和开放世界的射击游戏来说,在每一帧画面中,你所能看到的数据量可以相差十倍以上。随着我们前面介绍的雕刻工具(ZBrush等)的普及,包括3D扫描的普及,我们得到的模型的数据量也越来越大。举个例子,对于一个很简单的雕像来说,如果我们推进相机,我们会发现它有无数的细节。如果大家喜欢玩3A游戏,那么就会很在意场景的精度。精度问题非常重要,而精度问题恰恰又对现代引擎的基础架构产生了巨大的冲击。
希望同学们建立这样一个概念,即对于游戏引擎的绘制系统来说,它并不是一个静态的系统,它的技术一直在进步。这其中有一个很重要的发展方向,我们叫做“Cluster-Based Pipeline”。这是一条新的模型表达的主管线。他的基本思想来自于2015年育碧软件制作的《刺客信条·大革命》。在这款游戏中,有很多非常华丽建筑物,每个建筑物都有很多的细节。经过五六年的发展,同时随着硬件的发展,有越来越多的引擎在往这类管线上转移。
受到篇幅限制,我们无法详细展开介绍这类管线的核心思想,我们可以介绍一些最基础的概念。当我们面对一个非常精细的模型时,我们可以将其分成一个个小的分块,可以称之为Meshlet或者Cluster。而每一个Meshlet都是固定的,比如32个或者64个三角形大小。下图中的龙模型大约有几十万个面片,我们可以将其分成很多小型的Meshlet,每个Meshlet有64个面片。
之所以这样做,是因为对于现代计算机来说,显卡已经能够基于数据高效地创建很多几何细节,而并不需要像传统的管线那样,需要预先将顶点缓冲区和索引缓冲区构建好,再将网格数据传入显卡。现代显卡可以凭空计算出很多几何信息,而且如果我们输入一个三角形,现代显卡可以借此生成无数个三角形。因此,当我们将每个Meshlet的大小固定之后,在显卡上的计算都是极其高效且一致的。请大家回顾一下前文提到的GPU硬件架构和流式多处理器的概念,每个流式多处理器中都有很多小型的计算核心。所以,上图中的龙模型虽然复杂,但它的很多计算都是完全一致的。这就是Cluster-Based Pipeline的核心思想。
大家刚开始接触图形学时,会了解到经典的渲染管线。即构建好模型、材质之后,通过顶点着色器和像素着色器进行渲染。最早的渲染管线甚至都没有顶点着色器和像素着色器。从十几年前开始,显卡厂商开始尝试在显卡中提供创建精细的几何结构的功能。并引入了壳着色器(Hull Shader)、域着色器(Domain Shader)和几何着色器(Geometry Shader)。这些着色器可以将输入的三角形进行无限细化,生成我们想要的细节。随着硬件的发展,又演变出了“Mesh Shader”和“Amplifying Shader”。在Vulkan和DirectX中,这些Shader的名称有所不同。
这类Shader的核心想法是,我们可以使用一个算法,基于数据凭空生成很多几何细节,而且可以根据距离相机的远近,选择所生成的几何细节的精度。对于显卡来说,最高效的数据组织方式就是大小一致的一个个小分块。基于Cluster或者Meshlet的管线对于程序员的要求要比以前高很多。因为我们要进行大量的处理和运算,而且具体实现代码也不易理解。然而,这种方法的优势在于,它可以产生无数的细节,并且可以让艺术家自由发挥。这是一个非常值得大家关注的引擎的一个前沿的发展方向。
举个例子,如下图所示的怪物:
这个怪物有很多面片。如果我们不将其分成Meshlet,我们会发现,当相机在移动的时候,这个怪物会被整个裁剪掉,因为我们只能够按照物体的粒度进行裁剪。但现在我们可以将其手部的一部分裁剪掉,因为每个Meshlet都有自己的包围区域,我们可以在GPU上实时计算出裁剪区域。即在当前的相机位置的情况下,哪些部位可以不需要绘制,这样做的效率也很高。
这里我们需要提及虚幻引擎的Nanite,它实现了像素级的网格密度。可以认为,Nanite是将Meshlet的思想又往前深入了一步,做的更加工业化、更加成熟。这也是现代引擎的渲染管线发展的一个重要方向。
在这一讲中,我们介绍了最基础的渲染引擎架构的思想,包括显卡硬件的基础知识。如果大家真正进入游戏引擎开发行业,我们希望同学们能够关注一些最前沿的技术发展趋势。那么我们如何理解渲染系统?在渲染主题的第一节课中,希望同学们能够建立几个基础概念。
第一,游戏引擎的绘制系统是一个工程科学,并且深度依赖于你对现代图形硬件的理解。因此如果你想成为一个图形工程师,你必须要理解显卡的架构,知道显卡的性能卡点在哪里,了解各种性能限制。
第二,在游戏中,我们要解决的核心问题就是网格模型、材质等数据之间的关系。最经典Mesh和Submesh就是一个非常好的解决方案。但是最前沿的技术会有所不同。
第三,希望同学们建立一个概念,即在进行绘制的时候,尽可能的通过一些运算减少绘制工作,这样能够达到最佳性能。因此可见性是一个非常重要的概念。这里和大家分享一位业内大神说过的一句话,曾经有人问这位大神,你认为优化的最高境界是什么呢?大神回答“do nothing”。请注意,这里的“do nothing”并不是什么工作都不做的意思,而是需要通过算法让计算机尽可能少做事情,这时的效率一定是最高的,这就是“do nothing”的精髓。因此,在我们进行优化工作时,需要尽可能地让计算机“do nothing”。
最后,也是非常重要的一个趋势,即越来越多的绘制运算,包括一些复杂的处理,都已经从CPU转移到GPU,以利用现代GPU的高速处理能力。这就是GPU驱动(GPU-Driven)的思想,即将很多在CPU上进行的一些复杂运算(比如动画系统等)全部转移到显卡。这也是利用GPU帮助CPU分担负载的一个重要的方式。
当大家在理解绘制系统时,记住以上四点,基本上就可以了解绘制技术的发展方向。
本文编辑:Piccolo 社区编委会 彭渊
如对本节课有任何问题,欢迎加入我们的社群或给我们发送邮件:
Piccolo游戏引擎社区
Piccolo社区是中国开源游戏引擎社区,由游戏引擎行业大佬、共创官、学习者共同建立。你可以在我们的社区里交流技术、互助问答、参加活动,你也可以参与Piccolo 的共建,如撰写贡献代码、撰写技术文章、参与技术挑战等。
Piccolo游戏引擎
由中国游戏引擎社区Piccolo开源的一款Mini游戏引擎。采用世界-关卡-游戏对象-组件的简洁架构,便于理解游戏引擎架构思想,它不仅能有效的帮助开发者学习游戏引擎架构知识,也能帮助一线开发者实验引擎算法与第三方库、辅助个人项目快速启动。截止目前,Github点赞已突破3600+,累计下载量已超过20000+
Piccolo GitHub地址:https://github.com/BoomingTech/Piccolo/discussions
关注公众号GAMES104,回复【入群】,加入Piccolo社群