franciscochonge 发表于 2021-12-21 09:59

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


抓手
本文将会具体阐述,如何扩展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的组件里。

整个文件的代码如下:
using System;

namespace UnityEngine.Rendering.Universal
{
   
    public sealed class HologramBlock : VolumeComponent, IPostProcessComponent
    {
      
      public BoolParameter enableEffect = new BoolParameter(true);

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

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


但是目前还没有任何效果哈,下面的步骤就是给我们的Volume添加实际的效果。
步骤二:
创建自定义的ScriptableRendererFeature和ScriptableRenderPass
因为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类,并且
在定义类的上面添加,大体如下:
using UnityEngine.Rendering.Universal;

namespace UnityEditor.Rendering.Universal
{
   
    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的朋友们带来帮助。

NoiseFloor 发表于 2021-12-21 10:03

ScriptableRendererFeature.SetActive(bool active)
可以用这个来控制后处理的开关吧

量子计算9 发表于 2021-12-21 10:11

好,谢谢,我试试看

rustum 发表于 2021-12-21 10:16

请问这样写,有volume混合的效果吗?就是不同区域用不同的后处理参数,在两个区域中间,后处理是有混合过渡的。

kirin77 发表于 2021-12-21 10:25

不清楚,这个应该是shader的处理效果吧,可能要从shader着手。volume只是单纯提供了一个组件,这个组件仅仅提供了一些自定义的参数变量。至于什么区域用什么参数,可以在shader里用屏幕坐标来区分。

zifa2003293 发表于 2021-12-21 10:31

你好 步骤四那里我下载了一个国外大神cyan的blit脚本 本来想做自定义屏幕着色器的 但是按了add render feature之后点了blit选项就是不出来 请问您知知道原因吗

c0d3n4m 发表于 2021-12-21 10:38

这个你可以直接用自带的box volume实现

DungDaj 发表于 2021-12-21 10:48

可以用frame debuger查看它是否执行了。或者在他的excute方法里加打印,看是哪个地方没执行下去。

Ilingis 发表于 2021-12-21 10:51

hologramBlock= stack.GetComponent< 那里前面小写的hologramBlock是从哪里来的 为啥我的总是下划线报错

FeastSC 发表于 2021-12-21 10:52

public sealed class HologramBlock : VolumeComponent, IPostProcessComponent的实例,是一个私有的变量,可以下载我的源码看看哈。
页: [1] 2
查看完整版本: Unity的URP的自定义后处理效果(二) 之 自定义扩展 ...