Real-Time Shadows 1
Shadow Mapping:从光源处记录一张场景的最浅深度表,然后再从摄像机处看向场景的某个物体向光源连线,比较此时物体到光源距离和深度表记录的距离,如果大于深度表记录的距离,则说明被遮挡在阴影内,如果小于,则不在阴影内。
Shadow Mapping:有两个问题,自遮挡和阴影锯齿的问题。
自遮挡是因为Shadow Map上的像素记录的深度值是常数,它所记录的场景是离散的,此时所看到的场景是一片片的,当摄像机看向某个点的时候,该点的位置可能会被前面的场景遮挡。它的实际距离会大于前面场景的距离。
以平面为例,当光线垂直平面时,就不会有这个问题,当光线与平面几乎平行时,问题最大。
为了解决自遮挡的问题,可以忽略那段很小的距离,就把它当作光源能够照射到上面,这段很小的距离(bias)受光源与平面的夹角影响,夹角越大,距离会越短,相当于光线与平面垂直。夹角越小,相当于光线与平面平行,距离会越大。虽然解决了自遮挡的问题,但会在人物的脚部出现阴影断连的情detached shadow issue,这是因为在脚部的阴影也被当作光源能够照射到了。
有一种解决方法,就是在记录深度时,不仅记录最小深度值,还会记录次小深度值,然后比较的时候使用最小和次小的中位数来比较,但会有一系列问题,并没有实际使用。实际情况是一般使用一个不会产生自遮挡和阴影断连的bias来模糊处理。
另外一个问题是阴影的走样问题,因为shadow map是用像素来存储的,如果分辨率不够大,就会产生走样的问题。
补充一些微积分公式:
下图中的约等式,在积分限很小或g(x)变化也很小的时候会很近似相等。
带有visibility的渲染方程可用这个约等式把visibility拿到前面去
此时,积分限足够小代表了是点光源,如果Li变化很小,是说明面光源各处发出的Radiance相同,BRDF变化很小是代表了漫反射,在各个方向Radiance的变化都会相同。如果此约当式不符合,说明不适合使用Shadow Mapping。
PCF是阴影的抗锯齿方法,一般对阴影的判断是从着色点往光源方向连线,查询shadow map,PCF是和shadow map上对应像素周围的(比如9*9,此处的9*9代表了filter的大小)像素比较,因为是得到最后的visible值,所以需要获得能够不被遮挡的部分,即像素上的深度值比着色点的深度值要大。如果像素的的深度值大于着色点的深度值就记为1,小于就记为0,因为大于就会被遮挡,小于才能够被看见。然后取平均值。这种方法不但解决了走样问题,同时还能用于做软阴影。
如果不filter的话,就是硬阴影,如果filter比较大,阴影就会比较软。
可根据遮挡物和阴影接收面的距离来动态调整filter,来模仿出更好的实际效果。图中钢笔的笔尖部分,阴影比较锐利,是因为笔尖离纸面(阴影接收物)很近,钢笔的笔杠部分阴影比较模糊,也是因为笔杆离纸面比较远。
从图中可知, Filter size与blocker distance和面光源大小有关。
假设面光源大小不变且已知,filter就只和blocker depth有关,PCSS算法的第一步就是确定average blocker depth,也是从着色点连向光源(此处的光源是点光源,因为面光源没有shadow map,是在模拟面光源的软阴影)。记录周围能够遮挡着色点的像素深度,然后取平均值。在获得average blocker depth后,就可以根据average blocker depth来计算filter大小,然后就是PCF的步骤,从着色点连向光源,记录filter范围内的是否遮挡(即0,1值)取平均值。
在PCSS的第一步,需要查询某个范围来确定average blocker depth,从着色点连向面光源,也就是下图中的四棱锥,然后在shadow map上的范围就是查询范围。