目录
四、VSSM-Variance Soft Shadow Mapping
六、Distance field(SDF) soft shadows
一、Shadow mapping的回顾
Shadow mapping原理
- 我们从Light处看向场景并输出一个从light处看向场景所生成的深度图得到一张texture,也就是所谓的shadow map.
- 我们从camera处看向场景渲染一遍将,并参考1-pass生成的shadow map去判断物体是否在阴影中.
左图中,点连向light,其在shadow map的最浅深度 等于 图中从点连到light处的深度,所以点不在阴影中。右图,点连向light,其在shadow map的最浅深度 小于 图中从点连到light处的深度,所以点在阴影中。
shadow mapping是一个完全在图像空间中的算法,其优点:一旦shadow Map已经生成,就可以利用shadow map来获取场景中的几何表示而不是场景中的几何.其缺点:会产生自遮挡和走样现象.
从Light处看向场景生成的是shadow map,并不是Shading的结果,shadowmap颜色深的表示值比较小,也就是离light近,颜色浅的就是离light远,值大。
Shadow Mapping问题
-
自遮挡
- 走样-锯齿
自遮挡的现象:主要是由于shadow map记录的深度的不连续造成的地板上的东西会感觉像摩尔纹,但并不是,它是由数值精度造成的一种现象。我们生成了一个Shadow map,那么这个Shadow map肯定有自己的分辨率,从Light处看向场景时,沿某一像素看过去我们看到的位置就认为是像素所代表的深度,记录的深度值是离散的,不连续的,如图中红色斜线所示。图中眼睛看到的蓝色点,实际上不会被遮挡的,但其实际深度大于shadow map上对应像素记录的深度,造成了被遮挡的假象,由此产生了自遮挡现象。在light与平面趋于平行时候最严重。
消除自遮挡现象:
- 使用更高分辨率的shadow map,可以从一定程度上缓解自遮挡现象,因为高分辨率会使记录的深度变化更加平滑。
- 在深度比较时添加bias,在深度比较时不计算图中黄色部分,就可以解决自遮挡问题。当Light处垂直于场景时,我们可以让这个遮挡区域尽可能小一点,当light趋近于平行场景时,我们让这个区域尽可能大一点。
- 在生成shadow map的时候,不仅记录最小深度,还需要记录第二小的深度。
如果bias太大,也会出现另一个非常严重的问题——物体与阴影的割裂。
Second-depth shadow mapping
在生成shadow map的时候,不仅记录最小深度,还需要记录第二小的深度,在深度比较时,使用前面二者的平均值,可以有效地消除自遮挡现象。此方法不使用bias。但是开销大
走样-锯齿:shadow map本身就存在分辨率,当分辨率不够大自然会看到锯齿。
Shadow mapping math
但是在实时渲染中,我们只关心近似约等,我们不考虑不等的情况,因此我们将这些不等式当约等式来使用.
满足下列两个条件之一时,实时渲染领域认为他们近似相等:
- 的积分域很小
- 足够smooth,变化不大
右边第一个函数的分母:归一化操作。
假设f(x)是一个常值函数,也就是f(x) = 2,我们的积分域恒为0-3.那么约等式左边,把f(x) = 2代入,则可以提出来变为2倍的g(x)积分,而等式右侧第一个函数代入f(x)的积分是2 * 3 =6,分母的积分是3,结果也正好是2.正好也是2倍的g(x)积分。
积分中多了一项visibility,把visibility看作是f(x),提取出来并作归一化处理。红色区域部分时visibility,那么剩下的g(x)部分,也就是shading的结果。意义就是,我们计算每个点的shading,然后去乘这个点的visibality得到的就是最后的渲染结果。也就是shadow mapping的基本思想。
约等式成立条件判断:
- 我们要控制积分域足够小,也就是说我们只有一个点光源或者方向光源。
- 我们要保证shading部分足够光滑,也就是说brdf的部分变化足够小,那么这个brdf部分是diffuse的。
- 我们还要保证光源各处的radience变化也不大,类似于一个面光源。
解决:反走样的解决方案就是滤波,简单理解就是求平均。不是对渲染结果滤波或者对shadow map滤波。首先,对渲染结果滤波只会得到模糊的锯齿,走样结果依然存在;对shadow map滤波,虽然平均了深度,但是得到的visibility仍然非0即1,仍然无法解决走样问题。我们真正的滤波对象是渲染方程中的visibility项。
二、PCF-百分比渐进滤波
PCF的初衷是为了抗锯齿,反走样,比如上面的忍者阴影出现的锯齿状,是为了解决这个现象.后来发现可以用在软阴影上,通过把shadow结果求一个加权平均(或者叫filtering).
PCF不是直接在最后生成的结果上模糊,而是在你做阴影判断时进行filtering。
如果直接在shadow map上filtering就会造成阴影和物体交界直接糊起来,而且在第二个pass上做深度测试还是非0即1的结果,最后得到的仍然是硬阴影。
PCF的思想很简单,在Shadow Mapping深度比较的阶段,不仅考虑当前着色点深度与对应shadow map上那一个像素深度值的比较,我还考虑其周围的像素值记录的深度,最终返回一个代表visibility的由0-1组成的矩阵,最后求平均值作为真正的visibility。
如果滤波核的尺寸较大(比如9×9的滤波核)可以在滤波核尺寸范围内进行随机采样,并控制采样点个数,同样可以得到抗锯齿效果。
PCF做了什么事?
对于实际渲染的点X,我们找到其投影到shadow map上对应的像素点—p,我们不只考虑点p,还要考虑其周围一圈范围内的像素,区域内各像素的根据到点p的距离进行加权平均,离得远贡献小,离得近贡献大。区域内除点p的其他像素
在shadow map上记录的最小深度,也与x的实际深度进行比较,从而判断区域内有多少fragments是遮挡物(Blocker),然后算出平均的visilibity,从而得到了一个在0到1之间的软阴影效果。
我们会发现如果filter size越大,阴影本身越软,所以这个方法也就可以去绘制软阴影,也就是pcss技术。软阴影比硬阴影看起来更自然。
三、软阴影-PCSS
光源照射下产生的阴影区域,由于光源存在一定的体积,所以主要分为本影区和半影区。在本影区中,无法接收到来自光源的任何光线,看不到光源的任何部分;而在半影区中,光源仅部分被遮挡,因此与本影相比,半影的阴影要浅得多。
距遮挡物越远的阴影区域,阴影越软。 阴影接受物与阴影投射物的距离越小,阴影越锐利
左下和右上两个黄色虚线形成的三角是两个相似三角形。
PCSS(Percentage Closer Soft Shadows)的核心思想就是控制PCF算法中滤波核的大小来生成软阴影。一个直观的观察结果是距离遮挡物越远的区域,阴影越软,需要的滤波核尺寸越大。
如何确定一个blocker距离光源的位置:不能直接使用shadow map中对应单个点的深度来代表blcoker距离,因为如果该点的深度与周围点的深度差距较大(遮挡物的表面陡峭或者对应点正好有一个孔洞),将会产生一个错误的效果,选择使用平均遮挡距离来代替,所以平常我们指的blocker depth其实是Average blocker depth。
- 滤波核越小—–模糊程度越低—–阴影越硬(尖锐)
- 滤波核越大—–模糊程度越高—–阴影越软
那么在PCSS做了什么?
首先将shading point点x投应到shadow map上,找到其对应的像素点p。PCSS算法的实现流程如下:
第一步:Blocker search,即获取某个区域的平均遮挡物深度(在点p附近取一个范围(这个范围是自己定义或动态计算的),将范围内各像素的最小深度与x的实际深度比较,从而判断哪些像素是遮挡物,把所有遮挡物的深度记下来取个平均值作为blocker distance。)
第二步:Penumbra estimation,使用平均遮挡物深度计算滤波核尺寸(用取得的遮挡物深度距离来算在PCF中filtering的范围。)
第三步:Percentage Closer Filtering,对应该滤波核尺寸应用PCF算法。
那么PCSS中那些步骤会导致速度变慢?
- 第一步:多次采样查询深度信息—–速度变慢
- 第三步:阴影越软—–滤波核尺寸越大—–采样查询次数变多—–速度变慢
如果觉得区域过大不想对每一个texels都进行比较,就可以通过随机采样其中的texels,而不是全部采样,会得到一个近似的结果,近似的结果就可能会导致出现噪声。工业的处理的方式就是先稀疏采样得到一个有噪声的visibility的图,接着再在图像空间进行降噪。
四、VSSM-Variance Soft Shadow Mapping
Variance soft shadow mapping主要解决了PCSS中第一步和第三步慢。第三步其实就是应用PCF算法,而PCF算法的核心就是对渲染方程中的visibility项进行滤波,也就是求平均。从另一个角度考虑,我们想要得到的是某个区域中,深度大于当前着色点深度的范围占整个区域的比例。
从统计角度来看,假设查询区域的深度满足某个概率密度函数,对于当前着色点的深度d,我们希望得到的是概率P(x>t),而根据单边切比雪夫不等式可得:
VSSM的key idea是快速计算出某一区域内的均值和方差。在VSSM中,我们认为上述不等式两边相等。所以不论查询范围内深度符合哪种概率分布,只要知道该范围深度的均值和方差,就可以直接得到visibility项的平均。
用这个公式的原因是:
在shadow map中我们存储的是depth,因此depth也就是公式中的x,在指定区域范围后,可以快速的求出区域范围的平均值(期望)。
还需要额外生成一张shadow map,但是这张图上存depth^2,即square-depth map。
加速第三步总结:
- 我们通过生成shadow map和square-depth map得到期望值的平方和平方值的期望再根据公式 得到方差
- 通过mipmap或者SAT得到期望
- 得到期望和方差之后,根据切比雪夫不等式近似得到一个depth大于shading point点深度的面积.,也就是求出了未遮挡Shading point的概率,从而可以求出一个在1-0之间的visilibity.也就是省去了在这个范围内进行采样或者循环的操作,大大加速了第三步。
第一步的加速方法:
第一步中,我们需要获得的仍然是深度平均值,但注意此处的平均值是shadow map上某个区域中特定值的平均值,即区域中遮挡物的深度平均值,并不是整个区域的深度平均值。
假设着色点距光源的深度z = 7,shadow map上需要查询的区域如下图所示,那么我们需要的是图中蓝色区域(遮挡物)的深度平均值。
- 非遮挡物的深度平均值— 采样点中非遮挡物数量—
- 遮挡物的深度平均值— 采样点中遮挡物数量—
- 整个区域的深度平均值— 采样点数量—N
上述参数存在以下关系:
根据第三步的加速方案中的单边切比雪夫不等式,我们可以得到下式,且数据都可知。
假设非遮挡物深度全部等于当前着色点深度t,即,可以求出。但是接受平面是曲面或者与光源不平行的时候就会出问题。
VSSM的做法采用了非常多的大胆假设,同时非常的快,没有任何噪声,本质上其实也没有用正态分布,是直接用切比雪夫不等式来进行近似。但是现在最主流的方法仍然是PCSS。
如何在区域范围内快速的求出均值?
- MIPMAP
- Summed-Area Variance Shadow Maps-SAT
他是一个快速的,近似的,正方形的范围查询,由于他要做插值,因此即便是方形有时也会不准确。
SAT是百分百准确的一个数据结构,在1维情况下其实就是一维数组,SAT这种数据结构就是做了预处理,类似前缀和。
对于二维数组的情况,我们需要计算区域蓝的和,只需要这样计算:蓝=大绿-橘1-橘2+小绿,也就是说,如果我们生成了二维数组对应的SAT,只需要查询四次,每次查询图中白色点所在位置对应的值,然后求解。
生成二维数组SAT的方法:对于一个NxN的二维数组,首先,按行生成每行对应的一维SAT,N行计算完毕之后,形成一个新的NxN的二维数组;在新数组的基础上,按列生成每列对应的一维SAT,最终可以得到初始二维数组对应的二维SAT。
五、MSM-Moment shadow mapping
VSSM是为了解决PCSS的问题,但vssm由于做了很多假设,当假设不对的时候会有问题。当范围深度的分布并不是单峰的分布(如下图左边所示),而是多峰分布时,甚至是一些比较简单的分布时(如下图右边所示),使用单边切比雪夫不等式同样有可能是不准的。
为了避免VSSM中不是正态分布情况下的问题,就引入了更高阶的moments来得到更加准确的深度分布情况.想要描述的更准确,就要使用更高阶的moment(矩),矩的定义有很多,最简单的矩就是记录一个数的次方,VSSM 就等于用了前两阶的矩。
越多的阶数就和原本的分布越拟合。一般来说4阶就够用。
六、Distance field(SDF) soft shadows
Distance Function就是空间中任何一点,到某个物体的最小距离。
Signed的意思就是加入规定负数代表在物体内部,正数则表示在物体外部.这样不止定义了距离,还定义了方向。
SDF的应用:
- Ray Marching
- 生成软阴影
Ray Marching:假设我们已经知道场景的SDF,现在有一根光线,我们试图让光线和SDF所表示的隐含表面进行求交,也就是我们要用sphere tarcing(ray marching)进行求交.
任意一点的SDF我们是已知的,因此在点1时,我们以它的SDF(1)为半径做一个圆(此处假设在2D内,如果在3D内则是一个球),在这个圆内无论是哪个方向前进,只要不超过半径距离,都不会碰见物体,是安全的。因此我们可以利用这个特性不断的朝一个方向前进,直到SDF足够小,也就是代表离物体表面足够接近了,则进行求交操作。如果在超一方向trace非常远的距离但仍然什么都没trace到,此时就可以舍弃这条光线,也就是停止了。
软阴影:
将安全距离的概念进行延伸,在任意一点通过sdf可以获得一个safe angle。我们取点P为shading point往一方向打出一根光线,光线上的一点a,有一个SDF值SDF(a),也就是在a点以SDF(a)为半径所做的球或圆内是安全的,不会碰到物体.
把shading point和面光源相连,所得到的安全角度越小,被遮蔽的可能越高,就可以认为
safe angle越小,阴影越黑,越趋近于硬阴影;
safe angle够大就视为不被遮挡没有阴影,也就越趋近于软阴影。
如何求出safe angle:
我们以o为起点,沿一个方向推进,仍然是ray marhcing的步骤,在p1点以SDF(p1)进行推进,其余点也是一样,最后再取其中最小的角度作为总的safe angle。
计算:
从图我们可以知道,以p1点为例,从o点到p1的距离为斜边,sdf(p1)是直角边,因此我们用arcsin就可以求出safe angle了。
arcsin的计算量其实是十分大的,因此在shader中我们不用反三角函数,只要sdf长度除以光线走过的距离乘一个k值,再限定到1以内,就能得到遮挡值或者说是visibility,而k的大小是控制阴影的软硬程度。
SDF是一个快速的高质量的软阴影生成方法(比shadow map快是忽略了SDF生成的时间),但是在存储上的消耗非常大,而且生成SDF的花的时间也要很久,SDF是预计算,在有动态的物体的情况就得重新计算SDF。
暂无评论内容