Unity Shader 屏幕後效果——景深

景深效果的原理是,在攝像機的近裁剪平面和遠裁剪平面之間能夠設置一個焦距,在這個距離所在的平面上的物體最爲清晰,而這個距離以前或以後的物體成像是一種模糊狀態(根據距離逐漸模糊,最終達到最爲模糊的狀態)。html

在shader中,須要一張清晰的場景圖和一張模糊的場景圖,能夠經過每一個像素相對焦距的距離來斷定這個像素最終的清晰程度。在清晰圖和模糊圖之間作關於深度變化的插值運算。app

 

關於攝像機的近裁剪平面和遠裁剪平面,能夠直接在Camera組件的屬性面板中調節(默認的遠裁剪平面距離是1000):函數

 

模糊圖能夠直接採用高斯模糊實現,具體參考:spa

http://www.javashuo.com/article/p-bnxkqsed-he.html3d

清晰圖也就是未通過任何處理的渲染圖,直接就能夠獲得;code

關鍵問題在於如何獲得每一個像素在攝像機近裁剪平面和遠裁剪平面以前的位置,而這個位置信息就是渲染紋理的深度值。htm

 

在Unity中,能夠不用本身計算深度值,Unity提供了直接提取攝像機深度值的宏:blog

float depth=SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth);ip

depth=Linear01Depth(depth);get

並能夠直接利用內置函數將其轉化爲線性深度值(對於透視攝像機來講,本來的深度值是非線性的),以方便後續的插值計算。

 

在提取攝像機深度值以前,須要將攝像機的深度紋理模式設置爲Depth,同時在Shader中提早聲明_CameraDepthTexture變量:

MyCamera.depthTextureMode |= DepthTextureMode.Depth;

sampler2D _CameraDepthTexture;

 

C#控制腳本以下:

 1 using UnityEngine;
 2 
 3 public class DepthOfFieldCrtl : ScreenEffectBase
 4 {
 5     private const string _BlurSize = "_BlurSize";
 6     private const string _FocusDistance = "_FocusDistance";
 7     private const string _BlurTex = "_BlurTex";
 8 
 9     //用於控制高斯模糊的參數
10     [Range(.2f,3)]
11     public float blurSize = .6f;
12     [Range(0, 4)]
13     public int iterations = 3;
14     [Range(1, 8)]
15     public int downSample = 2;
16 
17     //歸一化後的焦距,0表示焦距處於攝像機近裁剪平面,1表示處於遠裁剪平面;爲了更好的調整效果,能夠將[0,1]範圍適當擴大
18     [Range(-.02f, 1.02f)]
19     public float focusDistance = .5f;
20 
21     private Camera myCamera=null;
22     public Camera MyCamera
23     {
24         get
25         {
26             if (myCamera == null)
27             {
28                 myCamera = GetComponent<Camera>();
29             }
30             return myCamera;
31         }
32     }
33 
34     //開啓攝像機深度模式
35     private void OnEnable()
36     {
37         MyCamera.depthTextureMode |= DepthTextureMode.Depth;
38     }
39 
40     //不用時還原
41     private void OnDisable()
42     {
43         MyCamera.depthTextureMode &= ~DepthTextureMode.Depth;
44     }
45 
46     private void OnRenderImage(RenderTexture source, RenderTexture destination)
47     {
48         if (Material)
49         {
50             //傳遞焦距
51             Material.SetFloat(_FocusDistance, focusDistance);
52 
53             var w = source.width / downSample;
54             var h = source.height / downSample;
55 
56             var buffer0 = RenderTexture.GetTemporary(w, h, 0);
57             buffer0.filterMode = FilterMode.Bilinear;
58 
59             Graphics.Blit(source, buffer0);
60 
61             //前兩個Pass作高斯模糊處理,結果保存在buffer0中
62             for(int i = 0; i < iterations; i++)
63             {
64                 Material.SetFloat(_BlurSize, blurSize*i+1.0f);
65 
66                 var buffer1 = RenderTexture.GetTemporary(w, h, 0);
67                 buffer1.filterMode = FilterMode.Bilinear;
68                 Graphics.Blit(buffer0, buffer1, Material, 0);
69                 RenderTexture.ReleaseTemporary(buffer0);
70 
71                 buffer0 = RenderTexture.GetTemporary(w, h, 0);
72                 Graphics.Blit(buffer1, buffer0, Material, 1);
73                 RenderTexture.ReleaseTemporary(buffer1);
74             }
75 
76             //最後一個Pass用原圖像和模糊圖(buffer0)進行關於深度的插值計算,具體見Shader腳本
77             Material.SetTexture(_BlurTex, buffer0);
78             Graphics.Blit(source, destination,Material,2);
79             RenderTexture.ReleaseTemporary(buffer0);
80         }
81         else
82             Graphics.Blit(source, destination);
83 
84     }
85 }

Shader腳本:

 1 Shader "MyUnlit/DepthOfFiled"
 2 {
 3     Properties
 4     {
 5         _MainTex ("Texture", 2D) = "white" {}
 6     }
 7     SubShader
 8     {
 9         CGINCLUDE
10 
11         #include "UnityCG.cginc"
12 
13         sampler2D _MainTex;
14         sampler2D _BlurTex;
15         //聲明攝像機深度
16         sampler2D _CameraDepthTexture;
17         half4 _MainTex_TexelSize;
18         fixed _FocusDistance;
19 
20         struct appdata
21         {
22             float4 vertex : POSITION;
23             float2 uv : TEXCOORD0;
24         };
25 
26         struct v2f
27         {
28             half4 uv:TEXCOORD0;
29             half2 uv_depth:TEXCOORD1;
30             float4 vertex:SV_POSITION;
31         };
32 
33         v2f vert(appdata v)
34         {
35             v2f o;
36             o.vertex=UnityObjectToClipPos(v.vertex);
37 
38             //xy存儲清晰紋理,zw存儲模糊紋理
39             o.uv.xy=v.uv;
40             o.uv.zw=v.uv;
41             o.uv_depth=v.uv;
42 
43             //須要對模糊紋理和深度紋理進行平臺差別化處理
44             #if UNITY_UV_STARTS_AT_TOP
45             if(_MainTex_TexelSize.y<0){
46                 o.uv.w=1.0-o.uv.w;
47                 o.uv_depth.y=1.0-o.uv_depth.y;
48             }
49             #endif
50 
51             return o;
52         }
53 
54         fixed4 frag(v2f i):SV_Target
55         {
56             fixed4 col = tex2D(_MainTex, i.uv.xy);
57             fixed4 bcol=tex2D(_BlurTex,i.uv.zw);
58 
59             //獲得深度值併線性歸一化
60             float depth=SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth);
61             depth=Linear01Depth(depth);
62 
63             //由於焦距也是設置的歸一化的值,直接相減取絕對值即獲得模糊係數,返回插值結果
64             fixed bVa=abs(depth-_FocusDistance);
65             return lerp(col,bcol,bVa);
66         }
67 
68         ENDCG
69 
70         ZTest Always
71         Cull Off
72         ZWrite Off
73 
74         //前兩個Pass高斯模糊
75         UsePass "MyUnlit/GaussianBlur/GAUSSIANBLUR_V"
76 
77         UsePass "MyUnlit/GaussianBlur/GAUSSIANBLUR_H"
78 
79         Pass
80         {
81             CGPROGRAM
82 
83             #pragma vertex vert
84             #pragma fragment frag
85 
86             ENDCG
87         }
88     }
89     FallBack Off
90 }

效果以下:

(注意調整攝像機近裁剪平面和遠裁剪平面的值處於合適的區間)

相關文章
相關標籤/搜索