渲染流水线:
1、首先相机摆放到场景一个位置和角度,场景的各个物体也已经被摆放好
2、拿到场景物体的顶点信息,根据顶点信息构成图元
3、经过透视投影,将图元转化为2*2的正方形,再把2*2的正方形扩展到屏幕大小
4、对图元进行采样,仅采样会导致抗锯齿,所以这里做MSAA
对图片进行模糊再采样,然后将一个像素也模拟成n*n个虚拟像素,观察图元内包含的虚拟像素来决定颜色的深度,然后再做平均(卷积)
5、经过Z-Buffer,进行深度测试,处理Z通道中的遮挡关系
6、对最后的图元中的像素信息进行着色,shader着色(考虑环境光、漫反射光、强光影响),将RGB颜色画在各个像素上完成渲染管线生成最后的屏幕图片
在Games101中,提到过的抗锯齿方法在UE5里面是都有的:
Ray Traced:
简单来说就是屏幕中各个像素的位置射出一条射线进行反向追踪,计算各种因素的光照信息
如图,经过反射光,折射光从而把这些光线亮度累加在一起,就是这个像素最终亮度的值
但是只是做每个像素的反射和折射计算累加会消耗很多性能,这里采用了包围盒的概念来提速,如果射线连包围盒都没有进入,那么他是不会和包围盒内的物体有反射或者折射效果的,如果光线进入了包围盒,再来计算其是否与包围盒内的物体有进行反射或者折射
再进行优化,就是对场景空间分成很多个立方体的包围盒,一个包围盒又可以细化为多个子集的包围盒,以此类推变成树状结构,光线先射进大的包围盒中,看是否包围盒内有物体,若有,则对大包围盒细化为小的包围盒,看小的包围盒内是否也有光线射中的物体,以此去判断,具体的方法就是Oct-Tree KD-Tree BSP-Tree
Shadow:
在光源位置生成一张深度图,再从相机视角出发,观察到的位置深度比光源处生成的深度图深度大,则该位置存在阴影,反之则没有阴影
上面实现的事硬阴影,如何实现软阴影?(Soft Shadow)
PCSS(Percentage Closer Soft Shadow)
在摄像机看到的像素对周围做N * N的卷积,在最外侧的阴影则会和没有阴影的一起做平均,则产生了软阴影,N值越大,性能消耗越高,软阴影效果越明显
VSSM(Variance Soft Shadow Mapping)
对最开始的阴影生成一张深度图,再对这个深度图做平方再存放起来,然后做以下公式
用二维前缀和计算出大概的期望,用这个期望值来代表该像素的阴影数值,X为Depth数值
这个方法以显存换性能
由于PCSS的第一步和第三步都是n平方的操作,所以性能不达标,所以就有VSSM的操作
1、计算灯光位置对场景的一个深度采样
2、计算一个深度的平均值的平方,和深度平方的平均值来计算方差
3、通过概率论可以计算出任意一个深度值在第一部的深度采样图的概率排名,通过这个排名就可以直接计算出O(1)大概多少是在阴影内的,根据概率就可以算出对应阴影卷积后的平均值
DFSM(Distance Field Shadow Mapping)
已知o是光源,P1是o最近的物体位置,P2是P1最近物体位置,P3是P2最近物体位置,通过距离场:oP1是已知的,P1P2 P2P3是已知的
通过近似的算法直接算出来对应的数值SDF(p)就是P1P2或者P2P3,p-o就是oP1 oP2 oP3这样的值,这样的比例去做近似表达角度大小的数值,角度越大,值越大,阴影越柔软,K值是为了让更多位置大于1,更多位置为硬阴影,k值越大,阴影外轮廓柔度越低,1为最黑的阴影,0~1根据值的大小,线性决定阴影强弱
每个像素,根据求出来的球谐函数的值进行计算
Wavelet对光照的计算
通过Wavelet的各个形状来替换图片上的各个形状
Wavelet不能处理快速旋转的场景,没有Wavelet的图可以快速进行替换,而SH可以对当前这一层的图片进行旋转计算匹配,支持快速旋转的场景
Wavelet不动的时候效果好很多,但是快速旋转不行
所以前向渲染都是通过一个主要光源计算像素
四个次要光源计算顶点
其余光源用球谐函数来计算,依然能达到一个很好的效果
VSM:虚幻用的Virtual Shadow Map
对阴影贴图进行分区
进行分区显示,也做了类似MipMaps的操作,越近,分的区越细腻,阴影贴图展现的精度也越高,如果视口上没有显示的分区,不进行整体渲染,只渲染视口上显示的分区
RSM(Reflective Shadow Map):这个其实不是做阴影的,只是利用贴图计算反射光对场景的贡献
将阴影贴图中,所有的像素都是间接光源,再以间接光源去做阴影贴图,从来达到反射光做出阴影效果
优化:将阴影贴图,离中心位置越近,间接光源越小,离中心位置越远,间接光源越大,并不是取所有的点,而是随机一部分点,从而达到性能优化的效果
LPV(Light Propagation Volumes)
在RSM中,我们通过shadow map找到并定义了一系列虚拟点光源,LPV的第一步仍是如此,在找到虚拟点光源后,考虑整个场景分成的一个个小格子,在对应的小格子中,可以找到其中的虚拟点光源
分成了体素,注入光源,将体素进行前后左右上下进行传播,最后实现了光照的反射的计算
当然LPV也是有缺点的,当体素大小不合理时,我光照明明遮住了,光线并不会传播过去,但是由于LPV不对可视性考虑,所以依然能传到被遮挡的体素上去,变得十分不合理
VXGI(Voxel Global IIIumination)
做成Oct-Tree类似的形式,将被照到的位置的法线信息,和面向光源的方向信息给存储起来
基于追踪出的圆锥面的大小,对格子的层级进行查询,就是对场景中的所有体素都要判断是不是与这个锥形相交,如果相交的话就要把对于这个点的间接光照的贡献算出来
AO(环境光遮蔽)【Ambient Occlusion】:
SSAO(Screen Space Ambient Occlusion)
先在屏幕表面的球形范围内随机生成一系列点,根据深度图,判断这些点深度是否小于深度图深度,再按照比例若小于深度图的点大于一半,则这部分不做AO,若小于一半,则做AO,取其中有绿色点的一半,若四个红色,一个绿色,则该点的暗面程度为1\5,值越小,颜色越黑
SSAO也有一个明显的缺陷,就是凹形的时候可能它没在物体内部,但是仍被认为深度大于深度图,从而造成计算不精准
另外,还有桌子明明没有和地面贴合,但是计算的时候不考虑是否贴合,只考虑球形范围内的点,是否深度小于深度图,来计算该点暗值,AO是贴近的缝之间存在暗面的效果,但SSAO明显这一点没做到
SSDO(Screen Space Directional Occlusion)
SSDO的检测采样做法和SSAO完全一样,如上图所示,在法线方向半球,(这里我们就假设已经知道法线了),撒点,然后根据深度图和投影到Camera的深度的对比来判断是否能对P点产生遮挡。
如果被遮挡,如上图第二幅图所示,根据法线计算它们对P点的贡献之和。其次图1的C点没有被挡到,于是我们可以直接采样环境光的直接光照,也就是说SSDO还提供了一种做环境光照的方法
当然Screen Space都存在一个严重的问题,就是屏幕外的没法渲染,因为只拿到屏幕上有限的内容去做操作,那么假如一个镜子,它朝向一个远方(屏幕上没有的地方),那么远方的内容不会被反射到镜子里面,这是个很严重的问题
以及屏幕上的内容,无法反射出三维信息的内容,从而三维反射不真实的情况,时常发生
SSR(Screen Space Reflection)
用MipMap取2*2深度的最小值,若深度最小值都没达到,则光线不可能会接触到物体表面
若光线到达这个最小值,则找到上一层的MipMap(更精细信息的MipMap),再去观察是否有到达物体表面,打到物体表面就将这一块渲染到反射面上,就达到反射的效果了