以下为个人学习笔记整理,课程官网传送门,作业传送门,会议系统传送门。
# Real-time Shadows
- Shadow mapping
- The math behind shadow mapping
- Percentage closer soft shadows
- Variance Soft Shadow Mapping
- MIPMAP and Summed-Area Variance Shadow Maps
- Moment shadow mapping
- Distance field soft shadows
# Shadow mapping
- 2-Pass Algorithm:详细说明见 Game101 Shadow Mapping
- 【Pass1】The light pass generates the SM(shadow mapping):从点光源出发,记录每个点的深度值
- 【Pass2】The camera pass uses the SM (recall last lecture):从摄像机出发,如果两者深度值相同,则可以被看见,否则视作阴影。
- 【优点】:可以用 SM 对场景阴影进行表示,不需要场景实际的几何。
- 【缺点】:有走样(锯齿)和自遮挡现象「self occlusion」。
# self occlusion
由于光源向四周发出光线并记录深度的过程中,存在一个精度问题,并非每个点拥有一个深度值,而是某块范围拥有一个深度值,这种情况下将导致距离非常接近的两个点会存在互相遮挡的情况。
# Adding a bias to reduce self occlusion
增加一个误差值可以有效的降低自遮挡带来的问题,但是会让原本产生阴影的部分出现丢失。
# Second-depth shadow mapping
引入第二深度的概念,在一张 shodow mapping 中存储「最小深度」和「第二小深度」的中间值。
如果某个点的深度大于「中间值」或者小于「中间值」,那么它将会被遮挡或显示。
这样做也会有一些问题:
- 所有物体必须有厚度,因为需要取到第二深度,如果物体只是一个面,那也毫无意义。
- 对于实时渲染来说,开销不值得。
- 有很多多余的
if
判断。 - 有很多需要修改最小和次小深度的操作。
- 有很多多余的
RTR does not trust in COMPLEXITY
# Aliasing
# The math behind shadow mapping
# Approximation in RTR
在实时渲染中,一些复杂的计算过程往往会通过一些近似的方程所替代,从而提高运行效率。
何时是近似的?
- 在
g(x)
的 max 和 min 越接近的时候。 - 在
g(x)
的有效积分域越小时。
# In Shadow Mapping
把该性质应用于光照方程,把「Visibility 函数」单独进行积分,这样就可以视为「Visibility 函数」积分 ×「shading」
- shading:所有光源的光线照射到该点的强度,这部分内容可以记录在 shading mapping 内。
计算光照的时候,何时是近似的?
- 当积分域很小,换句话说就是该点所接收到的光线范围很小:
- 点光源 or 方向光源情况下,积分域很小。
- 当函数曲线变化平滑(Smooth)时,换句话说光照在各个区域的强度变化不大:
- :物体本身的材质「BRDF」是 diffuse 的。BRDF 相关介绍
- : 有着恒定强度的光照的情况下。
# Percentage closer soft shadows
# Percentage Closer Filtering(PCF)
最初被用于抗锯齿,后来被用于制作软阴影(PCSS),主要原理是对一块范围内的阴影求平局(类似模糊效果)
filtering 操作不直接处理 shadow map:
- 对深度贴图做模糊处理,最终的阴影效果依旧是非 0 即 1,锯齿任然存在。
- 不是对已经存在锯齿的结果进行 filtering 操作,也不是对着色的结果做 filitering。
处理办法:
- 针对某个点,对其周围一定范围内的「shading point」和对应点的 「shadow map 深度值」进行比较,得到 0 or 1 组成的集合,再求平均。
- 「shading point」:从摄像机方向观察到的点周围的点。
PCF 不是对最终的阴影做模糊操作
最终的阴影做模糊,最终的效果依旧还是会有锯齿
用 PCF 处理后的图片
当求平均的范围变得很大时,就实现了软阴影的效果
# Percentage Closer Soft Shadows(PCSS)
决定一个阴影究竟是软阴影还是硬阴影,取决于阴影和物体的距离
- Filter size(range)<-> blocker distance
点光源是不存在软阴影的,只有面光源才有软阴影,而且只有点光源才会有 shadow map。因此很多情况下,通常会用点光源生成 shadow map,然后视作面光源来做软阴影。
# Relative average projected blocker depth
# PCSS algorithm
- 【step1】Blocker search:已知一个 「shading point」,通过点光源和「shading point」得到对应「shadow map」上的一个点。判断该点是否被遮挡,如果被遮挡,则该点为「blocker」,记下它的深度值。最终得到一张记录了所有「blocker」深度的图。再针对某个「blocker」,取其一定范围内的所有「blocker」的深度求平均。得到「average blocker」。
- 【step2】Penumbra estimation:通过「average blocker」计算出「filter size」。
- 【step3】Percentage Closer Filtering:用计算得到的「filter size」去对该点进行「PCF」处理。
# 思考:求「average blocker」所需的范围该是多大呢?
- 通过「light size」决定。
- 通过「shadow map」和 「light」的距离决定。
# A Deeper Look at PCF
- Filter / convolution:
- :点
p
邻域范围。 - :对
p
点周围的点q
,根据点q
和点p
的距离进行加权。
- :点
# In PCSS
- :表示符号函数。 如果大于 0,最终的结果就是 1; 如果小于 0,最终的结果就是 0。
- :表示点
q
「shadows map」 的深度和 「shading point」 的深度差,一般表示「shading point」x
是否被遮挡。 - :用来判断点
p
周围的点q
是否被遮挡。
因此 PCF 并不是对 「shadows map」 做 「filtering」:
如果对 「shadows map」 做 「filtering」,最终的 只能是非 0 即 1。
同样,PCF 也不是对图像的最终结果做「filtering」:
# Variance Soft Shadow Mapping
VSSM 用于解决 PCSS 效率低下的问题。[Fast blocker search【step1】and filtering【step3】](#PCSS algorithm)。
# Fast【step3】
【step1】和【step3】比较慢的地方在于求某个区域内数值的平均。以【step3】这项操作为例,求区域内遮挡 1 和未遮挡 0 的平均,可以进一步理解为,区域内 1 的占比。
因此原本需要求得一个范围内「遮挡」和「未遮挡」的平均变为需要知道一个区域内,比「shading point」深度值大的点(遮挡)的占比。
# 如何求得比「shading point」深度值大的点的占比呢?
- 首先,我们假设深度值的分布是符合正态分布的,或者说近似正态分布。
为了确定一个正态分布曲线,需要知道样本的「均值」和「方差」
如何得到样本的「均值」?
- 「MipMap」本身就是一个很好的求均值解决方案(但仅限矩形区域,且不够准确)。
- Summed Area Tables(SAT)
如何得到样标的「方差」?
:方差 = 平方的期望 - 期望的平方。
- 期望的平方:可以用上述求得的「均值」。
- 平方的期望:需要另外一张「shadow map」用于存储深度的平方,之后再通过上述方法求其均值。
因此,需要单独一张「shadow map」用于存储深度的平方。
得到了曲线的正态分布,接下来就需要求占比了
- 通过正态分布的 「PDF(Gaussian Distribution Function)」 曲线,求得 「CDF(Cumulative Distribution Function)」 曲线,便可以获取占比。
# 不是正态分布咋办?
万能的近似函数又来了,为了能够满足任何分布曲线。「CDF」曲线可以用「切比雪夫不等式」转成通用格式:
该函数可以满足任何分布曲线,但是有一定程度上的误差,且如果 t
在分布函数的左半侧,结果会更加的不准确。
# 总结 Fast【step3】
预处理
- 得到一张记录深度的「shadow map」。
- 得到一张记录深度平方的「shadow map」。
运行时
- 获取某点的深度平均值「MipMap」。O (1)
- 获取某点的深度平方的平均值「MipMap」。O (1)
- 得到占比概率([Visibility 函数](#In Shadow Mapping))。O (1)
- 干掉了采样和高消耗的循环。
缺陷
- 不支持实时情况下的深度变化,如果有深度变化需求需要动态修改「shadow map」
# Fast【step1】
【step3】的优化告一段落,接下来看【step1】的问题。【step1】需要查询周围的「blocker」并且需要求出所有「blocker」的「平均深度」。
- 需要对每个「块」进行遍历,效率低下。
- 需要对所有「blocker」深度求平均。
- 特别注意:「blocker」是被遮挡的「块」,即深度值小于「shadping point」的「块」—— 图中蓝色部分。
假设「shadping point」的深度是 。
- Blocker(z < t)的平均值记为 。
- Non-blocker(z > t)的平均值记为 。
- 因此,可以推导出如下公式:
- 表示 Non-blocker 数量
- 表示 Blocker 数量
# 未知,如何求?
需要计算 ,其中 均已知。
- 不妨做个大胆的假设:。
- 最终: Z_{occ} = \frac{NZ_{Avg} - N_1t}
# MIPMAP and Summed-Area Variance Shadow Maps
# MIPMAP
MipMap 的介绍可以回顾 Game101 的 Texture Mapping。
MipMap 有几个缺陷:
- 处理均值采用的是切分网格形式的平均。因此,被切分出的网格内任意一点的范围平均都是同样的结果,并不是真正意义上的取某个点周围的一定范围进行平均。
- 对于非矩形的区域没有办法很好的支持。
- 对于矩形区域,如果边长不是
2
的阶乘,例如6
,这样就需要对4
和8
来做三线性插值。
# Summed Area Tables(SAT)
# Prefix sum algorithm
「SAT」的核心在于前向求和算法,假设输入的内容是一个一维数组,经过前向求和算法后会得到一个新的一维数组,数组内每个元素记录的是前面 N
个元素的总和。因此,计算中间某 X
个连续的数只和将变得非常简单。
一维数组内的求和解决了,接下来思考一下二维数组应当如何处理呢?
原理类似,每个点距离一个从远点到该点组成的矩形面积的数据总和。通过类似求面积的方式,最终就能得到某个点的周围一个矩形空间内的点总和。
「蓝色方块」的面积便可以这么计算:「蓝色方块」=「大绿色方块」-「两个黄色方块」+「绿色小方块」
# 总结
- 「SAT」计算结果非常准确。
- 需要额外 的空间存储「SAT」。
- 求解平均值速度提升到了 。
# Moment shadow mapping
「MSM」用于解决「VSSM」的误差问题,「VSSM」的很多计算都是基于「正态分布」实现的,换句话说,如果分布曲线不是接近「正态分布」的情况下,结果将变得不准确。
# 【Issues1】深度值不符合正态分布
深度值不符会使得「Visibility 函数」的结果不准,最终会使得画面偏暗或者偏亮
- 偏暗:这种情况下还能够接收。
- 偏亮:偏亮则会出现漏光效果。阴影的某块区域会比周围更亮
Light leaking
# 【Issues2】由于「切比雪夫不等式」的特性, t
在分布曲线左侧时误差会非常大。
non-planarity artifact
# 目的
「MSM」主要是用来解决非正态分布情况下「VSSM」带来的误差【Issues1】。
为此,「MSM」引入了一个「higher order moments」来描述一个分布。
# Moments
由于「VSSM」的「正态分布曲线」是通过均值和均值平方计算得到的。因此可以成「VSSM」使用了 两种「moments」。「MSM」则是想通过使用更多的「moments」让结果变得更准确。
# 结论
如果用前 n 阶「moments」,便可以表示 阶的函数。
「PCF」函数本身是个 2
阶函数,由于「PCF」的结果比较准确。如果想要「VSSM」结果接近「PCF」,则需要使用前 4 阶「moments」。
# 总结
理论上越多个「moments」能达到的效果将会越好。存储上可能会有一定的消耗,另外:如何根据前 n 阶「moments」得到近似「PCF」(有两个台阶)的函数也是一个非常复杂的问题。
# Distance field soft shadows
「SDF」是一种更加高效的计算软阴影的实现方案。
# Distance Functions
定义空间中每个点到最近的一个物体表面的距离(带正负号)
# Blending Support
距离函数(Distance Functions)支持对多个简单的几何物体的距离场做「Blending」得到复杂几何物体的距离场
# Usages of Distance Fields
# 【Usage1】:Ray marching(sphere tracing)to perform ray-SDF intersection。
核心目的是用于对射线求交。
- 获取起始点的 「Distance」,获得射线的传播方向「Distance」距离的下一个点的「Distance」并继续推进。
- 移动一定距离后或者遇到某个物体表面时,算法结束,便可以快速获取射线是否命中物体表面,在射线碰撞检测方面使用较多,效率高。
# 【Usage2】:Use SDF to determine the(approx)percentage of occlusion。
核心目的是求得视锥的「可视率」。该情况下「Distance」一般不带符号
- 从「shading point」往某个方向查询,中间遇到了「Distance」不为 0 的点时,记录一下,直到遇到遮挡或者光源时停止。
- 视锥范围就是「Distance」不为 0 的点和「shading point」形成的最小切线角度 。
- 视锥的「可视率」就是 ( / 整个视锥角度)。
# 如何提高「可视率」计算效率?
还是老办法,用一个近似函数来代替~
并且引入一个系数 k
用于调节整个系统的「敏感度」,即:什么时候完全在阴影内,什么时候完全不在。
- 如果
k = 100
,那么超过0.1
以上的「可视率」就可以认为是完全不在阴影内,这样的效果会让阴影变得更加「硬」,边界分明。 - 同理,如果
k = 2
,那么超过0.5
以上的可是「可视率」才能认为是完全不在阴影内,剩下的地方都是从非阴影到阴影的过渡,这样的效果就更像「软阴影」。
# Distance Field:Visualization
针对场景中每个物体计算「Distance Field」,然后对图内每个点取所有物体内记录该点「Distance」的最小值作为「Distance」的最终值。
# 总结
「SDF」的优点:
- 速度快
- 高质量
「SDF」的缺点:
- 需要预处理
- 需要庞大的存储空间
# Antialiased / infinite resolution characters in RTR
基于距离函数的无锯齿字符