從這一部分開始,我的工作從學習別人的代碼要逐漸轉到自己敲代碼了,中間跨越還是挺大的,景深效果的原理看似簡單,也寫了快一天時間了。
下面我簡單說明兩種景深效果的寫法,一種是我自己探索着寫的,另一種是參考別人的博客寫的(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);
}
}