包含此页的版本:
不含此页的版本:
本节介绍如何为URP渲染器创建完整的可编写脚本渲染器功能。
本演练包含以下部分:
本页上的示例工作流程实现了自定义渲染器功能,该功能使用自定义渲染通道向相机在场景中创建特定视点图像的组件。输出要么绘制到屏幕上,要么作为纹理捕获。更多信息
请参阅术语表输出。
实现由以下部分组成:
一个ScriptableRendererFeature将ScriptableRenderPass实例每一帧。
一个ScriptableRenderPass执行以下步骤的实例:
创建临时渲染纹理(render texture) 一种特殊类型的纹理,在运行时创建和更新。要使用它们,请先创建一个新的渲染纹理,并指定要渲染到其中的摄像机之一。然后,你可以在材质中使用渲染纹理,就像使用常规纹理一样。更多信息
请参阅术语表使用RenderTextureDescriptor应用程序接口。
使用TextureHandle和 AddBlitPass API。
要为此示例工作流程设置项目,请执行以下作:
创建一个新的场景场景包含游戏的环境和菜单。将每个唯一的场景文件视为一个独特的关卡。在每个场景中,你放置你的环境、障碍物和装饰品,基本上是将你的游戏设计和构建成碎片。更多信息
请参阅术语表.
创建两个游戏对象:一个立方体游戏对象Unity 场景中的基本对象,可以表示角色、道具、风景、相机、航路点等。游戏对象的功能由附加到它的组件定义。更多信息
请参阅术语表叫Cube,以及一个名为Sphere.
使用着色器创建两个材质,用于指定基础颜色(例如,Universal Render Pipeline/Lit着色器)。调用材料Blue和Red,并将材质的基色分别设置为蓝色和红色。
分配Red立方体的材料和Blue材质到球体。
定位摄像机,使其视图中有立方体和球体。
在URP资产中,将 质量>抗锯齿(MSAA) 属性设置为 禁用(Disabled)。此步骤的目的是简化示例实现。
示例场景如下图所示:
创建新的 C# 脚本并将其命名BlurRendererFeature.cs.
在脚本中,删除 Unity 在BlurRendererFeature类。
添加以下内容using命令:
using UnityEngine.Rendering.Universal;
创建BlurRendererFeature继承自 ScriptableRendererFeature 类的类。
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;
创建BlurRenderPass继承自 ScriptableRenderPass 类的类。
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 上运行的程序。更多信息
请参阅术语表在一个通道中水平执行模糊,在另一个通道中垂直执行模糊。要让用户控制每个通道的模糊值,请添加以下内容BlurSettingsclass 添加到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类中,添加设置、材质和使用这些字段的构造函数的字段。添加TextureDesc领域也是如此。这TextureDesc类允许您指定渲染纹理的属性,例如宽度、高度和格式。
private BlurSettings defaultSettings;
private Material material;
private RenderTextureDescriptor blurTextureDescriptor;
public BlurRenderPass(Material material, BlurSettings defaultSettings)
{
this.material = material;
this.defaultSettings = defaultSettings;
}
在RecordRenderGraph方法,创建用于存储UniversalResourceData实例frameData参数。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字段来存储对输入和输出纹理的引用。初始化TextureDesc目标纹理。
目标纹理基于摄像机颜色纹理,因此可以使用摄像机颜色纹理的描述符作为定义目标纹理的起点。使用与相机颜色纹理相同的描述符可确保源纹理和目标纹理具有相同的大小和颜色格式(除非您选择更改描述符)。
TextureHandle srcCamColor = resourceData.activeColorTexture;
blurTextureDescriptor = srcCamColor.GetDescriptor(renderGraph);
blurTextureDescriptor.name = k_BlurTextureName;
blurTextureDescriptor.depthBufferBits = 0;
var dst = renderGraph.CreateTexture(blurTextureDescriptor);
在BlurRenderPass类,实现UpdateBlurSettings更新着色器值的方法。
private void UpdateBlurSettings()
{
if (material == null) return;
material.SetFloat(horizontalBlurId, defaultSettings.horizontalBlur);
material.SetFloat(verticalBlurId, defaultSettings.verticalBlur);
}
在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;
在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方法。
在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;
}
在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);
}
}
实现Dispose方法,该方法会销毁渲染器功能创建的材质实例。
protected override void Dispose(bool disposing)
{
if (Application.isPlaying)
{
Destroy(material);
}
else
{
DestroyImmediate(material);
}
}
有关完整的渲染器功能代码,请参阅自定义渲染器功能代码。
可编写脚本渲染器功能现已完成。下图显示了该功能在“游戏”视图中的效果和示例设置。

游戏视图中可脚本渲染器功能的效果。
本部分介绍如何实现体积组件,用于控制自定义渲染器功能的输入值。
创建新的 C# 脚本并将其命名CustomVolumeComponent.cs.
继承CustomVolumeComponent类从VolumeComponent类,将[Serializable]属性添加到类中。
添加using UnityEngine.Rendering;命令。
using System;
using UnityEngine.Rendering;
[Serializable]
public class CustomVolumeComponent : VolumeComponent
{
}
添加字段以控制自定义渲染器功能中定义的模糊设置。
[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);
}
在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 场景中,创建一个本地 Box Volume。如果缺少 Volume Profile,请单击 Profile 属性旁边的 New 创建一个新配置文件。将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 (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
}
}
}