Unity Water Shader

上圖是一個物體浸入水中的效果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. 折射;

 

 

關於相機透明度問題

深度增長,透明度下降。

相關文章
相關標籤/搜索