学习教程来自:【技术美术百人计划】图形 4.5 Dof景深基础
笔记
1. 什么是景深
相机对焦点前后相对清晰的成像范围
2. 景深的作用
突出表达
3. 移动端景深效果实现
3.1 制作思路
在后处理阶段,制作mask,分别渲染模糊场景和正常场景,再合并效果
3.2 原图模糊处理
在OnrenderImage对MainTex中的纹理模糊,传值给BlurTex,再进行混合
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag // 顶点着色器和片源着色器声明
#include "UnityCG.cginc"
struct appdata{ // 顶点着色器输入结构体,位置和UV
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f{ // 顶点着色器输出结构体,位置和UV
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _BlurOffset; // 模糊偏移
v2f vert (appdata v){
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex); // 对象空间转换到裁剪空间
o.uv = v.uv;
return o;
}
half4 frag (v2f i) : SV_Target{
//----------------------------------- 高斯模糊处理 -------------------------------------//
half2 uv1 = i.uv + _BlurOffset.xy * half2(1,0) * -2;
half2 uv2 = i.uv + _BlurOffset.xy * half2(1,0) * -1;
half2 uv3 = i.uv;
half2 uv4 = i.uv + _BlurOffset.xy * half2(1,0) * 1;
half2 uv5 = i.uv + _BlurOffset.xy * half2(1,0) * 2;
half2 uv6 = i.uv + _BlurOffset.xy * half2(0,1) * -2;
half2 uv7 = i.uv + _BlurOffset.xy * half2(0,1) * -1;
half2 uv8 = i.uv;
half2 uv9 = i.uv + _BlurOffset.xy * half2(0,1) * 1;
half2 uv10 = i.uv + _BlurOffset.xy * half2(0,1) * 2;
half4 s = 0;
s += tex2D(_MainTex, uv1) * 0.05;
s += tex2D(_MainTex, uv2) * 0.25;
s += tex2D(_MainTex, uv3) * 0.40;
s += tex2D(_MainTex, uv4) * 0.25;
s += tex2D(_MainTex, uv5) * 0.05;
s += tex2D(_MainTex, uv6) * 0.05;
s += tex2D(_MainTex, uv7) * 0.25;
s += tex2D(_MainTex, uv8) * 0.40;
s += tex2D(_MainTex, uv9) * 0.25;
s += tex2D(_MainTex, uv10) * 0.05;
s /= 2;
//return half4(final_depth.xxx, 1);
return half4(s.rgb, 1); // 高斯模糊的效果对比
//return half4(1,1,1, 1); // 高斯模糊的效果对比
}
ENDCG
}
3.3 获得景深Mask
获取深度Texture,对比设置的焦点并计算景深范围,范围内的不做模糊处理,值为0,范围外的渐变的规整到0-1并乘以smooth使其更平滑
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag // 顶点着色器和片源着色器声明
#include "UnityCG.cginc"
struct appdata{ // 顶点着色器输入结构体,位置和UV
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f{ // 顶点着色器输出结构体,位置和UV
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
sampler2D _BlurTex; // 模糊后的纹理
sampler2D _CameraDepthTexture; // 相机深度纹理
float4 _BlurOffset; // 模糊范围偏移
float _FocusDistance, _DepthOfField, _DofSmoothRange; // 焦点距离,景深,光滑过度
float _Step;
v2f vert (appdata v){
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex); // 对象空间转换到裁剪空间
o.uv = v.uv;
return o;
}
half4 frag (v2f i) : SV_Target{
half4 col = tex2D(_MainTex, i.uv); // 原图颜色
half4 blur_col = tex2D(_BlurTex, i.uv); // 模糊后的颜色
// half depth = Linear01Depth(tex2D(_CameraDepthTexture, i.uv)); // 避免远裁面的值对景深效果的影响
half depth = Linear01Depth(tex2D(_CameraDepthTexture, i.uv)).r * _ProjectionParams.z * _Step;//避免远裁面的值对景深效果的影响
float focusNear = _FocusDistance - _DepthOfField; // 近焦距点
float focusFar = _FocusDistance + _DepthOfField; // 远焦距点
half final_depth = 0;
if((depth>=focusNear)&&(depth<=focusFar)); // 在景深范围内的点不做模糊
else {
if(depth<focusNear){ // 在景深范围之外的点,全部归到0-1
final_depth = saturate(abs(focusNear-depth) * _DofSmoothRange); // 加入smooth使过度更平滑
}else{
final_depth = saturate(abs(focusFar-depth) * _DofSmoothRange);
}
}
half4 final_col = lerp(col, blur_col, final_depth*1.2); // 使用Mask进行混合
//return half4(final_depth,final_depth,final_depth, 1);
return half4(final_col.rgb, 1);
//return half4(depth.xxx, 1);
// return half4(col.rgb, 1);
}
ENDCG
}
3.4 脚本部分
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DOF : MonoBehaviour
{
public Material mat;
// Start is called before the first frame update
[Range(1, 4)]
public int _Iteration = 2; // 迭代次数
[Range(0, 15)]
public float _BlurRadius = 5; // 模糊半径
[Range(0, 10)]
public float _DownSample = 2; // 下采样次数
[Range(0, 10)]
public float _DepthOfField = 1.0f; // 景深
public float _FocusDistance = 1; // 焦距
void Start()
{
if (mat == null || SystemInfo.supportsImageEffects == false || mat.shader == null || mat.shader.isSupported == false){
enabled = false; // 判断材质和shader是否为空,是否被支持,来决定是否启用
return ;
}
}
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
{
mat.SetFloat("_DepthOfField", _DepthOfField);
mat.SetFloat("_FocusDistance", _FocusDistance);
int width = (int)(src.width / _DownSample); // 下采样减少计算量
int height = (int)(src.height / _DownSample);
//----------------------------- 高斯模糊 Pass 0 ----------------------------//
mat.SetVector("_BlurOffset", new Vector4(_BlurRadius / width, _BlurRadius / height, 0, 0));
RenderTexture RT1 = RenderTexture.GetTemporary(width, height);
RenderTexture RT2 = RenderTexture.GetTemporary(width, height); // 创建2张RT交替处理
Graphics.Blit(src, RT1, mat, 0);
//Graphics.Blit(src, dest, mat, 1);
for (int i = 0; i < _Iteration; i++) // 每次迭代减少尺寸,降采样
{
RenderTexture.ReleaseTemporary(RT2);
width = width / 2;
height = height / 2;
RT2 = RenderTexture.GetTemporary(width, height);
Graphics.Blit(RT1, RT2, mat, 0);
width = width / 2;
height = height / 2;
RenderTexture.ReleaseTemporary(RT1);
RT1 = RenderTexture.GetTemporary(width, height);
Graphics.Blit(RT2, RT1, mat, 0);
}
for (int i = 0; i < _Iteration; i++) // 每次迭代放大尺寸,升采样
{
RenderTexture.ReleaseTemporary(RT2);
width = width * 2;
height = height * 2;
RT2 = RenderTexture.GetTemporary(width, height);
Graphics.Blit(RT1, RT2, mat, 0);
width = width * 2;
height = height * 2;
RenderTexture.ReleaseTemporary(RT1);
RT1 = RenderTexture.GetTemporary(width, height);
Graphics.Blit(RT2, RT1, mat, 0);
}
//------------------------------- 混合 Pass 1 ------------------------------//
mat.SetTexture("_BlurTex", RT1);
Graphics.Blit(src, dest, mat, 1); // 使用shader的第二个Pass混合原图和模糊
RenderTexture.ReleaseTemporary(RT1);
RenderTexture.ReleaseTemporary(RT2); // 释放
}
}
}
4. 高级景深效果思路扩展
4.1 颜色泄露
对焦区域的颜色被模糊到了背景中
解决:扩散滤波,规定模糊的范围
4.2 模糊不连续
前景区域的模糊不连续
解决:前景单独计算,制作一个Mask去融合背景
4.3 散景模拟(Bokeh)
为了模拟不同光源在景深下的效果
解决:修改滤波的公式
作业
1. 实现景深效果
高斯模糊下
换成基于法线的双边滤波后,颜色泄露的情况有所改善,模糊的颜色也感觉正常了
2. 分析官方后处理插件PPS中景深效果的实现
简单看了下代码,看的比较浅,不一定理解的对,大概有以下过程
enum Pass
{
CoCCalculation, // 由深度、焦点距离、_LensCoeff计算CoC的值,感觉和上边插值用的final_depth类似
CoCTemporalFilter, // 开启TAA的话将Texture中CoC的值进行滤波
DownsampleAndPrefilter, // 下采样
BokehSmallKernel, // 5种模糊方式计算dof纹理(焦外模糊的颜色)
BokehMediumKernel,
BokehLargeKernel,
BokehVeryLargeKernel,
PostFilter,
Combine, // 将原图的颜色和模糊后的颜色结合
DebugOverlay
}