Shadow(PCF算法思路)------计算机图形学高质量实时渲染(二)

Shadow (PCF算法思路)

在这里插入图片描述
课程目录
本节课的内容主要分为三部分:
1.shadow mapping的回顾
2.shadow mapping的数学基础
3.PCSS和PCF

Shadow Mapping

在这里插入图片描述
要渲染一个点光源在场景中投射出的阴影,我们要用shadow mapping技术,在101中我们介绍过shadow mapping。

算法思想:

它是一个2-pass的算法,也就是我们会对场景渲染两次。
1-pass我们从light处看向场景并输出一个从light处看向场景所生成的深度图,也就是所谓的shadow map
2-pass我们从camera处看向场景渲染一遍,并参考1-pass生成的shadow map 去判断物体是否在阴影中

优缺点:

shadow mapping 是一个完全在图像空间中的算法。
优点:
一旦shadow map已经生成,就可以利用shadow map来获取场景中的几何表示而不是场景中的几何。
缺点:
会产生自遮挡和走样现象

实现步骤:

  • 1.从光源(light)处出发看向场景生成一张记录每个像素中最近物体深度的一张图,如图:
    在这里插入图片描述
    图中有很多像素,我们记录下每个像素中他们各自看到的最浅深度或是最近的物体他们的位置在哪,存下来从而得到一张texture。

  • 2.我们从相机(camera)出发,再渲染一遍场景
    在这里插入图片描述
    对于这次我们来看渲染中的每个像素中都要来判断是否能被light照到,如果被照到则不在阴影中,反之,则存在于阴影中。
    在这里插入图片描述
    对于这个点来说,如果将这点连向light处会发现在1-pass得到的shadow map中,这一点的最浅深度 = 2 - pass中从点连到light处的深度(2-pass中得到的点是可以投影回light处求出距离的),因此这一点是可见的,不在阴影中。
    在这里插入图片描述
    而对于这个点来说,仍然连向light处后会发现:
    1-pass的shadow map中这一点的最浅深度<2-pass中从点连到light处的深度,因此这一点时被遮挡的,在阴影中。
    在这里插入图片描述
    从这张图可以看到,light在左上方,场景中的遮挡关系和阴影表示出的效果是很不错的。
    在这里插入图片描述
    从light处看向场景生成的是shadow map,并不是shading的结果,而是在light的pass中生成一张深度的buffer,如图中,颜色深的表示值比较小,也就是离light近,颜色浅的就是离light远,值大。

下图为1-pass生成的深度buffer

下图为2-pass生成的深度buffer:
在这里插入图片描述
在这里插入图片描述
在OpenGL中,1-pass真就是在light处放置一个相机,然后往某一方向看去,定义fragmentbuffer写到某个texture上,然后fragment shader中定义写的是一个深度而非shading的结果,在2-pass中则只需要用1-pass得到的texture即可。

小插曲(Games101中的一个问题)

关于深度,我们在101中讨论过的一个问题。
在做透视投影时,我们是将透视投影挤压成正交投影,然后再拍平,在这个过程中,中间的点是会向近平面还是远平面移动?
答案是中间的所有点都会被推向远平面。
在这里插入图片描述
我们想说的是,在经过投影之后,得到的Z其实不是实际上几何上的点到light的距离,因此在真正生成阴影时比较两个pass中的depth时需要保持一致,也就是要么都用投影后的Z值比较,要么通过两点位置得一向量算实际距离。
也就是说在经过透视投影变换后,Z分量失去了线性关系,所以说需要一个透视插值矫正。

Shadow Mapping存在的问题

A:自遮挡

在这里插入图片描述

原因:

学过101的看见地板上的东西会感觉像摩尔纹,但并不是,它是由数值精度造成的一种现象,而摩尔纹是因为采样率导致的。

我们生成了一个shadow map,那么这个shadow map肯定有自己的分辨率,其每个像素要记录它所看见的最浅深度,可以理解为,每个像素内部它的深度是一个常数。

那么从light处看向场景时,沿某一像素看过去我们常看到的位置就认为是像素所代表的深度,也就是认为这个场景在像素覆盖区域内都是一个常数的深度。

在shadow map看来,场景被离散化为一系列红色小片形成的场景而不是直接的平面。

因为每个红色小片所代表的深度不一样,因此在2-pass,也就是从camera处看向场景时,后面的红色小片在连接light时,会被误认为被前面的红色小片遮挡住,从而产生了错误的阴影,这个现象在与平面区域平行时候最严重。

有了问题自然要去解决:
在这里插入图片描述

解决方案:

