【游戏引擎入门到实践】五.求解渲染方程的最基础解决方案

307 阅读8分钟

十多年前的3A游戏方案

原则:在渲染方程上进行简化

渲染方程: 
出射光(颜色)= 自发光 + 反射光。
物体最终颜色 = 自发光 + 漫反射+镜面反射+环境光; 

一.光照

1.主光源Main Light

可以是方向光、点光源、聚光灯

2.环境光Ambient Light

用一个平均值来简化代替环境光。

3.环境贴图Environment Map

简化~镜面反射。

(1)环境贴图由六张图片拼接而成,类似于将一个立方体的六个面展开之后所形成的图像。

(2)根据观察者所在的位置,以及表面的法线方向,进行反射,找到立方体所在位置的图像。这样就可以表达光场在物体表面所形成的强烈镜面反射效果。

4.总结

(1)将半球形的光场分布,模拟成一个均匀的环境光,来表示Irradiance辐照度的低频信息

(2)再加上一个突出的主光源

(3)然后通过环境贴图来表达Irradiance辐照度中的一些高频信息,这样就可以表示BRDF中的Specular项。

(4)思路跟微积分求解一样

在学习微积分时,给定一个通用函数,通过积分对其求解,是很难得到这个函数的解。

如果我们对方程的参数进行特例化,就很容易得到方程的解。

5.SenseCube项目实现玻璃的反光效果

(1)设置threejs的scene.environment 环境贴图

“为场景中的所有物理材质设置环境贴图”

(2)切换到高清模式,使用pbr材质,建筑物的玻璃可以看到镜面反射效果

二.材质效果~经验派,经典的“Blinn-Phong”

经验派,即不研究物理世界的本质,而是根据自己的观察对材质进行处理,拟合出一个经验模型,然后将经验模型和真实模型的效果进行对比,并对参数进行调整

1.定义

Blinn-Phong模型非常简单,分为三项,分别是

环境光项(Ambient)、

漫反射项(Diffuse)、

高光项(Specular),用幂函数来模拟。

2.原理

(1)漫反射 (Diffuse)

该项的计算是使用入射角的余弦项乘以入射光,再乘以一个反弹率,因为有些能量会在这个过程中被吸收。

(2)高光 (Specular)

该项用来模拟一些特别亮的效果,即看上去很像金属,光滑表面感觉的效果。

(3)环境光Ambient

该项一般是Diffuse项乘以一个百分比。

(4)将这三项相加,就得到了Blinn-Phong材质模型。

(5)线性叠加

1)体现了一个朴素的光学原理,即光线是可以线性叠加的。

2)在渲染方程中,也应用了这个原理,这就是为什么可以将方程中的所有出射光积分 (相加) 起来的原因。

这是光学的一个基本假设,不管这些光线来自于何方,来自于哪个光源,它们都可以被线性叠加到一起。

3.Blinn-Phong的两大问题

1.能量不保守(推荐用保守,而不是守恒)

(1)游戏渲染没有太大问题

(2)离线渲染,问题就凸显了

设置好Specular、Diffuse和Power等参数之后,最终计算出来的出射能量会大于入射能量。

(3)光线追踪算法,进行多次反弹追踪时,这个震荡差距就会被放大。

图中的球体内部本来存在一个黑腔。使用Blinn-Phong模型进行光线追踪时,如果有光漏到黑腔中,在经过多次反弹之后,就会出现图中左图的效果,黑腔会变亮。这个效果是错误的。

(4)Energy Conservation:通常翻译为“能量守恒”,但是“能量保守”会更合适

1)即反射光的能量只会小于或等于入射光的能量,而不会变大——引用自《Physically Based Rendering(3rd)》第350页关于“Energy Conservation”的解释。

2)因为这一过程中的能量并不会一直保持不变,在光射到物体的表面时,一定会有部分能量被表面吸收,因此能量会发生损失,在不断弹射的过程中,光线的能量会不断损失。因此,我们需要确保出射能量小于等于入射能量。

3)当不存在能量保守的限制时,在光线追踪环境下就会出现问题。

2.无法表达复杂材质的细节效果

(1)对于艺术家们十分不友好,Blinn-Phong模型无法表达一些复杂材质的细节效果。

