找回密码
 立即注册
查看: 588|回复: 10

Unity的URP的自定义后处理效果(二) 之 自定义扩展 ...

[复制链接]
发表于 2021-12-21 09:59 | 显示全部楼层 |阅读模式

抓手
本文将会具体阐述,如何扩展Unity URP的Volume。
之前写过一篇叫《Unity的URP的自定义后处理效果》的文章,传送门:
琅琅:Unity的URP的自定义后处理效果实现,但是并不完全是我想要的效果。
因为最终我是希望像Unity 的Volume一样去添加自定义的后处理效果,可以随时添加、关闭和打开。
最近参考了一些文章,终于知道怎么做了。以下是具体做法。
具体实现:
一共五个大步骤。
步骤一:
创建VolumeComponent类。
在com.unity.render-pipelines.universal@7.4.3\Runtime\Overrides源码里,有URP自带的后处理的类。
参考它们的bloom.cs,在URP的命名空间下定义自己的VolumeComponent类,这里我的类名叫HologramBlock。
using System;

namespace UnityEngine.Rendering.Universal
{
    //定义类
}
接着,在定义类的上面加上以下代码,就是把我们的类加入到Volume的组件里。
[System.Serializable, VolumeComponentMenu("HL/HologramBlock")]
整个文件的代码如下:
using System;

namespace UnityEngine.Rendering.Universal
{
    [System.Serializable, VolumeComponentMenu("HL/HologramBlock")]
    public sealed class HologramBlock : VolumeComponent, IPostProcessComponent
    {
        [Tooltip("是否开启效果")]
        public BoolParameter enableEffect = new BoolParameter(true);

        public bool IsActive() => enableEffect==true;

        public bool IsTileCompatible() => false;
    }
}
然后就可以在Volume下面找到我们的Volume组件啦!


但是目前还没有任何效果哈,下面的步骤就是给我们的Volume添加实际的效果。
步骤二:
创建自定义的ScriptableRendererFeatureScriptableRenderPass
因为Unity暂时在URP自定义扩展Volume并没有太多接口和相关文档说明,也有可能是我没找到。
所以目前的方法还是通过ScriptableRendererFeature和ScriptableRenderPass去做。
如果对部分理论有不理解的可以参考我之前的文章,传送门:琅琅:Unity的URP的自定义后处理效果实现
首先定义我们的ScriptableRendererFeature
using UnityEngine.Rendering.Universal;

public class HologramBlockRenderFeature : ScriptableRendererFeature
{

    HologramBlockRenderPass m_ScriptablePass;

    public override void Create()
    {
        m_ScriptablePass = new HologramBlockRenderPass();
        m_ScriptablePass.renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
        m_renderTargetHandle.Init("_ScreenTexture2");
    }
    RenderTargetHandle m_renderTargetHandle;

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        var dest =  RenderTargetHandle.CameraTarget;
        m_ScriptablePass.Setup(renderer.cameraColorTarget, dest);
        renderer.EnqueuePass(m_ScriptablePass);
    }
}
接着定义我们的ScriptableRenderPass,在Override的Execute方法里做我们的具体的后处理:
    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        if (holoMat == null)
        {
            UnityEngine.Debug.LogError("材质没找到!");
            return;
        }
        if (!renderingData.cameraData.postProcessEnabled) return;
        //通过队列来找到HologramBlock组件,然后
        var stack = VolumeManager.instance.stack;
        hologramBlock = stack.GetComponent<HologramBlock>();
        if (hologramBlock == null){return;}
        if (!hologramBlock.IsActive())return;

        var cmd = CommandBufferPool.Get(k_RenderTag);
        Render(cmd, ref renderingData);
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);        
    }
上面使用了VolumeManager.instance.stack的GetComponent方法获得我们的自定义Volume类的实例。
并获取里面的属性变量来做具体的后处理。
可以调用我们的HologramBlock组件的IsActive方法来判断是否执行该后处理。
诚然,这个IsActive返回的值,是你自己在继承的VolumeComponent类里定义的,
并非是VolumeComponent组件的启用和禁用决定的。
步骤三:
写后处理所需的shader。
写我们后处理的shader,该shader是一个视频错误效果。
我在ScriptableRenderPass的构造函数中用该shader创建了一个Material。
    public HologramBlockRenderPass()
    {
        var shader = Shader.Find("Hidden/HL/HologramBlockPE");
        holoMat = CoreUtils.CreateEngineMaterial(shader);
        m_temporaryColorTexture.Init("temporaryColorTexture");

    }
