七、渲染:渲染管线、后处理和其他的一切

7.1 环境光遮罩 Ambient Occlusion:AO

原理:在物体中,只有一部分能看到天光,其他部分会被遮挡住 \[ AO(\bold{p,n})=\frac{1}{\pi}\int_{\Omega}V(\bold{p},\omega)\bold{n}·\omega \ d\omega \] image-20230724102810366

7.1.1 预计算AO

使用光线追踪的算法,离线计算AO并将其保存到纹理中

  1. 需要额外的存储
  2. 只能应用于静态物体

image-20230724103150057

7.1.2 SSAO:Screen Space Ambient Occlusion

思想:

  1. 在渲染的每一帧中,可以得到每个像素的颜色&深度
  2. 将每个像素的深度信息连接到一起,就可以得到height field
  3. 根据height field可以得到几何关系,从而估算自遮挡

估算自遮挡:

  1. 在观察点p的给定半径的球形空间内,随机采样N个点
  2. 对这N个采样点,通过相机去投射,可以得到这些点的深度,与Z-Buffer中的深度进行对比
    1. 深度比Z-Buffer中的近,则证明其可以看见光
    2. 深度比Z-Buffer中的远,则证明其被当前绘制中的某个几何挡住了
  3. 则最后的自遮挡为:\(A(p)=1-\frac{Occlusion}{N}\)

image-20230724103340183

7.1.3 SSAO+

  1. 沿着p点的法线方向,采样半球面

image-20230724104812491

7.1.4 HBAO:Horizon Based Ambient Occlusion

思想:

  1. 要求的实际上是球面空间上的可见性
  2. 从p点出发,沿着各个方向去转,找到光线能够越过最高邻居的仰角,称为pitch angle
  3. 如果在每个方向都找到了,则会得到Occluded Area
  4. 根据Occluded Area,可以估算出有多大面积的天顶是可见的
  5. attenuation function:如果某个山离p点过远,则认为其对我没有影响

\[ A=1-\frac{1}{2\pi}\int_{\theta=-\pi}^{\pi}\int_{\alpha=t(\theta)}^{h(\theta)}W(\vec{\omega})\cos(\alpha)\ d\alpha d\theta \]

image-20230724105031680

具体实现:ray marching

  1. 将Z-Buffer视为height field
  2. 以P点为圆心,以几个像素为固定步长进行采样,采样一圈,得到最大horizon angle
  3. 注意:对于每个像素,要随机抖动步长、旋转方向

image-20230724105717883

7.1.5 GTAO:Ground Truth-based Ambient Occlusion

问题:不同方向的光,对于P点的反射贡献是不同的,有一个 \(\cos\theta\) 的修正 \[ \hat{A}(x)=\frac{1}{\pi}\int_{0}^{\pi}\int_{\theta_1(\phi)}^{\theta_2(\phi)}\cos(\theta-\gamma)^+|\sin(\theta)|\ d\theta d\phi\\ \gamma=angle(\vec{n},\vec{v}) \] image-20230724110713853

  1. 根据计算出的AO值,可以猜测光射进山谷后,多次Bounce后形成的亮度
    1. 例如山谷虽然被周围的山遮挡住了,但是底部仍旧有光,并且底部的光与周围的山的面的颜色有关系
  2. 根据不同的AO值,与对应的Multi Scattering值进行分析,得到一个多项式的关联
  3. 从而根据一个多项式,将Single Bounce转换为Multi Bounce的效果
    1. AO的值类似于BRDF中的roughness值,表示周围区域对当前点的遮挡性
    2. GTAO的发现类似于用多项式方程拟合了积分的效果

image-20230724111134323

7.1.6 Ray-Tracing Ambient Occlusion

image-20230724112133504

7.2 雾效

7.2.1 Depth Fog

思想:从眼睛看过去,随着距离,能见度逐渐下降

image-20230724112307755

7.2.2 Height Fog

思想:不同高度,雾的浓度不同

  1. Fog有一个最大值:当低于某个高度时,都是最大的Fog;当高于该高度时,Fog是以指数递减的
  2. 当人的位置\(O_z\)高于目标位置时,Fog要通过Ray Marching算法,从目标位置到当前位置进行积分

image-20230724112613301

7.2.3 Voxel-Based Volumetric Fog

将相机空间非均匀体素化:根据视锥进行切分(如左上图所示)

  1. 原因:如果进行均匀体素化,那么近处的颗粒太大,远处的颗粒太细
  2. 一般会用一个3D纹理存储运算结构

image-20230724113400597

7.3 抗锯齿

7.3.1 锯齿产生的原因

产生锯齿的原因:屏幕的分辨率是有限的,而几何世界的分辨率是无限的

  1. 边界的采样
  2. 纹理的采样
  3. 高光的采样

image-20230724114416137

7.3.2 Anti-aliasing

多采样几次,进行平均,从而产生过渡区域

image-20230724114726723

7.3.3 SSAA(Super-sample AA) & MSAA(Multi-sample AA)

SSAA:

  1. 在屏幕分辨率的基础上,绘制更高分辨率的图片,然后对高分辨率的图片进行下采样,得到屏幕上的图片
  2. 缺点:所有buffer、shading均变成原来的4倍

MSAA:

  1. 对空间仍是4倍采样,但是在渲染时,如果这4个sub-pixel对三角形的贡献相同的话,只进行一次shading;如果不止一个贡献的话,则对四个采样点均进行一次shading,然后计算平均值
  2. 只有在最后的shading时,会省略一些不需要的渲染
  3. 缺点:当三角形数量高于像素数量时,MSAA会彻底失效

