本节介绍如何为 URP 渲染器创建完整的Scriptable Renderer Feature。
注意:Unity 不再开发或改进不使用渲染图 API的渲染路径。开发新的图形功能时,请改用渲染图 API。要使用本页面的说明,请在 URP 图形设置(项目设置 > 图形)中启用兼容模式(渲染图已禁用)。
本演练包含以下部分
此页面上的示例工作流程实现了自定义渲染器功能,该功能使用自定义渲染传递为摄像机一个组件,用于创建场景中特定视点的图像。输出要么绘制到屏幕上,要么捕获为纹理。 更多信息
参见词汇表输出添加模糊效果。
该实现包含以下部分
一个ScriptableRendererFeature
实例,每帧将一个ScriptableRenderPass
实例排队。
一个ScriptableRenderPass
实例,执行以下步骤
要为该示例工作流程设置项目
创建一个新的场景场景包含游戏的环境和菜单。可以将每个唯一的场景文件视为一个唯一的关卡。在每个场景中,您放置环境、障碍物和装饰,从本质上讲是分段设计和构建游戏。 更多信息
参见词汇表。
创建两个游戏对象:一个名为Cube
的立方体游戏对象Unity 场景中的基本对象,可以表示角色、道具、场景、摄像机、路径点等。游戏对象的功能由附加到它的组件定义。 更多信息
参见词汇表和一个名为Sphere
的球体游戏对象。
创建两个材质,并使用一个允许您指定基本颜色的着色器(例如,Universal Render Pipeline/Lit
着色器)。将材质命名为Blue
和Red
,并将材质的基本颜色分别设置为蓝色和红色。
将Red
材质分配给立方体,将Blue
材质分配给球体。
定位摄像机,使其视野中包含立方体和球体。
示例场景应如下面的图像所示
创建一个新的 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.Universal;
创建继承自ScriptableRenderPass类的BlurRenderPass
类。
public class BlurRenderPass : ScriptableRenderPass
向类中添加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)
{
}
}
本节演示如何实现自定义模糊渲染传递的设置。
此示例中的渲染器功能使用着色器在 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
字段并在构造函数中初始化它
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);
}
在BlurRenderPass
类中,声明RTHandle
字段以存储对临时模糊纹理的引用。
private RTHandle blurTextureHandle;
在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);
}
在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);
}
在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);
}
实现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
方法中将其排队。
在BlurRendererFeature
类的Create
方法中,实例化BlurRenderPass
类。
在该方法中,使用renderPassEvent
字段指定何时执行渲染传递。
public override void Create()
{
if (shader == null)
{
return;
}
material = new Material(shader);
blurRenderPass = new BlurRenderPass(material, settings);
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
方法。该方法还调用渲染传递类中的Dispose
方法。
protected override void Dispose(bool disposing)
{
blurRenderPass.Dispose();
#if UNITY_EDITOR
if (EditorApplication.isPlaying)
{
Destroy(material);
}
else
{
DestroyImmediate(material);
}
#else
Destroy(material);
#endif
}
有关完整的渲染器功能代码,请参阅自定义渲染器功能代码部分。
可脚本化的渲染器功能现已完成。下图显示了该功能在游戏视图中的效果以及示例设置。
可脚本化的渲染器功能在游戏视图中的效果。
本节介绍如何实现一个体积组件,允许您控制自定义渲染器功能的输入值。
创建一个新的 C# 脚本,并将其命名为CustomVolumeComponent.cs
。
从VolumeComponent
类继承CustomVolumeComponent
类,向类中添加[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.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
}
}
}