版本:Unity 6 (6000.0)
语言:English
在 URP 兼容模式下编写 Scriptable Render Pass
URP 兼容模式下执行全屏 Blit 的示例

URP 兼容模式下完整 Scriptable Renderer Feature 示例

本节介绍如何为 URP 渲染器创建完整的Scriptable Renderer Feature

注意:Unity 不再开发或改进不使用渲染图 API的渲染路径。开发新的图形功能时,请改用渲染图 API。要使用本页面的说明,请在 URP 图形设置(项目设置 > 图形)中启用兼容模式(渲染图已禁用)

本演练包含以下部分

此示例实现概述

此页面上的示例工作流程实现了自定义渲染器功能,该功能使用自定义渲染传递摄像机一个组件,用于创建场景中特定视点的图像。输出要么绘制到屏幕上,要么捕获为纹理。 更多信息
参见词汇表
输出添加模糊效果。

该实现包含以下部分

  • 一个ScriptableRendererFeature实例,每帧将一个ScriptableRenderPass实例排队。

  • 一个ScriptableRenderPass实例,执行以下步骤

    • 使用RenderTextureDescriptor API 创建一个临时渲染纹理一种特殊的纹理类型,在运行时创建和更新。要使用它们,首先创建一个新的渲染纹理,并指定其中一个摄像机渲染到其中。然后,您可以像使用常规纹理一样在材质中使用渲染纹理。 更多信息
      参见词汇表

    • 使用RTHandleBlit API 将自定义着色器的两个传递应用于摄像机输出。

创建示例场景和游戏对象

要为该示例工作流程设置项目

  1. 创建一个新的场景场景包含游戏的环境和菜单。可以将每个唯一的场景文件视为一个唯一的关卡。在每个场景中,您放置环境、障碍物和装饰,从本质上讲是分段设计和构建游戏。 更多信息
    参见词汇表

  2. 创建两个游戏对象:一个名为Cube的立方体游戏对象Unity 场景中的基本对象,可以表示角色、道具、场景、摄像机、路径点等。游戏对象的功能由附加到它的组件定义。 更多信息
    参见词汇表
    和一个名为Sphere的球体游戏对象。

  3. 创建两个材质,并使用一个允许您指定基本颜色的着色器(例如,Universal Render Pipeline/Lit着色器)。将材质命名为BlueRed,并将材质的基本颜色分别设置为蓝色和红色。

  4. Red材质分配给立方体,将Blue材质分配给球体。

  5. 定位摄像机,使其视野中包含立方体和球体。

示例场景应如下面的图像所示

Sample scene
示例场景

创建可脚本化的渲染器功能并将其添加到通用渲染器

  1. 创建一个新的 C# 脚本,并将其命名为BlurRendererFeature.cs

  2. 在脚本中,删除 Unity 在BlurRendererFeature类中插入的代码。

  3. 添加以下using指令

    using UnityEngine.Rendering.Universal;
    
  4. 创建继承自ScriptableRendererFeature类的BlurRendererFeature类。

    public class BlurRendererFeature : ScriptableRendererFeature    
    
  5. BlurRendererFeature类中,实现以下方法

    • Create:Unity 在以下事件中调用此方法

      • 渲染器功能首次加载时。

      • 启用或禁用渲染器功能时。

      • 更改渲染器功能的检查器一个 Unity 窗口,显示有关当前选定的游戏对象、资源或项目设置的信息,允许您检查和编辑值。 更多信息
        参见词汇表
        中的属性时。

    • AddRenderPasses:Unity 每帧调用一次此方法,每个摄像机一次。此方法允许您将ScriptableRenderPass实例注入可脚本化的渲染器。

现在您拥有了带有其主要方法的自定义BlurRendererFeature渲染器功能。

以下是此步骤的完整代码。

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

public class BlurRendererFeature : ScriptableRendererFeature
{
    public override void Create()
    {

    }

    public override void AddRenderPasses(ScriptableRenderer renderer,
        ref RenderingData renderingData)
    {

    }
}

将渲染器功能添加到通用渲染器资源

将您创建的渲染器功能添加到通用渲染器资源。有关如何执行此操作的信息,请参阅页面如何将渲染器功能添加到渲染器

创建可脚本化的渲染传递

