版本:Unity 6 (6000.0)
语言:English
将 Scriptable Renderer Feature 应用于 URP 中的特定相机类型
URP 的 Scriptable Renderer Feature API 参考

URP 中完整 Scriptable Renderer Feature 示例

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

本演练包含以下部分

此示例实现概述

此页面上的示例工作流实现了一个自定义渲染器特性,该特性使用 自定义渲染通道相机场景中创建特定视点的图像的组件。输出要么绘制到屏幕上,要么捕获为纹理。 更多信息
请参阅 术语表
输出添加模糊效果。

实现由以下部分组成

  • 一个 ScriptableRendererFeature 实例,它每帧都会排队一个 ScriptableRenderPass 实例。

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

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

    • 使用 TextureHandleAddBlitPass API 将 自定义着色器 的两个通道应用于相机输出。

创建示例场景和游戏对象

要为您的项目设置此示例工作流

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

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

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

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

  5. 放置相机,使其视野中包含立方体和球体。

  6. 在 URP 资源中,将属性 质量 > 抗锯齿 (MSAA) 设置为 禁用。此步骤的目的是简化示例实现。

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

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.RenderGraphModule;
    using UnityEngine.Rendering.RenderGraphModule.Util;
    using UnityEngine.Rendering.Universal;
    
  3. 创建继承自 ScriptableRenderPass 类的 BlurRenderPass 类。

    public class BlurRenderPass : ScriptableRenderPass
    
  4. 向类中添加 RecordRenderGraph 方法。此方法在渲染图中添加和配置渲染通道。此过程包括声明渲染通道输入和输出,但不包括向命令缓冲区添加命令。Unity 每帧调用此方法一次,每次调用对应一个相机。

    public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
    { }
    

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

using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.RenderGraphModule.Util;
using UnityEngine.Rendering.Universal;

public class BlurRenderPass : ScriptableRenderPass
{
    public override void RecordRenderGraph(RenderGraph renderGraph,
    ContextContainer frameData)
    {
        
    }
}

实现自定义渲染通道的设置

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

  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 字段并在构造函数中对其进行初始化。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. RecordRenderGraph 方法中,创建用于存储 frameData 参数中的 UniversalResourceData 实例的变量。UniversalResourceData 包含 URP 使用的所有纹理引用,包括相机的活动颜色和深度纹理。

    UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
    
  6. 声明用于与着色器属性交互的变量。

    private static readonly int horizontalBlurId = Shader.PropertyToID("_HorizontalBlur");
    private static readonly int verticalBlurId = Shader.PropertyToID("_VerticalBlur");
    private const string k_BlurTextureName = "_BlurTexture";
    private const string k_VerticalPassName = "VerticalBlurRenderPass";
    private const string k_HorizontalPassName = "HorizontalBlurRenderPass";
    
  7. RecordRenderGraph 方法中,声明 TextureHandle 字段以存储对输入和输出纹理的引用。CreateRenderGraphTexture 是一个辅助方法,它调用 RenderGraph.CreateTexture 方法。

    TextureHandle srcCamColor = resourceData.activeColorTexture;
    TextureHandle dst = UniversalRenderer.CreateRenderGraphTexture(renderGraph, blurTextureDescriptor, k_BlurTextureName, false);
    
  8. 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);
    }
    
  9. RecordRenderGraph 方法中,添加用于存储 UniversalCameraData 数据的变量,并使用该数据设置 RenderTextureDescriptor 值。

    UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
    
    // The following line ensures that the render pass doesn't blit
    // from the back buffer.
    if (resourceData.isActiveTargetBackBuffer)
        return;
    
    // Set the blur texture size to be the same as the camera target size.
    blurTextureDescriptor.width = cameraData.cameraTargetDescriptor.width;
    blurTextureDescriptor.height = cameraData.cameraTargetDescriptor.height;
    blurTextureDescriptor.depthBufferBits = 0;
    
  10. RecordRenderGraph 方法中,添加函数以连续更新材质中的模糊设置。

    // Update the blur settings in the material
    UpdateBlurSettings();
    
    // This check is to avoid an error from the material preview in the scene
    if (!srcCamColor.IsValid() || !dst.IsValid())
        return;
    

实现渲染通道

RecordRenderGraph 方法中,使用 AddBlitPass 方法添加垂直和水平模糊渲染通道。

// The AddBlitPass method adds a vertical blur render graph pass that blits from the source texture (camera color in this case) to the destination texture using the first shader pass (the shader pass is defined in the last parameter).
RenderGraphUtils.BlitMaterialParameters paraVertical = new(srcCamColor, dst, material, 0);
renderGraph.AddBlitPass(paraVertical, k_VerticalPassName);

// The AddBlitPass method adds a horizontal blur render graph pass that blits from the texture written by the vertical blur pass to the camera color texture. The method uses the second shader pass.
RenderGraphUtils.BlitMaterialParameters paraHorizontal = new(dst, srcCamColor, material, 1);
renderGraph.AddBlitPass(paraHorizontal, k_HorizontalPassName);

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

在自定义渲染器特性中排队渲染通道

在本节中,您将在 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);
    
        blurRenderPass.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 方法。

    protected override void Dispose(bool disposing)
    {
        #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. CustomVolumeComponent 类继承自 VolumeComponent 类,向类中添加 [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.BeforeRenderingPostProcessing;
    }

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

    protected override void Dispose(bool disposing)
    {
        #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 UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.RenderGraphModule.Util;
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 const string k_BlurTextureName = "_BlurTexture";
    private const string k_VerticalPassName = "VerticalBlurRenderPass";
    private const string k_HorizontalPassName = "HorizontalBlurRenderPass";

    private BlurSettings defaultSettings;
    private Material material;

    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);
    }

    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 RecordRenderGraph(RenderGraph renderGraph,
    ContextContainer frameData)
    {
        UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();

        UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();

        // The following line ensures that the render pass doesn't blit
        // from the back buffer.
        if (resourceData.isActiveTargetBackBuffer)
            return;

        // Set the blur texture size to be the same as the camera target size.
        blurTextureDescriptor.width = cameraData.cameraTargetDescriptor.width;
        blurTextureDescriptor.height = cameraData.cameraTargetDescriptor.height;
        blurTextureDescriptor.depthBufferBits = 0;

        TextureHandle srcCamColor = resourceData.activeColorTexture;
        TextureHandle dst = UniversalRenderer.CreateRenderGraphTexture(renderGraph,
            blurTextureDescriptor, k_BlurTextureName, false);

        // Update the blur settings in the material
        UpdateBlurSettings();

        // This check is to avoid an error from the material preview in the scene
        if (!srcCamColor.IsValid() || !dst.IsValid())
            return;
        
        // The AddBlitPass method adds a vertical blur render graph pass that blits from the source texture (camera color in this case) to the destination texture using the first shader pass (the shader pass is defined in the last parameter).
        RenderGraphUtils.BlitMaterialParameters paraVertical = new(srcCamColor, dst, material, 0);
        renderGraph.AddBlitPass(paraVertical, k_VerticalPassName);
        
        // The AddBlitPass method adds a horizontal blur render graph pass that blits from the texture written by the vertical blur pass to the camera color texture. The method uses the second shader pass.
        RenderGraphUtils.BlitMaterialParameters paraHorizontal = new(dst, srcCamColor, material, 1);
        renderGraph.AddBlitPass(paraHorizontal, k_HorizontalPassName);
    }
}

体积组件代码

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

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 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
        }
    }
}
将 Scriptable Renderer Feature 应用于 URP 中的特定相机类型
URP 的 Scriptable Renderer Feature API 参考