专栏首页U3DUnity Shader 屏幕后效果——景深

Unity Shader 屏幕后效果——景深

景深效果的原理是,在摄像机的近裁剪平面和远裁剪平面之间可以设置一个焦距,在这个距离所在的平面上的物体最为清晰,而这个距离之前或之后的物体成像是一种模糊状态(根据距离逐渐模糊,最终达到最为模糊的状态)。

在shader中,需要一张清晰的场景图和一张模糊的场景图,可以通过每个像素相对焦距的距离来判定这个像素最终的清晰程度。在清晰图和模糊图之间做关于深度变化的插值运算。

关于摄像机的近裁剪平面和远裁剪平面,可以直接在Camera组件的属性面板中调节(默认的远裁剪平面距离是1000):

模糊图可以直接采用高斯模糊实现,具体参考:

https://www.cnblogs.com/koshio0219/p/11152534.html

清晰图也就是未经过任何处理的渲染图,直接就可以得到;

关键问题在于如何得到每个像素在摄像机近裁剪平面和远裁剪平面之间的位置,而这个位置信息就是渲染纹理的深度值。

在Unity中,可以不用自己计算深度值,Unity提供了直接提取摄像机深度值的宏:

float depth=SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth);

depth=Linear01Depth(depth);

并可以直接利用内置函数将其转化为线性深度值(对于透视摄像机来说,原本的深度值是非线性的),以方便后续的插值计算。

在提取摄像机深度值之前,需要将摄像机的深度纹理模式设置为Depth,同时在Shader中提前声明_CameraDepthTexture变量:

MyCamera.depthTextureMode |= DepthTextureMode.Depth;

sampler2D _CameraDepthTexture;

C#控制脚本如下:

 1 using UnityEngine;
 2 
 3 public class DepthOfFieldCrtl : ScreenEffectBase
 4 {
 5     private const string _BlurSize = "_BlurSize";
 6     private const string _FocusDistance = "_FocusDistance";
 7     private const string _BlurTex = "_BlurTex";
 8 
 9     //用于控制高斯模糊的参数
10     [Range(.2f,3)]
11     public float blurSize = .6f;
12     [Range(0, 4)]
13     public int iterations = 3;
14     [Range(1, 8)]
15     public int downSample = 2;
16 
17     //归一化后的焦距,0表示焦距处于摄像机近裁剪平面,1表示处于远裁剪平面;为了更好的调整效果,可以将[0,1]范围适当扩大
18     [Range(-.02f, 1.02f)]
19     public float focusDistance = .5f;
20 
21     private Camera myCamera=null;
22     public Camera MyCamera
23     {
24         get
25         {
26             if (myCamera == null)
27             {
28                 myCamera = GetComponent<Camera>();
29             }
30             return myCamera;
31         }
32     }
33 
34     //开启摄像机深度模式
35     private void OnEnable()
36     {
37         MyCamera.depthTextureMode |= DepthTextureMode.Depth;
38     }
39 
40     //不用时还原
41     private void OnDisable()
42     {
43         MyCamera.depthTextureMode &= ~DepthTextureMode.Depth;
44     }
45 
46     private void OnRenderImage(RenderTexture source, RenderTexture destination)
47     {
48         if (Material)
49         {
50             //传递焦距
51             Material.SetFloat(_FocusDistance, focusDistance);
52 
53             var w = source.width / downSample;
54             var h = source.height / downSample;
55 
56             var buffer0 = RenderTexture.GetTemporary(w, h, 0);
57             buffer0.filterMode = FilterMode.Bilinear;
58 
59             Graphics.Blit(source, buffer0);
60 
61             //前两个Pass做高斯模糊处理,结果保存在buffer0中
62             for(int i = 0; i < iterations; i++)
63             {
64                 Material.SetFloat(_BlurSize, blurSize*i+1.0f);
65 
66                 var buffer1 = RenderTexture.GetTemporary(w, h, 0);
67                 buffer1.filterMode = FilterMode.Bilinear;
68                 Graphics.Blit(buffer0, buffer1, Material, 0);
69                 RenderTexture.ReleaseTemporary(buffer0);
70 
71                 buffer0 = RenderTexture.GetTemporary(w, h, 0);
72                 Graphics.Blit(buffer1, buffer0, Material, 1);
73                 RenderTexture.ReleaseTemporary(buffer1);
74             }
75 
76             //最后一个Pass用原图像和模糊图(buffer0)进行关于深度的插值计算,具体见Shader脚本
77             Material.SetTexture(_BlurTex, buffer0);
78             Graphics.Blit(source, destination,Material,2);
79             RenderTexture.ReleaseTemporary(buffer0);
80         }
81         else
82             Graphics.Blit(source, destination);
83 
84     }
85 }

基类脚本见:

https://www.cnblogs.com/koshio0219/p/11131619.html

