Unity的URP的自定义后处理效果(三) 之 毛玻璃效果

7 篇文章 2 订阅
2 篇文章 0 订阅

抓手

本文详说如何在URP管线下做毛玻璃效果。

有两种方法,方法一是用URP自带的OpaqueTexture,但是Opaque是不透明的意思,

所以它的缺陷是半透明的物体它是不会渲染到OpaqueTexture里去的。

方法二是用RendererFeature,本文主要讲第二种方法。

底部附有工程下载。

实现原理

先在一帧里用RendererFeature用某个camera截一帧的图。

然后用CommandBuffer做多次高斯模糊,达到这种毛玻璃效果。

最后渲染到一个RenderTexture上。

具体实现

先写一个ScriptableRendererFeature和ScriptableRenderPass。

RendererFeature的具体理论可以看我之前的文章:《Unity的URP的自定义后处理效果》https://blog.csdn.net/zakerhero/article/details/106793571

先实现ScriptableRendererFeature,名为GlassBlurRenderPassFeature:

using UnityEngine;
using UnityEngine.Rendering.Universal;

public class GlassBlurRenderPassFeature : ScriptableRendererFeature
{
//设置一个RendererFeature的配置
    [System.Serializable]
    public class Settings
    {
        public RenderPassEvent renderEvent = RenderPassEvent.BeforeRenderingPostProcessing;
        public LayerMask layerMask = -1;
        public Material blurMat;
        public string textureName = "";
        public string cmdName = "";
        public string passName = "";
//目标RenderTexture 
        public RenderTexture blurRt = null;
    }

    GlassBlurRenderPass m_ScriptablePass;
    private RenderTargetHandle dest;
    public Settings settings;
    public override void Create()
    {
        m_ScriptablePass = new GlassBlurRenderPass(settings);
        m_ScriptablePass.renderPassEvent = settings.renderEvent;
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        var src = renderer.cameraColorTarget;
        dest = RenderTargetHandle.CameraTarget;
//把camera渲染到的画面src 传入GlassBlurRenderPass里。
        m_ScriptablePass.Setup(src,this.dest);
//注入队列
        renderer.EnqueuePass(m_ScriptablePass);
    }
}

接下来是ScriptableRenderPass,名为GlassBlurRenderPass

在构造函数中,我把GlassBlurRenderPassFeature的Settings的数据传过来了。

public class GlassBlurRenderPass : ScriptableRenderPass
{
    private CommandBuffer cmd;
    private string cmdName;
    private RenderTargetHandle dest;
    private Material m_blurMat;
    private RenderTexture m_blurRt;
    private RenderTargetIdentifier source { get; set; }
    RenderTargetHandle m_temporaryColorTexture;
    RenderTargetHandle blurredID;
    RenderTargetHandle blurredID2;
    public GlassBlurRenderPass(GlassBlurRenderPassFeature.Settings param)
    {
        renderPassEvent = param.renderEvent;
        cmdName = param.cmdName;
        m_blurMat = param.blurMat;
        m_blurRt = param.blurRt;

        blurredID.Init("blurredID");
        blurredID2.Init("blurredID2");
    }
//其他代码
}

在Setup方法中,我们把camera的渲染到的画面传入到变量source中。该方法在ScriptableRendererFeature的AddRenderPasses方法中被调用。

public void Setup(RenderTargetIdentifier src, RenderTargetHandle _dest)
    {
        this.source = src;
        this.dest = _dest;
    }

在Execute函数中,我们用CommandBuffer把source通过高斯模糊的shader处理完后,最终通过CommandBuffer把内容Blit到一个RenderTexture上。

 public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        if (renderingData.cameraData.isSceneViewCamera) return;
        //如果cullingMask包含UI层的camera,返回
        if ((renderingData.cameraData.camera.cullingMask & 1<<LayerMask.NameToLayer("UI"))>0)
            return;
//通过一个外部的静态变量,控制是否只执行一帧
        if (!GlassCtrl.takeShot)
            return;

        cmd = CommandBufferPool.Get(cmdName);
        Vector2[] sizes = {
                new Vector2(Screen.width, Screen.height),
                new Vector2(Screen.width / 2, Screen.height / 2),
                new Vector2(Screen.width / 4, Screen.height / 4),
                new Vector2(Screen.width / 8, Screen.height / 8),
            };

        int numIterations = 3;
        RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;

        opaqueDesc.depthBufferBits = 0;
        cmd.GetTemporaryRT(m_temporaryColorTexture.id, opaqueDesc, FilterMode.Bilinear);
        cmd.Blit(source, m_temporaryColorTexture.Identifier());
        for (int i = 0; i < numIterations; ++i)
        {

            cmd.GetTemporaryRT(blurredID.id, opaqueDesc, FilterMode.Bilinear);
            cmd.GetTemporaryRT(blurredID2.id, opaqueDesc, FilterMode.Bilinear);

            cmd.Blit(m_temporaryColorTexture.Identifier(), blurredID.Identifier());
            cmd.SetGlobalVector("offsets", new Vector4(2.0f / sizes[i].x, 0, 0, 0));
            cmd.Blit(blurredID.Identifier(), blurredID2.Identifier(), m_blurMat);
            cmd.SetGlobalVector("offsets", new Vector4(0, 2.0f / sizes[i].y, 0, 0));
            cmd.Blit(blurredID2.Identifier(), blurredID.Identifier(), m_blurMat);

            cmd.Blit(blurredID.Identifier(), m_temporaryColorTexture.Identifier());
        }
//把最终内容Blit到一个RenderTexture上。
        cmd.Blit(blurredID.Identifier(), m_blurRt);
        GlassCtrl.takeShot = false;

        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
        //Debug.LogError("glass pass render");
    }

高斯模糊shader的具体原理不在这里阐述。通俗的理解就是获取一个像素及周围的像素,通过一定的权重(算子)来算出最终模糊的颜色值。可以理解为,我有一堆糖果,但是我想从身边的人换巧克力等其他零食,通过一定的比例换出去换回来,最终我和其他人既有糖果,又有巧克力、冰激凌等各种零食。

具体的shader可以看底部的源码及工程下载,里面的SeparableBlur.shader文件。

最后把GlassBlurRenderPassFeature添加到工程UniversalRenderPipelineAsset_Renderer里,设置它,即可生效。

温馨提示

在Excute方法里,可以通过renderingData.cameraData.camera获取某个camera的数据,

甚至可以获得它的name,来判断它是哪个camera,再判断要不要执行该RendererFeature。

例子:

if ((renderingData.cameraData.camera.cullingMask & 1<<LayerMask.NameToLayer("UI"))>0) return;

源码下载:

链接:https://pan.baidu.com/s/1TLPTMJABDCtiADeQ-zFSig

提取码:u70p

源码工程使用:

Unity导入该工程后,切换URP环境,打开SampleScene并play。

选择Main Camera,在Inspector面板勾选Glass Ctrl组件的Show Glass即可看到效果。

参考文章

https://lab.uwa4d.com/lab/5b5613a3d7f10a201fd80bbb

  • 6
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值