軟陰影是通過對陰影圖進行多次采樣實現的
因為多個片段經常會對應同一個陰影紋理像素,單次采樣會產生嚴重的鋸齒問題
另外軟陰影還可以產生比較柔和的邊界,看起來比較自然
鋸齒產生的原因
仔細思考陰影鋸齒產生的原因,可以想到多個片段對應同一個陰影像素時,其在該陰影像素中對應的的位置是不同的
如下示意圖,黑色的大方格表示單個陰影紋理像素,虛線分割的部分表示對應到該陰影像素的9個片段

顯然對於AB來說其陰影值和C應該是不同的,算法中應該把這個因素考慮進去
可以想到,當紋理坐標沒有對應到正中心時,通過與相鄰像素的線性插值也許可以很好的解決我們遇到的鋸齒問題
2×2線性插值

如上圖,實線分割的四個區域表示陰影紋理中的四個像素,ABCD為其中心點
當一個片段映射到ABCD虛線包含的區域時,這四個像素都應該參與陰影計算,然后通過線性插值得出最終結果
假設紋理大小為float2 size(w, h),像素大小texelSize=float2(1/w, 1/h),當前紋理坐標uv
則以下代碼可計算四個點紋理坐標uva/uvb/uvc/uvd,插值系數factor(x, y)【 水平方向插值系數為x, 垂直方向插值為y】:
factor= frac(uv*size-0.5);
uva = (floor(uv*size-0.5) + 0.5) * texelSize; uvb = uva + float(1, 0) * texelSize; uvc = uva + float(0, 1) * texelSize; uvd = uva + float(1, 1) * texelSize;
a、b、c、d分別為A、B、C、D陰影值
//a=depthShadow(sm,uva, z,...); b=depthShadow(uvb,...)
ab = lerp(a, b, factor.x);
cd = lerp(c, d, factor.x);
shdow = lerp(ab, cd, factor.y); //最終陰影值
這樣即便單個陰影像素點也可以產生平滑的陰影,並具有典型的線性插值特征:

更大的采樣范圍
2x2線性插值雖然可以產生平滑過渡的陰影,但沒有完全解決鋸齒紋理,效果並不理想
這是因為使用的陰影圖本身就是柵格化的
處理這個問題的辦法是采用更大的采樣范圍比如3x3\4x4\5x5
其處理思路都是一樣的,比如以5x5為例:
首先計算各行的陰影值: row[i]= b+c+d+lerp(a, e, factor.x) , 其中a b c d e為同一行的五個點陰影結果
總陰影值 = ( row1+row2+row3 + lerp(row0, row4, factor.y) ) / 16
5X5采樣得到可以產生非常柔和的陰影
不過需要25次紋理采樣,性能相比3×3的9次或 4x4的16次要差些
