這篇文章主要介紹一種極為簡單的軟陰影實現方法。主要方法和PCF的shadowmapping大同小異,只是多了一步screenspace的高斯模糊,從而實現所謂的軟邊陰影。具體方法如下:
首先第一個PASS和傳統的shadow mapping演算法是一樣的,獲得光源空間下的物體最小深度,說白了就是把Camera移動到光源位置,渲染一遍,得到深度圖,這樣得到了情境到光源的最近的深度。如果你用的是DX,那麼你可能需要把深度寫到一個rendertarget裡面,方法如下:
Output.color = float4 ( pos.z / pos.w ,pos.z / pos.w , pos.z / pos.w , 1.0 ) ;
注意這裡的深度並不是Zbuffer裡面的最終的數值,因為它並沒有經過視口變換。它的範圍在-1到1之間。
如果你用的是Opengl,那麼可以方便的通過depth buffer獲得,但是這個值是經過視口變換的,它的範圍在0到1之間。
第二個PASS,把Camera拉回到它原來所在的位置。然後正常地渲染情境。常規的shadow mapping做法是把情境每個像素點轉化到光源空間下,與深度圖裡面的值進行比較,如果這個點轉化過去比同樣位置的點深度值大,那麼它肯定在這個點背後,光源是照不到的,因此這個點在陰影之中,反之則在光源照射下。我們可以簡單地這樣處理,如果這個點在陰影中間,那麼這個點最終的顏色要乘以一個0到1的係數,讓它變暗一點,比如0.5。這樣做有一個問題,就是最終產生出來的陰影可能會有鋸齒。:
這是由於陰影貼圖的精度造成的。這個時候PCF應運誕生了,哈哈。說白了就是不光當前像素點去比較,連同周圍的點一起比較,這樣邊緣不會這樣生硬,也不會有明顯的鋸齒,但是實際情況仍然不能令人滿意,因為在不是很大精度的深度貼圖中,PCF會產生令人討厭的馬賽克,:
為瞭解決這個問題,我們再次引入一個PASS,即screen-space的高斯模糊:
我們把第二個PASS產生情境的顏色(不帶陰影)放在一個紋理中,而把陰影的數值放在第二個紋理中。
第三個PASS:我們把第二個紋理,即存放陰影數值的紋理進行高斯模糊,這樣數值的邊緣就不會有很大的梯度,從而漸層到0.
最後一個PASS:把剛才的顏色紋理和高斯模糊後的紋理疊加起來,形成最終的效果:
恩,得到了較為理想的效果。不過screen-space的陰影有它的硬傷,就是它始終是screen-space技術,和情境的距離無關,也就是說情境不管遠景,模糊的尺度都是那麼大,這顯然是不對的,不過在遊戲當中如果稍加避免,就會得到不錯的效果。
希望這篇文章對你有用,如果有什麼疑問,請發郵件到我的郵箱:xingjinanmo@163.com,或者加我的QQ:182322035