本节演示如何创建可脚本化的渲染传递并将其实例排队到可脚本化的渲染器中。

  1. 创建一个新的 C# 脚本,并将其命名为BlurRenderPass.cs

  2. 在脚本中,删除 Unity 在BlurRenderPass类中插入的代码。添加以下using指令

    using UnityEngine.Rendering;
    using UnityEngine.Rendering.Universal;
    
  3. 创建继承自ScriptableRenderPass类的BlurRenderPass类。

    public class BlurRenderPass : ScriptableRenderPass
    
  4. 向类中添加Execute方法。Unity 每帧调用一次此方法,每个摄像机一次。此方法允许您实现可脚本化的渲染传递的渲染逻辑。

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    { }
    

以下是本节中 BlurRenderPass.cs 文件的完整代码。

using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class BlurRenderPass : ScriptableRenderPass
{
    public override void Execute(ScriptableRenderContext context,
        ref RenderingData renderingData)
    {
        
    }
}

实现自定义渲染传递的设置

本节演示如何实现自定义模糊渲染传递的设置。

  1. 此示例中的渲染器功能使用着色器在 GPU 上运行的程序。 更多信息
    参见词汇表
    ,该着色器在一遍中水平执行模糊,在另一遍中垂直执行模糊。为了让用户控制每个传递的模糊值,请将以下BlurSettings类添加到BlurRendererFeature.cs脚本中。

    [Serializable]
    public class BlurSettings
    {
        [Range(0,0.4f)] public float horizontalBlur;
        [Range(0,0.4f)] public float verticalBlur;
    }
    
  2. BlurRendererFeature类中,声明以下字段

    [SerializeField] private BlurSettings settings;
    [SerializeField] private Shader shader;
    private Material material;
    private BlurRenderPass blurRenderPass;
    
  3. BlurRenderPass类中,添加设置、材质和使用这些字段的构造函数的字段。

    private BlurSettings defaultSettings;
    private Material material;
    
    public BlurRenderPass(Material material, BlurSettings defaultSettings)
    {
        this.material = material;
        this.defaultSettings = defaultSettings;        
    }
    
  4. BlurRenderPass类中,添加RenderTextureDescriptor字段并在构造函数中初始化它

    using UnityEngine;
    
    private RenderTextureDescriptor blurTextureDescriptor;
    
    public BlurRenderPass(Material material, BlurSettings defaultSettings)
    {
        this.material = material;
        this.defaultSettings = defaultSettings;
    
        blurTextureDescriptor = new RenderTextureDescriptor(Screen.width,
            Screen.height, RenderTextureFormat.Default, 0);
    }
    
  5. BlurRenderPass类中,声明RTHandle字段以存储对临时模糊纹理的引用。

    private RTHandle blurTextureHandle;
    
  6. BlurRenderPass类中,实现Configure方法。Unity 在执行渲染传递之前调用此方法。

    public override void Configure(CommandBuffer cmd,
        RenderTextureDescriptor cameraTextureDescriptor)
    {
        //Set the blur texture size to be the same as the camera target size.
        blurTextureDescriptor.width = cameraTextureDescriptor.width;
        blurTextureDescriptor.height = cameraTextureDescriptor.height;
    
        //Check if the descriptor has changed, and reallocate the RTHandle if necessary.
        RenderingUtils.ReAllocateHandleIfNeeded(ref blurTextureHandle, blurTextureDescriptor);
    }
    
  7. BlurRenderPass类中,实现更新着色器值的UpdateBlurSettings方法。

    使用Blit方法将自定义着色器的两个传递应用于摄像机输出。

    private static readonly int horizontalBlurId =
        Shader.PropertyToID("_HorizontalBlur");
    private static readonly int verticalBlurId =
        Shader.PropertyToID("_VerticalBlur");
    
    ...
    
    private void UpdateBlurSettings()
    {
        if (material == null) return;
                    
        material.SetFloat(horizontalBlurId, defaultSettings.horizontalBlur);
        material.SetFloat(verticalBlurId, defaultSettings.verticalBlur);
    }
    
  8. Execute方法中调用UpdateBlurSettings方法。

    public override void Execute(ScriptableRenderContext context,
        ref RenderingData renderingData)
    {
        //Get a CommandBuffer from pool.
        CommandBuffer cmd = CommandBufferPool.Get();
    
        RTHandle cameraTargetHandle =
            renderingData.cameraData.renderer.cameraColorTargetHandle;
    
        UpdateBlurSettings();
    
        // Blit from the camera target to the temporary render texture,
        // using the first shader pass.
        Blit(cmd, cameraTargetHandle, blurTextureHandle, material, 0);
        // Blit from the temporary render texture to the camera target,
        // using the second shader pass.
        Blit(cmd, blurTextureHandle, cameraTargetHandle, material, 1);
    
        //Execute the command buffer and release it back to the pool.
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }
    
  9. 实现Dispose方法,该方法在渲染传递执行后销毁材质和临时渲染纹理。

    public void Dispose()
    {
        #if UNITY_EDITOR
                if (EditorApplication.isPlaying)
                {
                    Object.Destroy(material);
                }
                else
                {
                    Object.DestroyImmediate(material);
                }
        #else
                Object.Destroy(material);
        #endif
            
        if (blurTextureHandle != null) blurTextureHandle.Release();
    }
    

