上圖是一個物體浸入水中的效果app
咱們使用相機渲染的整個場景的深度圖減去須要忽略的模型的深度,這裏忽略的是圖中藍色部分,就保留了其餘的深度值。測試
用到Main Camera渲染的深度貼圖: sampler2D _CameraDepthTexture; //在shader中聲明,須要設置相機開啓渲染深度圖;深度圖中記錄的深度值:深度紋理中每一個像素所記錄的深度值是從0 到1 非線性分佈的。精度一般是 24 位或16 位,這主要取決於所使用的深度緩衝區。當讀取深度紋理時,咱們能夠獲得一個0-1範圍內的高精度值。若是你須要獲取到達相機的距離或者其餘線性關係的值,那麼你須要手動計算它。spa
在頂點着色器中獲取頂點在屏幕空間的位置,用作採樣深度圖的uv座標:3d
o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.scrPos = ComputeScreenPos(o.pos);//將返回片斷着色器的屏幕位置 COMPUTE_EYEDEPTH(o.scrPos.z);//計算頂點攝像機空間的深度:距離裁剪平面的距離,線性變化;
在像素着色器中採樣Main Camera渲染的深度貼圖(攝像機能看到的整個場景的深度圖):code
//計算當前像素深度 float depth= tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)).r;//深度值 [0,1] depth = LinearEyeDepth(depth);//深度根據相機的裁剪範圍的值[0.3,1000],是將通過透視投影變換的深度值還原了
將此圖的值構建出灰度圖,輸出查看深度圖效果: return float4(depth-999,depth-999,depth-999,1); 能夠看出深度圖中,沒有被遮擋的部分並無表現出線性漸變,說明區域的值被默認填充爲最大的深度,我並無找到相關的文件說明,有朋友找到,麻煩私信下。blog
如今咱們測試下頂點在屏幕空間的深度值(僅僅是計算須要計算的模型頂點的深度,與攝像機的深度圖有區別),該值被頂點着色器記錄在o.srcPos.z中,將值在像素着色器中輸出,查看效果: return float4(i.scrPos.z,i.scrPos.z,i.scrPos.z,1); it
(上圖)想象沿着攝像機的方向,該值呈現出線性分佈,說明是該點到近裁減面(以近裁剪面)的距離,假如攝像機裁剪空間是[0.3,10],那麼該值在大於等於1的時候呈現出白色,在[0,1]區間內是灰度圖。class
咱們將頂點的深度值做爲通道輸出: return float4(i.scrPos.z,i.scrPos.z,i.scrPos.z,i.scrPos.z); 變量
Tags {"Queue" = "Transparent"} Blend SrcAlpha OneMinusSrcAlpha
這個樣子,咱們就實現根據攝像機與頂點之間的深度值變化,實現與視角有關的透明效果,深度值在[0,1]範圍內,則會透明,大於1則會呈現出材質顏色;原理
如今咱們將咱們轉化過的深度圖深度值(線性分佈)與頂點深度相減: depth -= i.scrPos.z;
爲何其餘地方白色相減不等於黑色?
我的認爲:在深度圖中,沒有被遮擋的地方被默認填充爲最大深度1,轉化爲距離相機的線性距離時,也被轉化爲最大距離,例1000。這個值再減去模型的深度時,結果遠大於模型深度,用顏色顯示出來,即爲白色。
如今將深度值輸出到像素着色器的rgba通道:查看效果: return float4(depth,depth,depth, depth); 與模型相交部分變成了透明。
接下來,咱們使用變量ColorDepth控制深度值,並增長2個顏色,來表現深度的範圍:
Properties { _Color0("Water Color",Color) = (1,1,1,1)//水的顏色 _Color1("Water Depth",Color) = (0,0,0,0)//水的深度的顏色 _Alpha("Alpha",Range( 0,1))= 1//水面的正題透明度 _ColorDepth("ColorDepth",Range( 0,1))= 0//水的深度 }
//計算水的透明度: 使用深度值 float alpha = saturate( _Alpha*depth); //計算顏色深度: float colDepth = saturate(_ColorDepth*depth); colDepth = 1-colDepth; colDepth = lerp(colDepth, colDepth*colDepth*colDepth, 0.5);//調整深度,看我的喜愛 half3 col; col.rgb = lerp(_Color0.rgb, _Color1.rgb, colDepth); return float4(col.rgb, alpha );
在物體遮擋處,藍色越深,表示越深,天藍色,是海水顏色。如今除了物體遮擋處的深度表現正確,其餘地方審定其實不正確。咱們在水下加一塊地皮:
是否是有感受了,咱們再調節下顏色等參數:
源代碼:
Shader "JQM/DepthTest_1" { Properties { _Color0("Water Color",Color) = (1,1,1,1)//水的顏色 _Color1("Water Depth",Color) = (0,0,0,0)//水的深度的顏色 _Alpha("Alpha",Range( 0,1))= 1//水面的正題透明度 _ColorDepth("ColorDepth",Range( 0,1))= 0//水的深度 } SubShader { Tags {"Queue" = "Transparent"} Pass { Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct VertexOutput { float2 uv : TEXCOORD0; float4 pos : SV_POSITION; float4 scrPos : TEXCOORD1; }; float4 _Color0; float4 _Color1; float _Alpha;//水的透明度 float _ColorDepth; sampler2D _CameraDepthTexture; VertexOutput vert (appdata v) { VertexOutput o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.scrPos = ComputeScreenPos(o.pos);//將返回片斷着色器的屏幕位置 COMPUTE_EYEDEPTH(o.scrPos.z);//計算頂點攝像機空間的深度:距離裁剪平面的距離 return o; } fixed4 frag (VertexOutput i) : COLOR { //計算當前像素深度 float depth= tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)).r;//UNITY_PROJ_COORD:深度值 [0,1] depth = LinearEyeDepth(depth);//深度根據相機的裁剪範圍的值[0.3,1000],是將通過透視投影變換的深度值還原了 depth -= i.scrPos.z; //計算水的透明度: 使用深度值 float alpha = saturate( _Alpha*depth); //計算顏色深度: float colDepth = saturate(_ColorDepth*depth); colDepth = 1-colDepth; colDepth = lerp(colDepth, colDepth*colDepth*colDepth, 0.5);//調整深度,看我的喜愛 half3 col; col.rgb = lerp(_Color0.rgb, _Color1.rgb, colDepth); return float4(col.rgb, alpha ); } ENDCG } } }
如今基本完成了物體浸入水中的效果:
接下來,我還須要什麼效果呢?
1. UV偏移的動態水面;
2. 反射;
3. 折射;
關於相機透明度問題
深度增長,透明度下降。