shader文件内容在源码下载中可见,文件名是HologramBlockPE.shader。
步骤四:
配置我们的东西到Unity里。
在ForwardRendererData里Add Renderer Feature。




在Camera中勾选Post Processing


同时,在Camera的Environment的Volume Mask中选择相应的volume的gameObject的Layer。


步骤五:
最后,把HologramBlock添加到Volume里,就可以生效了。我使用了


这里我在代码里添加了Enable Effect来控制是否生效。


因为RenderFeature一旦加入到ForwardRendererData里,
它就一直会执行ScriptableRenderPass的Execute方法。这个我感觉挺不喜欢的。
最终效果:


该方法存在的问题:
RenderFeature一旦加入到ForwardRendererData里,它就一直会执行ScriptableRenderPass的Execute方法。
所以我只能通过外部的代码来控制Execute方法里的东西是否执行。勾线掉该组件,并非能让它不生效。
也可能是我没找到相应的接口吧。如果有问题,请朋友们指出。


为了解决这个问题,比如我可以写一个PostEffectCtrl的类,如果我不需要该后处理,
我就会将PostEffectCtrl的静态变量Close的值设为false,然后
使用PostEffectCtrl.Close来判断是否执行ScriptableRenderPass的Execute的内容,
不需要就return。
另外,可以给VolumeComponent类配对一个VolumeComponentEditor
如果你对界面显示有要求的话,可以写一个VolumeComponentEditor哈。
代码也比较简单,只需继承VolumeComponentEditor类,并且
在定义类的上面添加[VolumeComponentEditor(typeof(你的VolumeComponent类名))],大体如下:
using UnityEngine.Rendering.Universal;

namespace UnityEditor.Rendering.Universal
{
    [VolumeComponentEditor(typeof(HologramBlock))]
    sealed class HologramBlockEditor : VolumeComponentEditor
    {
    }
}
具体可以看我提供的工程里的源码哈,文件名为HologramBlockEditor.cs
源码下载:
链接:https://pan.baidu.com/s/1_9lkdanrnnZAFZAeIvbtEQ
提取码:amqx
参考文章:
感谢这位日本朋友的文章,还有知乎的会编程的猫的文章。感谢他们前人栽树后人乘凉。
https://qiita.com/t-matsunaga/items/09343ae7c683269374c4
https://zhuanlan.zhihu.com/p/161658349
HDRP如何自定义扩展Volume可以参考Keijiro Takahashi大神的工程:
https://gitee.com/langlang1988/KinoEight
以上,最后,希望这篇文章能给在使用URP的朋友们带来帮助。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
发表于 2021-12-21 10:03 | 显示全部楼层
ScriptableRendererFeature.SetActive(bool active)
可以用这个来控制后处理的开关吧
发表于 2021-12-21 10:11 | 显示全部楼层
好,谢谢,我试试看
发表于 2021-12-21 10:16 | 显示全部楼层
请问这样写,有volume混合的效果吗?就是不同区域用不同的后处理参数,在两个区域中间,后处理是有混合过渡的。
发表于 2021-12-21 10:25 | 显示全部楼层
不清楚,这个应该是shader的处理效果吧,可能要从shader着手。volume只是单纯提供了一个组件,这个组件仅仅提供了一些自定义的参数变量。至于什么区域用什么参数,可以在shader里用屏幕坐标来区分。
发表于 2021-12-21 10:31 | 显示全部楼层
你好 步骤四那里我下载了一个国外大神cyan的blit脚本 本来想做自定义屏幕着色器的 但是按了add render feature之后点了blit选项就是不出来 请问您知知道原因吗
发表于 2021-12-21 10:38 | 显示全部楼层
这个你可以直接用自带的box volume实现
发表于 2021-12-21 10:48 | 显示全部楼层
可以用frame debuger查看它是否执行了。或者在他的excute方法里加打印,看是哪个地方没执行下去。
发表于 2021-12-21 10:51 | 显示全部楼层
hologramBlock= stack.GetComponent< 那里前面小写的hologramBlock是从哪里来的 为啥我的总是下划线报错
发表于 2021-12-21 10:52 | 显示全部楼层
public sealed class HologramBlock : VolumeComponent, IPostProcessComponent的实例,是一个私有的变量,可以下载我的源码看看哈。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-16 09:43 , Processed in 0.120228 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表