游戏中渲染管线、后处理和其他的一切
[写在前面] 由于很多内容在GAMES202中有介绍,所以只记录本人觉得有必要再记录的。
环境光遮蔽 AO
HBAO
利用ray-marching思想绕一圈,获取可见边缘线,然后就可以更准确的获取可见度参数。
同时利用W函数来截断,即当ray-marching碰到的点过于远时直接不参与计算(即算作可见),因为太远的遮挡没意义。
实际使用时往往分两步:
- 利用深度图作为一个表示该平面高度情况的图
- 直接在2D图上做射线,并获取这个边缘线
GTAO
进一步考虑了法向问题,将不同角度的光的贡献比重考虑进去。
另外,大佬发现单次bounce计算出来的AO和多次的竟然是符合一定函数关系的(三次函数)(拟合结果),所以就可以直接算了。
雾效
深度雾效
一般深度雾效有三种:
高度雾效
因为雾效还跟高度有关,所以行业普通使用的方法是:
- 当高度低于某个阈值时,直接将Fog设为定值
- 高度高于阈值后,以指数级递减的方式继续往上
当在计算效果时,如果所处位置高于最高点,则需要利用ray-marching来逐次计算fog值(因为是一点点变薄的)
体素雾效
考虑到实际效果和效率,这里的体素不是均匀划分,而是根据视锥空间去进行划分,比如近处体素体积小,远处大。这种情况下能获得单根光线的效果
抗锯齿 AA
造成AA主要有三种原因:
- 几何原因。比如边的采样
- 纹理采样
- 高光采样
FXAA(Fast Approximate Anti-aliasing)
- 找到边缘像素
- 计算每个边缘像素的offset
- 利用这个offset重新去采样边缘点并进行融合
其中步骤1就是CV中经典处理
步骤2的细节如下:
先进行横向和纵向offset的计算(横向的offset大说明边是竖着的,反之亦然),然后在大的那个方向再比较,从而确定最终的边,也就是要blend的两个像素
步骤3中处理边如下:
简而言之就是计算这两条row或者column上的像素对,算它们的均值,且和相邻的像素对比较,如果大家的那两个像素均值都差不多,说明都是在这条边上,如果差的不一样,则说明遇到边的端点。比如图3中左边两个1和右边两个0导致的均值“1”和“0” 与中间的“0.5”是不一样的,说明对这行来说,边的端点就在这两个地方。
然后再对每个边上的像素进行融合,具体就是判断其和两个端点的距离关系,插值计算。或者可以理解为采样点向上移或向下移多少。
比如这里是先让两个端点变成它们插值的平均值(1 + 0)/ 2 = 0.5。然后对边上的每一对像素进行插值,完成跨一行的合理的过度
TAA
TAA中当前帧和过去帧的比重受物体运动速度与否,一般静止的物体两帧权重差不多,而运动的物体则更看重当前帧
后处理
Bloom
霓虹灯、强光导致的光散效果
常用方法是基于高斯滤波,由于直接对原图高斯过滤的话成本太大,所以采取金字塔结构,其原理如下:
- 首先对原图进行降采样获得不同尺度图片
- 从最小的开始,进行高斯模糊,再加上同尺度原图
- 一次迭代获得最终效果
Tone Mapping
解决光亮度范围过大导致的要么亮的地方过亮要么暗的地方过暗的问题。其实就是把HDR的图片信息映射到普通LDR的图片中。
最早时常用的映射函数是 Filmic s-curve tone-mapping,后来ACES被提出,优化了最终颜色被投入到各种不同显示终端的步骤。
Color Grading
其实就是颜色的映射,通过3D或者2D的颜色图(如下图),再设计一个映射的算法(函数),从而就可以把原图片转成另一种颜色风格的图片。对艺术家非常友好。
渲染管线
可以参考https://positiveczp.github.io/%E7%BB%86%E8%AF%B4%E5%9B%BE%E5%BD%A2%E5%AD%A6%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF.pdf
针对延迟渲染高显存等缺点,提出了Tile-Based Rendering(移动端):
- 将画面分为小块进行渲染
- Light culling By Tiles 因为1,所以光照效果的渲染也可以以小块为单位
- Depth Range Optimization 对点光源的渲染也考虑深度范围,对超出范围的不考虑渲染
进一步,又提出了cluster based rendeing,即不止分成对屏幕上的块,而是直接把视锥空间分成不同的小椎体
还有值得注意的新技术是VB,用来单独保存几何信息,也可以用来反向查找对应的渲染信息
Frame Graph
利用一个有向无环图去展示一帧下各个资源的依赖关系,从而可以进行一些BUG检测和优化。
这也是因为DX12等新API开放了更多底层内容(比如内存管理)等带来的新挑战。
V-Sync & G-Sync
V-Sync是为了解决屏幕上画面分割的问题,这是因为GPU渲染是一直渲染的,但每一帧所花费的时间是不一致的(由于场景复杂度不同),但是显示器刷新频率是固定的,所以如果错过了某次刷新频率,就会出现上下不同的情况(就是这一帧还没渲染完就刷新了)
而V-Sync保证每个frame buffer全部写完以后再整个刷新上去,但也会带来画面时快时慢的问题。
相关概念:Variable Refresh Rate 可变刷新率的显示器