景深(Depth Of Field),是指在摄影机镜头或其他成像器前沿能够取得清晰图像的成像所测定的被摄物体前后距离范围。
而景深效果是指在焦距之外的地方都是模糊的,只有焦距的地方清晰。
通过采样摄像机的深度纹理,得到当前屏幕纹理每个点的深度,使用深度值与焦距值得到每个点到焦距的距离,并使用距离来对原图像和高斯模糊处理过的图像进行插值并输出即可。
Shader代码
Shader "MyShaderTest/2_DepthOfField"
{
Properties
{
_MainTex ("Base (RGB)", 2D) = "white" {}
_BlurMainTex("Bloom (RGB)", 2D) = "black" {}
_LuminanceThreshold ("Luminance Threshold", Float) = 0.5
_BlurSize ("Blur Size", Float) = 1.0
_FocusStart("FocusStart", Float) = 0.4
_FocusEnd("FocusEnd", Float) = 0.6
}
SubShader
{
ZTest Always Cull Off ZWrite Off
CGINCLUDE
#include "UnityCG.cginc"
sampler2D _MainTex;
half4 _MainTex_TexelSize;
sampler2D _CameraDepthTexture;
sampler2D _BlurMainTex;
float _BlurSize;
float _FocusStart;
float _FocusEnd;
struct v2fBlur
{
float4 pos : SV_POSITION;
half2 uv[5] : TEXCOORD0;
};
//垂直方向(Y轴方向)的高斯模糊
v2fBlur vertBlurVertical(appdata_img v)
{
v2fBlur o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;
o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
return o;
}
//水平方向(X轴方向)的高斯模糊
v2fBlur vertBlurHorizontal(appdata_img v)
{
v2fBlur o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 uv = v.texcoord;
o.uv[0] = uv;
o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
return o;
}
fixed4 fragBlur(v2fBlur i) : SV_Target
{
float weight[3] = { 0.4026,0.2442,0.0545 };
fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
for (int it = 1; it < 3; it++)
{
sum += tex2D(_MainTex, i.uv[it * 2 - 1]).rgb * weight[it];
sum += tex2D(_MainTex, i.uv[2 * it]).rgb * weight[it];
}
return fixed4(sum, 1.0);
}
struct v2f
{
float4 vertex : SV_POSITION;
half2 uv : TEXCOORD0;
};
v2f vertDepthOfField(appdata_img v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
return o;
}
fixed4 fragDepthOfField(v2f i) : SV_Target
{
float linearDepth = Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv));
float distance = 0;
if (linearDepth < _FocusStart)
{
distance = abs(linearDepth - _FocusStart);
}
else if (linearDepth > _FocusEnd)
{
distance = abs(linearDepth - _FocusEnd);
}
half4 color = tex2D(_MainTex,i.uv);
half4 blurColor = tex2D(_BlurMainTex, i.uv);
color = lerp(color, blurColor, distance);
return fixed4(color.rgb,1);
}
ENDCG
Pass
{
CGPROGRAM
#pragma vertex vertBlurVertical
#pragma fragment fragBlur
ENDCG
}
Pass
{
CGPROGRAM
#pragma vertex vertBlurHorizontal
#pragma fragment fragBlur
ENDCG
}
Pass
{
CGPROGRAM
#pragma vertex vertDepthOfField
#pragma fragment fragDepthOfField
ENDCG
}
}
FallBack Off
}
挂在Camera上的C#脚本
using UnityEngine;
[ExecuteInEditMode]
public class DepthOfField : PostEffectsBase
{
public Shader shader;
private Material _material = null;
public Material Material
{
get
{
if(_material == null)
_material = CheckShaderAndMaterial(shader,_material);
return _material;
}
}
private Camera _camera;
public Camera Camera
{
get
{
if (_camera == null)
_camera = GetComponent<Camera>();
return _camera;
}
}
private void OnEnable()
{
Camera.depthTextureMode |= DepthTextureMode.Depth;
}
[Range(1f, 4.0f)]
public int blurSpread = 2;
[Range(0.01f, 1.0f)]
public float focusStart = 0.1f;
[Range(0.01f, 1.0f)]
public float focusEnd = 0.2f;
[Range(1, 4)]
public int iterations = 2;
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (Material != null)
{
int rtW = source.width;
int rtH = source.height;
Material.SetFloat("_FocusStart", focusStart);
Material.SetFloat("_FocusEnd", focusEnd);
RenderTexture buffer0 = RenderTexture.GetTemporary(rtW, rtH, 0);
buffer0.filterMode = FilterMode.Bilinear;
Graphics.Blit(source, buffer0);
for (int i = 0; i < iterations; i++)
{
Material.SetFloat("_BlurSize", 1.0f + i * blurSpread);
RenderTexture buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
Graphics.Blit(buffer0, buffer1, Material, 0);
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
buffer1 = RenderTexture.GetTemporary(rtW, rtH, 0);
Graphics.Blit(buffer0, buffer1, Material, 1);
RenderTexture.ReleaseTemporary(buffer0);
buffer0 = buffer1;
}
Material.SetTexture("_BlurMainTex", buffer0);
Graphics.Blit(source, destination,Material,2);
RenderTexture.ReleaseTemporary(buffer0);
}
else
{
Debug.Log("没有shader,没法干活儿");
Graphics.Blit(source, destination);
}
}
}