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

從這一部分開始,我的工作從學習別人的代碼要逐漸轉到自己敲代碼了,中間跨越還是挺大的,景深效果的原理看似簡單,也寫了快一天時間了。
下面我簡單說明兩種景深效果的寫法,一種是我自己探索着寫的,另一種是參考別人的博客寫的(https://blog.csdn.net/puppet_master/article/details/52819874),不得不承認,別人的方法還是要成熟一點。

景深效果給我的直觀理解就是,在距離攝像機的某個距離範圍內,圖像是清晰的,在那個範圍外的圖像都是模糊的。所以,結合前面所學習的內容,只需要首先提取出來距離攝像機[mindistance,maxdistance]的像素,然後作爲mask傳入紋理,接着再進行高斯模糊,mask標記過的地方就不進行高斯模糊,這樣就可以形成最終的效果。效果圖如下所示:
在這裏插入圖片描述主要分成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));

在這裏插入圖片描述
效果的話差別不是很大,完整代碼如下:

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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章