水體互動
水體互動效果在游戲中是一個很常見的需求,這里簡單實作一個可互動的水體,
本篇文章主要是介紹水體互動的實作思路,水體的渲染這里就不再詳細介紹,網上很多關于水體的渲染方法很多,可以自己百度、Google了解一下,這里不會過多提及,
效果圖,
先放一張最終的GIF效果圖!

實作思路
原理其實非常簡單,就是通過粒子系統不斷發射帶有波紋法線貼圖的面片,然后把這些法線渲染一張RenderTexture傳輸到Water Shader中,然后和Water Normal 疊加即可形成水波效果,
實作步驟可以簡單分為:
- 簡單的水體渲染
- 渲染水波法線RT
- 疊加法線
一、簡單的水體渲染
這里的水體渲染采用簡單的法線干擾實作,參考馮樂樂女神的《Unity Shader入門精要》里的水體渲染,
水的效果:

這里深水和潛水區的過渡是直接用場景深度值和水面深度值做差值, 差值越接近0,就越接近淺灘區,
獲取場景深度圖需要開啟 DepthModel: GetComponent<Camera>().depthTextureMode = DepthTextureMode.Depth;
Shader代碼:
//frag:
float2 screenPos = i.screenPos.xy/i.screenPos.w;
// 獲取螢屏深度
half existingDepth01 = tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos)).r;
half existingDepthLinear = LinearEyeDepth(existingDepth01);
half depthDifference = existingDepthLinear - i.screenPos.w;
// 深水和潛水顏色做插值
half waterDepthDifference01 = saturate(depthDifference / _DepthMaxDistance);
float4 waterColor = lerp(_ShallowColor, _DeepColor, waterDepthDifference01);
二、渲染水波法線的RenderTexture
1.首先在場景創建一個Camera,該Camera只渲染特定的Layer,即水波法線,Culling Mask 設定為WaterWave,Clear Flags設定為Slid Color,Background設定為黑色,并且位置旋轉都設定為和MainCamer一致,并且創建一個RenderTexture,拖拽到Target Texture上,

2.創建一個Shader,命名為WaterRing,該Shader用于渲染水波法線,
3.繼續創建一個粒子系統,材質Shader設定為剛才創建的WaterRing Shader,并且該粒子的Layer需要修改為WaterWave,
水波法線渲染:
水波法線的Shader可以非常簡單,直接渲染一張環狀的法線貼圖即可,
但是這里為了可以更方便的調整水波法線的一些細節就采用動態計算的方式來渲染,
這里計演算法線是通過ddx ddy的方式來計算,為了得到法線首先肯定需要知道高度差,那么我們可以先渲染一個環狀的高度圖,
這里就可以通過兩個smoothstep相減得到一個較為平滑的環狀高度,
效果如下:

Shader代碼:
fixed doubleSmoothstep(float4 uv)
{
float dis = distance(uv, 0.5);
float halfWidth = _RingWidth * 0.5;
float range = _RingRange;
float smoothness = _RingSmoothness;
float threshold1 = range - halfWidth;
float threshold2 = range + halfWidth;
float value = smoothstep(threshold1, threshold1 + smoothness, dis);
float value2 = smoothstep(threshold2, threshold2 + smoothness, dis);
return value - value2;
}
fixed4 frag(v2f i) : SV_Target
{
fixed normalCenter = doubleSmoothstep(i.uv);
return fixed4(normalCenter,normalCenter,normalCenter,1);
}
有了高度差就可以計算出法線了:

Shader代碼:
float normalCenter = doubleSmoothstep(i.uv);
// 波紋法線
float color0 = doubleSmoothstep(i.uv + half4(-1, 0, 0, 0) * 0.004);
float color1 = doubleSmoothstep(i.uv + half4(1, 0, 0, 0) * 0.004);
float color2 = doubleSmoothstep(i.uv + half4(0, -1, 0, 0) * 0.004);
float color3 = doubleSmoothstep(i.uv + half4(0, 1, 0, 0) * 0.004);
float2 ddxy = float2(color0 - color1, color2 - color3);
float3 normal = float3((ddxy * _BumpPower), 1.0);
normal = normalize(normal);
float4 finalColor = float4((normal * 0.5 + 0.5) * normalCenter * i.color.a, normalCenter * i.color.a);
return finalColor;
這里通過ddx ddy得到了法線后,需要把法線從[-1,1]映射到[0,1]范圍(normal * 0.5 + 0.5),
此時我們就可以通過引數動態調整該環狀法線的寬度、強度、范圍,
最后把該材質賦值給粒子,并且通過調整粒子引數使粒子隨著生命周期逐漸變大、頂點色的A通道也跟隨生命周期變化來控制透明度和強度,
此時如果不出意外的話可以看到RT是這樣的,

三、疊加法線
最后一步就是把之前渲染得到的RT傳遞到Water Shader中,通過螢屏坐標采樣得到水波法線,然后把值從[0,1]映射到[-1,1]范圍(normal * 2-1),然后和水的法線疊加即可,
螢屏坐標可以由ComputeGrabScreenPos計算得到,
Shader代碼:
float4 ringColor = tex2D(_RingTex, screenPos);
float3 ringNormal = UnpackNormal(ringColor).rgb;
ringNormal = mul(float3x3(i.TtoW0.xyz,i.TtoW1.xyz,i.TtoW2.xyz),ringNormal);
ringNormal = normalize(ringNormal) * ringColor.a * _RingPower;
// float3 normal = BlendNormals(ringNormal,waterNormal);
float3 normal = normalize(waterNormal+ringNormal);
效果如下:

最后再結合粒子即可實作一個動態的水波擴散的效果,


完整工程原始碼: https://github.com/csdjk/LearnUnityShader
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/387397.html
標籤:其他
