创建自定义通道
要创建自定义通道,请使用 GameObject > Volume > Custom Pass 将 Custom Pass Volume 组件添加到场景中。这将创建名为 Custom Pass 的游戏对象,其中包含 Custom Pass Volume 组件。使用此组件可以创建和配置自定义通道。
Custom Pass Volume 组件属性
属性 | 描述 |
---|---|
Mode | 使用下拉选单选择 Unity 用于确定该 Custom Pass Volume 是否会影响摄像机的方法: • Global:Custom Pass Volume 无边界,并可以影响场景中的每个摄像机。 • Local:允许为 Custom Pass Volume 指定边界,以便它仅影响边界内的摄像机。要设置边界,请向 Custom Pass Volume 的游戏对象添加碰撞体。 |
Injection point | 使用下拉选单定义 Unity 在 HDRP 渲染循环中何时执行此自定义通道。 有关每个注入点的更多信息,请参阅[注入点](#Injection Points.md) |
Priority | 如果为同一注入点分配了多个 Custom Pass Volume,请使用此属性来控制 Unity 执行它们的顺序。 Unity 按优先级顺序(从 0 开始)执行这些体积。 |
Fade Radius | 定义当接近此体积时 Unity 何时开始淡入自定义通道的效果。 值为 0 表示 HDRP 会在体积边缘直接应用此体积的效果。较大值表示效果会在远离体积的位置开始显现。 仅当 Mode 设置为 Local 时,才显示此属性。 |
Custom Passes | 单击添加 (+) 按钮可创建自定义通道。默认情况下,Custom Pass Volume 组件具有以下类型的自定义通道: • FullScreen Custom Pass:用于执行由 Unity 应用于摄像机视图或存储在自定义通道缓冲区中的效果。有关更多信息,请参阅全屏自定义通道 (Full-screen Custom Pass)。 • DrawRenderers Custom Pass:用于将自定义通道应用于摄像机视图中的游戏对象。有关更多信息,请参阅绘制渲染器自定义通道 (Draw renderers custom pass)。 如果自行创建自定义通道,它也会显示在此下拉选单中。有关更多信息,请参阅用 C# 编写你的自定义通道脚本。 如果此组件中有一个或多个自定义通道,可以单击 **-** 来删除一个通道。 |
全屏自定义通道
全屏自定义通道可将效果应用于整个屏幕。为此,它使用适用于全屏自定义通道的材质来渲染全屏四边形,并为屏幕上的每个像素执行片元着色器。
要创建全屏自定义通道,请单击 Custom Pass Volume 组件右下方的添加 (+) 按钮,然后选择 FullScreeenCustomPass。
FullScreenCustomPass 属性
可以使用以下属性在 Custom Passes 面板中配置全屏自定义通道:
属性 | 描述 |
---|---|
名称 | 使用此字段来命名此自定义通道。Unity 使用此名称在性能分析器中引用此 Custom Pass Volume。 |
Target Color Buffer | 选择 Unity 将颜色数据写入到的缓冲区: Camera:面向渲染自定义通道的当前摄像机颜色缓冲区。 Custom:使用在 HDRP 资源中分配的自定义通道缓冲区。 None:不会将数据写入缓冲区。 如果已启用 Fetch Color Buffer,则无法将颜色数据写入摄像机颜色缓冲区。 Target Color Buffer 和 Target Buffer 都设置为 None 时,Unity 不会执行自定义通道,因为没有要渲染到的缓冲区。 |
Target Depth Buffer | 选择 Unity 在其中写入和测试深度和模板数据的缓冲区。 此缓冲区不包含已在着色器属性中启用了 Depth Write 的透明对象。 Target Color Buffer 和 Target Buffer 都设置为 None 时,Unity 不会执行自定义通道,因为没有要渲染到的缓冲区。 |
Clear Flags | 在 Unity 执行此自定义通道之前,清除标志会丢弃缓冲区的内容。 此属性将清除标志分配给以下缓冲区之一: None:不会清除此通道中的任何缓冲区。 Color:清除深度缓冲区。 Depth:清除深度缓冲区和模板缓冲区。 All:清除颜色、深度和模板缓冲区中的数据。 |
Fetch Color Buffer | 启用此复选框可以允许该自定义通道从颜色缓冲区读取数据。 即使在启用多重采样抗锯齿 (MSAA) 后,此设置也适用。 启用 Fetch Color Buffer 和 MSAA 后,它会强制颜色缓冲区解析,并且自定义通道会使用以下注入点之一: 预折射前 (Before PreRefraction) 透明前 (Before Transparent) 不透明深度和法线后 (After Opaque Depth And Normal) 自定义通道无法读取和写入同一渲染目标。这意味着不能同时启用 Fetch Color Buffer 和使用 Target Color Buffer。 |
FullScreen Material | 此自定义通道在场景中渲染的材质。 |
Pass Name | 选择 Unity 用于绘制全屏四边形的着色器通道名称。 |
创建用于全屏自定义通道的材质
要使用全屏自定义通道,需要为自定义通道创建一个着色器以在场景中使用。Unity 中有一种用于全屏自定义通道的特定着色器。要创建兼容的着色器,请执行以下操作:
- 创建全屏自定义通道着色器。为此,请选择 Assets > Create > Shader > HDRP > Custom FullScreen Pass。
- 找到 Hierarchy 窗口,右键单击 Custom FullScreen Pass 着色器,然后选择 Create > Material。Unity 会创建一个使用前缀“FullScreen_”的新材质。
- 在 Custom Pass 组件中,单击
**+**
以创建自定义通道,然后选择 FullScreenCustomPass。 - 要将此材质分配给 FullScreenCustomPass,请使用 FullScreen Material 框右侧的图标来打开材质选择器,然后选择“FullScreen_...”材质。
- 如果有多个自定义通道,请选择希望此通道使用的通道名称。
为全屏自定义通道编辑 Unity 着色器
Unity 创建用于全屏自定义通道的材质时,它将为 Unity 着色器添加默认模板代码。
要编辑 Unity 着色器,请执行以下操作:
- 在 Assets 窗口中选择材质。
- 导航到 Inspector。
- 单击 Open。
此 Unity 着色器源文件包含以下 FullScreenPass
方法。可以在此处为全屏自定义通道添加自定义着色器代码:
float4 FullScreenPass(Varyings varyings) : SV_Target
{
float depth = LoadCameraDepth(varyings.positionCS.xy);
PositionInputs posInput = GetPositionInput(varyings.positionCS.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
float3 viewDirection = GetWorldSpaceNormalizeViewDir(posInput.positionWS);
float4 color = float4(0.0, 0.0, 0.0, 0.0);
// 如果当前不在 BeforeRendering 注入点中,则将摄像机颜色缓冲区加载到 Mip 0
if (_CustomPassInjectionPoint != CUSTOMPASSINJECTIONPOINT_BEFORE_RENDERING)
color = float4(CustomPassLoadCameraColor(varyings.positionCS.xy, 0), 1);
// 在此处添加自定义通道代码
// 淡化值可让你在摄像机接近自定义通道体积的同时增强效果的强度
float f = 1 - abs(_FadeValue * 2 - 1);
return float4(color.rgb + f, color.a);
}
FullScreenPass 方法获取以下输入数据:
- 深度
- 视图方向
- 世界空间中的位置
- 颜色
FullScreenPass
方法还包含 _FadeValue
变量;当摄像机靠近 Custom Pass Volume 时,此变量可用于增强效果的强度。此变量仅适用于已分配 Fade Radius 的本地 Custom Pass Volume。
绘制渲染器自定义通道
可以使用绘制渲染器自定义通道将效果应用于摄像机的视图中的游戏对象。
还可以使用此自定义通道,通过 HD Camera 组件中的 Culling Mask 下拉选单属性中的选项,将效果应用于摄像机视图之外的对象。
要创建绘制渲染器自定义通道,请单击 Custom Pass Volume 组件中的添加 (+) 按钮,然后选择 DrawRenderersCustomPass。
绘制渲染器自定义通道属性
可以使用以下属性在 Custom Passes 面板中配置绘制渲染器自定义通道:
属性 | 描述 |
---|---|
名称 | 使用此字段来命名此自定义通道。Unity 将此名称用作性能分析标记的名称以进行调试。 |
Target Color Buffer | 选择 Unity 将颜色数据写入到的缓冲区: Camera:面向渲染自定义通道的当前摄像机颜色缓冲区。 Custom:使用在 HDRP 资源中分配的自定义通道缓冲区。 None:不会将数据写入缓冲区。 |
Target Depth Buffer | Unity 写入和测试深度和模板数据的目标缓冲区: Camera:面向渲染自定义通道的当前摄像机深度缓冲区。 Custom:使用在 HDRP 资源中分配的自定义通道缓冲区。 None:不会将数据写入缓冲区。 此缓冲区不包含已在着色器属性中启用了 Depth Write 的透明对象。 |
Clear Flags | 在 Unity 执行此自定义通道之前,清除标志会丢弃缓冲区的内容。 此属性将清除标志分配给以下缓冲区之一: None:不会清除此通道中的任何缓冲区。 Color:清除深度缓冲区。 Depth:清除深度缓冲区和模板缓冲区。 All:清除颜色、深度和模板缓冲区中的数据。 |
Filters | 此部分可以确定 Unity 在此自定义通道中渲染的游戏对象。 |
Queue | 选择此自定义通道渲染的材质类型。 |
Layer Mask | 选择此自定义通道应用于的游戏对象层。 |
Overrides | |
Material | 选择材质以供 Custom Pass Volume 用来覆盖此自定义通道中包括的所有内容。 此字段接受无光照 Shader Graph、无光照 HDRP Unity 着色器或光照着色器。有关兼容材质的完整列表,请参阅材质和注入点兼容性。 可以使用菜单中的 Create > Shader > HDRP > Custom Renderers Pass 来创建与绘制渲染器自定义通道兼容的 Unity 着色器。 |
Pass Name | 将材质分配给 Material 字段时,将显示此字段。此字段的下拉选项会根据分配给 Material 字段的材质而变化。 选择 Unity 用于绘制全屏四边形的自定义通道。可以使用它在多个自定义通道效果之间切换。 |
Override Depth | 启用此复选框可在渲染的游戏对象的材质中覆盖深度渲染状态。 此设置可用于替换默认的 Depth Test 值,并使用自定义值来写入深度。 |
Depth Test | Unity 渲染游戏对象时,它使用 Depth Test 值来检查该对象是否位于另一对象后面。为此,Unity 测试给定游戏对象的像素的 z 值(深度),并与深度缓冲区中存储的值比较。 默认情况下,Depth Test 设置为 Less Equal,允许原始对象出现在测试参考对象的前面。使用下拉选单可选择用于深度测试的比较方法。每种比较方法都会更改着色器的渲染方式: Disabled:不执行深度测试。 Never:深度测试始终不通过。 Less:如果像素的 z 值小于存储的值,则深度测试通过。 Equal:如果像素的 z 值等于存储的值,则深度测试通过。 Less Equal:如果像素的 z 值小于或等于 Z 缓冲区值,则深度测试通过。这会将测试的像素渲染到另一个像素的前面。 Greater:如果像素的 z 值大于存储的值,则深度测试通过。 Not Equal:如果像素的 z 值不等于存储的值,则深度测试通过。 Greater Equal:如果像素的 z 值大于或等于存储的值,则深度测试通过。 Always:深度测试始终通过,不会与存储的值进行比较。仅在启用 Override Depth 时,才显示此设置。 |
Write Depth | 启用 Write Depth 可指示 Unity 为使用该材质的游戏对象写入深度值。 如果不希望 Unity 为每个游戏对象写入深度值,请将其禁用。 |
Sorting | 选择 Unity 在场景中渲染游戏对象之前对游戏对象进行排序的方式。 有关更多信息,请参阅排序条件。 |
ForwardOnly 支持
Unity 使用通道名称 (Pass Name) 来选择在 HDRP 材质上渲染着色器的哪个通道。要渲染对象颜色,请选择 Forward 或 ForwardOnly。如果只想渲染对象的深度,请使用 DepthForwardOnly 通道名称。
如果在创建新的绘制渲染器自定义通道时看到以下警告,可能是 HDRP 资源设置引起的:
要解决此问题,请导航至 Assets 文件夹中的 HDRP 资源(如果使用的是 HDRP 模板,则位于 Assets > Settings 中),并将 Lit Shader Mode 更改为 Both。有关更多信息,请参阅更改场景中渲染器的深度。
材质和注入点兼容性
注入点确定 Unity 在渲染循环中何时执行 Custom Pass Volume。
但是,并非所有材质都得到绘制渲染器自定义通道中每个注入点的支持。下表列出了绘制渲染器自定义通道中每个注入点支持的材质:
注入点 | 支持的材质类型 |
---|---|
渲染前 | 无光照前向,但不会写入摄像机颜色 |
不透明深度和法线后 | 无光照前向 |
预折射前 | 仅限无光照和光照前向 |
透明前 | 仅限无光照和光照前向(包含折射) |
后期处理前 | 仅限无光照和光照前向(包含折射) |
后期处理后 | 仅限无光照和光照前向(包含折射) |
Unity 渲染当前注入点不支持的材质时,将导致不确定的行为。例如,在不透明深度和法线后注入点中使用光照着色器渲染游戏对象将产生意外结果。
自定义渲染器通道着色器
可以使用自定义渲染器通道着色器来创建高级的自定义通道效果。要创建此着色器,请导航至 Assets > Create > Shader > HDRP > Custom Renderers Pass。这将创建一个名为 New Renderers CustomPass 的无光照 HDRP 着色器。此 Unity 着色器具有一个 ShaderLab 通道。
使用自定义渲染器通道着色器来创建高级效果
HDRP 包括一个特定的着色器结构,可存储渲染管线用于渲染场景材质的数据。在“片元着色器代码”部分中有这些结构的描述。
片元着色器代码
要在自定义渲染器通道着色器中编写片元着色器代码,请使用 GetSurfaceAndBuiltinData
方法。
此方法接受以下输入:
- 一个
FragInputs
结构,其中包含所有传递到片元着色器的几何体输入。有关此结构中属性的信息,请参阅 FragInput。 - 一个
float3
,其中包含当前像素的视图方向。 - 一个
PositionInputs
结构,其中包含着色器中可能需要的基于位置的实用程序属性。有关此结构中属性的信息,请参阅 PositionInputs。
GetSurfaceAndBuiltinData
方法不使用 return
来输出结果。实际上,它有两个参数,这些参数使用 out 修饰符。这一情况的原因是为了根据着色器的表面类型支持不同的输出。输出包括:
- 一个
SurfaceData
结构,其中包含最终颜色。有关此结构中属性的信息,请参阅 SurfaceData。 - 一个
BuiltinData
结构,其中包含 HDRP 可用于计算特定效果的其他信息。有关此结构中属性的信息,请参阅 BuiltinData。
以下示例演示了如何使用 GetSurfaceAndBuiltinData
方法来编写片元着色器代码。此示例对 _ColorMap
纹理的颜色进行采样,使用 Alpha 通道执行 Alpha 测试,然后输出表面的纹理颜色:
// 在此方法中添加用于在自定义通道中渲染游戏对象的代码
void GetSurfaceAndBuiltinData(FragInputs fragInputs, float3 viewDirection, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData)
{
float2 colorMapUv = TRANSFORM_TEX(fragInputs.texCoord0.xy, _ColorMap);
float4 result = SAMPLE_TEXTURE2D(_ColorMap, s_trilinear_clamp_sampler, colorMapUv) * _Color;
float opacity = result.a;
float3 color = result.rgb;
# ifdef _ALPHATEST_ON
DoAlphaTest(opacity, _AlphaCutoff);
# endif
// 将数据写回到输出结构
ZERO_INITIALIZE(BuiltinData, builtinData); // 不调用 InitBuiltinData,因为没有任何光照
builtinData.opacity = opacity;
builtinData.emissiveColor = float3(0, 0, 0);
surfaceData.color = color;
}
FragInputs
FragInputs
结构包含所有传递到片元阶段的几何体输入:
struct FragInputs
{
float4 positionSS; // 如果使用深度偏移,则 positionRWS.w 等于深度偏移
float3 positionRWS; // 相对摄像机空间位置
float4 texCoord0; // UV0
float4 texCoord1; // UV1
float4 texCoord2; // UV2
float4 texCoord3; // UV3
float4 color; // 顶点颜色
float3x3 tangentToWorld;
bool isFrontFace;
};
PositionInputs
PositionInputs
提供可能需要在着色器中使用的另一组属性。可以将其视为用于访问位置相关属性的实用程序结构。深度来自当前的摄像机深度缓冲区,并且 tileCoord
不可用(仅在计算着色器中使用):
struct PositionInputs
{
float3 positionWS; // 世界空间位置(可能相对于摄像机)
float2 positionNDC; // 视口内的归一化屏幕坐标:[0, 1)(偏移半像素)
uint2 positionSS; // 屏幕空间像素坐标:[0, NumPixels)
uint2 tileCoord; // 屏幕瓦片坐标:[0, NumTiles)
float deviceDepth; // 深度缓冲区的深度:[0, 1](通常反转)
float linearDepth; // 视图空间 Z 坐标:[Near, Far]
};
SurfaceData
SurfaceData
结构可用于设置对象表面的颜色。每种材质类型都有不同的表示形式。对于无光照着色器,它仅包含一个表示无光照对象颜色的字段:
struct SurfaceData
{
float3 color;
};
BuiltinData
BuiltinData
结构包含可以传递给 HDRP 的其他信息:
struct BuiltinData
{
real opacity; // 对象的半透明度
real alphaClipTreshold; // Alpha 裁剪的阈值
real3 bakeDiffuseLighting;
real3 backBakeDiffuseLighting;
real shadowMask0;
real shadowMask1;
real shadowMask2;
real shadowMask3;
real3 emissiveColor; // 发光颜色
real2 motionVector; // 尚不受支持
real2 distortion; // 失真矢量
real distortionBlur; // 失真模糊级别 [0, 1]
uint renderingLayers;
float depthOffset; // 深度偏移
real4 vtPackedFeedback;
};
顶点着色器代码
要在自定义渲染器通道着色器中编写顶点着色器代码,请使用 ApplyMeshModification 方法。默认情况下,着色器会将此方法注释掉,因此若要使用它,请移除注释斜杠 (//)。可以使用此方法将顶点变形或顶点动画添加到 Unity 着色器。
就输入而言,此方法接受:
- 一个 AttributeMesh 结构,其中包含当前顶点及其属性。有关此结构中属性的信息以及这些属性对应的定义,请参阅 AttributeMesh。
- 一个 float3,其中包含当前
timeParameters
。在此浮点数中,x 值表示时间(秒),y 值表示 sin(x),z 值表示 cos(x)。
就输出而言,此方法返回 AttributeMesh
,表示修改后的顶点。通常的工作流程是修改输入 AttributeMesh
并返回它。
请注意,此方法中的变换都是在对象空间中。如果要在世界空间中应用变换,请执行以下操作:
- 将对象空间数据转换为世界空间。使用
TransformObjectToWorld
方法来转换位置数据,并使用TransformObjectToWorldDir
方法来转换法线数据。 - 应用变换。
- 将世界空间数据转换回对象空间。使用
TransformWorldToObject
方法来转换位置数据,并使用TransformWorldToObjectDir
来转换法线数据。
有关如何使用此方法编写顶点着色器代码的示例,请参阅以下代码示例。此示例修改了顶点数据以稍微扩大网格并避免深度冲突:
# define HAVE_MESH_MODIFICATION
AttributesMesh ApplyMeshModification(AttributesMesh input, float3 timeParameters)
{
input.positionOS += input.normalOS * 0.0001; // 使网格略微扩大一点以避免深度冲突。
return input;
}
AtrributeMesh
AttributesMesh
结构具有以下定义:
struct AttributesMesh
{
float3 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT; // 将符号存储到 w 中
float2 uv0 : TEXCOORD0;
float2 uv1 : TEXCOORD1;
float2 uv2 : TEXCOORD2;
float2 uv3 : TEXCOORD3;
float4 color : COLOR;
};
在片元着色器中对 UV 采样
Unity 使用以下定义将数据发送到顶点和片元着色器:
ATTRIBUTES_NEED_TEXCOORD0
VARYINGS_NEED_TEXCOORD0
可以使用 ATTRIBUTES_NEED
和 VARYINGS_NEED
系统来确定 Unity 发送到顶点和片元着色器的数据。ATTRIBUTES_NEED
控制顶点数据,VARYINGS_NEED
控制片元数据。可使用以下定义列表来控制此数据:
# define ATTRIBUTES_NEED_NORMAL
# define ATTRIBUTES_NEED_TANGENT
# define ATTRIBUTES_NEED_TEXCOORD0
# define ATTRIBUTES_NEED_TEXCOORD1
# define ATTRIBUTES_NEED_TEXCOORD2
# define ATTRIBUTES_NEED_TEXCOORD3
# define ATTRIBUTES_NEED_COLOR
# define VARYINGS_NEED_POSITION_WS
# define VARYINGS_NEED_TANGENT_TO_WORLD
# define VARYINGS_NEED_TEXCOORD0
# define VARYINGS_NEED_TEXCOORD1
# define VARYINGS_NEED_TEXCOORD2
# define VARYINGS_NEED_TEXCOORD3
# define VARYINGS_NEED_COLOR
# define VARYINGS_NEED_CULLFACE
默认情况下,可以使用 ATTRIBUTES_NEED_TEXCOORD0
和 ATTRIBUTES_NEED_NORMAL
来访问 UV 0 和法线。
更改场景中渲染器的深度
可以覆盖自定义通道中任何渲染器组件的深度状态。这一过程将使用 Custom Pass Volume 组件中分配的深度渲染状态来替换场景中渲染的游戏对象的深度状态。有时可能需要使用此选项使场景中的某些游戏对象在自定义通道中不可见。
还可以使用此方法在自定义通道中渲染不在摄像机剔除遮罩中的游戏对象。Unity 会在 Equal 深度测试中渲染不在摄像机剔除遮罩中的不透明游戏对象。仅当游戏对象在深度缓冲区内时,才能更改渲染器的深度状态。要将这些不透明游戏对象包含在深度缓冲区中,请选择 Custom Pass Volume 组件,然后将 Depth Test 属性设置为 Less Equal。
Unity** 使用前向渲染路径来渲染自定义通道中的所有对象。如果场景设置为使用 **Deferred Only 渲染,这可能会导致问题。在 HDRP 资源中,将 Lit Shader Mode 更改为 Both 可以避免在构建项目时出现问题。
使用自定义缓冲区格式
可以使用自定义缓冲区来存储通道的结果,从而稍后在渲染过程中或两个自定义通道之间执行。在任何类型的自定义通道着色器中都可以对这些自定义缓冲区进行采样。
默认情况下,对于绘制着色器自定义通道,Unity 将目标缓冲区设置为摄像机缓冲区,但也可以选择自定义缓冲区。要使用自定义缓冲区格式 (Custom Buffer Format),场景中需要 HDRP 资源。如果已创建 HDRP 模板场景,在 Assets > Settings 中就会有一个名为 HRenderPipelineAsset 的 HDRP 资源。否则,请遵循 HDRP 资源中的说明,为场景创建新的 HDRP 资源。
要更改 HDRP 资源中的 Custom Pass 组件的缓冲区格式,请选择 Rendering > Custom Pass > Custom Buffer Format。
在 HDRP 资源中,还可以禁用自定义通道;为此,只需禁用 Custom Pass 属性。这会自动禁用自定义缓冲区分配。还可以在帧设置中选择是否渲染自定义通道。这不会影响自定义缓冲区分配。