|
本文主要阐述理论部分,Unity源码剖析部分请参考 Unity Shadow Map(二 实践) - 知乎 (zhihu.com)
关于shadow map 的参考资料:图形学中级学习推荐 - 知乎 (zhihu.com)
关于“卷积”的参考资料学习资料:图形学好书——虎书 - 知乎 (zhihu.com)
1.ShadowMap
1.1 ShadowMap原理
a. 光源深度图
光源被遮挡,照不到的地方,就形成阴影. 也可理解为光源“看”不到的地方. 从光源角度“看”, 记录那些距离光源最近的位置,其他地方都将被遮挡或者干脆在光源外. 技术角度说,这个“看”就是将摄像机放在光源处,通过摄像机的正交(平行光源)或透视矩阵将场景投影,将那些离光源最近(最小,如果是reverse-z,就是最大)的z值记录到 z-buffer中, 就得到光源深度图,也就是shadow map.
b. 比较与光源的距离
成像时,将观察点,和光源方向的,同一位置的(光源角度看),光源深度图中记录的,离光源最近的值比较,如果更远,说明在阴影中,如果更近(或同样近),说明被光源照亮. 技术角度,就是将渲染目标的坐标,从观察或世界坐标系,转换到光源坐标系,然后将z值与ShadowMap中的值比较,大于等于(如果是reverse-z,就是小于等于)时,离光源更远,有遮挡,在阴影中.
图中左边示意图表示 shadow map 记录了光源角度“观察”,距离光源最近的场景物体点. 右图中,a点在光源空间深度和shadow map记录一致,或者更小,不在阴影中,b点显然大于shadow map记录的深度,被遮挡,在阴影中.
1.2 ShadowMap 的问题
1.2.1 问题1:自阴影 Shadow acne
Shadow map 因为2个原因会产生自阴影
a. 有限精度和分辨率
深度数值的存储精度是有限的,并且这个有限精度还得分配到整个光源空间.
另外shadow map 的分辨率也是有限的,只能用一个深度值代表一个区域(shadow map的一个纹素)的深度值.
图1-2中,绿色部分代表渲染表面,红色射线表示shadow map有限分辨率时的采样中心点,采样中心两侧的黄色区域表示采样粒度,采样粒度内的深度值由采样中心的深度值代表, 蓝色阶梯表示因有限精度而不连的采样深度值,其中δ代表最小间隔. 由于采样间粒度不能无限小,深度值精度有限的原因,虽然渲染表面都暴露在光照中,但是采样区间内部,在采样中心的两侧,会造成一半区域的深度值小于采样中心深度值,被照亮,另一半区域的深度值,小于采样中心深度值,在阴影中, 形成自阴影显现.
b. Shadow map和Camera采样点不一致
光源深度的采样点和摄像机空间的采样点很难一样,加上精度有限的问题,导致摄像机的采样点和将该点转换到光源空间后,得到的深度仅仅是其所在shadow map采样区域的,中心采样点的深度.
如图1-3所示.黑色实线代表渲染表面,垂直的黄色区域光源空间采样区域, 红色线条表示采样中心, 实心圆点代表摄像机空间的采样点,同色的x表示与摄像机采样点所属于的,shadow map采样区域的中心采样点. 可以看到只有绿色点和shadow map 采样点(绿色X)比较后,判断为不在阴影中. 蓝色和橙色点虽然没有被遮挡,但和对应的shadow map 采样区域的中心点的深度相比偏大,所以判定为在阴影中. 自阴影由此产生.
自阴影消除方法
a. Constant bias
对物体表面进行施加一个向光源方向的常量偏移,如图1-4所示.
图中所物体表面,都想光源方向施加一个相同的偏移量,使得新的相机采样点比对应shadow map采样区域的中心采样点深度更小,消除了自阴影.
b. The Slope scale bias + Constant bias
超光源方向的常量偏移虽然简单,但是当物体表面完全垂直于光源方向时,其实并不需要这种偏移. 这就造成了,偏移量太小,有些采样点仍然“在阴影中”,偏移量太大,会造成其他问题(漏光和漂浮). 所以提出了根据表面与光照方向的倾斜程度,即光线和表面法线的tanθ来偏移,如图1-5所示,越是倾斜,偏移量越大,充分保证自阴影的消除. 但是这个偏移也不能太大,因为当倾斜程度和光线接近时, tanθ会非常大,所以要对最大值加以限制. 另外,当平面垂直光线时,偏移不能为0,还是要加一个常量偏移,避免因为精度问题而产生自阴影.
c. Normal offset bias
该方法是根据表面法线和光线的sinθ值的大小,将采样点沿法线法相偏移,同样可以达到表面相对光线越倾斜,偏移越多.
但是,因为偏移方向是沿着物体表面法线,而不是沿着光线,所以会造成对shadow map采样坐标(x,y)也发生变化. 可以将该方法理解为,将采样点移动到了一个虚拟的表面.
d. 偏移方式的副作用
偏移方式基本可以解决shadow map 自阴影的问题,但是当偏移量很大时,会有两个副作用: 漏光和漂浮.
漏光(Light leaks)
模型本来是封闭的,但是当偏移了迎光面的位置时,这种偏移是模型顶点沿着法线收缩(shadow cast pass),如果是突出多面体没有太大问题,但当模型是凹多面体时,模型可能出现“缝隙”,会漏光. 此时可以修改模型,也可以打开双面投影(Mesh Render->Lighting->Cast shadows->Two sided,会增加性能损耗).
漂浮(Peter Panning)
投影物和下面的阴影接受面本来是紧密接触的,比如人物站在地面上,但是由于偏移过大,地面和脚分开,产生了漂浮的问题.
e. 其他消除方式
背光面投影
还可以将物体背光面作为投影面,可消除自阴影.但是这容易造成漏光,且模型必须是密封且有一定厚度的,很薄的模型,比如植物叶子,纸片等没有效果.
中间面投影
单纯的背光面投影容易造成漏光,又提出了中见面投影,就是追踪离光源最近的两个面,取中间深度值作为投影“面”.缺点是需要额外损耗性能. 对很薄的模型仍然是无效的. 另外也有方式是,这个中间面不用计算,而是美术人员来确定.
f. Unity Shadow Map的偏移方式
Unity 使用normal offset bias 的方式来防止自阴影,但Unity 是在生成shadow map的时候,将投影面轻微远离光源,这样shadow map中深度值会比实际偏大,实际投影时,投影面的深度就会偏小,从而消除自阴影.
此外,在光源剪裁空间,Unity还会在法线偏移的基础上,直接施加一个随着与光源距离增加而减小的偏移量.
1.2.2 问题2:分辨率问题(Perspective aliasing)
与texture类似,当shadow map 纹素和相机像素能一一匹配的时候,不存在任何问题, 但这太理想了. 当相机和光源位置重合,不存在任何阴影,当两者位置不同,就会出现多个像素匹配一个shadow map纹素, 阴影就会出现锯齿或马赛克,如图1-7. 这主要是因为shadow map 的像素匹配被透视相机不同区域的不同缩放比例造成的, 如图1-8所示. 光源垂直照射投影,方格表示shadow map的纹素. 摄像机从左向右拍摄,近截面附近和远截面附近在屏幕上呈现相同的大小,但是,近界面附近对应4个shadow map纹素,远截面附近对应20个shadow map 纹素. 近截面附近的shadow map 分辨率显然是不够的,就会造成马赛克.
a. Cascaded Shadow Map(CSM)
马赛克现象可以通过提高shadow map的分辨率来解决,但这会带来更多的性能消耗. 另外,当增加Soft shadow时也会缓解失真,但也不能和那后的消除. 所以提出了更加高效的解决方法:级联阴影 即CSM, 其示意图如图1-10. 这种方式就是将camera视锥体,平行的分成几个平顶锥体(frustum),沿观察方向,下一个frustum的深度是前一个的2-3倍(Unity中默认是2倍),每个部分由光源的frustum包裹(平行光时就是长方体),并生成单独的shadow map. 如果某个渲染点被同时属于2个frustum,就选择更加靠近Camera的, 以提高分辨率.
Unity的Cascaded Shadow Map 设置如图1-12
b. 其他解决方案
除了使用级联阴影,还有一种解决方案是perspective shadow maps(PSM).这种方式使用与摄像机类似的成像原理来生成shadow map, 目的就是在靠近摄像机时提高分辨率,远离时降低分辨率. 摄像机的成像也是近大远小,越远的地方缩小越多. 具体来说,就是改变了场景投影到光源的方式,不再是根据光源观察方向对称,这个frustum可能被扭曲,缩放,平移,旋转, 如图 1-12所示. 与此类似的方式还有 TSM, LiSPSM.
1.3 Soft Shadow
软阴影就是介于完全阴影和完全照亮之间的,位于阴影边缘的半影. 软阴影可以改善shadow map 一个纹素匹配多个像素造成的马赛克问题,同时让阴影更真实,更符合实际情况.
1.3.1 Percentage Closer Filter(PCF)
造成软阴影的原因之一是光源是有大小体积的,而不是理想化的一个点. 如果被整个光源照到,就是完全的无阴影,如果完全照不到,就是全阴影,如果被部分光源照到,就是半影,如图1-3-1. 左图中的P点就是个处于部分光源照射的半影之中,且这个半影的大小和光源与遮挡物的距离有直接关系,距离越远,半影越小. 右图中是理想的点光源,PCF的思路就是以渲染点P为中心的一个区域(在shadow map中)采样,接着将所有采样点都与P点(在光源空间的)的实际深度比较,最后在所有比较结果中计算阴影或者照亮的百分比,这个百分比就决定了P点的半影明暗程度.
注意:
a.采样区域是P点对应的shadow map 纹理采样点(u,v)的周围,而不是p点的周围,以此推测模拟p点的半影情况.
b.所有采样点都和p点的实际深度比较,相当于假设p点周围对于光源方向是个等距离的平面或者弧面.
接下来看下最简单的PCF采样滤波,均值滤波,也就是对目标项目点为中心,对目标点的shadow map采样坐标(u,v)进行偏移,然后采样. 如图1-3-2,图中对包括目标点在内的3x3个点采样,然后将采样结果和目标点的实际深度z比较,再对比较结果进行平均,得到目标点的滤波结果.
1.3.2 滤波方式
a.均值滤波:
这个是最简单的PCF滤波,如果用滤波和卷积的方式来解释,就是用一个box filter来对阴影进行卷积操作. 对一个二维的点进行卷积操作的公式如式1-3-1, 其中r是卷积中滤波函数,或者说是权重函数的有效半径,在这个半径之外,权重函数等于0, 而滤波的中心就是目标像素点的函数值,1-3-1中的s[i, j], 在PCF中,这个s[i,j]就是采样且深度比较的结果. 权重函数如式1-3-2,该函数在目标像素点极其周围一定半径内,对其进行加权求和,为了使得像素在滤波后不至于整体改变亮度,权重函数积分或者求和后,应该等于1. 式1-3-2中,当r=1时,ω=1/9, 其实就是求9个像素函数值的平均值.
b.三角滤波(Tent Filter):
在均值滤波中,采样半径内的所有点都是相同的权重,且滤波函数边缘是阶跃形式的. 另外一种简单,但是更为平滑的滤波函数是三角滤波,因为滤波函数看起来就像三角形, 函数如式1-3-3, 函数图像如图1-3-4. 同样,三角滤波的区域的积分或者是求和应该为1, 也就是权重和为1,以防止改变信号的整体强度.
1.3.3 Unity中三角滤波应用
当三角滤波应用在PCF中时,情况稍稍复杂. 硬件采样时,其实是以2x2为单位进行采样的,并且会对这4个采样像素进行双线性插值. Unity中3x3的滤波中,就是进行4次硬件采样,也就是采样4x4=16个像素, 然后用三角滤波在UV两个方向加权4x4个像素,每个像素的权重=U方向权重* V方向权重. 而U,V方向上单个像素所占的权重=像素对应的三角形区域的面积/三角形总面积, 如图1-3-5中,S1,S2,S3,S4区域的面积除以三角形面积,就分别代表P1,P2,P3,P4像素对应的权重大小. 因为要覆盖4个像素,所以Tent Filter的形状通常为底边长3,高为1.5的等腰三角形, 这样就能覆盖4个像素. 之所以是3, 是因为offset的偏移区间为[-0.5,0.5], 超过这个区间,就可以算作是另外4个像素的滤波, 使用底边和高的比例尺寸(不是更高,或者更矮,更大或者更小)来得到的权重来滤波,会有更好的效果, 而且 这是个等腰直角三角形,计算权重上也更加方便.
a. U和V方向加权
这4x4个像素,不看成4个孤立点,而是一块像素区域,然后用Tent Filter函数的对应面积(积分)比例表示某个像素区域的权重. Tent Filter的中心对准目标渲染点P的shadow map采样坐标u 或v,然而 u, v坐标极其可能不是某个像素块的中心,那么tent filter三角形的中心也会左右偏移(offset), 以便对齐目标采样点的U和V坐标. 但三角形偏移后,对应采样坐标中心的4个像素点比例也会变化,如图1-3-6所示.
通过Tent Filter加权,得到目标采样点附近4x4个像素的权重,每个像素的权重就是横向权重和纵向权重相乘的结果, 如图1-3-9. 实际采样过程中16个像素需要由硬件采样4次,每次得到一个2x2的group, 这个group的权重就是4个像素的权重和,如图1-3-10,黄色group的权重就是4个黄色像素权重的和. 通常对纹理采样,硬件就是采样一个2x2的group, 然后根据采样点的坐标,对group进行双线性插值,再返回插值结果. 在图1-3-10中,黄色采样点的坐标正好位于4个黄色像素的中心,那么双线性插值的结果就是4个黄色像素的平均值. 而PCF的真正目标点P,是4个双线性插值结果P1,P2,P3,P4的加权平均值, 其中各自的权重就是每个group中4个像素权重的和.
b. Group 采样坐标计算
硬件采样的2x2group, 采样点不会像图1-3-10那样始终位于group的中心, 而可能是group的任意位置, 如图1-3-11.此时可进行双线性插值得到P点的值,如式1-3-14, 1-3-15, 1-3-16,本质上就是根据s坐标与V1V2的距离,计算横向权重比,插值A1,B1得到E1值,插值C1,D1,得到F1值; 再根据t坐标与U1,U2的距离, 计算纵向权重比,插值E1,1F得到P1值. 而在3x3PCF滤波中,如前所述,已经知道了横向和纵向的插值比例,但是并不知道这个p点的坐标, 所以也无法得到P点的采样值. 所以现在的工作就是利用已知的横向纵向权重比,得到P1点的采样坐标(s1 ,t1 ),然后用UV采样.
注意:P点距离A越近,A值的比例越大,s1-u1越小,u2-s1越大,所以A的比例是u2-s1.
4x4=16个像素的最终权重,已经计算好了,如图1-3-9,所以4个2x2的Group中的权重也就确定了,但是这4个Group的采样值还不知道,所以现在需要计算每个Group的采样坐标,进而得到采样值. 由1-3-13式可得到的4个横向,4个纵向权重(ω1, ω2, ω3, ω4)和( φ1, φ2, φ3, φ4). 由1-3-14可看出,P1的插值过程中,A,B的权重比等于距离比(B1P1比A1P1),当通过Tent Filter确定A1,B1插值权重时,距离比也就确定了,现在就是要通过权重比,得到距离.
1.对P1点坐标四舍五入取整,得到采样中心参考点O的坐标
2.O点坐标平移(-1.5,-1.5)得到A点坐标
3. 利用P1在Group中的权重,得到P1相对A1点的距离(XA1P1,YA1P1),
如1-3-17和1-3-18式
4. A1相对O点坐标+P1相对A点坐标 = P1相对O点坐标,而O点坐标可通过P点得到的
根据1-3-19式,可以通过Tent Filter计算得到的4x4个权重,进一步算出A1,A2,A3,A4相对于P1,P2,P3,P4的距离,加上A1,A2,A3,A4相对O的位移,再加上O点坐标(O点坐标通过P点采样坐标取整得到), 就得到P1,P2,P3,P4的采样坐标为:
c. 加权求和
得到P1,P2,P3,P4的采样之后,剩下的就是加权求和了,它们的权重就是每个Group内2x2个像素的权重和, 如图1-3-9所示,插值如1-3-21所示.
1.3.4 阴影接受面深度偏移
之前1-2-1节已经讨论过,为了防止自阴影,对深度图进行偏移. 但是这个偏移仅仅能防止渲染点P和自己对应的深度纹理比较时产生的自阴影. 但在PCF中,情况有所不同,渲染点P不仅要和自己采样坐标(u , v)对应的深度纹理值比较,还要将(u , v)附近的深度纹理采样值,和渲染点P的深度值z比较, 比较对象都是渲染点P的深度z, 如果此时光线方向和P点为中心的采样“平面”夹角较小时,也会产生自阴影,如图1-3-14.图中,P为目标渲染点,在光源空间中深度Pz,P对应的shadow纹理坐标为u,u附近的u+1,u+2为实现PCF时增加的附近采样点,深度分别为d1,d2. 按照PCF算法,应该将采样值d,d1,d2分别与P点的光源空间深度Pz比较,从图上看有,Pz<=d, Pz > d1, Pz> d2, 然而这个结果显然是错误的,因为P旁边的A,B两点显然位于光源下,而不是阴影中. 所以最后计算得到的阴影肯定是错误的. 所以就需要偏移P点的光源空间深度Pz,对于u+1采样点,Pz偏移δ1到 P’位置,就可有Pz - δ1 =Pz’, Pz’<=d1,但是对于u+2采样点,需偏移δ2,才能满足Pz - δ2=Pz”. 也就是说必须偏移P点的光源深度Z,才能消除PCF带来的自阴影,并且这个偏移量δ随着采样坐标的移动而变化.
Z(偏移量)随着采样坐标(u, v)的移动而变化, 数学描述就是z对采样坐标(u , v)的变化率,也就是z对(u , v)的导数.Unity中用离散方式(差分)计算导数的函数是ddx(),ddy(), 但是,这两个函数仅仅只能求得传入参数相对于屏幕空间坐标(x , y)的变化率(倒数/差分), 也就是说只能得到∂z/∂x, ∂z/∂y, 另外∂u/∂x, ∂u/∂y也可以得到,但是∂z/∂u, ∂z/∂v是得不到的, ddx(),ddy()只能对x , y求导. 所以问题就归结为,如何利用∂z/∂x, ∂z/∂y, ∂u/∂x, ∂u/∂y,来得到∂z/∂u, ∂z/∂v.
从已知开始, z对x,y的导数,根据链式求导,得到z对u,v的导数,再分别对x和y 求导, 如式1-3-22, 红色表示未知,绿色表示可以通过ddx(),ddy()得到.写成矩阵形式如1-3-23式
参考资料:
Real-Time Rendering, Fourth Edition 4th
Fundamentals of Computer Graphics, Fourth Edition by Marschner, Steve Shirley, Peter (http://z-lib.org)
https://docs.unity3d.com/Manual/shadow-cascades.html
https://blog.csdn.net/cgy56191948/article/details/105726682
https://zhuanlan.zhihu.com/p/369761748
https://zhuanlan.zhihu.com/p/39 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|