URP屏幕后处理

URP屏幕后处理

由于Unity对URP的屏幕后处理需集成要到2019.4版本后才稳定,所以升级Unity版本到2019.4.9

Post Processing

在创建我们自己屏幕后处理系统前,我们需要先学会使用UnityURP管线中集成的屏后处理系统。

低于Unity2019.4版本的URP是没有Volume组件的

  1. 我们可以将Volume组件添加到GameObject,或者在Hierarchy面板中中右键单击并在Volume标题下选择某些内容。

    • Global Volume代表后处理效果应用所有摄像机
    • Box Volume会在场景中创建一个盒子区域(该区域在Scene中用BoxCollider组件显示轮廓),只有摄像机进入该区域才会应用后处理效果
    • Sphere Volume是一个球形区域,效果同上
    • Convex Mesh Volume允许我们使用自定义的网格区域
  2. Volume组件上有如下参数

    属性 描述
    Mode Global:使Volume无边界并影响场景中的每一个摄像机。 Local:为Volume指定边界,Volume只影响在边界内部的射线机。
    Blend Distance URP从Volume Collider开始融合的最远距离。0表示URP立即应用Volume的Override;该属性只有在非Global Volume下才出现
    Weight Volume在场景中的影响值
    Priority 当Volume在场景中有多个Volume时,URP通过此值决定使用哪一个Volume。URP优先使用priority更高的
    Profile Profile Asset存储URP处理Volume的数据
  3. 点击Profile旁的New按钮,新建一个后处理配置文件,然后我们就可以通过Add Override添加各种URP内置后处理效果了。

    有关URP后处理效果清单见https://docs.unity3d.com/Packages/[email protected]/manual/EffectList.html

  4. 当我们为配置文件添加色差效果Chromatic Aberration,并开启效果后会发现,在Scene视图中已经出现了效果,但在Game视图中却没有任何变化。

    接下来我们需要将场景中的摄像机开启屏幕后处理,选中场景中摄像机,在Inspector面板中找到Rendering栏下的Post Processing开关,勾选上。

    现在Game中已经出现效果

这里列出URP中可用的后处理效果

  • 高光(Bloom)高光效果从图片中的光亮部分的边缘长生羽毛形状的光。并且产生遮挡。Bloom中有一个Lens Dirt特征,这个可以用于产生全屏光斑。
  • 通道混合器(Channel Mixer):通道混合器提供每个颜色通道的信息,通过调整不同通道的影响可以实现不同的屏幕效果。
  • 色差(Chromatic Aberration):仿真实现了真实摄像机无法将所有颜色融合到一个点。
  • 颜色调整(Color Adjustments) 调整整体的色调、亮度和对比度
  • 颜色曲线(Color Curves):通过曲线的方式调整色相、饱和度或者亮度。
  • 景深(Depth Of Field): 景深组件模拟相机镜头的聚焦特性。现实生活中,相机只能对特定距离清晰对焦。距离摄像机较远和较近的物体无法聚焦。根据模糊效果引入了Bokeh(散景),Bokeh指当图像失焦时出现在明亮区域的视觉伪像。
  • 胶片颗粒(Film Grain):Film Grain是加工摄影胶片的随机光学纹理,因为存在卤化银产生的金属银或染料云的小颗粒,其已经接收到了足够的光子。
  • 镜头变形(Lens Distortion):扭曲图片模拟真实的镜头
  • 提升伽马增益(Lift Gamma Gain):调整图像的lift、Gamma、Gain
  • 运动模糊(Motion Blur):模拟真实摄像机在拍摄运动物体时,运动速度大于曝光时间的时候出现的模糊效果。
  • 帕尼尼投影(Panini Projection):一种圆柱形投影,保持垂直方向上的垂直,在渲染大视角的时候提供更好的效果。视角120度无后处理。
  • 阴影 中间色调 高光(Shadows Midtones Hightlights):使用圆环工具进行三个熟知的调整
  • 分割色调(Split Toning):根据亮度值对不同的区域进行着色,帮助我们实现更加独特的视觉效果。可以通过此效果实现阴影和高光部分不同的色调。
  • 色调映射(Tonemapping):在屏幕上重新映射HDR的过程
  • 玩具相机(Vignette):暗角效果和去饱和,现实中造成这种结果的原因通常是堆叠滤镜,辅助镜片和不当的遮光罩。
  • 白平衡(White Balance):应用白平衡组件,消除不真实的偏色,从而使白色在图像中呈现白色。我们也可以用来呈现冷色或者暖色的渲染效果。

