天天看點

unity螢幕特效綜述6 景深效果

從這一部分開始,我的工作從學習别人的代碼要逐漸轉到自己敲代碼了,中間跨越還是挺大的,景深效果的原理看似簡單,也寫了快一天時間了。

下面我簡單說明兩種景深效果的寫法,一種是我自己探索着寫的,另一種是參考别人的部落格寫的(https://blog.csdn.net/puppet_master/article/details/52819874),不得不承認,别人的方法還是要成熟一點。

景深效果給我的直覺了解就是,在距離錄影機的某個距離範圍内,圖像是清晰的,在那個範圍外的圖像都是模糊的。是以,結合前面所學習的内容,隻需要首先提取出來距離錄影機[mindistance,maxdistance]的像素,然後作為mask傳入紋理,接着再進行高斯模糊,mask标記過的地方就不進行高斯模糊,這樣就可以形成最終的效果。效果圖如下所示:

unity螢幕特效綜述6 景深效果

主要分成2個步驟:

1.提取Mask

2.高斯模糊

1.提取Mask

首先需要通過深度紋理擷取每個像素的深度,然後判斷深度範圍,如果在範圍之外,則直接clip掉,否則直接return 白色

fixed4 FODFrag(FODv2f i) :SV_Target
{
       float linearDepth =  Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth));
       clip(linearDepth - _MinDistance);
       clip(_MaxDistance-linearDepth);
       return fixed4(1, 1, 1, 1.0);
}
           

盡量不要使用if去判斷,shader裡面執行選擇語句效率很低

2.高斯模糊

fixed mask = tex2D(_BlurMask,i.uv[0]).r;
                     
float weight[3] = { 0.4026,0.2442,0.05045 };
fixed3 sum2 = tex2D(_MainTex,i.uv[0]);
fixed3 sum = sum2 * weight[0];
sum += tex2D(_MainTex,i.uv[1])*weight[1];
sum += tex2D(_MainTex,i.uv[2])*weight[1];
sum += tex2D(_MainTex,i.uv[3])*weight[2];
sum += tex2D(_MainTex,i.uv[4])*weight[2];
              
fixed3 finalAns = lerp(sum,sum2,mask);
return fixed4(finalAns,1.0);
           

基本上就是魔改前面的代碼,如果該像素的mask是白色,那麼就不對其進行模糊。

完整shader代碼:

