学习教程来自:【技术美术百人计划】图形 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{ // 顶点着色器输入结构体,位置和UVfloat4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{ // 顶点着色器输出结构体,位置和UVfloat4 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{ // 顶点着色器输入结构体,位置和UVfloat4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{ // 顶点着色器输出结构体,位置和UVfloat2 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-1final_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
}