实时阴影渲染(三):软阴影深度校正

上一篇介绍的软阴影技术已经可以生成很好的软阴影

再结合第一篇介绍的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)+depthBias;

  depth(c)  <     depth(c)+depthBias;

  depth(b) + dz <   depth(c)+depthBias;

从而得出整个平面都不在阴影中的结果

 

计算深度差dz

在GPU设计体系中,片段程序执行时并不知道其它片段的信息

但好在GPU的实现中提供了ddx ddy两个例外的指令,通过这两条指令可以得出相邻片段间的偏导,也就是相邻片段之间的数据差值

因为实际采样一般为3*3,5*5,我们需要计算水平和垂直两个方向上的深度差zx、zy

假设在当前片段所在三角面对应一个阴影纹理像素的深度差为float2(zx, zy), 阴影纹理宽高为float2(w,h)

通过求解二元一次方程组:

  ddx(uv) * float2(zx, zy) * float2(w,h)= ddx(z)

  ddy(uv) * float2(zx, zy) *float2(w,h)= ddy(z)

 就可以得到zx、zy

注:uv  为当前片段的深度纹理uv坐标, z 为当前片段深度值 ,这些都是已知

 

2017-9-22修改: +方程求解说明

之前的方程写的意思不明确,想表达的是:

    ddx(u)*zx*w + ddx(v)*zy*h = ddx(z)   即 x方向z变化量【ddx(z)】 = x方向u变化量【ddx(u)】 * 单位u变化对应的z变化量【zx】 +  x向v变化量【ddx(v)】 * 单位v变化对应的z变化量【zy】

    ddy(u)*zx*w + ddy(v)*zy*h = ddy(z)   同上

 

求解我就不写了,相当于求解 方程组:

a* zx + b * zy = c

d* zx + e * zy = f 即利用a b c d e f六个已知量,计算两个未知量zx\zy,但写出来挺长,还不如自己推导便于理解

 

 方程ddx(u)*zx*w + ddx(v)*zy*h = ddx(z) 成立的意思是:

当前片段x方向变化对应纹理坐标uv两个变化,而阴影纹理u、v的变化分别产生各自的z变化值,累加就是ddx(z)

 

posted @ 2016-10-22 14:36  wiki3D  阅读(3709)  评论(5编辑  收藏  举报