此部分的完整代码位于自定义渲染传递代码部分。

在自定义渲染器功能中将渲染传递排队

在本节中,您将在BlurRendererFeature类的Create方法中实例化渲染传递,并在AddRenderPasses方法中将其排队。

  1. BlurRendererFeature类的Create方法中,实例化BlurRenderPass类。

    在该方法中,使用renderPassEvent字段指定何时执行渲染传递。

    public override void Create()
    {
        if (shader == null)
        {
            return;
        }
        material = new Material(shader);
        blurRenderPass = new BlurRenderPass(material, settings);
    
        renderPassEvent = RenderPassEvent.AfterRenderingSkybox;
    }
    
  2. BlurRendererFeature类的AddRenderPasses方法中,使用EnqueuePass方法将渲染传递排队。

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (renderingData.cameraData.cameraType == CameraType.Game)
        {
            renderer.EnqueuePass(blurRenderPass);
        }
    }
    
  3. 实现销毁渲染器功能创建的材质实例的Dispose方法。该方法还调用渲染传递类中的Dispose方法。

    protected override void Dispose(bool disposing)
    {
        blurRenderPass.Dispose();
        #if UNITY_EDITOR
            if (EditorApplication.isPlaying)
            {
                Destroy(material);
            }
            else
            {
                DestroyImmediate(material);
            }
        #else
                Destroy(material);
        #endif
    }
    

有关完整的渲染器功能代码,请参阅自定义渲染器功能代码部分。

可脚本化的渲染器功能现已完成。下图显示了该功能在游戏视图中的效果以及示例设置。

The effect of the Scriptable Renderer Feature in the Game view
可脚本化的渲染器功能在游戏视图中的效果。

实现体积组件

本节介绍如何实现一个体积组件,允许您控制自定义渲染器功能的输入值。

  1. 创建一个新的 C# 脚本,并将其命名为CustomVolumeComponent.cs

  2. VolumeComponent类继承CustomVolumeComponent类,向类中添加[Serializable]属性。添加using UnityEngine.Rendering;指令。

    using System;
    using UnityEngine.Rendering;
    
    [Serializable]
    public class CustomVolumeComponent : VolumeComponent
    {
    
    }
    
  3. CustomVolumeComponent类中添加BoolParameter字段。此字段允许您启用或禁用自定义渲染器功能。

    public class BlurVolumeComponent : VolumeComponent
    {
        public BoolParameter isActive = new BoolParameter(true);
    }
    
  4. 添加用于控制自定义渲染器功能中定义的模糊设置的字段。

    [Serializable]
    public class CustomVolumeComponent : VolumeComponent
    {
        public BoolParameter isActive = new BoolParameter(true);
        public ClampedFloatParameter horizontalBlur =
            new ClampedFloatParameter(0.05f, 0, 0.5f);
        public ClampedFloatParameter verticalBlur =
            new ClampedFloatParameter(0.05f, 0, 0.5f);
    }
    
  5. BlurRenderPass脚本中,更改UpdateBlurSettings方法,使其使用体积中定义的设置,如果未设置体积,则使用默认设置。

    private void UpdateBlurSettings()
    {
        if (material == null) return;
    
        // Use the Volume settings or the default settings if no Volume is set.
        var volumeComponent =
            VolumeManager.instance.stack.GetComponent<CustomVolumeComponent>();
        float horizontalBlur = volumeComponent.horizontalBlur.overrideState ?
            volumeComponent.horizontalBlur.value : defaultSettings.horizontalBlur;
        float verticalBlur = volumeComponent.verticalBlur.overrideState ?
            volumeComponent.verticalBlur.value : defaultSettings.verticalBlur;
        material.SetFloat(horizontalBlurId, horizontalBlur);
        material.SetFloat(verticalBlurId, verticalBlur);
    }
    
  6. 在 Unity 场景中,创建一个局部盒子体积。如果缺少体积配置文件,请通过单击配置文件属性旁边的新建来创建一个新的配置文件。将Custom Volume Component覆盖添加到体积中。

    Box Volume properties
    盒子体积属性
  7. 启用Custom Volume Component覆盖中的设置,并为该体积设置值。移动体积,使摄像机位于其中。体积中的设置会覆盖自定义渲染器功能中的默认设置。

