URP自定义后处理(相机滤镜)

前言

之前做游戏一直想弄个可以实时触发相机滤镜的效果,自处找了教程和资料,想要做到自定义效果的话最好办法是在unity 内部实现,这个办法比较硬核,其实不适合我这样的小白,所以我在实现的过程中非常痛苦,我用的unity URP 模式其实自带后处理隐藏菜单的,这功能可以通过添加Volume组件启用,但是要在底层代码中自己写个脚本,类似在底层接一根线出来。

一、自定义render feature

先从底层接线,自定义render feature,怎么定义呢,先找到你的渲染管线,打开菜单栏edit—project setting - Graphics 里面查看你的初始管线,一般是高级管线(high quality),你如果去找setting文件夹里会发现有低中高三个管线,新版 URP模式的渲染管线有一个很奇怪的bug,就是你在edit里直接换管线不会有任何变化,(不知道这是不是unity的bug)有个在游戏公司的学姐告诉我他们一般会把highQ的删了重新设置一个管线,参数之类的自己可以看官方介绍视频去调整,这里不说了。

找到highQuality管线之后双击就能看到它在setting文件夹的位置,inspector栏中再点击它的“儿子”,(这个高清管线的核心管线就是这个 forwardRender,双击它)

好的,接下来就是关键的一步了,这里我放了个油管大神的教学视频,你同时还需要它的重要道具一个叫blit的脚本:

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
// this was used on https://gamedevbill.com, but originally taken from https://cyangamedev.wordpress.com/2020/06/22/urp-post-processing/
// Saved in Blit.cs
public class Blit : ScriptableRendererFeature
{

    public class BlitPass : ScriptableRenderPass
    {
        public enum RenderTarget
        {
            Color,
            RenderTexture,
        }

        public Material blitMaterial = null;
        public int blitShaderPassIndex = 0 ;
        public FilterMode filterMode { get; set; }

        private RenderTargetIdentifier source { get; set; }
        private RenderTargetHandle destination { get; set; }

        RenderTargetHandle m_TemporaryColorTexture;
        string m_ProfilerTag;

        public BlitPass(RenderPassEvent renderPassEvent, Material blitMaterial, int blitShaderPassIndex, string tag)
        {
            this.renderPassEvent = renderPassEvent;
            this.blitMaterial = blitMaterial;
            this.blitShaderPassIndex = blitShaderPassIndex;
            m_ProfilerTag = tag;
            m_TemporaryColorTexture.Init("_TemporaryColorTexture");
        }

        public void Setup(RenderTargetIdentifier source, RenderTargetHandle destination)
        {
            this.source = source;
            this.destination = destination;
        }

        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            CommandBuffer cmd = CommandBufferPool.Get(m_ProfilerTag);

            RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
            opaqueDesc.depthBufferBits = 0;

            // Can't read and write to same color target, use a TemporaryRT
            if (destination == RenderTargetHandle.CameraTarget)
            {
                cmd.GetTemporaryRT(m_TemporaryColorTexture.id, opaqueDesc, filterMode);
                Blit(cmd, source, m_TemporaryColorTexture.Identifier(), blitMaterial, blitShaderPassIndex);
                Blit(cmd, m_TemporaryColorTexture.Identifier(), source);
            }
            else
            {
                Blit(cmd, source, destination.Identifier(), blitMaterial, blitShaderPassIndex);
            }

            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
        }

        public override void FrameCleanup(CommandBuffer cmd)
        {
            if (destination == RenderTargetHandle.CameraTarget)
                cmd.ReleaseTemporaryRT(m_TemporaryColorTexture.id);
        }
    }

    [System.Serializable]
    public class BlitSettings
    {
        public RenderPassEvent Event = RenderPassEvent.AfterRenderingOpaques;

        public Material blitMaterial = null;
        public int blitMaterialPassIndex = 0;
        public Target destination = Target.Color;
        public string textureId = "_BlitPassTexture";
    }

    public enum Target
    {
        Color,
        Texture
    }

    public BlitSettings settings = new BlitSettings();
    RenderTargetHandle m_RenderTextureHandle;

    BlitPass blitPass;
    private volumepass Volumepass;

    public override void Create()
    {
        var passIndex = settings.blitMaterial != null ? settings.blitMaterial.passCount - 1 : 1;
        settings.blitMaterialPassIndex = Mathf.Clamp(settings.blitMaterialPassIndex, -1, passIndex);
        blitPass = new BlitPass(settings.Event, settings.blitMaterial, settings.blitMaterialPassIndex, name);
        m_RenderTextureHandle.Init(settings.textureId);
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        var src = renderer.cameraColorTarget;
        var dest = (settings.destination == Target.Color) ? RenderTargetHandle.CameraTarget : m_RenderTextureHandle;

        if (settings.blitMaterial == null)
        {
            Debug.LogWarningFormat("Missing Blit Material. {0} blit pass will not execute. Check for missing reference in the assigned renderer.", GetType().Name);
            return;
        }

        if (!renderingData.cameraData.postProcessEnabled) return;
        var stack = VolumeManager.instance.stack;
        Volumepass = stack.GetComponent<volumepass>();
        if (Volumepass == null) { return; }
        if (!Volumepass.IsActive()) return;


        blitPass.Setup(src, dest);
        renderer.EnqueuePass(blitPass);
    }
}