(2)Blinn-Phong模型的特点——无论怎么调整参数,物体看起来都会具有塑料感。木头、石头的效果像塑料,金属效果也像塑料。

三.阴影.光源的可见性问题~阴影贴图方案

1.古早的一种方案

(1)Shadow Volume、Perspective Shadow等

(2)那时候硬件不支持阴影贴图 (Shadow Map)

2.其他简化模拟阴影的方法

2-1.法向量模拟

不追求效果的项目,比如gis等,用法向量模拟,根据每个面法向量,将颜色值乘以对应比例制造阴影效果。

2-2.平面阴影投射 planarshadow。

(1)通过顶点着色器将人物模型的每个顶点坐标按照一个特定的光照角度映射到xoz平面上,伪造出了阴影效果。

(2)本质相当于clone了一份人物模型把它压成一个平面全涂成半透明黑色。

(2)核心逻辑在顶点着色器里计算。相比使用shadowmap实现影子,通过顶点投影的方式性能更好。

参考这篇:mp.weixin.qq.com/s?__biz=MzU…

3.阴影贴图方案~Shadow Map

1.原理:

因为我们需要知道的是某个点到光源的可见性。

(1)首先,我们从光源的视角渲染一次场景,渲染过程中并不需要进行着色,只需要计算出每个点的深度信息即可。这个深度就表示了每个点到光源的距离。

(2)然后从相机位置渲染场景,并将每个点反向投影到光源视角的场景中,以得到该点在阴影贴图中的深度。

(3)将第一步该点的深度,与第二步阴影贴图中采样深度值 做比较。

1)如果该点的深度更大,则说明该点位于阴影中。

2)否则,该点就不在阴影中。

(4)代码实现

该方法十分简单,具体的算法细节,可参考GAMES101和GAMES202课程。这里只介绍阴影贴图的基本思想。

threejs源码目录:src/renders/webgl/WebGLShadowMap.js

2.问题

1.失真Artifact,也叫自遮挡问题

(1)从光源处渲染场景,和从相机处渲染场景的采样率不同时出现。学过信号系统就会知道,当两者的采样率不一致时,阴影就会产生各种各样的失真(Artifact)

这就是阴影贴图中的自遮挡问题,特别是对于很精细的结构来说,如果阴影贴图的精度不够,或者采样率不够,亦或深度的精度不够,就会产生很多失真。

(2)特别是对于很精细的结构来说,如果阴影贴图的精度不够,或者采样率不够,亦或深度的精度不够,就会产生很多失真。

1-1.解决办法~偏移 (Bias)

(1)简单的办法是添加一个偏移,将容差变得更大,这样可以解决大部分问题。

(2)然而,有时候又会产生人脚浮空的问题 (即所谓的“Pater-Panning”效应)

体验一些早期的做的不是特别好的游戏,就会发现,人的阴影与脚总会相隔一定距离,比如5公分或10公分。

2.光源特别靠近地平线,很难处理;

(1)比如落日余晖,阴影会拉得非常长。

2-1.解决方法

(1)比较新的一款游戏,为了实现类似的效果,使用了两套阴影算法。

1)当太阳比较接近天顶的时候,使用了一套算法;

2)当太阳开始逐渐接近地平线,比如清晨和傍晚时,就逐渐过渡到另外一套算法;

3.senseCube的使用

3-1.全场景实时阴影阴影,不清晰问题

(1)shadow map resolution阴影贴图分辨率

只有人物阴影,1024也都清晰;

全场景实时阴影,2048也不清晰,4096清晰一点点,还是不能接受;

(2)最终方案

场景阴影用阴影烘焙贴图,只有人物才搞实时阴影;

四.总结~十多年前的3A游戏方案

1.光照

使用一个简单的主光源,加上环境光,再使用环境贴图实现反射,就解决了渲染方程中关于光的部分。

2.材质

使用Blinn-Phong模型处理材质,将渲染方程中的积分部分变成了一个多项式时间就可以计算出来的问题。

3.阴影

使用阴影贴图来处理阴影;

用这三步,基本解决了前文中提出的三个挑战。

4.使用场景

略显过时,是十多年前的3A游戏方案。

(1)当游戏运行时的硬件配置特别低的时候,可以采用该方案。

(2)移动端也可以使用这种方案。

(3)性能优化手段的材质LOD方案。