本文參考自教程,加上自己的一點心得體會臀玄。
首先,根據(jù)水面和場景的深度差異劃分為淺水和深水畅蹂,淺水和深水各取兩個極值健无,然后根據(jù)深度進行插值。所謂的淺水液斜,就是水面下的物體與水面的距離較小累贤,顏色較淺;所謂的深水少漆,就是水面下的物體與水面的距離較大臼膏,顏色較深。
那么示损,如何獲得場景深度呢渗磅?Unity引擎自帶了_CameraDepthTexture,但這是基于屏幕坐標系的texture屎媳,如何在frag shader里去采樣到正確的值呢夺溢?對于頂點而言,我們先通過UnityObjectToClipPos將其轉(zhuǎn)換到齊次坐標系烛谊,此時和的取值范圍均為,然后根據(jù)視口變換:
我們發(fā)現(xiàn)嘉汰,實際的采樣坐標即為丹禀。Unity提供了兩個內(nèi)置函數(shù)幫助我們進行采樣:ComputeScreenPos和tex2Dproj。ComputeScreenPos這個函數(shù)得到的結(jié)果為,tex2Dproj函數(shù)會對傳入的采樣坐標進行透視除法再采樣双泪。
然而持搜,采樣出來的深度值是經(jīng)過透視投影變換后的深度,已經(jīng)不再是線性的焙矛,我們需要把它還原為原始的深度值再進行使用葫盼。這其實本質(zhì)上是投影變換的逆變換。Unity也提供了一個內(nèi)置函數(shù):LinearEyeDepth村斟。通過該函數(shù)得到原始深度贫导,與水面的原始深度做差,利用這個差異值就可以對顏色進行線性插值了蟆盹。有兩點需要意識到:
- 場景深度texture里保存的深度信息是水面渲染之前的孩灯,也就是說是不包含水面的深度信息的,另外逾滥,在開啟early-z的情況下峰档,水面frag代碼執(zhí)行的條件就是水面的深度值要比場景深度值要小
- 水面的原始深度值就是ComputeScreenPos這個函數(shù)返回的。在進行透視除法之前寨昙,原始深度值一直保存在vertex的分量
綜上讥巡,以上實現(xiàn)的shader代碼如下:
float existDepth01 = tex2Dproj(_CameraDepthTexture, i.screenPos).r;
float existDepth = LinearEyeDepth(existDepth01);
float difference = existDepth - i.screenPos.w;
float difference01 = saturate(difference / _MaxDistanceCutOff);
float4 waterColor = lerp(_ShallowWaterColor, _DeepWaterColor, difference01);
效果如圖。
第二步舔哪,我們需要為水面加上一些擾動信息欢顷,來實現(xiàn)波紋效果。使用一張噪聲貼圖尸红,直接采樣出顏色與現(xiàn)有顏色疊加:
可以發(fā)現(xiàn)吱涉,由于顏色直接疊加等舔,把大部分區(qū)域的顏色刷得太亮了女揭,而我們想要的,其實是亮的地方顏色保持不變啤呼,暗的地方刷成波紋的顏色盅蝗,因此加一個cutoff進行過濾:
接下來鳖链,我們希望靠近岸邊的邊緣部分也要有波紋《漳靠近岸邊的水面和場景的深度差異較小芙委,可以利用這一點降低對噪聲的cutoff值,讓邊緣部分的噪聲也可以add到水面上:
另外狂秦,可以再加一張擾動貼圖灌侣,采樣兩個通道對噪聲貼圖的uv進行偏移,來實現(xiàn)流動的效果裂问。
現(xiàn)在侧啼,大體的感覺已經(jīng)有了牛柒。細心觀察可以發(fā)現(xiàn),水面與物體接觸的地方痊乾,波紋應(yīng)該更明顯一點皮壁,也就是與物體接觸的地方需要額外降低對噪聲的cutoff值。這時哪审,需要借助場景中的法線數(shù)據(jù)來處理蛾魄。水面與物體接觸的地方法線差異比較大,其他地方法線差異比較小湿滓。容易想到用兩個法向量的點積來衡量向量的差異程度滴须。
由于Unity自帶的法線紋理精確有限,這里考慮自己渲染出一張場景的法線紋理圖茉稠。額外新建一個camera描馅,通過設(shè)置它的depth,使用SetReplacementShader方法而线,替換掉場景中其他所有shader铭污,讓其都用寫法線的方式渲染,得到RT膀篮。這個方法可以替換掉所有對于Tag相同的shader嘹狞,讓它們用指定的shader渲染,對于Tag不同的誓竿,則跳過渲染磅网,這里可以參照Unity官方文檔。
接下來筷屡,我們可以通過修改shader的tag和blend模式涧偷,來讓水面變得透明。同時毙死,我們還希望能夠調(diào)整波紋的顏色燎潮。為了讓波紋自身的顏色能與水面本身的顏色融合而不是相加,我們采用alpha-blend的模式扼倘,讓波紋自身的顏色blend到水面上确封,這時,之前用到的cutoff得到的結(jié)果可以用在設(shè)置波紋的alpha上再菊。
最后爪喘,對波紋邊緣處,可以用smoothstep對cutoff進行平滑纠拔。
總結(jié)一下秉剑,實現(xiàn)一個簡單的卡通水面效果,分為如下幾個步驟:
- 根據(jù)水面與場景的深度差異稠诲,劃分出淺水區(qū)和深水區(qū)秃症;
- 使用噪聲貼圖blend到水面上候址,達到水波紋的效果吕粹,注意在水面邊緣种柑,深度差異小的地方水波紋的效果大;在水面中央有物體的地方匹耕,法線差異大聚请,水波紋效果也大;
- 通過調(diào)整blend的模式稳其,讓水波紋可以設(shè)置自身的顏色blend到水面上驶赏;
- 水波紋效果邊緣可以用smoothstep進行平滑。