上一篇介绍的软阴影技术已经可以生成很好的软阴影
再结合第一篇介绍的PSSM就可以实现不错的阴影效果
但在实际应用中的会遇到一个很重要的问题:阴影渲染中的自阴影问题
这种问题的产生的原因和锯齿原因类似:
视空间中的像素和灯光空间像素不一致
比如视空间中的一块区域(多个像素)在渲染阴影时对应同一个像素,因而仅产生一个深度值(中心深度)
对于灯光空间中倾斜的三角面来说,这些值是应该不同的
一般对这种问题的解决办法是通过一个小的深度偏移量,或者再通过背面渲染缓解这种问题
而采用多个采样的深度软阴影技术,这种问题会更加显著,这样仅采用一个小小的偏移就不够了
但如果深度偏移太大又会导致本该出现阴影的地方没有阴影
下图以水平3像素的软阴影采样为例说明这种问题:
如上图,假设场景中仅存在ABC所在的平面,ABC为相邻的三个阴影像素,
采用水平3采样计算c点阴影时,会得到强度为0.333的阴影,因为 depth(a)>depth(c)+depthBias
这样渲染那出来的结果是:整个平面都在强度为0.333阴影中;而实际上整个平面都不应该有阴影
除非我们将depthBias设的足够大,但AB平面越倾斜、采样数越多所需要的depthBias的值就越大
这样通过增大depthBias的值是不能解决问题的
思考一下,如果我们能知道ac点的深度差dz = depth(c)-depth(a)的值,就可以通过计算比较出:
depth(a) - dz < depth(c)&#43;depthBias;
depth(c) < depth(c)&#43;depthBias;
depth(b) &#43; dz < depth(c)&#43;depthBias;
从而得出整个平面都不在阴影中的结果
计算深度差dz
在GPU设计体系中&#xff0c;片段程序执行时并不知道其它片段的信息
但好在GPU的实现中提供了ddx ddy两个例外的指令&#xff0c;通过这两条指令可以得出相邻片段间的偏导&#xff0c;也就是相邻片段之间的数据差值
因为实际采样一般为3*3&#xff0c;5*5&#xff0c;我们需要计算水平和垂直两个方向上的深度差zx、zy
假设在当前片段所在三角面对应一个阴影纹理像素的深度差为float2(zx, zy), 阴影纹理宽高为float2(w,h)
通过求解二元一次方程组&#xff1a;
ddx(uv) * float2(zx, zy) * float2(w,h)&#61; ddx(z)
ddy(uv) * float2(zx, zy) *float2(w,h)&#61; ddy(z)
就可以得到zx、zy
注&#xff1a;uv 为当前片段的深度纹理uv坐标&#xff0c; z 为当前片段深度值 &#xff0c;这些都是已知
2017-9-22修改&#xff1a; &#43;方程求解说明
之前的方程写的意思不明确&#xff0c;想表达的是:
ddx(u)*zx*w &#43; ddx(v)*zy*h &#61; ddx(z) 即 x方向z变化量【ddx(z)】 &#61; x方向u变化量【ddx(u)】 * 单位u变化对应的z变化量【zx】 &#43; x向v变化量【ddx(v)】 * 单位v变化对应的z变化量【zy】
ddy(u)*zx*w &#43; ddy(v)*zy*h &#61; ddy(z) 同上
求解我就不写了&#xff0c;相当于求解 方程组&#xff1a;
a* zx &#43; b * zy &#61; c
d* zx &#43; e * zy &#61; f 即利用a b c d e f六个已知量&#xff0c;计算两个未知量zx\zy&#xff0c;但写出来挺长&#xff0c;还不如自己推导便于理解
方程ddx(u)*zx*w &#43; ddx(v)*zy*h &#61; ddx(z) 成立的意思是&#xff1a;
当前片段x方向变化对应纹理坐标uv两个变化&#xff0c;而阴影纹理u、v的变化分别产生各自的z变化值&#xff0c;累加就是ddx(z)