rainyeve
  • 首页
  • 编程
  • 涂鸦
  • 其他
  • 关于
2020年2月5日
编程

【Unity SRP】模糊的菜单背景

【Unity SRP】模糊的菜单背景
2020年2月5日
编程

在LWRP中用Scriptable Render Pass来实现模糊场景的屏幕后特效。官方给出了PerObjectBloom,ToonOutline等等不少的示例可参考。不过SRP版本迭代得非常快,很多示例的写法很快就过时了,而且导入后还会经常碰到异常的效果,免不了调试一番。

菜单背景模糊动画

实现思路

先给CustomForwardRendererData写一个ScriptableRendererFeature。为了节约性能,额外用代码控制何时AddRenderPasses。

 public class BlurGrabPass : UnityEngine.Rendering.Universal.ScriptableRendererFeature
{
    [Serializable]
    public class Settings
    {
        public Vector2 m_BlurAmount;
        public Material m_BlurMaterial;
    }

    private Material m_BlurMaterial;

    public Settings settings;

    private GrabPassImpl m_grabPass;

    public override void Create()
    {
        m_BlurMaterial = settings.m_BlurMaterial;
        m_grabPass = new GrabPassImpl(m_BlurMaterial, settings.m_BlurAmount);
        //m_grabPass.renderPassEvent = RenderPassEvent.AfterRenderingSkybox;
        //防止粒子效果被剔除,防止PostProcessing效果被剔除,包括camera fog
        m_grabPass.renderPassEvent = UnityEngine.Rendering.Universal.RenderPassEvent.AfterRendering;
    }

    public override void AddRenderPasses(UnityEngine.Rendering.Universal.ScriptableRenderer renderer, ref UnityEngine.Rendering.Universal.RenderingData renderingData)
    {
        //无时无刻都使用GrabPass会降低性能(真机测试降低了10fps),于是加入条件控制
        if(GameSceneManager.instance 
        && GameSceneManager.instance.blurAmount.x>0.01f 
        && GameSceneManager.instance.blurAmount.y>0.01f) {
            m_grabPass.UpdateBlurAmount(GameSceneManager.instance.blurAmount);
            renderer.EnqueuePass(m_grabPass);
        }
    }
} 

主要目的是给菜单的背景材质准备一张经过模糊处理的屏幕截图(命名做_GrabBlurTexture)。因为LWRP不再支持build-in shader的grab pass,所以要自己写抓取屏幕截图的代码。

 public class GrabPassImpl : UnityEngine.Rendering.Universal.ScriptableRenderPass
{
    const string k_RenderGrabPassTag = "Blur Refraction Pass";

    private Material m_BlurMaterial;

    private Vector2 m_BlurAmount;

    private RenderTextureDescriptor m_OpaqueDesc;
    private RenderTextureDescriptor m_BaseDescriptor;
    private UnityEngine.Rendering.Universal.RenderTargetHandle m_ColorHandle;

    public GrabPassImpl(Material blurMaterial, Vector2 blurAmount)
    {
        m_BlurMaterial = blurMaterial;
        m_ColorHandle = UnityEngine.Rendering.Universal.RenderTargetHandle.CameraTarget;
        m_BlurAmount = blurAmount;
    }

    public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
    {
        cameraTextureDescriptor.msaaSamples = 1;
        m_OpaqueDesc = cameraTextureDescriptor;
        cmd.GetTemporaryRT(m_ColorHandle.id, cameraTextureDescriptor, FilterMode.Bilinear);
    }

    public void UpdateBlurAmount(Vector2 newBlurAmount)
    {
        m_BlurAmount = newBlurAmount;
    }