我们只需要把在黄色区域内的遮挡关系给舍弃掉就可以了,而且是有技巧的,当light处垂直于场景时,我们可以让这个遮挡区域尽可能小一点,当light趋近于平行场景时,我们让这个区域尽可能大一点。我们可以引入一个bias的概念(黄色区域),来降低自遮挡情况,bias是根据角度调整的,并非常数。
具体方式就是当一个点深度大于记录深度的值超过一个阈值时,我们才认为这个点在阴影内,这也是工业界使用较多的一个办法。
但是这也引出了另一个问题:
在这里插入图片描述
可以看出来劳拉的腿断了,这个工业界叫做Peter Panning。

从图中我们可以看到,有一段的阴影被舍弃了,因为当Bias调的过大时,我们会丢失原本应该存在的阴影。

解决方案的解决方案

Second-depth shadow mapping
此时我们就舍弃biasd的概念,而是在渲染时不仅存最小的深度,我们还要存第二小的深度,然后我们用最小深度和第二小蛇毒中间的深度来作比较。
(个人理解,我们在渲染时生成两张深度图,一张存储的是最浅的,一张存储的是第二浅的,之后将两张图平均从而得到最后的深度图,然后用这张平均得来的深度图和物体算遮挡关系)。
在这里插入图片描述
以这个为例:
在这里插入图片描述
假设一根光线照过来,我们不用最小深度来比较,而是用由最小和第二小深度所得到的红色线来做后续的阴影,此处就没有Bias的事情了。
在这里插入图片描述
假设图中是人物的鞋,鞋底是接着一个平面的,我们以右边最极端的部分为例,当一个光线从右边鞋头部分打向鞋底,就算它紧贴着平面,我们能得到一个明显的遮挡关系。
然后实际中并没有人去使用这个奇数,因为场景内的物体必须都是watertight(非面片),还有就是算的太复杂,开销太大,实时渲染并不相信复杂度。
在这里插入图片描述
只相信绝对的速度!

B:走样

shadow mapping的第二个问题就是走样。

原因:

shadow map本身就存在分辨率,当分辨率不够大自然会看到锯齿,因为shadow map上每一个像素都可以看为小片,那么投出来的阴影自然会存在锯齿。
在这里插入图片描述

阴影贴图背后数学原理(大名鼎鼎Split-Sum):

简而言之,目前看来在图形学中数一数二重要的数学技巧,split sum。

在微积分中有很多有用的不等式,如图中的两个不等式为例:
在这里插入图片描述
但是在实时渲染中,我们只关心近似越等,我们不考虑不等的情况,因此我们将这些不等式当约等式来使用。
在这里插入图片描述
如果你有两个函数的乘积,你又想把他们的乘积积分起来,你可以将其拆出来,也就是:

大名鼎鼎(split sum)
两个函数乘积的积分≈两个函数积分的乘积

首先我们来解释一下为什么右边第一个函数多了个分母。

分母这一项的作用是为了保证左右能量相同而做的归一化操作。

我们来用一个例子来解释这个归一化的操作,我们假设f(x)是一个常值函数,也就是f(x) = 2,我们的积分域恒为0-3.
那么约等式左边,把f(x) = 2代入,则可以提出来变为2倍的g(x)积分。
而等式右侧第一个函数代入f(x)的积分是2*3=6.分母的积分是3,结果也正好是2,正好也是2倍的g(x)的积分。

接下来我们来讨论在什么情况下约等式结果更加准确。

一般需要以下两个条件:
1.g(x)积分的support较小,这里的support我们可以暂时理解为积分域。
2.g(x)在积分域上足够光滑(变化不大)。

此时我们把rendering equation带入这个约等式中:
在这里插入图片描述
我们把visibility看作是f(x),提取出来并作归一化处理:
在这里插入图片描述
红色区域部分时visibility,那么剩下的g(x)部分,也就是shading的结果。
因此其表示的意义就是,我们计算每个点的shading,然后去乘这个点的visibility得到的就是最后的渲染结果。
这也就是shadow mapping的基本思想。

那么什么时候这个约等式比较正确呢?
a)我们要控制积分域足够小,也就是说我们只有一个点光源或者是方向光源。
b)我们要保证shading部分足够光滑,也就是说brdf的部分变化足够小,那么brdf部分是diffuse的。
c)我们还要保证光源各处的radiance变化也不大,类似于一个面光源。

PCF(Percentage Closer Filtering)与PCSS(Percentage Closer Soft Shadows)

首先我们来说一下软阴影和硬阴影:
在这里插入图片描述
在这里插入图片描述
我们可以看出,软阴影的效果要更加真实也更自然,强于硬阴影的效果。
为了实现软阴影的效果,我们首先会用一个工具PCF—percentage closer filtering:
PCF的初衷是为了抗锯齿,反走样,比如上面的忍者阴影出现的锯齿状,是为了解决这个现象。
后来发现可以用在软阴影上,通过把shadow结果求一个加权平均(或者叫做filtering)
首先我们要强调一点:

