本节介绍如何为 URP 渲染器创建完整的 Scriptable Renderer Feature。
本演练包含以下部分
此页面上的示例工作流实现了一个自定义渲染器特性,该特性使用 自定义渲染通道 为 相机场景中创建特定视点的图像的组件。输出要么绘制到屏幕上,要么捕获为纹理。 更多信息
请参阅 术语表 输出添加模糊效果。
实现由以下部分组成
一个 ScriptableRendererFeature
实例,它每帧都会排队一个 ScriptableRenderPass
实例。
一个 ScriptableRenderPass
实例,执行以下步骤
使用 RenderTextureDescriptor
API 创建一个临时的 渲染纹理一种特殊的纹理类型,在运行时创建和更新。要使用它们,首先创建一个新的渲染纹理,并指定一个相机渲染到其中。然后,您可以像使用普通纹理一样在材质中使用渲染纹理。 更多信息
请参阅 术语表。
使用 TextureHandle
和 AddBlitPass API 将 自定义着色器 的两个通道应用于相机输出。
要为您的项目设置此示例工作流
创建一个新的 场景场景包含游戏环境和菜单。可以将每个唯一的场景文件视为一个唯一的关卡。在每个场景中,您放置环境、障碍物和装饰,从本质上讲,将游戏分段设计和构建。 更多信息
请参阅 术语表。
创建两个游戏对象:一个名为 Cube
的立方体 游戏对象Unity 场景中的基本对象,可以表示角色、道具、场景、相机、路径点等。游戏对象的功用由附加到它的组件定义。 更多信息
请参阅 术语表 和一个名为 Sphere
的球体游戏对象。
创建两种材质,使用允许您指定基本颜色的着色器(例如,Universal Render Pipeline/Lit
着色器)。将材质命名为 Blue
和 Red
,并将材质的基本颜色分别设置为蓝色和红色。
将 Red
材质分配给立方体,将 Blue
材质分配给球体。
放置相机,使其视野中包含立方体和球体。
在 URP 资源中,将属性 质量 > 抗锯齿 (MSAA) 设置为 禁用。此步骤的目的是简化示例实现。
示例场景应如下面的图像所示
创建一个新的 C# 脚本,并将其命名为 BlurRendererFeature.cs
。
在脚本中,删除 Unity 在 BlurRendererFeature
类中插入的代码。
添加以下 using
指令
using UnityEngine.Rendering.Universal;
创建继承自 ScriptableRendererFeature 类的 BlurRendererFeature
类。
public class BlurRendererFeature : ScriptableRendererFeature
在 BlurRendererFeature
类中,实现以下方法
现在,您拥有了具有其主要方法的自定义 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)
{
}
}
将您创建的渲染器特性添加到通用渲染器资源。有关如何执行此操作的信息,请参阅页面 如何将渲染器特性添加到渲染器。
本节演示如何创建可脚本化的渲染通道并将其实例排队到可脚本化的渲染器中。
创建一个新的 C# 脚本,并将其命名为 BlurRenderPass.cs
。
在脚本中,删除 Unity 在 BlurRenderPass
类中插入的代码。添加以下 using
指令
using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.RenderGraphModule.Util;
using UnityEngine.Rendering.Universal;
创建继承自 ScriptableRenderPass 类的 BlurRenderPass
类。
public class BlurRenderPass : ScriptableRenderPass
向类中添加 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)
{
}
}
本节演示如何实现自定义模糊渲染通道的设置。
此示例中的渲染器特性使用 着色器在 GPU 上运行的程序。 更多信息
请参阅 术语表,该着色器在一遍中水平执行模糊,在另一遍中垂直执行模糊。为了让用户控制每个通道的模糊值,请将以下 BlurSettings
类添加到 BlurRendererFeature.cs
脚本中。
[Serializable]
public class BlurSettings
{
[Range(0,0.4f)] public float horizontalBlur;
[Range(0,0.4f)] public float verticalBlur;
}
在 BlurRendererFeature
类中,声明以下字段
[SerializeField] private BlurSettings settings;
[SerializeField] private Shader shader;
private Material material;
private BlurRenderPass blurRenderPass;
在 BlurRenderPass
类中,添加设置、材质和使用这些字段的构造函数的字段。
private BlurSettings defaultSettings;
private Material material;
public BlurRenderPass(Material material, BlurSettings defaultSettings)
{
this.material = material;
this.defaultSettings = defaultSettings;
}
在 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);
}
在 RecordRenderGraph
方法中,创建用于存储 frameData
参数中的 UniversalResourceData
实例的变量。UniversalResourceData
包含 URP 使用的所有纹理引用,包括相机的活动颜色和深度纹理。
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
声明用于与着色器属性交互的变量。
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";
在 RecordRenderGraph
方法中,声明 TextureHandle
字段以存储对输入和输出纹理的引用。CreateRenderGraphTexture
是一个辅助方法,它调用 RenderGraph.CreateTexture
方法。
TextureHandle srcCamColor = resourceData.activeColorTexture;
TextureHandle dst = UniversalRenderer.CreateRenderGraphTexture(renderGraph, blurTextureDescriptor, k_BlurTextureName, false);
在 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);
}
在 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;
在 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
方法中将其排队。
在 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;
}
在 BlurRendererFeature
类的 AddRenderPasses
方法中,使用 EnqueuePass
方法排队渲染通道。
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
if (renderingData.cameraData.cameraType == CameraType.Game)
{
renderer.EnqueuePass(blurRenderPass);
}
}
实现销毁渲染器特性创建的材质实例的 Dispose
方法。
protected override void Dispose(bool disposing)
{
#if UNITY_EDITOR
if (EditorApplication.isPlaying)
{
Destroy(material);
}
else
{
DestroyImmediate(material);
}
#else
Destroy(material);
#endif
}
有关完整的渲染器特性代码,请参阅 自定义渲染器特性代码 部分。
现在,可脚本化的渲染器特性已完成。下图显示了该特性在游戏视图中的效果和示例设置。
可脚本化的渲染器特性在游戏视图中的效果。
本节介绍如何实现一个体积组件,该组件允许您控制自定义渲染器特性的输入值。
创建一个新的 C# 脚本,并将其命名为 CustomVolumeComponent.cs
。
将 CustomVolumeComponent
类继承自 VolumeComponent
类,向类中添加 [Serializable]
属性。添加 using UnityEngine.Rendering;
指令。
using System;
using UnityEngine.Rendering;
[Serializable]
public class CustomVolumeComponent : VolumeComponent
{
}
向 CustomVolumeComponent
类中添加 BoolParameter
字段。此字段允许您启用或禁用自定义渲染器特性。
public class BlurVolumeComponent : VolumeComponent
{
public BoolParameter isActive = new BoolParameter(true);
}
添加字段以控制自定义渲染器特性中定义的模糊设置。
[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);
}
在 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);
}
在 Unity 场景中,创建一个 局部盒子体积。如果缺少 体积配置文件,请通过单击 配置文件 属性旁边的 新建 来创建一个新配置文件。将 Custom Volume Component
覆盖 添加到体积中。
启用 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
}
}
}