游戏引擎渲染流程

一、渲染概述

我们首先看到渲染技术的发展

游戏渲染面临的挑战:

一个容器中同一时刻有大量的游戏对象需要进行渲染,并且不同对象渲染的形式、算法还有所差异,这些使得游戏的绘制系统变得非常复杂;其次,游戏引擎的渲染还要与当代的硬件适配;同时,游戏引擎需要保证渲染的稳定性,即使进入非常巨大复杂的场景,也要在至少1/30ms内完成绘制,随着玩家需求的提高,这一时间甚至可能被缩短到1/60ms、1/120ms之内;游戏引擎的渲染还要考虑到可用的计算资源,一般渲染可用资源只能占到百分之十左右,还需预留资源用于GamePlay等系统。

二、渲染流水线

这一过程将一个个顶点数据进行投影(Projection)、光栅化(Rasterization),形成许多像素点,许许多多的像素点便组成了我们看到的画面。

2.1 着色

在着色的时候我们会对某些常量/变量进行访问,比如说屏幕长和宽的像素数量;会进行一些数学运算,比如计算光照时寻找法线的位置,以及光线的衰减程度;还会进行纹理采样等计算。

2.2 纹理采样(Texture Sampling)

为了防止走样现象,也就是为了防止在游戏中当我们的视角由近及远移动时产生的画面抖动,我们会在层级与层级之间进行插值,还会在每个层级中的相邻像素点之间进行插值,构成多级渐远纹理(Mipmap)。

2.3 GPU的概念

对于图形程序中的复杂计算,我们交由另一独立硬件处理------GPU,也正是GPU不断的发展进步,才有了现在越来越精致的画面。

SIMD and SIMT

  • SIMD指单指令多数据流(Single Instruction Multiple Data),一般是用于处理矩阵变换等复杂运算
  • SIMT指单指令多线程(Single Instruction Multiple Threads),一条指令交由多个线程处理,这即是GPU算力远高于CPU的原因

2.4 GPU Architecture

  • 图形处理集群(Graphics Processing Cluster),用于计算、光栅化、着色、纹理处理的专用硬件块
  • 流式多核处理器(Streaming Multiprocessor),作为GPU的一个组成部分,用于运行CUDA
  • 纹理单元(Texture Units)用于采样和过滤纹理的纹理处理单元
  • CUDA核心 ,允许不同处理器同时处理数据的并行处理器

2.5 CPU到GPU的数据流

数据在不同的运算单元之间读取传输是十分消耗资源的,所以一般只让数据由CPU单向流动到GPU。

2.6 缓存(Cache)

我们在存放数据时一般集中存放,就是为了便于缓存的读取,即缓存命中数据(Cache hit);若缓存不断的无法读取数据(Cache miss),不断的重新读取,将会大大降低处理的效率。

现代计算机的结构就是一条流水线,每一个环节的效率低下都可能限制整体的表现。

一个应用的表现就受限于:

  • 内存
  • 算术逻辑单元
  • 纹理贴图
  • 带宽

三、Renderable可渲染对象

3.1 Mesh

起初的网格体数据储存方式并不高效,它储存每个点的位置,法线朝向等属性,进而储存每三个点组成的三角形的数据,而这一个个三角形的数据便构成了这个网格体的数据。

因为每个顶点是由多个三角形公用的,所以我们可以通过只储存每个顶点的数据和对应的索引值,绘制三角形时根据索引值顺序绘制来提高效率。

为什么每个顶点都需要定义法线方向?

当绘制表面有折线时,可能会出现顶点位置一样,但法线方向完全不同的情况,所以每个顶点都需要定义法线方向。

3.2 Materials

材质决定了物体的外观和被光线照射时的表现

比较著名的材质模型有:Phong Model、PBR Model等等

3.3 纹理(Texture)是材质的一种非常重要的表达方式

3.4 着色器(Shaders)也是Renderable对象的一个重要组成部分

四、渲染方程

1986年,元老级人物James Kajiya提出了渲染方程式,这一方程可抽象概括所有的渲染计算。

