以下为个人学习笔记整理,课程官网传送门,作业传送门,会议系统传送门。
# Real-time Global Illumination
实时渲染中所描述的全局光照实际上就是比「直接光照」多一次的「间接光照」。
# Reflective Shadow Maps(RSM)
由于要用「次级光源(反射物)」去照亮周围的物体,又因为周围物体对「次级光源」的观察方向都是不同的 —— 需要知道「次级光源」各个方向上的光照强度。因此为了简化计算:不考虑观察方向带来的影响,假设所有的「反射物」的「BRDF」都是 diffuse 的。这样从任意方向的观察结果都是一致的。
GAME101 全局光照回顾。沿用之前推导出来的公式。考虑了「辐射强度」和「距离平方」衰减以及「单位立体角对应的面积」
假设每个「次级光源」都是一个点(patch),那么计算一个物体的「间接光照」,就需要把所有 patch 作为光源计算一次光照。
这里把「次级光源」视作 q
被照亮的物体视作 p
。
结合上面的公式,就可以得到 p
点沿着 方向从「次级光源」接受到的光照强度公式。
- 由于「次级光源」的「BRDF」假设为 diffuse。
- 「次级光源」的「BRDF」:,这里的「BRDF」是「次级光源」而非观察物体的。
- 「次级光源」沿着 方向出射的光线:$L_i = f_r \cdot \frac {\Phi}{dA}, \Phi \text {is the incident flux or energy} $,出射的光照强度等于各个方向上接收到的入射的总光照强度 X 「BRDF」/ 单位区域。
的计算已经解决了,剩下的「Visibility 函数」该如何处理呢?
一种办法是针对任何的「次级光源」都生成一张「shadow map」,不过这显然不太合适。因此就不计算了 😅。
# Not all pixels in the RSM can contribute
- Visibility 函数(就算是 diffuse 的也很难处理)
- Orientation:「次级光源」的入射光方向和法线夹角不可能反射到某个物体,例如桌子上的「次级光源」不可能影响
x
的渲染结果。 - Distance:距离太远了也不行。
这里做一个大胆的假设:「shadow map」上两个像素点的距离,可以直接视作空间坐标内两个点的距离,这种情况下就可以简单的获得每个「次级光源」的作用范围:
- Acceleration
- 对某个「shading point」周围选取一定数量的「次级光源」(采样)
- 类似 PCSS 的一些预处理操作
# What is needed to record in an RSM?
- depth:深度。
- world coordinate:世界坐标(计算距离)。
- normal:法线,计算方向和 项。
- flux:通量,计算光照强度。
# Often used for flashlights in video games
- 优点
- 容易实现
- 缺点
- 很多假设会让结果不准确。
- 没有考虑「Visibility 函数」。
- 直接光源越多,要做的「shadow map」就越多。
- 采样数量和图片质量之间的权衡。
# Light Propagation Volumes(LPV)
# 【step1】Generation
和「RSM」处理类似,用「shadow map」来找出所有的「次级光源」
# 【step2】Injection
- 将 3D 场景切分为 3D 网格。
- 每个格子内部是否包含了「次级光源」
- 对每个格子内的「次级光源」求和,得到以 3D 网格为单位的「次级光源」
- 用「SH 函数」来进一步描述每个 3D 网格的 球面出射光照 ——2 阶(4 个基函数)就足以描述。
# 【step3】Propagation
- 计算每个格子的「次级光源」的光照向六个面(不考虑斜对角)传播后的光照(不考虑「Visibility 函数」,所有格子都可以向四周传播光照,不会被遮挡)。
- 对所有格子内的所有光照求和并用「SH 函数」表示。
- 重复上述步骤多次,直到整个系统稳定。
# 【step4】Rendering
- 对任意的「shading point」,查询 3D 网格内对应的格子。
- 获取该网格内的入射光强度。
- 并计算「shading point」的光照。
# Any problems?
对遮挡的判断不理想,以格子为单位进行划分的情况下,格子内任意点的光照强度是一致的,就算实际情况中该点被遮挡了。
# Light leaking
# Voxel Global Illumination(VXGI)
# Difference with RSM
- 场景全部转化为一块块的格子(可以得到所有「次级光源」的格子和光照强度)。
- 从「摄像机」出发到某个「shading point」并反射出一个视锥,计算视锥覆盖的「格子」(次级光源)的贡献。
# 【step1】Voxelize the entire scene
# 【step2】Build a hierarchy
# 【step3】Pass1 from the light
- 通过光源计算任何一个「patch」的直接光照方向,以及「patch」表目的发现方向。并对多个「patch」进行合并得到一个更大的层级,记录层级上的入射光照方向分布和法线分布,这样就可以支持 glossy 的「BRDF」—— 当场计算出出射光线分布。
# 【step4】Pass2 from the camera
- 对 glossy 的表面,沿着反射方向绘制一个圆锥。
- 根据圆锥的长度,再对应层级大小的位置取「patch」进行光照贡献计算(越远取的层级越大)。
- 对于 diffuse 的「BRDF」这个视锥如何绘制呢?
- 用少数几个较大的圆锥来进行计算
# Real-time Global Illumination(screen space)
# What is 「screen space」?
- 信息都来自于「screen」
- 相当于对已经渲染过的图片做后期处理
# Screen space Ambient Occlusion(SSAO)
# Why AO?
- 实现简单。
- 增强了物体间的相对位置,画面更加立体。
# What is SSAO?
用于在「屏幕空间」对环境光照近似的产物
# 【idea1】
假设环境光在任何位置都是相同的,参考「Blinn-Phong」模型
# 【idea2&3】
考虑不同位置对于环境光的接收程度「Visibility 函数」。
# Theory Ambient occlusion
# rendering equation
- 由于是间接光照,可以直接视作常数,「BRDF」可以当成 diffuse 的,也是常数。
- 间接光照强度是常数,因此非常的 smooth,可以用「split sum」进行准确拆分并非近似。
- 另一种理解:相当于对积分范围的平均乘以光照强度积分。
- why ?
因为 ,所以对半球积分就变成对圆积分,至此:把 3d 空间投影到了屏幕空间。
# Actually Screen Space Ambient occlusion
- 光照()本身是个常量。
- 「Diffuse BRDF」——。
- 然后把这两个常数项提出来,公式如下:
# Calc「Visibility」
【object space】
- Raycasting:需要额外打一次射线
- Depends of scene complexity:场景越复杂越慢
【screen space】
- Done in a post-rendering pass:在渲染结束后处理。
- No pre-processing required:不需要预处理。
- Doesn't depend on scene complexity:不依赖复杂场景。
- Simple:实现简单。
- Not physically accurate:但是在物理上不准确。
# Limited radius
间接光照的照射范围有限,因此可以设置一个有效距离。
# Ambient occlusion using the z-buffer
针对某个「shading point」(白色点),取其周围一个球形区域内进行随机采样,得到弱点的样本。
对于每个样本,通过该点的深度值和「shadow map」的深度进行比较来判断该点是否会对「shading point」接收到的「间接光照」产生贡献 —— 红色表示无贡献,绿色表示有贡献。
只需要对「法线」所在的半球做采样操作:剩下半边都是物体内部,一般不考虑穿透物体内部的光线。
# SSAO problem
石凳空间上和地面不会有任何遮挡,但是由于是屏幕空间,因此在石凳和地面的交界处出现了错误的阴影。
# Screen Space Directional Occlusion(SSDO)
「SSDO」非常接近「RSM」,核心思想:
- 针对每个「shading point」,随机的射出若干个射线
- 如果射线遇到障碍物,那么障碍物发出的光线视作「间接光照」
- 如果射线没有遇到障碍物,那么就算作「直接光照」
# Screen Space Directional Occlusion
Comparsion w/SSAO
- AO:「间接照明」(橙色)+「非间接照明」(黄色)
- DO:「非间接照明」(橙色)+「间接照明」(黄色)
same as path tracing
# Consider unoccluded and occluded directions spearately
- Visibility(橙色)。这部分不是「间接光照」,不需要计算。
- Unvisibility(黄色)。这部分是「间接光照」,需要计算。
# Simple
和「HBAO」类似,对每个「shading point」的可见半球内随机采样。然后连接「摄像机」和「采样点」。判断深度值计算可见性,得到所有能够贡献「间接光照」的采样点(A、B、C)。
再对所有能够贡献「间接光照」的采样点进行距离和法线筛选,得到最终的贡献采样点(B、C)。
不过这样会有问题:
- 如果摄像机无法看到的「采样点」,会被排除,例图三如点 A(不应该)。
- 如果在「shadow map」深度比「采样点」小(光线无法照射到),这种情况下又会被计算贡献(不应该)。
# Issues
- 只能表示小范围的全局光照
- 可见性不准确
- Screen space 的通病:对于「摄像机」看不见的物体信息,将会缺失(假设一面墙背后有一个点光源,那么这种情况下,点光源将不会产出任何的光线)。
# Screen Space Reflection(SSR)
在屏幕空间上做光线追踪,不需要几何信息。
考虑任何光线和场景的屏幕空间如何求交。
就效果上来说,像是对地面做了一个镜面反射
# Basic SSR Algorithm
# Mirror Reflection
- 对地板上所有的片段进行光线反射计算,得到的交点作为改片段的反色光进行着色。
# High smoothness
# Medium smoothness
diffuse 的着色实际上就是取多条反射光线
# Medium smoothness + normals
可以再加上法线使得渲染更有「层次」。
# Variable smoothness
# Process
# Linear Raymarch
- 根据「shading point」和「摄像机」计算出反射方向
- 按照一定的步长进行查询,直到遇到「depth」比「shadow map」上记录的深度大时,可以视为光线和场景有交点。
- 计算交点处的光线贡献。
# Hierarchical ray trace
Generate Depth Mip-Map
核心思想是通过「MipMap」记录某个范围精度内的最小深度,加快查询速度。
每个「MipMap」上的点,记录的是周围 N x N 区域内深度的最小值。
和 3D 空间内的 「BVH」以及「KD-tree」非常类似 ——「BVH」和「KD-tree」介绍
每次移动到下一块区域时,计算移动到的点的深度值,并且和该「MipMap」记录的深度比较:
- 如果深度值比「MipMap」的要小,说明不会相交,继续移动。
- 如果深度值比「MipMap」的要大,说明交点就在像素范围内。
- 有交点的情况下,再对该区域的更「深」一层的「MipMap」进行查询,直到找到对应的交点。
# Hidden Geometry Problem
因为「Screen space」本身的问题,反射结果缺少了手掌和手指的背面。
# Edge Cutoff
另一种是物体本身超出了屏幕,这种情况下会丢失原本超出屏幕的信息。
通常会采用一个「距离衰减」的方式让画面不至于穿帮
# Shading using SSR
这里和「path tracing」的处理一致,计算光线打到的物体所反射出的光线,并且反射物(次级光源)必须也是「diffuse」的。
# Support
# Sharp and blurry reflections
支持光滑和粗糙物体的处理(BRDF)。
# Contact hardening
根据「reflection ray」的视锥大小,对不同范围内进行「filtering」,就可以实现近处清晰,远处模糊的效果。
# Specular elongation
取决于地面的「BRDF」,各项同性情况下可以实现拉伸的效果
# Per-pixel roughness and normal
地面的法线分布可以实现大理石等材质效果。
# Other approach
对 BRDF 的反射区域内选取更加重要(影响占比高)的方向进行采样,而不需要随机在范围内采样。
空间上的复用,tracing 一个「shading point」时,得到的点(蓝点)顺带计算一下周围「shading point」的贡献。
对 screen space 进行 filtering。然后就不需要对多个方向进行 tracing 就可以得到多个 tracing 的平均。