渲染管线与后处理
June 25, 2023About 14 min
渲染管线与后处理
Info
目的:建立整个知识体系,明白相关的方法和思想
Ambient Occlusion

- AO让结构空间感更加清晰
- 效果:
在光场中表现出了一定的明暗关系,小凹槽比较黑,凸出的地方比较亮→ 符合生活实践


- 对比图

- 数学原理:
对于表面上的每一个点, 它在它可看到的正半球面,只能看到部分天光,其他天光被周围的几何遮挡住了.

- 法线只能提现明暗变化,不能告诉几何变化.
- 加入AO烘培贴图(右)可以丰富角色的几何细节
- 通过预计算完成

- SSAO的思想:
- 通过相机渲染可以得到一帧场景图, 这帧图像包含颜色信息和深度信息。
- 把每个点的深度信息连接到一起就能得到一张height field, 然后通过height field可知道每个区域的自遮挡关系。
- 方法:
- 从眼睛中射出一根光线相交到看到物体的某一个点(比如P),可以获得这个点的三维空间坐标。
- 以相交P点为球心,给定一个半径创建一个球,随机洒若干个采样点。
- 用相机去投射采样点,可以知道它的深度,若它深度比ZBuffer的深度更近表示它在可以看到光的地方,反之表示被某个几何挡住了。
- 最后被挡住的采样点记录为Occulusion, 总的数量记录为N,可以创建一个简单的工程。
Info
这里很重要的一个思想是Screen Space, 通过局部采样几何信息来避免对整个世界的运算。

- 通过法线朝向只需要计算对应的半球,会优化SSAO另外一个半球采样点无效的问题


- 缺点:
距离较远的遮挡物也会产生遮挡效果,这个是由于屏幕空间无法区分的原因

- 求球面空间的可见性
- 通过slice切片旋转找到光线刚好通过最高遮挡点的仰角,最后可得到一个遮挡区域图.
然后估算出多大区域的天顶是可见的。 - hack: 认为距离很远遮挡物对AO没有变化,图中的红色方框的表达式.


- 不同角度射出来的光的强度不一样,通过红色方框的表达式可知把入射角加入了计算。

Info
思想: 如果我们能算出AO值,也能猜到光在里面来回bounce最后形成的亮度,就能得到最接近真实的一个效果.
通过不同的AO值做分析,通过统计学得到光来回bounce的最终结果--基于数学上的拟合方程.

- 现代GPU可以快速地计算Ray cast,每帧对每个像素射出1~2个Ray, 通过时序对数据进行收集.
Fog


- 最简单雾效实现

- 雾是一个气溶胶,一般是在靠近地面的地方,超过后会降低。
- 所以假设Fog有个最大高度值,高于某个高度会成指数型递减。

- 体素化实现的雾效,能看到光射出的光柱
- 把相机整个空间进行划分,用视锥进行切分,然后进行各种积分运算
- 通过3D Texture来存中间结果,尽量保持对屏幕上像素块采样是一致的
Anti-aliasing 反走样


- 屏幕的分辨率有限,采样率远低于真实世界

- 核心思想:屏幕上每个像素采样是不充分的, 可以通过多采样几个像素,然后平均,这样可以产生过渡区域,这样可以很光滑.

- SSAA 超采样
- 把所有的尺寸double一下,扩大到4X大小的图形做一个down sampling.
- MSAA
- 因为锯齿产生的问题是发生了几何边缘。
- 对空间还是4倍的采样,在做shading的时候去看如果4个采样点落在同一个geomerty, 只shading一次,否则采样4次通过权重计算。
- 硬件支持,依然需要花费4倍的开销(如图右), 只是跳过了一些不需要的计算。

- 通过原始图像获取图片中颜色变化边缘的edge
- 把图像转换为灰度图(亮度空间)
右侧公式也是把彩色照片变成黑色照片的方式,每个比例值是一个色感经验数值. - 把采样点上下左右邻居加到一起算差异值,当平均差异值大于某个阈值的时候就视作边界.

- 分别算出竖向和横向的卷积绝对值,判断那种变化多,然后和差别很大的像素做blending
- 比如图中示例是横向差别大,然后比较左边和右边那个差距大,得到一个offset的朝向, 然后计算blending.

- blending方式:
- 从这个点出发沿着朝向的垂直方向一致找,如图所示就是沿左右一直找.
- 把查找点通过同样的朝向组成一对检查颜色变化, 如果变化和最初点的变化相似就继续检查。
- 如果变化和最初点的组变化差异很大,则认为找到边的两个端点,如图所示左边端点是白色,右边是黑色。

- 比较最初点距离左右端的长度

