上一篇介紹的軟陰影技術已經可以生成很好的軟陰影
再結合第一篇介紹的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)