【Shader】深度圖:水面渲染、能量罩和地形掃描效果

前言

這篇文章開始寫的時(shí)候還是國(guó)慶的第一天,一直拖到現(xiàn)在才寫完……

最近工作變動(dòng),我又有了相對(duì)更多時(shí)間來研究渲染相關(guān)的東西了,還是挺爽的。之前初步研究了一下深度圖,利用深度圖可以做到很多有意思的效果,這里簡(jiǎn)單講一下怎么用深度圖實(shí)現(xiàn)地形掃描和簡(jiǎn)單的水面渲染效果。

什么是深度圖

深度圖是一張灰度圖,其每個(gè)像素的明度值表示的是該像素到攝像機(jī)的距離。

想要在Unity中獲取深度圖,需要如下操作:

  1. 對(duì)Camera進(jìn)行設(shè)置,讓其生成深度貼圖(默認(rèn)是不生成的)
    這一步需要C#腳本,隨便創(chuàng)建一個(gè)腳本并編寫以下代碼,這句代碼只要執(zhí)行過一次,攝像機(jī)就會(huì)一直保存這個(gè)配置。
  Camera.main.depthTextureMode |= DepthTextureMode.Depth;

??如果操作正確,Camera的Inspector下面會(huì)出現(xiàn)以下的提示。

  1. 在Shader中獲取深度圖數(shù)據(jù)
    直接在CGPROGRAM中申明約定名稱的變量,Unity會(huì)自動(dòng)將相機(jī)生成的深度圖數(shù)據(jù)填充到這個(gè)變量中。
  sampler2D _CameraDepthTexture;

地形掃描效果

有了深度圖就很容易實(shí)現(xiàn)地形掃描效果。使用后處理實(shí)現(xiàn),將某個(gè)范圍的深度值區(qū)域進(jìn)行高亮的著色后以濾色混合到畫面上,然后腳本控制深度參數(shù)從近到遠(yuǎn)變化,效果如下:

完整Shader代碼:(后處理Shader,需要用后處理腳本掛載到攝像機(jī)上才有用)

Shader "PostEffect/Scan"
{
    Properties
    {
        [HideInInspector] _MainTex ("Texture", 2D) = "white" {}
        [HDR] _ScanColor ("Scan Color", Color) = (0,1,0,1)
        _GridTex ("Grid Tex", 2D) = "white" {}
        _GridTile ("Grid Tile", float) = 1
        _Value ("Value", Range(0,1)) = 1
        _Width ("Width", Range(0,1)) = 0.1
        _HighlightWidth ("Hightlight Width", Range(0,1)) = 0.01
    }
    SubShader
    {
        Tags { "PreviewType" = "Plane" }
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            float _Value;
            float _HighlightWidth;
            float _Width;
            float _GridTile;
            float3 _ScanColor;
            sampler2D _CameraDepthTexture;
            sampler2D _GridTex;
            sampler2D _MainTex;

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : POSITION;    
                float4 screenPos : TEXCOORD1;
                float3 worldPos : TEXCOORD2;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.uv = v.uv;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.screenPos = ComputeScreenPos(o.vertex);
                o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz / 1.0;
                return o;
            }

            float greyscale(float4 color)
            {
                return (color.r + color.g + color.b) / 3;
            }
            fixed3 screen(fixed3 color0, fixed3 color1)
            {
                return 1 - (1 - color0) * (1 - color1);
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float depth = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos));
                float linear01Depth = Linear01Depth(depth); //將深度值映射到[0,1]范圍

                fixed z = linear01Depth;
                half3 color = half3(0,0,0);
                half3 scan = _ScanColor;
                // _Value = _Time.y / 4 % 1.0; //如果不想使用腳本控制動(dòng)畫,也可以直接在Shader里實(shí)現(xiàn)動(dòng)畫
                if(z > _Value - _HighlightWidth && z < _Value)
                {
                    float intensity = greyscale(float4(scan,1));
                    color = fixed4(intensity,intensity,intensity,1);
                }
                else if(z > _Value - _Width && z < _Value)
                {
                    color = lerp(fixed3(0,0,0), scan, (z - _Value + _Width) / _Width);
                    float2 uv = i.worldPos.xy * _GridTile * z;
                    color *= tex2D(_GridTex, uv);
                }
                float4 srcColor = tex2D(_MainTex, i.uv);
                return float4(screen(color, srcColor.rgb), 0);
            }
            ENDCG
        }
    }
}

水面渲染和能量罩

守望先鋒溫斯頓的護(hù)罩:

可以看到護(hù)罩的網(wǎng)格與其他物體相交的地方會(huì)有高亮漸變。就是用深度圖實(shí)現(xiàn)的——利用透明物體不進(jìn)行深度寫入的特性,在每個(gè)片元中將當(dāng)前片元的深度和屏幕深度(屏幕深度中沒有透明物體的深度信息)對(duì)比,就能計(jì)算出當(dāng)前物體和其他環(huán)境物體相交的程度。

不過溫斯頓護(hù)罩別人也做過很多了,所以這里我就做了水面的效果,原理上一樣的——通過判斷深度差異計(jì)算出水面的深淺,并體現(xiàn)在水面的顏色、透明度以及和地形交界處的白色水花效果上:

除了上面說的三項(xiàng)基于深度圖實(shí)現(xiàn)的特性,還實(shí)現(xiàn)了法線、光滑度(基于StandardPBR)、頂點(diǎn)運(yùn)動(dòng)(簡(jiǎn)單的Sin曲線)。
最終呈現(xiàn)的效果如下:(波光粼粼的感覺是后處理Bloom的功勞)

