从这一部分开始,我的工作从学习别人的代码要逐渐转到自己敲代码了,中间跨越还是挺大的,景深效果的原理看似简单,也写了快一天时间了。
下面我简单说明两种景深效果的写法,一种是我自己探索着写的,另一种是参考别人的博客写的(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);
}
}