Shader "Hidden/FieldOfDepth"
{
       Properties
       {
              _MainTex("Texture", 2D) = "white" {}
              _BlurSize("Blur Size",Float) = 1.0
              _MinDistance("Min Distance",Range(0,1)) = 0.01
              _MaxDistance("Max Distance",Range(0,1)) = 0.2
              _BlurMask("Blur Mask",2D) = "white"{}
       }
       SubShader
       {
              CGINCLUDE
              #include "UnityCG.cginc"
              sampler2D _MainTex;
              half4 _MainTex_TexelSize;
              float _BlurSize;
              uniform float _MinDistance;
              uniform float _MaxDistance;
              sampler2D _CameraDepthTexture;
              sampler2D _BlurMask;
              struct v2f
              {
                     float4 pos : SV_POSITION;
                     half2 uv[5] : TEXCOORD0;
                     
              };
              v2f GaussianBlurVerticle(appdata_img v)
              {
                     v2f o;
                     o.pos = UnityObjectToClipPos(v.vertex);
                     half2 uv = v.texcoord;
                     o.uv[0] = uv;
                     o.uv[1] = uv + half2(_MainTex_TexelSize.x*_BlurSize,0);
                     o.uv[2] = uv - half2(_MainTex_TexelSize.x*_BlurSize,0);
                     o.uv[3] = uv + half2(_MainTex_TexelSize.x*_BlurSize * 2,0);
                     o.uv[4] = uv - half2(_MainTex_TexelSize.x*_BlurSize * 2,0);
       
                     return o;
              }
              v2f GaussianBlurHorizontal(appdata_img v)
              {
                     v2f o;
                     o.pos = UnityObjectToClipPos(v.vertex);
                     half2 uv = v.texcoord;
                     o.uv[0] = uv;
                     o.uv[1] = uv + half2(0, _MainTex_TexelSize.y*_BlurSize);
                     o.uv[2] = uv - half2(0, _MainTex_TexelSize.y*_BlurSize);
                     o.uv[3] = uv + half2(0, _MainTex_TexelSize.y*_BlurSize * 2);
                     o.uv[4] = uv - half2(0, _MainTex_TexelSize.y*_BlurSize * 2);
                     
                     return o;
              }
              fixed4 GaussianBlurFrag(v2f i) : SV_Target
              {
                     fixed mask = tex2D(_BlurMask,i.uv[0]).r;
                     
                     float weight[3] = { 0.4026,0.2442,0.05045 };
                     fixed3 sum2 = tex2D(_MainTex,i.uv[0]);
                     fixed3 sum = sum2 * weight[0];
                     sum += tex2D(_MainTex,i.uv[1])*weight[1];
                     sum += tex2D(_MainTex,i.uv[2])*weight[1];
                     sum += tex2D(_MainTex,i.uv[3])*weight[2];
                     sum += tex2D(_MainTex,i.uv[4])*weight[2];
              
                     fixed3 finalAns = lerp(sum,sum2,mask);
                     return fixed4(finalAns,1.0);
              }
              struct FODv2f
              {
                     float4 pos : SV_POSITION;
                     float2 uv : TEXCOORD0;
                     float2 uv_depth : TEXCOORD1;
              };
              FODv2f FODVert(appdata_img v)
              {
                     FODv2f o;
                     o.uv.xy = v.texcoord;
                     o.uv_depth = v.texcoord.xy;
                     o.pos = UnityObjectToClipPos(v.vertex);
                     return o;
              }
              fixed4 FODFrag(FODv2f i) :SV_Target
              {
                     float linearDepth =  Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth));
                     clip(linearDepth - _MinDistance);
                     clip(_MaxDistance-linearDepth);
                     return fixed4(1, 1, 1, 1.0);
              }
              ENDCG
              ZTest Always Cull Off ZWrite Off
              Pass
              {
                     CGPROGRAM
                     #pragma vertex FODVert
                     #pragma fragment FODFrag
                     ENDCG
              }
              Pass
              {
                     CGPROGRAM
                     #pragma vertex GaussianBlurVerticle
                     #pragma fragment GaussianBlurFrag
                     ENDCG
              }
              Pass
              {
                     CGPROGRAM
                     #pragma vertex GaussianBlurHorizontal
                     #pragma fragment GaussianBlurFrag
                     ENDCG
              }
       }
       FallBack "Diffuse"
}
           
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DepthOfField : PostEffectsBase
{
    public Shader DepthOfFieldShader;
    private Material DepthOfFieldMaterial;
    private Camera DepthOfFieldCamera;
    [Range(0,1)]
    public float NearDistance;
    [Range(0,1)]
    public float FarDistance;
    private void OnEnable()
    {
        camera.depthTextureMode = DepthTextureMode.Depth;
    }
    public Camera camera
    {
        get { return Camera.main; }
    }
    public Transform cameraTransform
    {
        get { return camera.GetComponent<Transform>(); }
    }
    public Material material
    {
        get { DepthOfFieldMaterial=  CheckShaderAndCreateMaterial(DepthOfFieldShader, DepthOfFieldMaterial);
            return DepthOfFieldMaterial;
        }
    }
    [Range(0, 4)]
    public int iteration = 3;
    [Range(0.2f, 3.0f)]
    public float blurSpread = 0.6f;
    [Range(1, 8)]
    public int dowmSample = 1;
    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (material != null)
        {
            material.SetFloat("_MinDistance",NearDistance);
            material.SetFloat("_MaxDistance", FarDistance);
            RenderTexture buffer0 =  RenderTexture.GetTemporary(source.width,source.height,0);
            Graphics.Blit(source, buffer0, material,0);
            material.SetTexture("_BlurMask", buffer0);
            RenderTexture.ReleaseTemporary(buffer0);
            int rtW = source.width / dowmSample;
            int rtH = source.height / dowmSample;
            buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
            Graphics.Blit(source, buffer0);
            for (int i = 0; i < iteration; i++)
            {
                material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
                RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
                Graphics.Blit(buffer0, buffer1, material, 1);
                RenderTexture.ReleaseTemporary(buffer0);
                buffer0 = buffer1;
                buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
                Graphics.Blit(buffer0, buffer1, material, 2);
                buffer0 = buffer1;             
            }
            RenderTexture.ReleaseTemporary(buffer0);
            Graphics.Blit(buffer0,destination);
        }
        else
            Graphics.Blit(source, destination);
    }
}
           

