【Unity Shader】結合Projector和Rendertexture實現實時陰影

之前已在簡書上發過,這裏重新整理了一下,並附上實現代碼。


在unity中實現陰影的文章網上看了不少,包括常用的shadowMap,或直接投射Rendertexture等。比如unity中實現shadowmap,可以通過投射燈光空間的深度圖,並在投射物體上進行深度比較,判斷是否處於陰影的範圍,以此來渲染陰影。將深度圖投射到接受陰影的物體上的效果如圖所示:


你所需要做的就是在燈光空間渲染一張深度紋理,並投射到接受陰影的物體上,並和接受陰影的物體上對應像素位置的深度(燈光空間)進行比較,來確定當前像素是否處於陰影即可,此外還要考慮深度圖的精度以及以此會造成的ZFighting等,當然這並不是本文討論的重點。

這邊我主要介紹一種直接投射燈光空間攝像機的Rendertexture來實現陰影的方法,並將在稍後將其和projector結合。當然同時熟悉這兩項技術的開發者應該已經清楚,使用projector實現陰影意味着你將會消耗額外的drawcall,實際上被投射projector並且未在shader中使用"IgnoreProjector"="true"的物體都會在自身shader渲染完(也可能是渲染前,具體看自身渲染隊列和projector的shader的渲染隊列的先後順序)後再次使用projector的shader渲染一次,這表示當被projector投射且未標記忽略投影機或不處於投影機忽略的層的物體會再次使用projector的material渲染,所以如果將projector應用在某些場景,比如擁有複雜的場景元素,且大部分是單獨的物體而不是合併的,會佔用較多drawcall,當然如果projector隻影響一小部分元素,比如projector隻影響單獨模型的地面,則可以不必擔心drawcall的問題。

首先比較一下這種技術和shadowmap技術,實際上個人感覺很大程度上兩者的技術其實差不多,都需要用到屏幕投影,只不過shadowmap投射的是深度圖(深度緩衝),而本文介紹的是直接投射屏幕紋理(幀緩衝),因此投射的紋理是帶Alpha通道的。


和shadowmap不同的是,燈光空間的攝像機應該只看到投射陰影的物體:


此時投射後的效果大致如圖所示:


需要注意的是,正如剛剛所講,投射的Rendertexture是帶alpha通道的,這意味着被投射物體的貼圖的Alpha值會存入這張Rendertexture,如圖是燈光空間的攝像機看到的投射陰影的物體:


理論上此時這個攝像機投射的rt應該也是這樣的效果(除了背景部分會透明),但是如果投射陰影的物體使用的紋理帶有Alpha通道,儘管物體可能並未使用開啓blend的shader,比如上圖中的物體時默認的Diffuse,但如果使用的紋理格式是帶Alpha通道的,比如RGBA Compressed DXT5並且這張紋理本身也具有Alpha通道信息,則也會渲染到RT中,如下圖是使用了帶Alpha通道的紋理得到的Rendertexture,其身體使用RGBA格式,而頭部紋理則不具有Alpha通道信息:


可以看到實際渲染得到的rt也會記錄紋理的Alpha。將其紋理格式改成RGB Compressed DXT1就不會渲染出半透明的RT了。

當然使用這種方式投射rendertexture必然造成的一個問題是,由於沒有投射接受陰影的平面,導致一旦投射陰影的物體穿透接受陰影的物體時會造成陰影的穿幫:


接受陰影物體Shader主要實現代碼: 


其中viewMatrix爲燈光空間攝像機的worldToCameraMatrix,projMatrix爲燈光空間攝像機的投影矩陣。

當然使用這種方式實現陰影的不足之處在於需要明確的知道投射陰影的物體和接受陰影的物體。

接下來將嘗試將其與Projector結合,注意之前已經討論過,使用projector意味着額外的drawcall,尤其是場景中物件很多且全部都是分離的物體時,不建議使用這種方式。當然如果場景中只有極少部分物體需要接受陰影,比如只有主要地形,則不妨可以嘗試使用這種方式,因爲使用projector,你可以很方便的在shader中加入IgnoreProjector標籤來忽略投影機的作用,或者直接在projector上修改projector影響的層,並且不需要爲接受陰影的物體單獨編寫shader或腳本,只需要一個projector即可。

從unity標準資源包中的projector shader我們大致可以瞭解,projector shader中需要兩個4階矩陣,          分別爲_Projector和_ProjectorClip,其中後者主要用於近遠裁面的淡入淡出,並不是必須的。而前者的_Projector,注意這個矩陣應該區別於攝像機的projection矩陣(儘管攝像機和projector在很多參數上很相似),原因是官方的projector shader中直接通過:o.uvShadow = mul (_Projector, vertex);計算得到投影紋理座標,這意味着_Projector矩陣應該同時實現將vertex轉換到世界空間,再轉換到projector的局部空間,最後轉換到projector的投影空間的功能,所以其性質應該類似UNITY_MATRIX_MVP矩陣,所以使用projector實現投射rendertexture的效果,只需在projector位置創建一個攝像機,並複製projector的參數,並將這個攝像機的rendertexture傳遞給projector的material,具體shader實現如下:


其中_FadeTex是一張表示陰影衰減的貼圖,其r、g通道效果如下:


需要注意第二個通道的紋理使用_ProjectorClip,用於實現近裁面到遠裁面的衰減效果,而第一個通道的紋理則用於將rt周圍進行遮罩,如果不使用則當投射陰影的物體剛好到達或超出projector攝像機時,由於我的rt的wrapmode爲clamp,會產生這種效果:


最後實現的陰影如下:


另外由於投射的是帶Alpha通道的Rendertexture,意味着可以方便的對其使用模糊shader完成模糊效果,這裏是我自己編寫的模糊腳本效果圖:



最後附上工程

鏈接:http://pan.baidu.com/s/1mgTkDhY 密碼:ggh8

之前的簡書文章地址:http://www.jianshu.com/p/1fade1e8c309

原創文章,如需轉載請註明出處。


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