    public override void Execute(ScriptableRenderContext context, ref UnityEngine.Rendering.Universal.RenderingData renderingData)
    {
        CommandBuffer buf = CommandBufferPool.Get(k_RenderGrabPassTag);

        using (new ProfilingSample(buf, k_RenderGrabPassTag))
        {
            // copy screen into temporary RT
            int screenCopyID = Shader.PropertyToID("_ScreenCopyTexture");
            buf.GetTemporaryRT(screenCopyID, m_OpaqueDesc, FilterMode.Bilinear);
            buf.Blit(m_ColorHandle.Identifier(), screenCopyID);

            m_OpaqueDesc.width = m_OpaqueDesc.width>>1;
            m_OpaqueDesc.height = m_OpaqueDesc.height>>1;

            // get two smaller RTs
            int blurredID = Shader.PropertyToID("_BlurRT1");
            int blurredID2 = Shader.PropertyToID("_BlurRT2");
            buf.GetTemporaryRT(blurredID, m_OpaqueDesc, FilterMode.Bilinear);
            buf.GetTemporaryRT(blurredID2, m_OpaqueDesc, FilterMode.Bilinear);

            // downsample screen copy into smaller RT, release screen RT
            buf.Blit(screenCopyID, blurredID);
            
            // horizontal blur
            buf.SetGlobalVector("offsets", new Vector4(m_BlurAmount.x / Screen.width, 0, 0, 0));
            buf.Blit(blurredID, blurredID2, m_BlurMaterial);
            // vertical blur
            buf.SetGlobalVector("offsets", new Vector4(0, m_BlurAmount.y / Screen.height, 0, 0));
            buf.Blit(blurredID2, blurredID, m_BlurMaterial);

            
            // horizontal blur
            buf.SetGlobalVector("offsets", new Vector4(m_BlurAmount.x * 2 / Screen.width, 0, 0, 0));
            buf.Blit(blurredID, blurredID2, m_BlurMaterial);
            // vertical blur
            buf.SetGlobalVector("offsets", new Vector4(0, m_BlurAmount.y * 2 / Screen.height, 0, 0));
            buf.Blit(blurredID2, blurredID, m_BlurMaterial);
            
            // horizontal blur
            buf.SetGlobalVector("offsets", new Vector4(m_BlurAmount.x * 4 / Screen.width, 0, 0, 0));
            buf.Blit(blurredID, blurredID2, m_BlurMaterial);
            // vertical blur
            buf.SetGlobalVector("offsets", new Vector4(0, m_BlurAmount.y * 4 / Screen.height, 0, 0));
            buf.Blit(blurredID2, blurredID, m_BlurMaterial);

            //Set Texture for Shader Graph
            buf.SetGlobalTexture("_GrabBlurTexture", blurredID);

            buf.ReleaseTemporaryRT(screenCopyID);
            buf.ReleaseTemporaryRT(blurredID);
            buf.ReleaseTemporaryRT(blurredID2);
        }

        context.ExecuteCommandBuffer(buf);
        CommandBufferPool.Release(buf);

    }

    public override void FrameCleanup(CommandBuffer cmd) {
        cmd.ReleaseTemporaryRT(m_ColorHandle.id);
    }
} 

blit方法使用的m_BlurMaterial的shader,是采用简单加权取邻近像素颜色的方式进行模糊。

 Shader "MyAct/Blur" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "" {}
    }

    CGINCLUDE
    
    #include "UnityCG.cginc"
    
    struct v2f {
        float4 pos : POSITION;
        float2 uv : TEXCOORD0;

        float4 uv01 : TEXCOORD1;
        float4 uv23 : TEXCOORD2;
        float4 uv45 : TEXCOORD3;
    };
    
    float4 offsets;
    float4 tintColor;
    
    sampler2D _MainTex;
    
    v2f vert (appdata_img v) {
        v2f o;
        o.pos = UnityObjectToClipPos(v.vertex);

        o.uv.xy = v.texcoord.xy;

        o.uv01 =  v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1);
        o.uv23 =  v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 2.0;
        o.uv45 =  v.texcoord.xyxy + offsets.xyxy * float4(1,1, -1,-1) * 3.0;

        return o;
    }
    
    half4 frag (v2f i) : COLOR {
        half4 color = float4 (0,0,0,0);

        color += 0.40 * tex2D (_MainTex, i.uv);
        color += 0.15 * tex2D (_MainTex, i.uv01.xy);
        color += 0.15 * tex2D (_MainTex, i.uv01.zw);
        color += 0.10 * tex2D (_MainTex, i.uv23.xy);
        color += 0.10 * tex2D (_MainTex, i.uv23.zw);
        color += 0.05 * tex2D (_MainTex, i.uv45.xy);
        color += 0.05 * tex2D (_MainTex, i.uv45.zw);
        
        return color;
    }
    ENDCG
Fallback off

} 
在菜单背景的材质的shader中使用_GrabBlurTexture

Review

在Unity2019.3中LWRP变成了URP,并且post processing stack进行了升级整合,可以使用内置volume的Depth Of Field来实现场景画面模糊,因为是按景深来模糊,所以更有镜头感。

URP Volume,Depth of Field
Post Views: 4,731

shader unity3d

上一篇【Unity Shader】2D火焰UV动画下一篇 【Unity Shader】裂缝效果

分类目录

  • 其他 (2)
  • 旅行 (1)
  • 涂鸦 (6)
  • 编程 (28)
Email: wuch441692@163.com