学完了GAMES202,复习一下全程的笔记。
Real-time Shadows
Shadow Mapping
阴影贴图:是一种生成实时阴影的方法,可以生成多种光源的阴影,常用于方向光,点光源和聚光灯也可以使用,阴影贴图的核心就是生成一张阴影深度纹理。是一个2Pass算法。是图像空间的算法,好处:不需要场景几何知识.坏处:导致自遮挡和偏差问题.(阴影贴图甚至是早期离线渲染的方法)
1Pass:灯光Pass,渲染灯光观察空间的深度图,记录最小深度,生成阴影贴图。
2Pass:渲染Pass,将被渲染点,经过灯光空间变换转换回灯光空间,和相应位置的阴影贴图保存的深度比较,如果深度更大,则视为在阴影中;如果和阴影贴图保存的深度一致,那么就不在阴影中。
深度保存和比较时,相比较的深度的计算过程要一致,比如灯光空间的深度图保存了深度真值,那么比较也用深度真值;保存了MVP后的z值,比较也用MVP后的z值。
自阴影问题
根本原因就是阴影贴图的分辨率不够,因此多个 pixel 会对应阴影贴图上的同一个点。在以灯光空间观察时,由于灯光方向与投影物体表面存在角度偏差,导致阴影贴图单个像素保存的采样深度对应在了投影物体的上方,投影物体本在表面,反而被判定为了遮蔽区。
解决方法:增加shadow bias,在2Pass计算渲染点在灯光空间的实际深度时,将其深度增加偏差,向灯光方向拉一些,就比阴影贴图中保存的深度要小了,也就不会被判定为遮蔽。
而shadow bias加多了也会出现问题,就是阴影平移,导致阴影看起俩与物体分离了。所以我们最终的目标是要找到一个刚好能够消除自阴影的 bias 值。现在常用的方法是基于物体斜度的,slope scale based depth bias。
基于斜度的偏移可以根据表面的斜度,更改深度的偏差值。当表面与灯光的夹角越小,那么阴影贴图中一个像素对应的表面区域越大,其深度变化也就越大,就需要添加更大的bias。表面与灯光的夹角可以通过法线与灯光方向的夹角余弦值来计算(dot(normal,lightDir)),所以bias要和夹角余弦值成反比。最终公式是
其中,是斜度偏差范围的系数,表示斜度偏差的程度,是根据夹角余弦值计算,是基础的阴影偏差。由此可知,与都是需要手动给出的,通过调试才能得到最优的效果。
// 第一种,直接拿tan当成slope直接与constantBias相加
float GetShadowBias(float3 lightDir , float3 normal , float maxBias , float baseBias)
{
float cos_val = saturate(dot(lightDir, normal));
float sin_val = sqrt(1 - cos_val*cos_val); // sin(acos(L·N))
float tan_val = sin_val / cos_val; // tan(acos(L·N))
float bias = baseBias + clamp(tan_val,0 , maxBias);
return bias;
}
// 第二种,将1-cos值当成slope,然后套公式
// dot product returns cosine between N and L in [-1, 1] range
// then map the value to [0, 1], invert and use as offset
float offsetMod = 1.0 - clamp(dot(N, L), 0, 1)
float offset = minOffset + maxSlopeOffset * offsetMod;
学术界解决方法:第二深度阴影图:不仅求最近深度的深度图,还要求第二近平面的深度图,二者求平均,得到中间深度的深度图。问题:模型必须有正面有反面,不能是绝对平面,否则深度出现问题.(因为过程更加麻烦,虽然复杂度没有增加)
走样、阴影锯齿问题
本质原因是阴影贴图分辨率不足,多个相邻着色点对应同一个阴影贴图像素。
工业界解决方法:级联阴影贴图(cascade shadow)。
PCSS(Percentage closer soft shadow,为面光源生成软阴影)
PCF
在介绍PCSS之前先介绍PCF(Percentage closer filter),PCF是常见的软化阴影的方式。基础的Shadow mapping中,我们把着色点直接分为了可见和不可见两类(非0即1),但PCF对一个着色点判断在不在阴影中,不是将其分为0或1,而是多重采样周围相邻的NxN大小的区域,求出在阴影中的百分比,来描述这个着色点的被遮蔽情况。
PCF有一些固有问题:
- 1.PCF是基于多重采样的,这毫无疑问会影响性能
- 2.PCF虽然能够实现模糊阴影边缘的效果,但效果并不符合物理,真正的物理阴影与遮挡物的距离相关,遮挡物越远,阴影越软。
PCSS
PCSS借鉴了PCF的思路,核心思想是:既然阴影到遮挡物的距离的远近,决定了阴影软的程度(越近越硬,越远越软),那么可以根据阴影到遮挡物的距离,定义不同的滤波核大小(越近越小,越远越大) 。本质是:根据遮挡物距离自适应的滤波核大小。
方法:很容易想出PCSS的流程:计算遮挡距离->根据遮挡距离计算滤波核大小->做PCF滤波
- 计算平均遮挡深度. 最理想的方法是将着色点与面光源的各处连线,连线能在阴影图中划出区域,计算区域内的平均遮挡深度.首先根据光源大小和阴影图的分辨率,计算光源面积在阴影图上的对应的像素范围,然后在这个范围内做平均滤波,计算平均遮蔽深度.(仅在发生遮挡时,才把此处的深度计算进平均遮挡距离) (这里需要注意一定要计算平均深度,不能图省事直接把着色点对应的阴影贴图中的深度就拿来当作平均深度,因为无法平均而且不能确保发生遮挡,导致阴影只是相当于硬阴影中采样了)
- 计算PCF滤波核大小。PCSS 通过一个和光源位置相关的相似三角形来控制软阴影的采样搜索范围。下面这个图展示了PCSS如何根据光源,遮挡物,和阴影投射目标三者确定搜索范围。
PCSS处理的光源是一个面光源。利用相似三角形,可以得知:
于是
- 做PCF。根据求得滤波半径,在着色点附近固定数量个采样点处采样。(也可以根据确定滤波核和采样点大小,这里比较自由)
插播数学知识
在RTR中,更关心不等式,很多时候不等式可以用来当作约等式来估算。
有以下不等式:此式成立条件 1.积分域足够小 2.足够光滑(变化不大)
这个公式的用处在于:
可以将渲染方程在符合条件的时候,适当做拆项来近似求解。渲染方程中的可视项部分表示了遮挡与阴影;其余为着色项,光线表示光照项,表示表面BRDF。
Variance Soft Shadow Mapping(VSSM)
PCSS 有两个重要的性能问题:1.计算平均遮挡深度 和 2.PCF 过程中会比较慢,于是提出了VSSM。下面VSSM逐个解决这两个问题。
PCF实际在做的事情:在这个被遮蔽的着色点附近,有多少个(百分之多少)的纹素的深度是在该着色点前面的(也就是深度比当前着色点浅, 也就是说遮蔽了这个着色点). 本质是在:求该着色点的深度在附近能排到前百分之多少。
VSSM的思想:不需要准确地得知附近具体每个点的深度,而是假设附近的深度是一个概率分布(这里假设正态分布),于是已知正态分布的均值和方差,以及该点的深度,就可以估算该点的深度排名百分比。
如何快速计算区域内深度均值和方差:
均值:两种方式 1.基于硬件的MipMap(将深度MipMap) 2.基于Summed Area Table(SAT)
SAT类似前缀和,是一种100%准确的方式,在一个SAT中,一个元素的值为原table这个位置的元素左侧和上侧的元素之和。
而对于任意矩形的值,可以通过简单的加减法计算得到。想求均值只需要将得到的结果除矩形大小即可。
方差:有公式,可以在阴影贴图计算过程中,顺便保存另一个阴影贴图(或SAT)为深度的平方(可以在同一纹理的不同通道储存),并在着色时采样期望(从MipMap中或SAT中),计算得到方差
有了正态分布的均值和标准差,可以估算出当前着色点的CDF(积分分布,即概率密度积分,可以衡量当前随机变量的位置)
我们现在假设阴影深度分布属于正态分布,但问题是正态分布的CDF没有解析解。所以VSSM应用了切比雪夫不等式:只要得知该分布的均值和方差,可以计算该随机变量位置到分布结束的面积,不大于一个确定的值(切比雪夫不等式不需要假定分布,所以只需要得到均值和方差,不关心具体分布。切比雪夫不等式成立条件:随机变量大于均值时成立)。
所以可得:
以上,基本解决了PCSS需要逐个采样点采样做PCF的性能问题。
对于PCSS第一步,遮挡查找并计算平均遮挡深度的过程,需要计算该块内遮挡物的平均深度,而不是全部的平均深度。也就是,或者可以表示为。我们已经可以通过MipMap等方法获取平均深度,那么遮挡深度与非遮挡深度的加权平均即为平均深度,公式为
为没有遮挡着色点的平均深度,我们可以通过切比雪夫不等式得知,那么。由于我们不得而知,那么大胆假设(即假设没有遮挡着色点的平均深度就是该着色点的深度,因为大多数情况下阴影投射的位置是平面,可以大概认为和当前着色点深度相似)。
以上,我们也能更简单地计算出PCSS中的遮挡平均深度。
但是目前大多数的方法是PCSS,因为降噪的方法更好,VSSM会出现其他问题(比如在曲面上投影,光源不平行)
Moment Shadow Mapping(MSM,矩阴影贴图)
VSSM是为了解决PCSS的问题而出现,但VSSM做了许多大胆假设,当假设不成立时,VSSM便出现了问题。
比如,1.遮挡关系足够复杂时,附近深度分布可以认为成一个分布,但当遮挡十分简单,比如单纯的一个平面盖住了另一个平面,这时候将其深度认为是一种分布并不成立,于是VSSM自然出现了错误;2.切比雪夫不等式不符合成立条件,即时失效。
VSSM失效的根本原因可以总结为:对深度分布估计不准确。
解决方案自然是:使用更高阶的矩来估计分布(在理论中,在一定情况下,矩可以完全决定分布,每一阶矩都透露了分布的一部分信息。矩可以简单理解为随机变量的指数,使用前m阶矩估计分布,阶数越高,分布估计越准确)。
MSM的思路:用前m阶矩拟合PDF或CDF,用一个纹理的不同通道来储存前4阶矩,但是具体怎么估计,太麻烦没讲。
MSM的优点:获得很好的结果。缺点:需要额外存储空间(可以接受),拟合过程耗费性能。
Signed Distance Field(SDF) soft shadows(有向距离场软阴影)
有向距离场SDF,是一种标量场,场中每一点都记录了该点到定义该场的物体之间的最小距离,并且用正负号表示该点在物体内还是在物体外,若距离为0,则认为该点在物体表面上。
SDF在几何方面的应用是,不同的物体有不同的距离场,在距离场与距离场间做插值运算,就能对几何形状进行混合过渡。
Ray Marching以及DFSS(距离场软阴影)
通过SDF可以实现一种与场景求交的方式,当场景中的任一点向任意方向投射光线,Ray Marching可以计算当前投射点的位置与最近的物体SDF的距离,将这个最短距离作为安全距离,从而实现在该方向上的步进。循环多次,直到步进次数达到一定值或者步长小到一定范围,即可认为光线与物体相交,退出循环。
由Ray Marching可以得到一个启发,由渲染点到各个方向发射光线,可以计算出该光线上距离最近物体的SDF的距离,也可以由这个距离得出“安全角度”,取得最小的安全角度作为最终的安全角度。我们可以发现,某点的安全角度越小,说明这角度对应的方向距离遮挡物更近,那么生成的阴影也就应该越硬,反之,某点的安全角度越大,则此处生成的阴影也就越软。当安全角度大于某个值时,可以认定没有阴影。
计算反三角函数得到安全夹角开销过大,可以通过近似的方法得到。公式为
其中为可调节参数,控制阴影的总体软硬。
SDF的优势:
速度快,在Ray marching体系下,计算SDF只是顺便产物,所以这种意义上速度快。距离场的生成时间比较长,但预计算生成后进行使用,就只是Ray marching的过程,相比于阴影图而言是快的,生成的软阴影质量高。
劣势:
需要预计算和存储SDF,对于动态物体需要逐帧更新,需要较大的存储空间,有其他的Artifact,表面参数化不好得到,不容易贴uv。