渲染方程式表明:经过任意点x反射到观察点中的辐射通量由x点自身发光和其他点反射到x点的辐射通量组成,其中其他点反射到x点的光照又可分为直接光照和间接光照。

4.1 面临的挑战

1.阴影(Shadow)是我们判断物体空间关系的重要条件,我们该如何模拟出真实的阴影呢?

光源的复杂性,光源有点光源、方向光源、面光源等不同种类,在实际应用中光照强度也有所不同。

radiance指光照射到物体上反射出的能量;而irradiance则指入射的能量。

  1. 如何高效的对双向反分布函数(BRDF,Bidirectional Reflectance Distribution Function)和入射辐射率的乘积进行积分。
  2. 因为光可以反射,所以全局范围内任何一个物体都可以作为光源,即一束Output的光下一次可能作为Input输入,这样形成了一个递归的过程,典型案例Cornell Box。

五、简易光照解决方案

5.1 光源的简化

我们使用方向光源、点光源、锥形光源等作为Main Light,取Ambient Light作为除主光外的环境光的均值,以此简化复杂的计算。

对于能够反射环境的材质,我们可以设计一种环境贴图,通过采样环境数据来表现

5.2 材质的简化

基于一个光照可以线性叠加的假设(在渲染方程式中也有用到),Blinn-Phong模型通过叠加Ambient(环境)、Diffuse(漫反射)、和Specular(高光)来简单粗暴的描述材质的着色计算

当然,Blinn-Phong模型也有缺陷。

  1. 能量不保守,使用Blinn-Phong模型的出射光照能量可能大于入射光照的能量,这在计算光线追踪时会带来很大的问题:这一过程在光线追踪中经过无限次反弹后,会使得本该暗的地方变得过于明亮。
  2. 难以表现真实的质感,Blinn-Phong模型虽然比较经典,但它却很难表现出物体在真实世界中的模样,总是有一种"塑料"感。

5.3 阴影的简化

Shadow简单说来就是人眼可见区域中,光线无法照到的地方。在过去十几年中,对于Shadow最常见的处理方式便是Shadow Map

Shadow Map的思想可以简单概括为:第一次先在光源处放置相机,以z-buffer的方式储存一张对应的深度缓冲,第二次将相机放置在观察的位置,并将视锥内的点的深度和深度缓冲中的对应点(三维坐标转换为二维坐标后,在平面坐标系中对应的点)的深度进行对比,若前者大于后者,则认为视锥中的点处于阴影中

Shadow Map在使用时也会出现问题,光源处的采样率和观察处的采样率不一样,会出现走样,最经典的就是处理精细结构时的自遮挡问题

到这里我们就实现了对于三个挑战的一个简易光照解决方案:

六、基于预计算的全局光照

只用直接光照会使得场景的平面感很强,而使用全局光照(直接光照+间接光照)能很大程度上的还原真实情况

如何表现全局光照

  • 我们需要储存数以万计的光照探测器,因此我们需要一个很好的压缩比率
  • 材质的BRDF卷积运算涉及到复杂的多项式积分运算,我们需要利用数学方法简化积分运算

6.1 卷积定理(Convolution Theorem)

对于空间域中的一个数字信号(下图以照片为例),我们可以通过傅里叶变换将其转化为频率域的一段频率,截取频率的一小段就可以实现对频率整体的一个粗糙的表达,这时我们再通过反向傅里叶变换就可以得到原数字信号的大概情况。通过这一数学性质,我们不需要再去进行复杂的乘积累加和运算

6.2 球谐函数(Spherical Harmonics)

球谐函数就是一组基函数的集合,并且基函数越多,它的表达能力就越强(我个人理解就是回归性越强)

球谐函数有以下性质:

正交性,这些基函数卷积在一起时值为0

球谐函数的二阶导数为0,它的图像变换是光滑的

使用球谐函数,我们就可以通过一阶多项式近似的表达一个球面光照(低频信号)

6.3 Lightmap