扩展Volume

既然我们已经学会了如何使用Volume组件,那么我们接下来所需做的就是如何去扩展该组件,增加我们的屏幕后处理效果。

解读Volume运作

首先我们可以在URP包目录下的[email protected]\Runtime\Overrides文件夹中找到,包含了所有为Volume配置文件添加的效果的属性脚本。为方便做比较,我们打开目录中的Bloom脚本,可以发现脚本中只有属性,且这些属性名对应了Volume组件中的效果属性。

然后我们可以找到一个文件[email protected]\Runtime\Passes\PostProcessPass.cs,这个PostProcessPass就是URP内置后处理的核心,它继承了ScriptableRenderPass,方便在URP管线下扩展额外的Pass通道。

所有继承ScriptableRenderPass组件的子类需要实现一个方法:

public abstract void Execute(ScriptableRenderContext context, ref RenderingData renderingData);

其实该方法的作用和我们在Built-in管线下写后处理框架时用到的oid OnRenderImage(RenderTexture src, RenderTexture dest)方法功能是一样的,在每帧渲染,渲染管线都会调用该方法。然后我们会在该方法里通过Shader的Pass进行后处理。

其中的ScriptableRenderContext 参数我们在前面SRP尝试章节里已经见识过,它是定义自定义渲染管道使用的状态和绘制命令,使用该参数我们可以进行额外的Pass绘制。RenderingData也好理解,就是当前调用该方法的绘制信息,其中包含调用该次绘制的相机信息、光源信息、阴影信息等;注意,该参数带有ref关键字,说明我们是可以直接对这些绘制信息进行修改,并影响后续管线的成像的。

可编程渲染功能

从前面URP源代码可以看出,其实URP的后处理方式和Built-in管线下的后处理方式思路相同,都是通过C#代码去调用Shader进行后处理。但有一点不同,在Built-in管线下,后处理脚本我们是继承Mono的,直接将脚本挂在对象上就可运作(mono自动调用OnRenderImage方法);但是URP下继承的是ScriptableRenderPass,该类是一个对象,我们由如何让他运作呢。

在我们URP管线的Inspector面板下,RendererList中有我们当前真正使用的渲染器列表,

跳转到使用渲染器的Inspector面板后我们可以看到Post ProcessData,Post ProcessData正是URP用于引用各种后处理Shader的对象。

而下方的Rederer Features则是我们的重点,它允许我们添加额外的渲染功能,也就是我们可以写一个功能脚本,在脚本中创建我们自定义的ScriptableRenderPass对象,然后添加到该列表中,后续URP管线在渲染过程中会自动调用该功能,也就是间接调用我们自定义的后处理对象,以此实现URP管线下的自定义后处理。

想要编写额外的渲染功能,我们需要将脚本继承ScriptableRendererFeature,并且实现AddRenderPassesCreate函数。Create方法会在该功能初始化时调用,我们可以在方法中创建我们的后处理对象。AddRenderPasses将会在相机被设置渲染器时调用。

调整屏幕亮度、饱和度、对比度

既然运作方式解析了,那么进入实操环节。

创建Shader

创建Shader-Brightness Saturation And Contrast,该Shader比较简单,没有什么新的语法。

在不知道该Shader是否正确的情况下,可以创建一个Plane拖入到摄像机视野内(这很重要),再创建该Shader的材质,将材质赋给Palne。最后再给材质添加纹理,调整不同的参数,在Game中看效果是否正确。

