zoukankan      html  css  js  c++  java
  • 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 }

    效果如下:

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

  • 相关阅读:
    Java—异常处理总结
    CSS white-space 属性
    兼容性问题的总结
    学习利用vertical-align:middle实现在整个页面居中
    多口USB HUB信号延长器 USBX-M200(针对于A客户使用时很棒吧)
    凌华运动控制卡调试记录
    记录:EPALN Electric P8 2.4.4.8366 安装记录
    一步一步创建一个动态库
    (转)VS中的路径宏 vc++中OutDir、ProjectDir、SolutionDir各种路径说明
    凌华运动控制卡的函数。
  • 原文地址:https://www.cnblogs.com/koshio0219/p/11178215.html
Copyright © 2011-2022 走看看