有了球谐函数这一便捷工具,我们就可以将许多几何物体拍下存放在一张贴图上(这张贴图通常被称为"atlas"),这一过程又分为几个步骤

首先我们需要将几何物体进行简化,而后在参数空间内为每个几何物体分配近似的texel精度

下面我们在场景内加入全局光照,就可以表现出非常真实的效果

相应的,使用Lightmap有以下优缺点:

Pros:1.实时运行效率很高;2.可以表现出全局光照的许多细节

Cons:1.漫长的预计算时间;2.只能处理静态的场景和静态的光照;3.内存换时间,占用空间较大

6.4 Light Probe

我们可以在空间内放置许多采样点,对于每个采样点采集其对应的光场,当有物体移动经过某一采样点时,通过寻找附近的采样点并计算插值,就可以得到该采样点的光照

那么这么多的采样点我们该如何生成呢?我们首先在空间内均匀的产生采样点,再根据玩家的可到达区域和建筑物的几何结构进行延拓,相对均匀的分布采样点

6.5 Reflection Probe

我们还会做一些数量不多但采样精度非常高的Reflection Probe用于表现环境,一般它们与Light Probe分开采样

综合使用Light Probes和Reflection Probes,我们已经可以实现一个不错的全局光照的效果,它给我们带来以下好处:

  • 实时运行效率很高
  • 既可以处理动态物体又可以处理静态物体,并且可以实时更新
  • 既可以处理漫反射也可以处理镜面着色

当然它也有一些缺陷:

  • 大量的Light probes需要我们进行预计算
  • 相比于Lightmap,它对于全局光照和重叠部分的软阴影的细节处理精度较低

七、基于物理的材质(PBR)

7.1 微平面理论(Microfacet Theory)

这一理论的思想可以概括为:一个平面表面的光滑程度取决于它的法向量的聚集度,法向量全都集中在一起时,它的反光就相对较好

7.2 基于微平面理论的BRDF模型

通过微平面理论我们可以将BRDF分为两部分,一部分是漫反射(diffuse)(这一部分的积分值为c/Π,c取决于入射的能量),另一部分则是高光(spectual),在该部分中引入了CookTorrance模型,其中DFG模型是CookTorrance模型中的核心元素。漫反射和高光的区别在于材质是金属还是非金属,金属中的电子能够吸收光子,它的高光就比较明显;而非金属中的电子不能吸收光子,光子只能在其内部进行一系列的漫反射

DFG模型中的D指法向分布方程(Normal Distribution Function)、F指菲涅尔现象(Fresnel Equation)、G指微表面几何内部的自遮挡(Geometric attenuation term)



7.3 MERl BRDF

为了便于艺术家们的使用,引擎工作者们对大量现实物体采样,构造出了MERL BRDF数据库,其中包含大量材质的BRDF参数

7.4 PBR主流模型

Specular Glossiness(SG),这个模型中Diffuse控制漫反射部分,Specular控制菲涅尔现象,Glossiness控制材质的光滑程度。这一模型的参数设置较少,便于艺术家们使用,但也因其过于灵敏而容易导致奇怪的现象

Metallic Roughness(MR),这一模型中首先设置一个Base Color,而后通过金属度(Metallic)来控制Diffuse和菲涅尔现象。仅调节金属度虽然使得灵活度下降了,但却不容易出问题,这也使得MR模型现今被更多的使用

八、 基于图像的光照(IBL)

如果能给对环境光照进行提前的处理,我就能计算出环境光对场景中物体的影响。

相关推荐
一点媛艺1 小时前
Kotlin函数由易到难
开发语言·python·kotlin
姑苏风1 小时前
《Kotlin实战》-附录
android·开发语言·kotlin
奋斗的小花生2 小时前
c++ 多态性
开发语言·c++
魔道不误砍柴功2 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
闲晨2 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
老猿讲编程2 小时前
一个例子来说明Ada语言的实时性支持
开发语言·ada
Chrikk3 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*3 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue3 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man4 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang