景深效果的原理是,在攝像機的近裁剪平面和遠裁剪平面之間能夠設置一個焦距,在這個距離所在的平面上的物體最爲清晰,而這個距離以前或以後的物體成像是一種模糊狀態(根據距離逐漸模糊,最終達到最爲模糊的狀態)。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 }
效果以下:
(注意調整攝像機近裁剪平面和遠裁剪平面的值處於合適的區間)