Shader "URP/Brightness Saturation And Contrast"
{
    Properties
    {
        // 基础纹理
        _MainTex ("Base (RGB)", 2D) = "white" { }
        // 亮度
        _Brightness ("Brightness", Float) = 1
        // 饱和度
        _Saturation ("Saturation", Float) = 1
        // 对比度
        _Contrast ("Contrast", Float) = 1
    }
    SubShader
    {
        Tags { "RenderPipeline" = "UniversalPipeline" }
        
        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        
        CBUFFER_START(UnityPerMaterial)
        float4 _MainTex_ST;
        half _Brightness;
        half _Saturation;
        half _Contrast;
        CBUFFER_END
        
        ENDHLSL
        
        Pass
        {
            // 开启深度测试 关闭剔除 关闭深度写入
            ZTest Always Cull Off ZWrite Off
            
            HLSLPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            
            // 声明纹理
            TEXTURE2D(_MainTex);
            // 声明采样器
            SAMPLER(sampler_MainTex);
            
            struct a2v
            {
                float4 vertex: POSITION;
                
                float4 texcoord: TEXCOORD0;
            };
            
            struct v2f
            {
                float4 pos: SV_POSITION;
                half2 uv: TEXCOORD0;
            };
            
            v2f vert(a2v v)
            {
                v2f o;
                
                o.pos = TransformObjectToHClip(v.vertex.xyz);
                
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                
                return o;
            }
            
            half4 frag(v2f i): SV_Target
            {
                // 纹理采样
                half4 renderTex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
                
                // 调整亮度 = 原颜色 * 亮度值
                half3 finalColor = renderTex.rgb * _Brightness;
                
                // 调整饱和度
                // 亮度值(饱和度为0的颜色) = 每个颜色分量 * 特定系数
                half luminance = 0.2125 * renderTex.r + 0.7154 * renderTex.g + 0.0721 * renderTex.b;
                half3 luminanceColor = half3(luminance, luminance, luminance);
                // 插值亮度值和原图
                finalColor = lerp(luminanceColor, finalColor, _Saturation);
                
                // 调整对比度
                // 对比度为0的颜色
                half3 avgColor = half3(0.5, 0.5, 0.5);
                finalColor = lerp(avgColor, finalColor, _Contrast);
                
                return half4(finalColor, renderTex.a);
            }            
            ENDHLSL            
        }
    }
    
    Fallback Off
}

创建属性参数脚本

  1. 编写类BrightnessSaturationContrast继承VolumeComponent组件和IPostProcessComponent接口,用以继承Volume框架。

  2. 在方法中我们根据材质所需的三个参数亮度、饱和度、对比度创建三个属性。

            public ClampedFloatParameter brightness = new ClampedFloatParameter(0f, 0, 3);
            public ClampedFloatParameter saturation = new ClampedFloatParameter(0f, 0, 3);
            public ClampedFloatParameter contrast = new ClampedFloatParameter(0f, 0, 3);
    

