效果
原理:
开启摄像机的深度模式,将深度保存到一张名为_CameraDepthTexture(Unity5.0之后才有)内置的纹理中.
如果深度在焦点范围内就用原图,否则就用模糊图。
Shader:
Shader "DepthOfFiled" { Properties { _MainTex ("Texture", 2D) = "white" {} _BlurSize ("Blur Size", Float) = 0.1 } CGINCLUDE #include "UnityCG.cginc" sampler2D _MainTex; sampler2D _BlurTex; sampler2D _CameraDepthTexture; uniform half4 _MainTex_TexelSize; uniform float _BlurSize; uniform float _FocusDistance; uniform float _FocusRange; static const half weight[4] = {0.0205, 0.0855, 0.232, 0.324}; static const half4 coordOffset = half4(1.0h,1.0h,-1.0h,-1.0h); struct v2f_blurSGX { float4 pos:SV_POSITION; half2 uv:TEXCOORD0; half4 uvoff[3]:TEXCOORD1; }; struct v2f_dof { float4 pos:SV_POSITION; half2 uv:TEXCOORD0; }; v2f_blurSGX vert_BlurHorizontal(appdata_img v) { v2f_blurSGX o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.uv = v.texcoord.xy; half2 offs = _MainTex_TexelSize.xy*half2(1,0)*_BlurSize; o.uvoff[0] = v.texcoord.xyxy+offs.xyxy*coordOffset*3; o.uvoff[1] = v.texcoord.xyxy+offs.xyxy*coordOffset*2; o.uvoff[2] = v.texcoord.xyxy+offs.xyxy*coordOffset; return o; } v2f_blurSGX vert_BlurVertical(appdata_img v) { v2f_blurSGX o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.uv = v.texcoord.xy; half2 offs = _MainTex_TexelSize.xy*half2(0,1)*_BlurSize; o.uvoff[0] = v.texcoord.xyxy+offs.xyxy*coordOffset*3; o.uvoff[1] = v.texcoord.xyxy+offs.xyxy*coordOffset*2; o.uvoff[2] = v.texcoord.xyxy+offs.xyxy*coordOffset; return o; } fixed4 frag_Blur(v2f_blurSGX i):SV_Target { fixed4 c = tex2D(_MainTex,i.uv)*weight[3]; for(int idx=0; idx<3; idx++) { c+=tex2D(_MainTex,i.uvoff[idx].xy)*weight[idx]; c+=tex2D(_MainTex,i.uvoff[idx].zw)*weight[idx]; } return c; } v2f_dof vert_Dof(appdata_img v) { v2f_dof o; o.pos = mul(UNITY_MATRIX_MVP,v.vertex); o.uv = v.texcoord.xy; return o; } fixed4 frag_Dof(v2f_dof i):SV_Target { fixed4 c = tex2D(_MainTex,i.uv); fixed4 b = tex2D(_BlurTex,i.uv); float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv); //将深度值转化到01线性空间 depth = Linear01Depth(depth); return lerp(c,b,saturate(sign(abs(depth-_FocusDistance)-_FocusRange))); } ENDCG SubShader { // No culling or depth //Cull Off ZWrite Off //Pass 0 Pass { ZTest Always CGPROGRAM #pragma vertex vert_BlurHorizontal #pragma fragment frag_Blur ENDCG } //Pass 1 Pass { ZTest Always CGPROGRAM #pragma vertex vert_BlurVertical #pragma fragment frag_Blur ENDCG } //Pass 2 Pass { ZTest Off Cull Off ZWrite Off Fog{ Mode Off } ColorMask RGBA CGPROGRAM #pragma vertex vert_Dof #pragma fragment frag_Dof ENDCG } } }
C#代码
using UnityEngine; using System.Collections; [ExecuteInEditMode] [RequireComponent(typeof(Camera))] public class DepthOfFieldPostEffect : MonoBehaviour { public Material Mat;public float BlurSize =10; public int interator = 2; [Range(0,1)] public float FocusDistance = 0.5f; [Range(0,0.5f)] public float FocusRange=0.1f; void OnEnable() { GetComponent<Camera> ().depthTextureMode = DepthTextureMode.Depth; } void OnDisable() { GetComponent<Camera> ().depthTextureMode = ~DepthTextureMode.Depth; } // Use this for initialization void Start () { } // Update is called once per frame void Update () { } void OnRenderImage(RenderTexture src,RenderTexture dest) { var w = src.width / 8; var h = src.height / 8; var tmp1 = RenderTexture.GetTemporary (w, h); var tmp2 = RenderTexture.GetTemporary (w, h); Mat.SetFloat ("_BlurSize", BlurSize); Mat.SetFloat ("_FocusDistance", FocusDistance); Mat.SetFloat ("_FocusRange", FocusRange); Graphics.Blit (src, tmp1); for (int i = 0; i < interator; i++) { Graphics.Blit (tmp1, tmp2, Mat,0); Graphics.Blit (tmp2, tmp1, Mat,1); } Mat.SetTexture ("_BlurTex", tmp1); Graphics.Blit (src, dest,Mat,2); RenderTexture.ReleaseTemporary (tmp1); RenderTexture.ReleaseTemporary (tmp2); } }