此示例中所有脚本的完整代码

本节包含此示例中所有脚本一段代码,允许您创建自己的组件、触发游戏事件、随时间推移修改组件属性并以任何您喜欢的方式响应用户输入。 更多信息
参见词汇表
的完整代码。

自定义渲染功能代码

以下是自定义渲染功能脚本的完整代码。

using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering.Universal;

public class BlurRendererFeature : ScriptableRendererFeature
{
    [SerializeField] private BlurSettings settings;
    [SerializeField] private Shader shader;
    private Material material;
    private BlurRenderPass blurRenderPass;

    public override void Create()
    {
        if (shader == null)
        {
            return;
        }
        material = new Material(shader);
        blurRenderPass = new BlurRenderPass(material, settings);
        
        blurRenderPass.renderPassEvent = RenderPassEvent.AfterRenderingSkybox;
    }

    public override void AddRenderPasses(ScriptableRenderer renderer,
        ref RenderingData renderingData)
    {
        if (renderingData.cameraData.cameraType == CameraType.Game)
        {
            renderer.EnqueuePass(blurRenderPass);
        }
    }

    protected override void Dispose(bool disposing)
    {
        blurRenderPass.Dispose();
        #if UNITY_EDITOR
            if (EditorApplication.isPlaying)
            {
                Destroy(material);
            }
            else
            {
                DestroyImmediate(material);
            }
        #else
                Destroy(material);
        #endif
    }
}

[Serializable]
public class BlurSettings
{
    [Range(0, 0.4f)] public float horizontalBlur;
    [Range(0, 0.4f)] public float verticalBlur;
}

自定义渲染通道代码

以下是自定义渲染通道脚本的完整代码。

using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class BlurRenderPass : ScriptableRenderPass
{
    private static readonly int horizontalBlurId =
        Shader.PropertyToID("_HorizontalBlur");
    private static readonly int verticalBlurId =
        Shader.PropertyToID("_VerticalBlur");

    private BlurSettings defaultSettings;
    private Material material;

    private RenderTextureDescriptor blurTextureDescriptor;
    private RTHandle blurTextureHandle;

    public BlurRenderPass(Material material, BlurSettings defaultSettings)
    {
        this.material = material;
        this.defaultSettings = defaultSettings;

        blurTextureDescriptor = new RenderTextureDescriptor(Screen.width,
            Screen.height, RenderTextureFormat.Default, 0);
    }

    public override void Configure(CommandBuffer cmd,
        RenderTextureDescriptor cameraTextureDescriptor)
    {
        // Set the blur texture size to be the same as the camera target size.
        blurTextureDescriptor.width = cameraTextureDescriptor.width;
        blurTextureDescriptor.height = cameraTextureDescriptor.height;

        // Check if the descriptor has changed, and reallocate the RTHandle if necessary
        RenderingUtils.ReAllocateHandleIfNeeded(ref blurTextureHandle, blurTextureDescriptor);
    }

    private void UpdateBlurSettings()
    {
        if (material == null) return;

        // Use the Volume settings or the default settings if no Volume is set.
        var volumeComponent =
            VolumeManager.instance.stack.GetComponent<CustomVolumeComponent>();
        float horizontalBlur = volumeComponent.horizontalBlur.overrideState ?
            volumeComponent.horizontalBlur.value : defaultSettings.horizontalBlur;
        float verticalBlur = volumeComponent.verticalBlur.overrideState ?
            volumeComponent.verticalBlur.value : defaultSettings.verticalBlur;
        material.SetFloat(horizontalBlurId, horizontalBlur);
        material.SetFloat(verticalBlurId, verticalBlur);
    }

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

        RTHandle cameraTargetHandle =
            renderingData.cameraData.renderer.cameraColorTargetHandle;

        UpdateBlurSettings();

        // Blit from the camera target to the temporary render texture,
        // using the first shader pass.
        Blit(cmd, cameraTargetHandle, blurTextureHandle, material, 0);
        // Blit from the temporary render texture to the camera target,
        // using the second shader pass.
        Blit(cmd, blurTextureHandle, cameraTargetHandle, material, 1);

        //Execute the command buffer and release it back to the pool.
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }

    public void Dispose()
    {
    #if UNITY_EDITOR
        if (EditorApplication.isPlaying)
        {
            Object.Destroy(material);
        }
        else
        {
            Object.DestroyImmediate(material);
        }
    #else
                Object.Destroy(material);
    #endif

        if (blurTextureHandle != null) blurTextureHandle.Release();
    }
}