image-20230724114910115

7.3.4 FXAA:Fast Approximate Anti-Aliasing

在采样率不变的情况下,进行抗锯齿

思想:产生锯齿的地方,一般都是边界,此时会产生颜色的跳变,只需要对边界进行抗锯齿即可

  1. 对每个像素点,进行十字滤波:

    1. 计算上下左右四个邻居以及自己的差异值,当差异值大于某个阈值时,就是边界
    2. 将颜色转化为亮度,只计算亮度的差异值:\(L=0.299*R+0.587*G+0.114*B\)

    image-20230724115838038

  2. 计算差异的方向:

    1. 使用两个卷积矩阵,分别计算横向与纵向的差异权重,判断差异方向
    2. 确定方向后,比较两个邻居,判断谁的差异更大

    image-20230724120357356

  3. 边界搜索算法:

    1. 从当前点出发,与应该与其进行混合的点形成一对,求亮度平均值\(L_{avg}\)、亮度差异值\(L_{offset}\)
    2. 朝着垂直于这一对的方向的两边进行搜索,找到像素对的颜色变化与当前点像素对不同为止
    3. 此时就找到了边界的两个端点

    image-20230724144851026

  4. 计算混合系数

    1. 比较当前点距离左端和右端的距离,找到更近的端点\(targetP\)
    2. 根据平均亮度、当前点亮度、端点亮度,计算混合系数

    image-20230724145257417

  5. 与相邻的像素进行混合

    image-20230724145657591

7.3.5 TAA:Teamporal Anti-Aliasing

  1. 利用前一帧的数据进行混合
  2. 每个点都需要计算上一帧的对应位置,生成motion vector

image-20230724145900584

image-20230724150017897

7.4 后处理

7.4.1 Blooming 光晕效果

image-20230724152124723

  1. 首先,取出当前帧中高亮的部分

    1. 计算每一个像素的亮度,与平均光场亮度做对比,超过阈值的像素取出其RGB值

    image-20230724154401888

  2. 然后,对取出的像素进行高斯模糊

    image-20230724154659877

  3. 先进行几次下采样,对下采样之后的结果进行Blur,然后再进行上采样,与原有的图像进行混合,此时模糊的区域就会很大

    image-20230724155120135

7.4.2 Tone Mapping

将HDR颜色转化为SDR颜色,从而显示HDR图像

image-20230724162530557

关键在于定义一条Tone Mapping曲线,用于转换颜色

  1. filmic曲线

    image-20230724163300239

  2. ACES:Academy Color Encoding System

    image-20230724163550628

7.4.3 Color Grading

通过一个Lookup Table,整体改变图像的色彩空间,从而快速修改游戏画面的颜色

image-20230724163949045

image-20230724164015141

image-20230724164344381

7.5 渲染管线

image-20230724164827551

7.5.1 Forward Rendering

对每一个mesh、每一个light,渲染一遍

image-20230724165010279

  1. 透明物体必须在不透明物体之后绘制
  2. 多个透明物体按照由远及近的顺序绘制
  3. 天空最后绘制

image-20230724165037797

7.5.2 Deffered Rendering 延迟渲染

  1. 先计算所有物体的材质信息,将其保存到一个巨大的G-Buffer中
  2. 然后再计算所有的光,将其应用到G-Buffer中

image-20230724165433022

优点:

  1. 对光的处理非常方便
  2. 渲染很容易debug

缺点:

  1. 需要更多的内存、读写开销
  2. 不支持透明物体
  3. 对MSAA不友好

image-20230724165643986

7.5.3 Tile-based Rendering

将画面拆成一小块一小块,降低对读写的压力

image-20230724165933701

从而可以判断每个光会对哪些tile产生影响,进行光的裁剪

image-20230724170117258

对于每一个tile,可以在计算Z-Buffer的时候,得到该tile的深度范围,从而计算出光源可能影响到的tile

image-20230724170247601

7.5.4 Forward+ Rendering

分块进行Forward Rendering

image-20230724170500085

7.5.5 Cluster-based Rendering

直接对Z空间进行切分,划分为一个个的四棱锥

image-20230724170548584

7.5.6 Visibility Buffer

将几何信息与材质信息分开,存储几何信息,反向找到材质

  1. 在Visibility Buffer中,记录每个像素属于哪个几何体的哪个三角形,从而可以反向查该三角形应用哪种材质

image-20230724170734043

7.6 Frame Graph

用一个有向无环图表示资源之间的依赖关系,由系统自动管理实际资源的使用和释放

image-20230724173121032

7.7 V-Sync & G-Sync

7.7.1 Screen Tearing 屏幕撕裂

引擎渲染的刷新率是不一定的,而屏幕的刷新率是固定的,从而会导致画面的撕裂

image-20230724173615680

7.7.2 V-Sync 垂直同步

垂直同步(VSync) :将游戏或应用程序的图像帧速率与显示监视器的刷新速率进行同步,有助于建立稳定性。 如果不同步,则可能会导致画面撕裂,即图像看起来在整个屏幕上呈现水平方向毛刺或重影的效果。 启用VSync 后,便会获得完美对齐的画面而不会出现毛刺

将整个Frame渲染完成后,等到下一个屏幕刷新时间,统一写进去

  1. 可能会导致画面一会快一会慢

image-20230724173646994