urp管线的自学hlsl之路 第二十二篇 景深和散景模糊——实践篇
雪风carel
编辑于 2020年07月05日 17:05
收录于文集
共38篇

        这是最后一篇讲静态模糊效果的文章了,后面的更新不会再包含静态模糊的内容了。这次就直接在urp管线里完成一个景深的效果,它支持如下效果。

近处景深模糊

远处景深模糊

近处和远处同时模糊,中间不模糊(感觉怪怪的)建议少用

         而它的核心原理,是我们在RenderFeather里通过上一篇专栏里(https://www.bilibili.com/read/cv6640445​)中讲解的模糊算法在shader的第一个pass里计算得到散景模糊图并把它存到一张降采样的RT里,然后使用源相机图像和这个模糊图像在shader里的第二个pass里进行插值混合。RenderFeather的处理非常简单,执行函数如下图所示。

RenderFeather的执行函数

      RenderFeather的其他部分和之前的专栏一样,这里我在参数里说明了各个参数的作用,希望能帮到读者的理解。

参数意义

shader部分,关于第二个pass的线性混合,我们需要在shader里得到这样一个曲线:

混合曲线

        把这个曲线根据线性的深度(深度的获取在这篇文章里讲过,这里不多做赘述https://www.bilibili.com/read/cv6519977​)输出,就能得到这样一个图像:近处和远处白色的部分(值为1)为模糊的图像,中间黑色的部分(值为0)为清晰的图像;然后通过renderfeather去控制距离,smooth去控制过渡的平滑度。

把曲线输出效果

        在这个pass里,我们通过smoothstep函数去实现这个效果,分别计算近处和远处,然后加起来,写法如下。

片元函数

        最终把混合的图像输出,就能得到近处和远处都模糊,而中间不模糊的效果。我们还可以通过调整近处的距离为0,就只让远处模糊;或者调整远处距离很大,只让近处模糊。

混合输出效果图

        这里就完成了我们的效果了,整体来说本效果的关键是上一篇文章(https://www.bilibili.com/read/cv6640445​)里利用黄金角度和平均分布去得到圆斑的算法,下面附上源码,如果直接复制报错,是空格的问题,先替换所有的空格。

RenderFeather

using UnityEngine;

using UnityEngine.Rendering;

using UnityEngine.Rendering.Universal;

[ExecuteInEditMode]

public class bokehBlur : ScriptableRendererFeature

{

    [System.Serializable]public class setting

    {

        public Material mat;

       [Tooltip("降采样,越大性能越好但是质量越低&#​34;),Range(1,7)]public int downsample=2;

      [Tooltip("迭代次数,越小性能越好但是质量越低&#​34;),Range(3,500)] public int loop=50;

      [Tooltip("采样半径,越大圆斑越大但是采样点越分散&#​34;),Range(0.1f,10)]  public float R=1;

      [Tooltip("模糊过渡的平滑度&#​34;),Range(0,0.5f)]public float BlurSmoothness=0.1f;

       [Tooltip("近处模糊结束距离&#​34;)] public float NearDis=5;

       [Tooltip("远处模糊开始距离&#​34;)] public float FarDis=9;

        public RenderPassEvent Event=RenderPassEvent.AfterRenderingOpaques;

        public string name="散景模糊&#​34;;

    }

   public setting mysetting=new setting();

    class CustomRenderPass : ScriptableRenderPass

    {

        public Material mat;

        public int loop;

        public float BlurSmoothness;

        public int downsample;

        public float R;

        public float NearDis;

        public float FarDis;

        RenderTargetIdentifier sour;

        public string name;

        int width;

        int height;

        readonly static int BlurID=Shader.PropertyToID("blur&#​34;);//申请之后就不在变化

        readonly static int SourBakedID=Shader.PropertyToID("_SourTex&#​34;);

        public void setup(RenderTargetIdentifier Sour)

        {

            this.sour=Sour;

            mat.SetFloat("_loop&#​34;,loop);

            mat.SetFloat("_radius&#​34;,R);

            mat.SetFloat("_NearDis&#​34;,NearDis);

            mat.SetFloat("_FarDis&#​34;,FarDis);

            mat.SetFloat("_BlurSmoothness&#​34;,BlurSmoothness);

        }

        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)

        {

            CommandBuffer cmd=CommandBufferPool.Get(name);

            RenderTextureDescriptor desc=renderingData.cameraData.cameraTargetDescriptor;

            width=desc.width/downsample;

            height=desc.height/downsample;

            cmd.GetTemporaryRT(BlurID,width,height,0,FilterMode.Bilinear,RenderTextureFormat.ARGB32);

            cmd.GetTemporaryRT(SourBakedID,desc); 

            cmd.CopyTexture(sour,SourBakedID);//把相机图像复制到备份RT图,并自动发送到shader里,无需手动指定发送

            cmd.Blit(sour,BlurID,mat,0);//第一个pass:把屏幕图像计算后存到一个降采样的模糊图里

            cmd.Blit(BlurID,sour,mat,1);//第二个pass:发送模糊图到shader的maintex,然后混合输出

            context.ExecuteCommandBuffer(cmd);

            cmd.ReleaseTemporaryRT(BlurID);

            cmd.ReleaseTemporaryRT(SourBakedID);

            CommandBufferPool.Release(cmd);

        }

    }

    CustomRenderPass m_ScriptablePass= new CustomRenderPass();

    public override void Create()

    {

        m_ScriptablePass.mat=mysetting.mat;

        m_ScriptablePass.loop=mysetting.loop;

        m_ScriptablePass.BlurSmoothness=mysetting.BlurSmoothness;

        m_ScriptablePass.R=mysetting.R;

        m_ScriptablePass.renderPassEvent = mysetting.Event;

        m_ScriptablePass.name=mysetting.name;

        m_ScriptablePass.downsample=mysetting.downsample;

        m_ScriptablePass.NearDis=Mathf.Max(mysetting.NearDis,0);

        m_ScriptablePass.FarDis=Mathf.Max(mysetting.NearDis,mysetting.FarDis);

    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)

    {

        m_ScriptablePass.setup(renderer.cameraColorTarget);

        renderer.EnqueuePass(m_ScriptablePass);

    }

}

Shader

Shader "WX/URP/Post/broke blur&#​34;

{

    Properties

    {

    [HideinInspector]  _MainTex("MainTex&#​34;,2D)="white&#​34;{}

    }

    SubShader

    {

        Tags{

        "RenderPipeline&#​34;="UniversalRenderPipeline&#​34;

        "RenderType&#​34;="Transparent&#​34;

        }

        Cull Off ZWrite Off ZTest Always

        HLSLINCLUDE

        #include &#​34;Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        CBUFFER_START(UnityPerMaterial)

        float4 _MainTex_TexelSize;

        CBUFFER_END

        half _NearDis;

        half _FarDis;

        float _BlurSmoothness;

        float _loop;

        float _radius;

        TEXTURE2D(_MainTex);

        SAMPLER(sampler_MainTex);

        TEXTURE2D(_SourTex);

        SAMPLER(sampler_SourTex);

        SAMPLER(_CameraDepthTexture);

         struct a2v

         {

             float4 positionOS:POSITION;

             float2 texcoord:TEXCOORD;

         };

         struct v2f

         {

             float4 positionCS:SV_POSITION;

             float2 texcoord:TEXCOORD;

         };

        ENDHLSL

         pass

        {

            HLSLPROGRAM

            #pragma vertex VERT

            #pragma fragment FRAG

            v2f VERT(a2v i)

            {

                v2f o;

                o.positionCS=TransformObjectToHClip(i.positionOS.xyz);

                o.texcoord=i.texcoord;

                return o;

            }

            real4 FRAG(v2f i):SV_TARGET

            {

                float a=2.3398;

                float2x2 rotate=float2x2(cos(a),-sin(a),sin(a),cos(a));

                float2 UVpos=float2(_radius,0);

                float2 uv;

                float r;

                real4 tex=0;

                for(int t=1;t<_loop;t++)

                {

                  r=sqrt(t);

                  UVpos=mul(rotate,UVpos);

                  uv=i.texcoord+_MainTex_TexelSize.xy*UVpos*r;

                  tex+=SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,uv);

                }

                return tex/(_loop-1);

            }

            ENDHLSL 

        }

        pass

        {

          HLSLPROGRAM

          #pragma vertex vert

          #pragma fragment frag

          v2f vert(a2v i)

          {

            v2f o;

            o.positionCS=TransformObjectToHClip(i.positionOS.xyz);

            o.texcoord=i.texcoord;

            return o;

          }

          real4 frag(v2f i):SV_TARGET

          {

            float depth=Linear01Depth(tex2D(_CameraDepthTexture,i.texcoord).x,_ZBufferParams).x;

            real4 blur=SAMPLE_TEXTURE2D(_MainTex,sampler_MainTex,i.texcoord);

            real4 Sour=SAMPLE_TEXTURE2D(_SourTex,sampler_SourTex,i.texcoord);

            _NearDis*=_ProjectionParams.w;

            _FarDis*=_ProjectionParams.w;

            float dis=1-smoothstep(_NearDis,saturate(_NearDis+_BlurSmoothness),depth);//计算近处的

             dis+=smoothstep(_FarDis,saturate(_FarDis+_BlurSmoothness),depth);//计算远处的

            real4 combine=lerp(Sour,blur,dis);

            return combine;

          }

          ENDHLSL

        } 

    }

}