其實(shí)這個(gè)水面效果近看就會(huì)發(fā)現(xiàn)很扯淡,非常假。畢竟水面渲染還有很多學(xué)問可以做,這里主要是展示一下深度圖的應(yīng)用而已。

完整Shader代碼:

Shader "Custom/Water"
{
    Properties
    {
        _Color ("Shallow Color", Color) = (1,1,1,1)
        _DeepColor ("Deep Color", Color) = (1,1,1,1)
        [HDR] _EdgeColor ("Edge Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _NormalMap ("Normal Map", 2D) = "bump" {}
        _NormalScale ("Normal Scale", Range(0,5)) = 1.0
        _EdgeTex ("Edge", 2D) = "white" {}
        _Edge ("Edge Value", Range(0,100)) = 1
        _EdgeWidth ("Edge Width",Range(0,1)) = 0.0
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        // _Metallic ("Metallic", Range(0,1)) = 0.0
        _Value ("Value", Range(0,1)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue"="Transparent" "ForceNoShadowCasting"="True"}
        ZWrite Off
        Blend SrcAlpha OneMinusSrcAlpha
        BlendOp Add

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows vertex:vert alpha
        #pragma target 3.5

        sampler2D _MainTex;
        sampler2D _CameraDepthTexture;
        sampler2D _EdgeTex;
        sampler2D _NormalMap;

        fixed _Value;
        half3 _EdgeColor;
        half _Glossiness;
        half _NormalScale;
        // half _Metallic;
        fixed4 _Color;
        fixed _Edge;
        fixed _EdgeWidth;
        fixed4 _DeepColor;

        struct Input
        {
            float2 uv_MainTex;
            float2 uv_NormalMap;
            float2 uv_EdgeTex;
            float waveNoise;
            float3 worldNormal;
            float3 viewDir;
            float4 screenPos;
            float eyeZ;
            float4 test;
        };

        fixed greyscale(fixed3 input)
        {
            return (input.r + input.g + input.b) / 3; 
        }
        fixed3 screen(fixed3 color0, fixed3 color1)
        {
            return 1 - (1 - color0) * (1 - color1);
        }

        void vert(inout appdata_full v, out Input o)
        {
            UNITY_INITIALIZE_OUTPUT(Input, o);
            //
            COMPUTE_EYEDEPTH(o.eyeZ);
            //
            v.vertex.y += sin(v.vertex.z / 2 + _Time.y * 2) * 0.5;
            v.vertex.y += sin(v.vertex.x / 2 + _Time.y * 2) * 0.5;
        }

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            float3 worldNormal = normalize(IN.worldNormal);
            float3 viewDir = normalize(IN.viewDir);

            float screenZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(IN.screenPos)));  
            float intersect = (1.0 - pow((screenZ - IN.eyeZ),0.3));
            float v = saturate(intersect);

            fixed3 Edge = tex2D(_EdgeTex, IN.uv_EdgeTex + sin(_Time) / 50) * _Edge * _EdgeColor;
            Edge = lerp(fixed3(0,0,0),Edge,smoothstep(1 - _EdgeWidth,1,v));

            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * lerp(_Color, _DeepColor, saturate(lerp(_Value, 1 - _Value, screenZ - IN.eyeZ)));
            o.Albedo = screen(c.rgb, Edge);
            o.Alpha = c.a;

            half3 normal = UnpackScaleNormal(tex2D(_NormalMap, IN.uv_NormalMap + _Time / 200), _NormalScale);
            o.Normal = normal;
            o.Metallic = 0;
            o.Smoothness = _Glossiness;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

除了上面這些,深度圖還可以做景深、霧效、SSAO等效果,不過因?yàn)橛袆e的需求來了,暫時(shí)沒有繼續(xù)研究下去。感興趣的可以自己搜索一下,深度圖的相關(guān)內(nèi)容還是非常多的。


參考
神奇的深度圖:復(fù)雜的效果,不復(fù)雜的原理
Unity3D中的深度紋理和法線紋理
Unity Shader-深度圖基礎(chǔ)及應(yīng)用
Unity Shader-深度相關(guān)知識(shí)總結(jié)與效果實(shí)現(xiàn)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)載自VR設(shè)計(jì)云課堂[http://www.itdecent.cn/u/c7ffdc4b379e]Unity S...
    水月凡閱讀 1,177評(píng)論 0 0
  • Basic Shaderlab unity 使用一個(gè)叫做 shaderlab 的語言用來包裝和組織整個(gè)shader...
    goteet閱讀 3,392評(píng)論 0 12
  • 轉(zhuǎn)載注明出處:點(diǎn)擊打開鏈接 Shader(著色器)是一段能夠針對(duì)3D對(duì)象進(jìn)行操作、并被GPU所執(zhí)行的程序。Shad...
    游戲開發(fā)小Y閱讀 3,693評(píng)論 0 4
  • 原文地址:https://unity3d.com/cn/learn/tutorials/temas/perform...
    hellokazhang閱讀 4,788評(píng)論 0 13
  • 昨兒個(gè)家長(zhǎng)會(huì),初時(shí)昂著脖子激動(dòng)了一陣,因?yàn)槁牭叫童鞋的名字被表?yè)P(yáng)了兩次。之后領(lǐng)導(dǎo)在臺(tái)上講話,我在臺(tái)下犯困?;氐浇?..
    小不稀閱讀 415評(píng)論 0 1

友情鏈接更多精彩內(nèi)容