ShadowMap自陰影在PS.1.x下的實現方法

來源:http://www.sunnycrystal.net/showdocs.aspx?id=12

目前至少有兩種廣泛採用的實時陰影發生技術。一種是ShadowVolume,還有一種就是ShadowMap。

ShadowVolume基於的是幾何體算法,通過延伸光照輪廓區域進行正反面兩次渲染在屏幕的模板緩衝區內分離出陰影區域(本站中有Z-FAIL陰影的算法要點)。ShadowVolume能夠得到十分精確的陰影區域,畫質較好,而且渲染流程十分簡單,缺點是建立光照輪廓延伸體十分費時費資源,另外陰影邊緣視覺上比較“硬”。

ShadowMap基於的是Shader技術,通過建立Z深度圖、逐像素比較深度得到陰影區域。該方法形成陰影速度非常快,對於一些固定的建築物之類的圖素形成陰影尤其出色,對於一些運動物體的陰影最多隻需渲染兩次物體即可形成陰影(筆者的配置爲ATI9550、CeleronD2.4G,ShadowMap陰影基本上對速度沒有影響,而生成ShadowVolume則每幀繪圖時間增加了2個毫秒);缺點是需要顯卡有對VS、PS的支持,而且內存消耗較大,同時只能生成平行光下的陰影,而且無法做到將陰影投影到所有的物體。

所以,兩種陰影互爲優缺點,採用何種陰影方式需要按照自己的需求來。下面是自投影ShadowMap的D3D8實現要點(可同時參考D3D9的例子,因爲筆者覺得d3d9速度比d3d8慢故採用了d3d8)。

1、建立Lookup紋理(2048 * 1),這個紋理是深度值的索引。所謂深度,指的是屏幕Z值。紋理每一個像素點的RGBA = ( 像素點橫座標 & 0xFF, ( 像素點橫座標 & 0xFF00 ) >> 3,  0, 0 ),比如像素橫座標爲XP=256,則像素RGBA = ( 255, 32, 0, 0 )。這個紋理很重要,以後紋理的顏色值就可以代表Z值。

2、設置Lookup紋理;設置LightViewProj:將眼睛移至光源處,觀察方向爲光源照射方向,屏幕的投影矩陣設爲正交(D3DXMatrixOrthoLH);然後渲染物體到一張紋理,建立深度圖。此處的要點是VertexShader,渲染的時候只對定點做位置變換即可,然後把變換後的頂點Z值寫入oT0進行保存(mov oT0, r0.z)。此時的紋理就叫做DepthTexture,每個像素的顏色值就保存着Z值。

3、恢復視角到原正常觀察位置,設置DepthTexture紋理,再次渲染物體,分別保存每個頂點在LVP空間(LightViewProj)下的實際深度和LVP下該頂點對應受光面頂點的深度到oT1和oT2。
    如何找到每個像素對應的LVP空間的受光面像素呢?可以在VS進行頂點渲染的時候將每個頂點對應到LightViewProj的受光面,則經過插值得到的像素也一一對應到了LVP空間。頂點對應的LVP頂點計算方法爲:頂點 * 頂點變換矩陣 * LVP_View矩陣 * LVP_Proj矩陣。同時通過對齊DepthTexture將LVP下受光面上對應頂點的深度保存下來,也就是LVP頂點貼圖座標要與DepthTexture對齊,此時該頂點的紋理顏色值 = LVP空間下受光面的Z值(參看要點2的最後一句話)。然後保存LVP空間下該頂點的實際深度用於在PS中進行深度比較。

4、將屏幕上的每個像素在LVP下的深度與LVP下的受光面像素逐一比較深度(Z值),Z值大的就在陰影區,需要將該像素設置爲陰影顏色,Z值小於等於的則在照亮區域,顏色不變。在PS中將每個像素對應的DepthTexture顏色值進行如下操作:
    mov_d8 r2.r, r2.r//r2.r值右移3位
    add r2.r, r2.r, r2.g//累加r2.r與r2.g值,得到對應於LVP空間受光面像素深度值
    將此深度值與實際的深度值(r1)進行sub、cmp操作比較即可得出是否在受光面,如果是陰影區就將該像素(r0)染黑一點即可。

ShadowMap是一個相對比較複雜的操作,目前基本上找不到D3d8下的ShadowMap資料,希望這篇文章可以爲需要在D3d8下實現ShadowMap的朋友提供幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章