Version: 6000.3
语言: 中文
将可编写脚本的渲染器功能应用于URP中的特定摄像机类型
适用于 URP 的可脚本渲染器功能 API 参考

URP 中完整的可脚本渲染器功能示例

本节介绍如何为URP渲染器创建完整的可编写脚本渲染器功能

本演练包含以下部分:

此示例实现概述

本页上的示例工作流程实现了自定义渲染器功能,该功能使用自定义渲染通道相机在场景中创建特定视点图像的组件。输出要么绘制到屏幕上,要么作为纹理捕获。更多信息
请参阅术语表
输出。

实现由以下部分组成:

  • 一个ScriptableRendererFeatureScriptableRenderPass实例每一帧。

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

    • 创建临时渲染纹理(render texture一种特殊类型的纹理,在运行时创建和更新。要使用它们,请先创建一个新的渲染纹理,并指定要渲染到其中的摄像机之一。然后,你可以在材质中使用渲染纹理,就像使用常规纹理一样。更多信息
      请参阅术语表
      使用RenderTextureDescriptor应用程序接口。

    • 使用TextureHandleAddBlitPass API。

创建示例场景和游戏对象

要为此示例工作流程设置项目,请执行以下作:

  1. 创建一个新的场景场景包含游戏的环境和菜单。将每个唯一的场景文件视为一个独特的关卡。在每个场景中,你放置你的环境、障碍物和装饰品,基本上是将你的游戏设计和构建成碎片。更多信息
    请参阅术语表
    .

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

  3. 使用着色器创建两个材质,用于指定基础颜色(例如,Universal Render Pipeline/Lit着色器)。调用材料BlueRed,并将材质的基色分别设置为蓝色和红色。

  4. 分配Red立方体的材料和Blue材质到球体。

  5. 定位摄像机,使其视图中有立方体和球体。

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

示例场景如下图所示:

示例场景
示例场景

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

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

  2. 在脚本中,删除 Unity 在BlurRendererFeature类。

  3. 添加以下内容using命令:

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

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

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

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

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

      • 当您更改inspector一个 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. 创建BlurRenderPass继承自 ScriptableRenderPass 类的类。

    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 上运行的程序。更多信息
    请参阅术语表
    在一个通道中水平执行模糊,在另一个通道中垂直执行模糊。要让用户控制每个通道的模糊值,请添加以下内容BlurSettingsclass 添加到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类中,添加设置、材质和使用这些字段的构造函数的字段。添加TextureDesc领域也是如此。这TextureDesc类允许您指定渲染纹理的属性,例如宽度、高度和格式。

    private BlurSettings defaultSettings;
    private Material material;
    
    private RenderTextureDescriptor blurTextureDescriptor;
    
    public BlurRenderPass(Material material, BlurSettings defaultSettings)
    {
        this.material = material;
        this.defaultSettings = defaultSettings;
    }
    
  4. RecordRenderGraph方法,创建用于存储UniversalResourceData实例frameData参数。UniversalResourceData包含URP使用的所有纹理引用,包括摄像机的活动颜色和深度纹理。

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

    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";
    
  6. RecordRenderGraph方法,声明TextureHandle字段来存储对输入和输出纹理的引用。初始化TextureDesc目标纹理。

    目标纹理基于摄像机颜色纹理,因此可以使用摄像机颜色纹理的描述符作为定义目标纹理的起点。使用与相机颜色纹理相同的描述符可确保源纹理和目标纹理具有相同的大小和颜色格式(除非您选择更改描述符)。

    TextureHandle srcCamColor = resourceData.activeColorTexture;
    blurTextureDescriptor = srcCamColor.GetDescriptor(renderGraph);
    blurTextureDescriptor.name = k_BlurTextureName;
    blurTextureDescriptor.depthBufferBits = 0;
    var dst = renderGraph.CreateTexture(blurTextureDescriptor);
    
  7. BlurRenderPass类,实现UpdateBlurSettings更新着色器值的方法。

    private void UpdateBlurSettings()
    {
        if (material == null) return;
    
        material.SetFloat(horizontalBlurId, defaultSettings.horizontalBlur);
        material.SetFloat(verticalBlurId, defaultSettings.verticalBlur);
    }
    
  8. RecordRenderGraph方法中,添加用于存储UniversalCameraData数据,并确保通行证不会blit“位块传输”的简写术语。blit作是将数据块从内存中的一个位置传输到另一个位置的过程。
    请参阅术语表
    从后缓冲区。

    UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
    
    // The following line ensures that the render pass doesn't blit
    // from the back buffer.
    if (resourceData.isActiveTargetBackBuffer)
        return;
    
  9. 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);

此部分的完整代码位于自定义渲染密码中

将渲染通道排入自定义渲染器功能中的队列

在本节中,你将在Create方法BlurRendererFeature类,并将其排入AddRenderPasses方法。

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

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

    public override void Create()
    {
        if (shader == null)
        {
            return;
        }
        material = new Material(shader);
        blurRenderPass = new BlurRenderPass(material, settings);
    
        blurRenderPass.renderPassEvent = RenderPassEvent.AfterRenderingSkybox;
    }
    
  2. AddRenderPasses方法BlurRendererFeature类,则使用EnqueuePass方法。

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

    protected override void Dispose(bool disposing)
    {
        if (Application.isPlaying)
        {
            Destroy(material);
        }
        else
        {
            DestroyImmediate(material);
        }
    }
    

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

可编写脚本渲染器功能现已完成。下图显示了该功能在“游戏”视图中的效果和示例设置。

游戏视图中可编写脚本渲染器功能的效果
游戏视图中可脚本渲染器功能的效果。

实现卷组件

本部分介绍如何实现体积组件,用于控制自定义渲染器功能的输入值。

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

  2. 继承CustomVolumeComponent类从VolumeComponent类,将[Serializable]属性添加到类中。

    添加using UnityEngine.Rendering;命令。

    using System;
    using UnityEngine.Rendering;
    
    [Serializable]
    public class CustomVolumeComponent : VolumeComponent
    {
    
    }
    
  3. 添加字段以控制自定义渲染器功能中定义的模糊设置。

    [Serializable]
    public class CustomVolumeComponent : VolumeComponent
    {
        public ClampedFloatParameter horizontalBlur =
            new ClampedFloatParameter(0.05f, 0, 0.5f);
        public ClampedFloatParameter verticalBlur =
            new ClampedFloatParameter(0.05f, 0, 0.5f);
    }
    
  4. 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);
    }
    
  5. 在 Unity 场景中,创建一个本地 Box Volume。如果缺少 Volume Profile,请单击 Profile 属性旁边的 New 创建一个新配置文件。将Custom Volume Component 覆盖到体积。

  6. 启用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 (blurRenderPass == null)
        { 
            return;
        }    
        if (renderingData.cameraData.cameraType == CameraType.Game)
        {
            renderer.EnqueuePass(blurRenderPass);
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (Application.isPlaying)
        {
            Destroy(material);
        }
        else
        {
            DestroyImmediate(material);
        }
    }
}

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

    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;

        TextureHandle srcCamColor = resourceData.activeColorTexture;
        blurTextureDescriptor = resourceData.activeColorTexture.GetDescriptor(renderGraph);
        blurTextureDescriptor.name = k_BlurTextureName;
        blurTextureDescriptor.depthBufferBits = 0;
        var dst = renderGraph.CreateTexture(blurTextureDescriptor);

        // 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 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
        }
    }
}

其他资源

将可编写脚本的渲染器功能应用于URP中的特定摄像机类型
适用于 URP 的可脚本渲染器功能 API 参考