參考其他人的部落格,他們的實作方法跟我的還是有那麼一點差別的,比我多考慮了漸變的問題,在焦距附近的模糊程度應該是漸變的,我的是要麼模糊,要麼不模糊,效果不大好。

其方法如下:

擷取兩張圖,一張高斯模糊的圖,一張原圖,在兩張圖之間進行內插補點。

下面是他部落格裡的代碼,不過講道理,0和1好像寫反了。。我把它修正後才能正常運作。

fixed4 final = (depth <= _focalDistance) ? ori : lerp(ori, blur, clamp((depth - _focalDistance) * _farBlurScale, 0, 1));
//上面的結果,再進行一次計算,如果depth大于焦點的物體,使用上面的結果和模糊圖像內插補點,得到近景模糊效果
final = (depth > _focalDistance) ? final : lerp(ori, blur, clamp((_focalDistance - depth) * _nearBlurScale, 0, 1));
           
unity螢幕特效綜述6 景深效果

效果的話差别不是很大,完整代碼如下:

Shader "Hidden/FieldOfDepth"
{
       Properties
       {
              _MainTex("Texture", 2D) = "white" {}
              _BlurSize("Blur Size",Float) = 1.0
              _MinDistance("Min Distance",Range(0,100)) = 0.01
              _MaxDistance("Max Distance",Range(0,100)) = 0.2
              _Focus("Focus",Float) = 1
              _BlurMask("Blur Mask",2D) = "white"{}
       }
       SubShader
       {
              CGINCLUDE
              #include "UnityCG.cginc"
              sampler2D _MainTex;
              half4 _MainTex_TexelSize;
              float _BlurSize;
              uniform float _MinDistance;
              uniform float _MaxDistance;
              sampler2D _CameraDepthTexture;
              float _Focus;
              sampler2D _BlurMask;
              struct v2f
              {
                     float4 pos : SV_POSITION;
                     half2 uv[5] : TEXCOORD0;
                     
              };
              v2f GaussianBlurVerticle(appdata_img v)
              {
                     v2f o;
                     o.pos = UnityObjectToClipPos(v.vertex);
                     half2 uv = v.texcoord;
                     o.uv[0] = uv;
                     o.uv[1] = uv + half2(_MainTex_TexelSize.x*_BlurSize,0);
                     o.uv[2] = uv - half2(_MainTex_TexelSize.x*_BlurSize,0);
                     o.uv[3] = uv + half2(_MainTex_TexelSize.x*_BlurSize * 2,0);
                     o.uv[4] = uv - half2(_MainTex_TexelSize.x*_BlurSize * 2,0);
       
                     return o;
              }
              v2f GaussianBlurHorizontal(appdata_img v)
              {
                     v2f o;
                     o.pos = UnityObjectToClipPos(v.vertex);
                     half2 uv = v.texcoord;
                     o.uv[0] = uv;
                     o.uv[1] = uv + half2(0, _MainTex_TexelSize.y*_BlurSize);
                     o.uv[2] = uv - half2(0, _MainTex_TexelSize.y*_BlurSize);
                     o.uv[3] = uv + half2(0, _MainTex_TexelSize.y*_BlurSize * 2);
                     o.uv[4] = uv - half2(0, _MainTex_TexelSize.y*_BlurSize * 2);
                     
                     return o;
              }
              fixed4 GaussianBlurFrag(v2f i) : SV_Target
              {
                     float weight[3] = { 0.4026,0.2442,0.05045 };
                     fixed3 sum = tex2D(_MainTex,i.uv[0])* weight[0];
                     sum += tex2D(_MainTex,i.uv[1])*weight[1];
                     sum += tex2D(_MainTex,i.uv[2])*weight[1];
                     sum += tex2D(_MainTex,i.uv[3])*weight[2];
                     sum += tex2D(_MainTex,i.uv[4])*weight[2];       
                     return fixed4(sum,1.0);
              }
              struct FODv2f
              {
                     float4 pos : SV_POSITION;
                     float2 uv : TEXCOORD0;
                     float2 uv_depth : TEXCOORD1;
                     float2 uv_blur : TEXCOORD2;
              };
              FODv2f FODVert(appdata_img v)
              {
                     FODv2f o;
                     o.uv = v.texcoord;
                     o.uv_depth = v.texcoord.xy;
                     o.pos = UnityObjectToClipPos(v.vertex);
                     o.uv_blur = v.texcoord;
                     return o;
              }
              fixed4 FODFrag(FODv2f i) :SV_Target
              {
                     float linearDepth =  Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth));
                     fixed4 ori = tex2D(_MainTex,i.uv);
                     fixed4 blur = tex2D(_BlurMask,i.uv_blur);
                     fixed4 finalAns = (linearDepth<= _Focus)? ori :  lerp(ori,blur,clamp((linearDepth - _Focus)*_MaxDistance,0,1));
                     finalAns = (linearDepth > _Focus) ? finalAns :  lerp(ori,blur,clamp((_Focus - linearDepth)*_MinDistance,0,1));
                     return finalAns;
              }
              ENDCG
              ZTest Always Cull Off ZWrite Off
              Pass
              {
                     CGPROGRAM
                     #pragma vertex FODVert
                     #pragma fragment FODFrag
                     ENDCG
              }
              Pass
              {
                     CGPROGRAM
                     #pragma vertex GaussianBlurVerticle
                     #pragma fragment GaussianBlurFrag
                     ENDCG
              }
              Pass
              {
                     CGPROGRAM
                     #pragma vertex GaussianBlurHorizontal
                     #pragma fragment GaussianBlurFrag
                     ENDCG
              }
       }
       FallBack "Diffuse"
}
           
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DepthOfField : PostEffectsBase
{
    public Shader DepthOfFieldShader;
    private Material DepthOfFieldMaterial;
    private Camera DepthOfFieldCamera;
    [Range(0,100)]
    public float NearDistance;
    [Range(0,100)]
    public float FarDistance;
    private void OnEnable()
    {
        camera.depthTextureMode = DepthTextureMode.Depth;
    }
    public Camera camera
    {
        get { return Camera.main; }
    }
    public Transform cameraTransform
    {
        get { return camera.GetComponent<Transform>(); }
    }
    public Material material
    {
        get { DepthOfFieldMaterial=  CheckShaderAndCreateMaterial(DepthOfFieldShader, DepthOfFieldMaterial);
            return DepthOfFieldMaterial;
        }
    }
    [Range(0, 4)]
    public int iteration = 3;
    [Range(0.2f, 3.0f)]
    public float blurSpread = 0.6f;
    [Range(1, 8)]
    public int dowmSample = 1;
    public float Focus;
    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        if (material != null)
        {
            material.SetFloat("_MinDistance",NearDistance);
            material.SetFloat("_MaxDistance", FarDistance);
            material.SetFloat("_Focus",Focus);
            int rtW = source.width / dowmSample;
            int rtH = source.height / dowmSample;
            RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
            Graphics.Blit(source, buffer0);
            for (int i = 0; i < iteration; i++)
            {
                material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
                RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
                Graphics.Blit(buffer0, buffer1, material, 1);
                RenderTexture.ReleaseTemporary(buffer0);
                buffer0 = buffer1;
                buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
                Graphics.Blit(buffer0, buffer1, material, 2);
                buffer0 = buffer1;             
            }
            material.SetTexture("_BlurMask", buffer0);
            RenderTexture.ReleaseTemporary(buffer0);
            Graphics.Blit(source, destination,material,0);
        }
        else
            Graphics.Blit(source, destination);
    }
}
           

繼續閱讀