自定义后期处理
高清渲染管线 (High Definition Render Pipeline, HDRP) 允许你编写自己的后期处理效果,这些效果会自动集成到体积中。自定义效果需要两个文件。一个是 C# 自定义后期处理(C# 文件),另一个是关联的全屏着色器(HLSL 文件)。你可以为每个文件生成一个模板:
C# 自定义后期处理:在 Assets 文件夹中右键单击,然后选择 Create > Rendering > C# Post Process Volume。
全屏着色器:在 Assets 文件夹中右键单击,然后选择 Create > Shader > HDRP > Post Process。
请注意,默认情况下,如果只是将自定义效果添加到体积中,则自定义效果不会运行。还需要将效果添加到项目的支持效果列表中。(与用于效果排序的列表相同,请参阅“效果排序”部分)。
示例
本示例说明如何创建灰度效果。要开始这一过程,请执行以下操作:
创建一个 C# 自定义后期处理文件(在 Assets 文件夹中右键单击,然后选择 Create > Rendering > C# Post Process Volume),并将其命名为 GrayScale。请注意,由于序列化在 Unity 中的工作方式,因此文件名和类名必须相同,否则 Unity 不能正确地对其进行序列化。
将示例代码从 GrayScale C# 脚本部分复制到 C# Post Process Volume 中。
创建一个全屏后期处理着色器(在 Assets 文件夹中右键单击,然后选择 Create > Shader > HDRP > Post Process),并将其命名为 GrayScale。
将示例代码从 GrayScale Shader 部分复制到您的后期处理着色器中。
将 GrayScale 效果添加到您的项目执行的自定义后期处理列表中。为此,请选择 Edit > Project Settings > HDRP Default Settings,然后在 After Post Process 列表的底部,单击 +,再选择 GrayScale。
现在可以向场景中的 Volumes 添加 GrayScale 后期处理覆盖。要更改效果设置,请单击折叠箭头下方的小号
all
文本,然后使用 Intensity 滑动条进行调整。(可选)可为后期处理效果创建自定义编辑器。有关如何执行此操作的信息,请参阅自定义编辑器。
GrayScale C# 脚本
以下是 C# 自定义后期处理文件。自定义后期处理效果将配置数据和逻辑存储在同一个类中。要创建效果的设置,可以使用从 VolumeParameter
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using System;
[Serializable, VolumeComponentMenu("Post-processing/Custom/GrayScale")]
public sealed class GrayScale : CustomPostProcessVolumeComponent, IPostProcessComponent
{
[Tooltip("Controls the intensity of the effect.")]
public ClampedFloatParameter intensity = new ClampedFloatParameter(0f, 0f, 1f);
Material m_Material;
public bool IsActive() => m_Material != null && intensity.value > 0f;
public override CustomPostProcessInjectionPoint injectionPoint => CustomPostProcessInjectionPoint.AfterPostProcess;
public override void Setup()
{
if (Shader.Find("Hidden/Shader/GrayScale") != null)
m_Material = new Material(Shader.Find("Hidden/Shader/GrayScale"));
}
public override void Render(CommandBuffer cmd, HDCamera camera, RTHandle source, RTHandle destination)
{
if (m_Material == null)
return;
m_Material.SetFloat("_Intensity", intensity.value);
m_Material.SetTexture("_InputTexture", source);
HDUtils.DrawFullScreen(cmd, m_Material, destination);
}
public override void Cleanup() => CoreUtils.Destroy(m_Material);
}
首先,该示例代码使用 ClampedFloatParameter,您可能不熟悉这个类型。这种类型是浮点数,您可以将其限制为某一范围。在构造函数中:
第一个参数是属性的默认值。
第二个参数表示将属性限制到的最小值。
第三个参数表示将属性限制到的最大值。
接下来是一个 IsActive() 函数。HDRP 在 Render 函数之前调用此函数,以确保能够处理效果。如果此函数返回 false
,则 HDRP 不会处理该效果。最佳做法是检查发生了效果损坏或无效情况的每个属性配置。在此示例中,IsActive() 确保了可以找到 GrayScale.shader 并且强度大于 0。
借助 injectionPoint 覆盖,可以指定 HDRP 在管线中的何处执行效果。当前有三个注入点:
AfterOpaqueAndSky。
BeforeTAA。
BeforePostProcess。
AfterPostProcess。
有关 HDRP 在何处注入自定义后期处理通道的更多详细信息,请参阅下图:
现在已经有了 Setup、Render 和 Cleanup 函数。这些函数在这里分别用于分配、使用和释放效果所需的资源。该示例使用的唯一资源是单个材质。该示例在 Setup 中创建材质,然后在 Cleanup 中使用 CoreUtils.Destroy() 来释放材质。在 Render 函数中,您可以访问 CommandBuffer,因此可以将任务加入队列中以供 HDRP 执行。此处可以使用 HDUtils.DrawFullScreen 来渲染全屏四边形。它使用 CommandBuffer 以及您传入的材质,然后将结果 bilt 到目标 RTHandle。
GrayScale 着色器
HDRP 使您能够完全控制顶点和片元着色器,因此您可以编辑它们以适合您的需求。请注意,着色器默认包含的 Common.hlsl 和 Color.hlsl 中有很多实用函数。这意味着您可以在您的效果中访问这些实用函数。例如,GrayScale 着色器使用 Luminance() 函数将线性 RGB 值转换为等效的亮度。
Shader "Hidden/Shader/GrayScale"
{
HLSLINCLUDE
#pragma target 4.5
#pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/FXAA.hlsl"
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/RTUpscale.hlsl"
struct Attributes
{
uint vertexID : SV_VertexID;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings Vert(Attributes input)
{
Varyings output;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
output.texcoord = GetFullScreenTriangleTexCoord(input.vertexID);
return output;
}
// 用于控制后期处理效果的属性列表
float _Intensity;
TEXTURE2D_X(_InputTexture);
float4 CustomPostProcess(Varyings input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
uint2 positionSS = input.texcoord * _ScreenSize.xy;
float3 outColor = LOAD_TEXTURE2D_X(_InputTexture, positionSS).xyz;
// 我们手动进行混合,而不是依靠硬件混合
// 这是必要的,因为颜色缓冲区包含来自先前后期处理效果的垃圾。
return float4(lerp(outColor, Luminance(outColor).xxx, _Intensity), 1);
}
ENDHLSL
SubShader
{
Pass
{
Name "GrayScale"
ZWrite Off
ZTest Always
Blend Off
Cull Off
HLSLPROGRAM
#pragma fragment CustomPostProcess
#pragma vertex Vert
ENDHLSL
}
}
Fallback Off
}
如果您的所有场景均未引用着色器,则 Unity 不会构建着色器,并且当您在编辑器外部运行应用程序时,效果不起作用。要解决此问题,请将着色器添加到 Resources 文件夹,或者选择 Edit > Project Settings > Graphics,然后将着色器添加到 Always Included Shaders 列表。
⚠️ 请注意,当 HDRP 执行后期处理效果时,它将使用渲染目标池系统。这意味着你不知道当前颜色缓冲区包含的内容,这就是为什么永远不要使用任何可能显示该颜色缓冲区的指令的原因。请勿在着色器中使用透明度、混合模式或 clip() 指令,否则会破坏效果。
着色器输入
默认情况下,着色器模板为您提供以下输入:
输入 | 描述 |
---|---|
positionCS | 像素的裁剪空间位置。此值介于 0 和当前屏幕大小之间。 |
texcoord | 全屏 UV 坐标。此值介于 0 到 1 之间。 |
_InputTexture | 源纹理。GrayScale C# 脚本将此项传递给着色器。 |
_Intensity | 效果的强度。GrayScale C# 脚本将此项传递给着色器。 |
效果排序
HDRP 允许您在每个注入点自定义您的自定义后期处理效果的顺序。要对效果排序,请执行以下操作:
选择 Edit > Project Settings,然后选择 HDRP Default Settings 选项卡。
向下滚动,直到找到 Custom Post Process Orders 部分。此部分包含三个列表,每个注入点一个。
将自定义效果添加到这些列表中,以便 HDRP 可以渲染这些自定义效果。
HDRP 按照从列表自上而下处理效果,每个列表的执行顺序为:
透明前。
TAA 前。
后期处理前。
后期处理后。
自定义编辑器
默认情况下,Unity 自动为类创建一个编辑器,但是,如果您想以更大力度控制 Unity 如何显示某些属性,则可以创建一个自定义编辑器。如果您确实创建了自定义编辑器脚本,请务必将该脚本放在名为 Editor 的文件夹中。
以下是 GrayScale 效果的自定义编辑器示例:
using UnityEditor.Rendering;
using UnityEngine;
using UnityEngine.Rendering.HighDefinition;
using UnityEditor;
[VolumeComponentEditor(typeof(GrayScale))]
sealed class GrayScaleEditor : VolumeComponentEditor
{
SerializedDataParameter m_Intensity;
public override bool hasAdvancedMode => false;
public override void OnEnable()
{
base.OnEnable();
var o = new PropertyFetcher<GrayScale>(serializedObject);
m_Intensity = Unpack(o.Find(x => x.intensity));
}
public override void OnInspectorGUI()
{
PropertyField(m_Intensity);
}
}
这个自定义编辑器并不是真正有用,因为它生成的结果与 Unity 创建的编辑器生成的结果相同。自定义体积组件编辑器还支持“更多选项”按钮。要添加此按钮,必须将 hasAdvancedMode 覆盖设置为 true。然后,在 OnInspectorGUI 内部,你可以使用 isInAdvancedMode 布尔值显示更多属性。
故障排除
如果您的效果无法正确显示:
在项目设置 (Project Settings) 中,确保此效果列出在其中一个后期处理顺序列表中(请参阅效果排序)。
确认效果的着色器可以编译,并且对后期处理体积中材质的引用不为 null。
在包含后期处理的体积中,确保其具有足够高的优先级,并且您的摄像机在其边界范围内。
确认着色器不包含任何 clip() 指令,混合模式是否设置为 Off,并且输出 Alpha 始终为 1。
已知问题和限制
- 重命名自定义后期处理类名称和文件会将其从 HDRP Project Settings 的列表中删除,从而导致不再渲染效果。