https://www.youtube.com/watch?v=4apbNiPC3yQ&t=9s​www.youtube.com/watch?v=4apbNiPC3yQ&t=9s

脚本具体的细节我就不说了,其实代码之类的我也不是很懂,劝小白真的不要像我一样随意去碰unity高阶外挂式脚本。。。。如果真的求知欲很重的话,建议小白先努力学习一下c#,我这里也是参考了这位大佬的文章:

bzyzhang:练习项目(十二):URP中自定义后处理实例​zhuanlan.zhihu.com/p/348978998?utm_source=wechat_session&utm_medium=social&utm_oi=684404484239659008

把脚本仍进unity,然后继续上面步骤,点击add render feature,blit出来了,点击:

接下来出现你的自定义参数模块了,为了易于辨认,然后我改了newblit的名字,你们随意,blit material一栏就是你们可以设置自己的屏幕滤镜(shader)材质(没错!!!就是用shader的办法自己做屏幕滤镜!!!)我这里自己做了个材质,用了特殊shader,shader怎么写具体可以参考油管大佬GameDevBill的视频。

一些滤镜你们去尝试吧,但是现在还有一个问题,如果想让这个滤镜偶尔出来一下并且和玩家交互要怎样实现,你不可能一直在setting里面的forward管线组件里去开关,所以在获得这个功能之后需要做一个和外界连接的触发开关。

接下来这里具体说一下接下来怎么做触发,在我原本的设想中,是让player触碰某个物件然后做一个触发效果,出现屏幕滤镜,现在底层的线拉出来了,怎么和前台操作界面关联呢。

二、volume pass组件

接下来要在hierarchy新建一个volume,然后添加一个叫VolumPass的脚本就可以实现了,脚本如下:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace UnityEngine.Rendering.Universal
{
    [System.Serializable,VolumeComponentMenu("volumepass")]
    public class volumepass : VolumeComponent, IPostProcessComponent
    {
        [Tooltip("是否开启效果")]
        public BoolParameter enableEffect = new BoolParameter(true, true);
        public FloatParameter GrabWidth = new FloatParameter(2, true);
        public FloatParameter GrabHeight = new FloatParameter(2, true);
      
        public bool IsActive() => enableEffect == true;
        public bool IsTileCompatible() => false;
    }
}

在volume组件内置模块点击 add override,出现这个脚本选项

volume pass的具体作用就是给render feature 接一个开关,实现了以后长这样,你可以自主选择是否打勾开启效果:

还记得我想做的触发效果吗,还没完,最后一步了!!!走到这里真的心累,不过还好最难的已经过去了。。。。

还记得刚刚hierarchy里的volume吗,右键添加一个cube,cube设置不可见,目的是为了做trigger触发机关,所以别忘了添加boxcollider,(其实直接在volume里面添加collider组件也可以,我这里是为了方便识别。。。)

所以这里还要再添加一个叫 posttrigger的脚本组件,这个脚本应该是最基本的c#内容了,别说你看不懂了)主要功能就是当player碰到这个cube就会触发这个滤镜:

using System.Collections;
using System.Collections.Generic;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering;

using UnityEngine;


public class posttrigger : MonoBehaviour
{


    private Volume volume;
    private volumepass volumpas;

    // Start is called before the first frame update
    private void Start()
    {
        volume = GetComponent<Volume>();
        volume.profile.TryGet(out volumpas);
    }

    // Update is called once per frame
    private void OnTriggerExit(Collider other)
    {
        if (other.tag == "Player")
        {
            volumpas.enableEffect.Override(false);
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player")
        {

            volumpas.enableEffect.Override(true);
        }
    }
}
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值