Shader脚本:

 1 Shader "MyUnlit/DepthOfFiled"
 2 {
 3     Properties
 4     {
 5         _MainTex ("Texture", 2D) = "white" {}
 6     }
 7     SubShader
 8     {
 9         CGINCLUDE
10 
11         #include "UnityCG.cginc"
12 
13         sampler2D _MainTex;
14         sampler2D _BlurTex;
15         //声明摄像机深度
16         sampler2D _CameraDepthTexture;
17         half4 _MainTex_TexelSize;
18         fixed _FocusDistance;
19 
20         struct appdata
21         {
22             float4 vertex : POSITION;
23             float2 uv : TEXCOORD0;
24         };
25 
26         struct v2f
27         {
28             half4 uv:TEXCOORD0;
29             half2 uv_depth:TEXCOORD1;
30             float4 vertex:SV_POSITION;
31         };
32 
33         v2f vert(appdata v)
34         {
35             v2f o;
36             o.vertex=UnityObjectToClipPos(v.vertex);
37 
38             //xy存储清晰纹理,zw存储模糊纹理
39             o.uv.xy=v.uv;
40             o.uv.zw=v.uv;
41             o.uv_depth=v.uv;
42 
43             //需要对模糊纹理和深度纹理进行平台差异化处理
44             #if UNITY_UV_STARTS_AT_TOP
45             if(_MainTex_TexelSize.y<0){
46                 o.uv.w=1.0-o.uv.w;
47                 o.uv_depth.y=1.0-o.uv_depth.y;
48             }
49             #endif
50 
51             return o;
52         }
53 
54         fixed4 frag(v2f i):SV_Target
55         {
56             fixed4 col = tex2D(_MainTex, i.uv.xy);
57             fixed4 bcol=tex2D(_BlurTex,i.uv.zw);
58 
59             //得到深度值并线性归一化
60             float depth=SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth);
61             depth=Linear01Depth(depth);
62 
63             //因为焦距也是设置的归一化的值,直接相减取绝对值即得到模糊系数,返回插值结果
64             fixed bVa=abs(depth-_FocusDistance);
65             return lerp(col,bcol,bVa);
66         }
67 
68         ENDCG
69 
70         ZTest Always
71         Cull Off
72         ZWrite Off
73 
74         //前两个Pass高斯模糊
75         UsePass "MyUnlit/GaussianBlur/GAUSSIANBLUR_V"
76 
77         UsePass "MyUnlit/GaussianBlur/GAUSSIANBLUR_H"
78 
79         Pass
80         {
81             CGPROGRAM
82 
83             #pragma vertex vert
84             #pragma fragment frag
85 
86             ENDCG
87         }
88     }
89     FallBack Off
90 }

效果如下:

(注意调整摄像机近裁剪平面和远裁剪平面的值处于合适的区间)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Unity Shader 屏幕后效果——边缘检测

    https://www.cnblogs.com/koshio0219/p/11131619.html

    汐夜koshio
  • Unity Shader 屏幕后效果——高斯模糊

    https://www.cnblogs.com/koshio0219/p/11137155.html

    汐夜koshio
  • Unity 离线建造系统

    很多游戏,特别是养成类手游,都会有自己独特的建造系统,一个建造装置的状态循环或者说生命周期一般是这样的:

    汐夜koshio
  • 英特尔无人车!灯,等灯等灯!

    看,一辆英特尔无人车,行驶在以色列街头。当这辆车优雅而稳定的通过一个路口时,路边的行人不禁驻足观望。

    量子位
  • 教程 | 先理解Mask R-CNN的工作原理,然后构建颜色填充器应用

    选自matterport 作者:Waleed Abdulla 机器之心编译 参与:刘晓坤 上年 11 月,matterport 开源了 Mask R-CNN 实...

    朱晓霞
  • 教程 | 先理解Mask R-CNN的工作原理,然后构建颜色填充器应用

    选自matterport 作者:Waleed Abdulla 机器之心编译 参与:刘晓坤 上年 11 月,matterport 开源了 Mask R-CNN 实...

    机器之心
  • 微软北大联合提出换脸 AI 和脸部伪造检测器,演绎现实版「矛与盾」?

    一个致力于造假,一个专注于打假;光是听起来,就不禁让人联想到「矛」与「盾」的故事。那到底哪个更胜一筹呢?VB 发布了的相关内容介绍了这两个成果,我们将其整理及编...

    AI研习社
  • Java代理设计模式详解

    代理即通过代理类,找到适合你的实现类。相当于现实生活中的中介的角色,你想租房子,这个时候你又不想自己找房子,那你可以找中介,通过中介找到合适自己的房子,同时你也...

    迹_Jason
  • 实用的Python(2)利用Python制作gif动图

      moviepy是一个专门用于视频剪辑制作的模块,可以自动化完成很多繁琐的视频剪辑处理工作,除了处理视频数据之外,moviepy中还内置了可以制作gif动图的...

    Feffery
  • Java程序员月薪达到三万,需要技术水平达到什么程度?

    美的让人心动

扫码关注云+社区

领取腾讯云代金券