创建可编程渲染Pass脚本

  1. 创建C#脚本AdditionPostProcessPass,继承自ScriptableRenderPass,并实现Execute方法。

  2. 创建所需属性

            //标签名,用于续帧调试器中显示缓冲区名称
            const string CommandBufferTag = "AdditionalPostProcessing Pass";
        
            // 用于后处理的材质
            public Material m_Material;
    
            // 属性参数组件
            BrightnessSaturationContrast m_BrightnessSaturationContrast;
    
            // 颜色渲染标识符
            RenderTargetIdentifier m_ColorAttachment;
            // 临时的渲染目标
            RenderTargetHandle m_TemporaryColorTexture01;
    
  3. 创一个入口函数,用于后续渲染管线功能脚本写入参数

            // 设置渲染参数
            public void Setup(RenderTargetIdentifier _ColorAttachment, Material Material)
            {
                this.m_ColorAttachment = _ColorAttachment;
    
                m_Material = Material;
            } 
    
  4. 修改Execute方法,在方法中我们通过VolumeManager.instance.stack单例获取Volume框架中所有的堆栈,在从堆栈中获取我们上一部创建的属性参数组件,由于Execute是每帧调用,所有该组件也是实时更新的。

    然后我们使用标签名获取一个命令缓冲区,将该命令缓冲区与Execute的参数RenderingData渲染信息一起交给Render方法进行后处理。

    在后处理完成后我们调用context.ExecuteCommandBuffer(cmd);方法执行该命令缓冲区,最后释放内存。

            public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
            {
                // 从Volume框架中获取所有堆栈
                var stack = VolumeManager.instance.stack;
                // 从堆栈中查找对应的属性参数组件
                m_BrightnessSaturationContrast = stack.GetComponent();
    
                // 从命令缓冲区池中获取一个带标签的命令缓冲区,该标签名可以在后续帧调试器中见到
                var cmd = CommandBufferPool.Get(CommandBufferTag);
    
                // 调用渲染函数
                Render(cmd, ref renderingData);
    
                // 执行命令缓冲区
                context.ExecuteCommandBuffer(cmd);
                // 释放命令缓存
                CommandBufferPool.Release(cmd);
                // 释放临时RT
                cmd.ReleaseTemporaryRT(m_TemporaryColorTexture01.id);
            }
    
  5. 接下来我们编写渲染方法Render,在Render方法中我们获取属性参数组件中的参数,赋值给材质。

    然后通过RenderingData对象中的相机信息创建一个临时缓冲区,然后将颜色渲染器中的颜色通过Shader进行计算后保存到临时缓冲区中。

    最后在从临时缓冲区中读取出来返还到主纹理中。

         // 渲染
         void Render(CommandBuffer cmd, ref RenderingData renderingData)
         {       
             // VolumeComponent是否开启,且非Scene视图摄像机
             if (m_BrightnessSaturationContrast.IsActive() && !renderingData.cameraData.isSceneViewCamera)
             {
                 // 写入参数
                 m_Material.SetFloat("_Brightness", m_BrightnessSaturationContrast.brightness.value);
                 m_Material.SetFloat("_Saturation", m_BrightnessSaturationContrast.saturation.value);
                 m_Material.SetFloat("_Contrast", m_BrightnessSaturationContrast.contrast.value);
    
                 // 获取目标相机的描述信息
                 RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
                 // 设置深度缓冲区
                 opaqueDesc.depthBufferBits = 0;
                 // 通过目标相机的渲染信息创建临时缓冲区
                 cmd.GetTemporaryRT(m_TemporaryColorTexture01.id, opaqueDesc);
    
                 // 通过材质,将计算结果存入临时缓冲区
                 cmd.Blit(m_ColorAttachment, m_TemporaryColorTexture01.Identifier(), m_Material);
                 // 再从临时缓冲区存入主纹理
                 cmd.Blit(m_TemporaryColorTexture01.Identifier(), m_ColorAttachment);
             }
    

创建可编程渲染功能脚本

  1. 创建类AdditionPostProcessRendererFeature,继承ScriptableRendererFeature抽象类,并实现AddRenderPassesCreate方法

  2. 设置三个参数,公开属性Shader、私有属性AdditionPostProcessPass对象和材质

            // 用于后处理的Shader 
            public Shader shader;
            // 后处理Pass
            AdditionPostProcessPass postPass;
            // 根据Shader生成的材质
            Material _Material=null;
    
  3. 修改Create方法,在方法中创建AdditionPostProcessPass对象,并修改该类的渲染时机,在透明物体渲染后。

  4. 修改AddRenderPasses方法,在方法中通过Shader创建材质,然后从ScriptableRenderer参数中获取主纹理;接下来将纹理和材质传入AdditionPostProcessPass对象中,最后将该对象添加到渲染管线队列中。

            public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
            {
                // 检测Shader是否存在
                if (shader == null)
                    return;
    
                // 创建材质
                if (_Material==null)
                    _Material = CoreUtils.CreateEngineMaterial(shader);
    
                // 获取当前渲染相机的目标颜色,也就是主纹理
                var cameraColorTarget = renderer.cameraColorTarget;     
    
                // 设置调用后处理Pass
                postPass.Setup(cameraColorTarget, _Material);
                
                // 添加该Pass到渲染管线中
                renderer.EnqueuePass(postPass);
            }
    

配置管线

  1. 配置Volume组件,将BrightnessSaturationContrast属性参数组件添加到效果列表中

  2. 配置管线,在URP管线的配置文件中添加我们编写的渲染功能AdditionPostProcessRendererFeature

  3. 赋值Shader

  4. 修改属性参数组件的参数,以降低饱和度

完整代码

Shader——Brightness Saturation And Contrast

Shader "URP/Brightness Saturation And Contrast"
{
    Properties
    {
        // 基础纹理
        _MainTex ("Base (RGB)", 2D) = "white" { }
        // 亮度
        _Brightness ("Brightness", Float) = 1
        // 饱和度
        _Saturation ("Saturation", Float) = 1
        // 对比度
        _Contrast ("Contrast", Float) = 1
    }
    SubShader
    {
        Tags { "RenderPipeline" = "UniversalPipeline" }
        
        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        
        CBUFFER_START(UnityPerMaterial)
        float4 _MainTex_ST;
        half _Brightness;
        half _Saturation;
        half _Contrast;
        CBUFFER_END
        
        ENDHLSL
        
        Pass
        {
            // 开启深度测试 关闭剔除 关闭深度写入
            ZTest Always Cull Off ZWrite Off
            
            HLSLPROGRAM
            
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            
            // 声明纹理
            TEXTURE2D(_MainTex);
            // 声明采样器
            SAMPLER(sampler_MainTex);
            
            struct a2v
            {
                float4 vertex: POSITION;
                
                float4 texcoord: TEXCOORD0;
            };
            
            struct v2f
            {
                float4 pos: SV_POSITION;
                half2 uv: TEXCOORD0;
            };
            
            v2f vert(a2v v)
            {
                v2f o;
                
                o.pos = TransformObjectToHClip(v.vertex.xyz);
                
                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                
                return o;
            }
            
            half4 frag(v2f i): SV_Target
            {
                // 纹理采样
                half4 renderTex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
                
                // 调整亮度 = 原颜色 * 亮度值
                half3 finalColor = renderTex.rgb * _Brightness;
                
                // 调整饱和度
                // 亮度值(饱和度为0的颜色) = 每个颜色分量 * 特定系数
                half luminance = 0.2125 * renderTex.r + 0.7154 * renderTex.g + 0.0721 * renderTex.b;
                half3 luminanceColor = half3(luminance, luminance, luminance);
                // 插值亮度值和原图
                finalColor = lerp(luminanceColor, finalColor, _Saturation);
                
                // 调整对比度
                // 对比度为0的颜色
                half3 avgColor = half3(0.5, 0.5, 0.5);
                finalColor = lerp(avgColor, finalColor, _Contrast);
                
                return half4(finalColor, renderTex.a);
            }            
            ENDHLSL            
        }
    }
    
    Fallback Off
}

BrightnessSaturationContrast

using System;

// 通用渲染管线程序集
namespace UnityEngine.Rendering.Universal
{
    // 实例化类     添加到Volume组件菜单中
    [Serializable, VolumeComponentMenu("Addition-Post-processing/BrightnessSaturationContrast")]
    // 继承VolumeComponent组件和IPostProcessComponent接口,用以继承Volume框架
    public class BrightnessSaturationContrast : VolumeComponent, IPostProcessComponent
    {
        // 在框架下的属性与Unity常规属性不一样,例如 Int 由 ClampedIntParameter 取代。
        public ClampedFloatParameter brightness = new ClampedFloatParameter(0f, 0, 3);
        public ClampedFloatParameter saturation = new ClampedFloatParameter(0f, 0, 3);
        public ClampedFloatParameter contrast = new ClampedFloatParameter(0f, 0, 3);
        // 实现接口
        public bool IsActive()
        {
            return active;
        }

        public bool IsTileCompatible()
        {
            return false;
        }
    }
}

AdditionPostProcessPass

namespace UnityEngine.Rendering.Universal
{
    /// 
    /// 附加的后处理Pass
    /// 
    public class AdditionPostProcessPass : ScriptableRenderPass
    {
        //标签名,用于续帧调试器中显示缓冲区名称
        const string CommandBufferTag = "AdditionalPostProcessing Pass";
    
        // 用于后处理的材质
        public Material m_Material;

        // 属性参数组件
        BrightnessSaturationContrast m_BrightnessSaturationContrast;

        // 颜色渲染标识符
        RenderTargetIdentifier m_ColorAttachment;
        // 临时的渲染目标
        RenderTargetHandle m_TemporaryColorTexture01;

        public AdditionPostProcessPass()
        {          
            m_TemporaryColorTexture01.Init("_TemporaryColorTexture1");
        }

        // 设置渲染参数
        public void Setup(RenderTargetIdentifier _ColorAttachment, Material Material)
        {
            this.m_ColorAttachment = _ColorAttachment;

            m_Material = Material;
        }     

        /// 
        /// URP会自动调用该执行方法
        /// 
        /// 
        /// 
        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            // 从Volume框架中获取所有堆栈
            var stack = VolumeManager.instance.stack;
            // 从堆栈中查找对应的属性参数组件
            m_BrightnessSaturationContrast = stack.GetComponent();

            // 从命令缓冲区池中获取一个带标签的渲染命令,该标签名可以在后续帧调试器中见到
            var cmd = CommandBufferPool.Get(CommandBufferTag);

            // 调用渲染函数
            Render(cmd, ref renderingData);

            // 执行命令缓冲区
            context.ExecuteCommandBuffer(cmd);
            // 释放命令缓存
            CommandBufferPool.Release(cmd);
            // 释放临时RT
            cmd.ReleaseTemporaryRT(m_TemporaryColorTexture01.id);
        }

        // 渲染
        void Render(CommandBuffer cmd, ref RenderingData renderingData)
        {       
            // VolumeComponent是否开启,且非Scene视图摄像机
            if (m_BrightnessSaturationContrast.IsActive() && !renderingData.cameraData.isSceneViewCamera)
            {
                // 写入参数
                m_Material.SetFloat("_Brightness", m_BrightnessSaturationContrast.brightness.value);
                m_Material.SetFloat("_Saturation", m_BrightnessSaturationContrast.saturation.value);
                m_Material.SetFloat("_Contrast", m_BrightnessSaturationContrast.contrast.value);

                // 获取目标相机的描述信息
                RenderTextureDescriptor opaqueDesc = renderingData.cameraData.cameraTargetDescriptor;
                // 设置深度缓冲区
                opaqueDesc.depthBufferBits = 0;
                // 通过目标相机的渲染信息创建临时缓冲区
                cmd.GetTemporaryRT(m_TemporaryColorTexture01.id, opaqueDesc);

                // 通过材质,将计算结果存入临时缓冲区
                cmd.Blit(m_ColorAttachment, m_TemporaryColorTexture01.Identifier(), m_Material);
                // 再从临时缓冲区存入主纹理
                cmd.Blit(m_TemporaryColorTexture01.Identifier(), m_ColorAttachment);
            }
        }       
    }
}