1.PCF不是直接在最后生成的结果上模糊,而是在你做阴影判断时进行filtering。
如忍者那张图,并不是在他的基础上去进行模糊处理。
在这里插入图片描述
就跟我们在反走样时一样,我们不能先得到一个走样的结果再去做在这个走样的结果上进行模糊。
2.也不是Filter the shadow map
如果直接在shadow map上filtering就会造成阴影和五一交界直接糊起来,而且在第二个pass上做深度测试还是非0即1的结果,最后得到的仍然是硬阴影。

简言之就是,是在做阴影判断的时候进行滤波,不是图像后处理

PCF算法思想

我们之前在做点是否在阴影中时,把shading point连向light,然后与shadow map对应的这一点深度比较判断是否在阴影内,之前我们是做一次比较,这里的区别是,对于这个shading point我们仍要判断是否在阴影内,但是我们把其投影到light之后不再只找其对应的单个像素,而是找其周围一圈的像素,把周围的像素深度比较的结果加起来平均一下,就得到一个0-1之间的数,就得到了一个模糊的结果
在这里插入图片描述
如图,蓝点是本来应该找的单个像素,现在我们对其周围3*3个像素的范围进行比较,由于是在shadow map上,因此每个像素都代表一个深度,我们让在shadow map上范围内的每个像素都与shading point的实际深度进行一下比较,如果shadow map上范围内的像素深度小于shading point的实际深度,则输出1,否则输出0.

从而得到9个非0即1的值:
在这里插入图片描述
最终我们用得到的加权平均值0.667作为shading point的可见性。在计算阴影的时候我们就拿着个作系数来绘制阴影。
可以看到抗锯齿效果很好。
在这里插入图片描述

PCSS算法思想

我们会发现如果filter size越大,阴影本身很软,所以这个方法也就可以去绘制软阴影,也就是pcss技术。
在这里插入图片描述
在这幅图中,我们可以看见,笔尖的阴影十分锐利(硬),因此我们可以认为

阴影接受物与阴影投射物的距离越小,阴影越锋利

因此,我们要解决的一个问题是我们如何决定一个软阴影的半影区,换句话说,就是filter size 有多大的问题:

在这里插入图片描述
我们可以看到左下和右上两个黄色虚线形成的三角形是两个相似的三角形。
如果我们将blocker的位置移动一下,比如越靠近receiver,我们会发现在这里插入图片描述也就会越小,用数学来表示半影区在这里插入图片描述
在这里插入图片描述
这里的dReceiver和Wlight的大小我们是知道的,所以我们只需要拿到blocker的深度即可。

所以,在ppt中写道,filter size <——> blocker distance

但是这里又有一个问题了,如何确定一个blocker距离光源的位置?

不能直接使用shadow map中对应单个点的深度来代表blocker 距离,因为如果该点的深度与周围点的深度差距较大(遮挡物的表面陡峭或者对应点刚好有一个孔洞,将会产生一个错误的效果),我们选择使用平均遮挡距离来代替,所以平常我们指的blocker depth其实是average blocker depth。

blocker上的每个点距离光源的距离是不同的,深度也是不一样的。
这里我们采用取平均深度的方式来表示blocker的深度。

求blocker距离的方法如下:

首先,我们把目标shading point转换到light space找到shading point在shadow map上对应的像素。
如果shading point的深度大于这个shadow map上点对应的深度,则说明shadow map上的点就是一个blocker,然后我们取shadow map上这个点(像素)周围的一些像素,找出能够挡住shading point的点的像素,并求出他们的深度平均值作为blocker的深度。

这个方法还是有一个问题,这个问题就是我们虽然找出了filter size的大小。但是我们需要知道寻找blocker之一步骤中,我们需要找到周围的一些像素,那这个范围又是多少呢?

一般我们有两种方法可以解决这个问题。
第一种,就是自己规定一个,比如4x4,16x16,比较简单但并不实用。
第二种,是通过计算得到一个范围大小:
在这里插入图片描述
在这里插入图片描述
我们计算shadow map的时候在光源处设置过相机,如图所示,我们把shadow map放在由相机看向场景形成的视锥中的近截面上,然后将光源shading point相连,在shadow map上截出来的面就是要查询计算平均遮挡距离的部分,这部分的深度求一个均值,就是blocker到光源的平均遮挡距离。
离光源越远,遮挡物也会更多,所以需要在一个shadow map上的一个小区域内查找blocker
离光源越近,遮挡物会少,所以需要在shadow map上的一个大区域内查找blocker。

PCSS总结

这样我们就得到了PCSS的三个步骤:

  • 1.寻找blocker,并计算平均深度
  • 2.通过blocker深度计算fileter size
  • 3.按照PCF方式绘制软阴影

PCSS本质上就是求出了阴影中需要做PCF的半影部分后再进行PCF的计算,这样动态调节了半影范围,也就是动态设置了PCF的搜索范围,这样我们的硬阴影部分清晰,软阴影部分模糊,动态的实现了不错的软阴影效果。

简言之就是自适应滤波核的PCF。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值