体积组件代码

以下是体积组件脚本的完整代码。

using System;
using UnityEngine.Rendering;

[Serializable]
public class CustomVolumeComponent : VolumeComponent
{
    public BoolParameter isActive = new BoolParameter(true);
    public ClampedFloatParameter horizontalBlur =
        new ClampedFloatParameter(0.05f, 0, 0.5f);
    public ClampedFloatParameter verticalBlur =
        new ClampedFloatParameter(0.05f, 0, 0.5f);
}

模糊效果的自定义着色器

本节包含实现模糊效果的自定义着色器代码。

Shader "CustomEffects/Blur"
{
    HLSLINCLUDE
    
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        // The Blit.hlsl file provides the vertex shader (Vert),
        // the input structure (Attributes), and the output structure (Varyings)
        #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"

        float _VerticalBlur;
        float _HorizontalBlur;
    
        float4 _BlitTexture_TexelSize;
    
        float4 BlurVertical (Varyings input) : SV_Target
        {
            const float BLUR_SAMPLES = 64;
            const float BLUR_SAMPLES_RANGE = BLUR_SAMPLES / 2;
            
            float3 color = 0;
            float blurPixels = _VerticalBlur * _ScreenParams.y;
            
            for(float i = -BLUR_SAMPLES_RANGE; i <= BLUR_SAMPLES_RANGE; i++)
            {
                float2 sampleOffset =
                    float2 (0, (blurPixels / _BlitTexture_TexelSize.w) *
                        (i / BLUR_SAMPLES_RANGE));
                color +=
                    SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp,
                        input.texcoord + sampleOffset).rgb;
            }
            
            return float4(color.rgb / (BLUR_SAMPLES + 1), 1);
        }

        float4 BlurHorizontal (Varyings input) : SV_Target
        {
            const float BLUR_SAMPLES = 64;
            const float BLUR_SAMPLES_RANGE = BLUR_SAMPLES / 2;
            
            UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
            float3 color = 0;
            float blurPixels = _HorizontalBlur * _ScreenParams.x;
            for(float i = -BLUR_SAMPLES_RANGE; i <= BLUR_SAMPLES_RANGE; i++)
            {
                float2 sampleOffset =
                    float2 ((blurPixels / _BlitTexture_TexelSize.z) *
                        (i / BLUR_SAMPLES_RANGE), 0);
                color +=
                    SAMPLE_TEXTURE2D(_BlitTexture, sampler_LinearClamp,
                        input.texcoord + sampleOffset).rgb;
            }
            return float4(color / (BLUR_SAMPLES + 1), 1);
        }
    
    ENDHLSL
    
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline"}
        LOD 100
        ZWrite Off Cull Off
        Pass
        {
            Name "BlurPassVertical"

            HLSLPROGRAM
            
            #pragma vertex Vert
            #pragma fragment BlurVertical
            
            ENDHLSL
        }
        
        Pass
        {
            Name "BlurPassHorizontal"

            HLSLPROGRAM
            
            #pragma vertex Vert
            #pragma fragment BlurHorizontal
            
            ENDHLSL
        }
    }
}
在 URP 兼容模式下编写 Scriptable Render Pass
URP 兼容模式下执行全屏 Blit 的示例