AdditionPostProcessRendererFeature

namespace UnityEngine.Rendering.Universal
{
    /// 
    /// 可编程渲染功能
    /// 必须要继承ScriptableRendererFeature抽象类,
    /// 并且实现AddRenderPasses跟Create函数
    /// 
    public class AdditionPostProcessRendererFeature : ScriptableRendererFeature
    {
        // 用于后处理的Shader 
        public Shader shader;
        // 后处理Pass
        AdditionPostProcessPass postPass;
        // 根据Shader生成的材质
        Material _Material=null;

        //在这里,您可以在渲染器中注入一个或多个渲染通道。
        //每个摄像机设置一次渲染器时,将调用此方法。
        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            // 检测Shader是否存在
            if (shader == null)
                return;

            // 创建材质
            if (_Material==null)
                _Material = CoreUtils.CreateEngineMaterial(shader);

            // 获取当前渲染相机的目标颜色,也就是主纹理
            var cameraColorTarget = renderer.cameraColorTarget;     

            // 设置调用后处理Pass
            postPass.Setup(cameraColorTarget, _Material);
            
            // 添加该Pass到渲染管线中
            renderer.EnqueuePass(postPass);
        }

       
        // 对象初始化时会调用该函数
        public override void Create()
        {
            postPass = new AdditionPostProcessPass();
            // 渲染时机 = 透明物体渲染后
            postPass.renderPassEvent = RenderPassEvent.AfterRenderingTransparents; 
        }
    }
}

你可能感兴趣的:(URP屏幕后处理)