- 最终算出每个点真实采样时的周边插值


- 核心思想: 对过去帧做加权平均.

- hack:当物体高速移动时当前帧的权重更高,如果静止权重是差不多的.
- 问题:有一点小小的offset和残影
Post-Process 后处理

- Bloom 光晕效果
- Tone Mapping 让画面正常曝光
- Color Grading 风格化表达
Bloom

- 凸透镜成像不能完美地聚焦到平面上会发生一些发散(Airy disk)

- 渲染出一个光照贴图,找到贴图中高亮的部分
- 计算像素的Luminance值与阈值比较,超过的部分认为是高亮区域

- 高斯模糊–对于高亮的部分进行模糊处理
- 通过高斯方程来完成,高斯方程的特点是一个M*M的卷积运算可以通过两次M个像素的卷积实现。
然后可以得到Bloom区域

- 通过缩小计算+放大还原的方式计算出Bloom的区域
- 可以减少运算量

- 最终效果
Tone Mapping

- HDR的渲染特点是阳光直射的地方亮度非常高,阴影里面亮度非常低
- 左边天空过于曝光出现了色偏
- Tone Mapping就是通过曲线进行重新映射

- 经典的Tone Mapping 曲线 - Filmic
- 通过多项式公式进行拟合得到的

- 转换后可以无差别支持各种终端 – 电影行业的经验总结


- ACES 亮部足够亮,色差饱满,暗部清晰,整个画面明暗有致。
- 贴合人眼感受: 对高度比的图很敏感
Color Grading


- Color Grading的别称交LUT, 目的是根据游戏需求调整整个场景的颜色,比如BOSS战
- 本质上就是原始颜色到想调到的色相空间的映射,通过表格来记录和查询

- 把RGB的颜色存放到一个3D Texture, 任何一个颜色都可以查表转换为另外一个颜色
- 不支持3D Texture可以通过一个2D的Texture来存储
- 因为颜色是连续所以通过16*16或者32*32去进行存储来节约空间

- 通过工具很方便创建LookupTable

- 游戏场景的运用
Render Pipeline 总结

- 所有东西基于标准方程

- 世界通过Mesh, Texture, Shader绘制,然后通过culling把不要的东西裁剪掉,要观察的世界就被画出来了,

- 光和世界的变换,光和材质的交互,绘制一个很有光感真实感的世界

- 如何表达世界中的地形,天空和云等环境效果

- 加入各种效果让世界更精彩

- 简单来看就是绘制顺序,必须有一套规则去控制算法的先后执行。
- 图中就实现了一个简单的Pipeline.

- 前向渲染就是上面说的按着绘制顺序依次完成渲染

- 透明物不能和不透明物体混合到一起, 必须最后绘制。
- 透明物体绘制需要进行Transparent sorting, 就是透明度排序
- 把所有透明物体由远及近的绘制,当然如果被不透明挡住不用绘制

- 早期经典游戏使用样例

- 延迟渲染
- 思想:渲染时先把所有的物体渲染一遍,不计算和光的关系
- 把各种材质先放到一个巨大的Buffer里去,这个Buffer一般称呼为GBuffer
- 最后再计算光的部分,所有像素不会产生OverDraw,shading只会算最后一次

- 对光的处理非常方便,容易Debug
- GBuffer非常废

- 通过把屏幕拆成小块来降低Frame Buffer的读写压力

- 额外好处是光的影响范围可以拆分到每个Tile中去
- 构建light list

- 渲染的时候会先生成一个ZBuffer的深度---Pre-z
- 每个Tile可以知道最远和最近的Z,也可以测试光的影响深度



- 把世界基于Z空间切分成各种四棱锥–Cluster
- 不用计算Z-min, z-max, 一个Tile一个Tile去计算对light的visibility

- 比较前沿的新Pipeline
- GBuffer存储材质的全属性,VBuffer存储几何的全属性,分离几何和材质
- 通过几何属性可以反向找到材质,同时通过Vbuffer可以剔除没有用到的材质信息,这样Gbuffer可以存储更多的材质
- 直接用GPU去做光栅化比硬件更快

- 虚幻的Pipeline


- 通过有向无环图由系统去检查依赖,自动优化资源可重用部分
- 思想: 在做一个大型系统的时候,需要去思考完成一个工具让协作的同事不冲突
Render to Monitor

- 关闭VSync后,会发现画面上下错开的情况–屏幕撕裂

- 原因:引擎每次渲染的时间是不一定的, 显示器刷新频率是一致的。
如果错过显示器的刷新频率就出现等待的情况,也就是撕裂的产生原因.

- 通过V-sync强行同步

- 直接控制显示器刷新率和游戏相同