Skip to content

Commit 4460168

Browse files
Vic-Coopersebastienlagarde
authored andcommitted
First Custom Pass docs commit #3808
1 parent f411023 commit 4460168

17 files changed

+903
-563
lines changed

com.unity.render-pipelines.high-definition/Documentation~/Custom-Pass-Creating.md

Lines changed: 387 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Viewing a Custom Pass in the Frame Debugger
2+
3+
You can use the [Frame Debugger](https://docs.unity3d.com/Manual/FrameDebugger.html) to examine every draw call you issued in the Custom Pass. To find your pass, look for the name you gave your Custom Pass in the Custom Pass Volume component.
4+
5+
In this example, the Custom Pass is called **My Outline Pass**. The **Frame Debug** window (menu:
6+
7+
**Window > Analysis > Frame Debugger**) displays the steps that **My Outline Pass** executes in the left window, highlighted in the image below:
8+
9+
![](images/Custom_Pass_Frame_Debug.png)
10+
11+
To learn more about the Frame Debugger, see the [Frame Debugger](https://docs.unity3d.com/Manual/FrameDebugger.html) documentation.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Injection Points
2+
3+
To determine when Unity executes a Custom Pass Volume, select an **Injection Point** in the [Custom Pass Volume](Custom-Pass-Creating.md#Custom-Pass-Volume) component.
4+
5+
Each injection point affects the way Custom Passes appear in your scene. There are six injection points in the HDRP render loop. Unity can only execute one Custom Pass Volume in each injection point.
6+
7+
Injection points give the Custom Pass Volume component access to a selection of buffers. Each available buffer has different read or write access at each **injection point**. Each buffer contains a subset of objects rendered before your pass.
8+
9+
In a **DrawRenderers Custom Pass** you can only use certain materials at specific injection points. For a full list of compatible materials, see [Material and injection point compatibility](Custom-Pass-Creating.md#Material-Injection-Point-Compatibility).
10+
11+
To analyse the actions Unity performs in a render loop and see where Unity executes your Custom Pass, use the [frame debugger](https://docs.unity3d.com/Manual/FrameDebugger.html).
12+
13+
Unity triggers the following injection points in a frame, in order from top to bottom:
14+
15+
16+
17+
| **Injection point** | **Available buffers** | **Description** |
18+
| ------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
19+
| BeforeRendering | Depth (Write) | Unity clears the depth buffer immediately before this injection point. <br/><br/>In this injection point you can write to the depth buffer so that Unity doesn’t render depth-tested, opaque objects.<br/><br/>You can also clear the buffer you allocated or the `Custom Buffer`.<br/><br/>When you select this Injection point for a [FullscreenCustom Pass](Custom-Pass-Creating.md#Full-Screen-Custom-Pass), Unity assigns the camera color buffer as the target by default. |
20+
| AfterOpaqueDepthAndNormal | Depth (Read \| Write), Normal and roughness (Read \| Write) | The available buffers for this injection point contain all opaque objects.<br/><br/>In this injection point you can modify the normal, roughness and depth buffer. HDRP takes this into account in the lighting and the depth pyramid.<br/><br/>Normals and roughness are in the same buffer. You can use `DecodeFromNormalBuffer` and `EncodeIntoNormalBuffer` methods to read/write normal and roughness data. |
21+
| BeforePreRefraction | Color (no pyramid \| Read \| Write), Depth (Read \| Write), Normal and roughness (Read) | The available buffers for this injection point contain all opaque objects and the sky.<br/><br/>In this injection point you can render any transparent objects that require refraction. These objects are then included in the color pyramid that Unity uses for refraction when it renders transparent objects. |
22+
| BeforeTransparent | Color (Pyramid \| Read \| Write), Depth (Read \| Write), Normal and roughness (Read) | The available buffers for this injection point contain:<br/>- All opaque objects.<br/>- Transparent PreRefraction objects.<br/>- Transparent objects with depth-prepass and screen space reflections (SSR) enabled.<br/><br/>In this Injection Point you can sample the color pyramid that Unity uses for transparent refraction. You can use this to create a blur effect. All objects Unity renders in this injection point will not be in the color pyramid.<br/><br/>You can also use this injection point to draw some transparent objects that refract the whole scene, like water. |
23+
| BeforePostProcess | Color (Pyramid \| Read \| Write), Depth (Read \| Write), Normal and roughness (Read) | The available buffers for this injection point contain all geometry in the frame that uses High Dynamic Range (HDR). |
24+
| AfterPostProcess | Color (Read \| Write), Depth (Read) | The available buffers for this injection point contain the final render of the scene, including post-process effects.<br/><br/>This injection point executes the available buffers after Unity applies any post-processing effects.<br/><br/>If you select this injection point, objects that use the depth buffer display jittering artifacts.<br/><br/>When you select this injection point for a [FullscreenCustom Pass](Custom-Pass-Creating.md#Full-Screen-Custom-Pass), Unity assigns the camera color buffer as the target by default. |
25+
26+
The following diagram describes where Unity injects Custom Passes into an HDRP frame:
27+
28+
![](images/HDRP-frame-graph-diagram.png)
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
# Scripting your own Custom Pass in C#
2+
3+
You can extend the CustomPass class in the Custom Pass API to create complex effects, such as a Custom Pass that has more than one buffer or uses [Compute Shaders](https://docs.unity3d.com/Manual/class-ComputeShader.html).
4+
5+
When you create your own C# Custom Pass using the instructions in [The Custom Pass C# Template](#Custom-Pass-C#-template), it automatically appears in the list of available Custom Passes in the Custom Pass Volume component.
6+
7+
<a name="Custom-Pass-C#-template"></a>
8+
9+
## **The Custom Pass C# template**
10+
11+
To create a new Custom pass, go to **Assets > Create > Rendering > C# Custom Pass**. This creates a new script that contains the Custom Pass C# template:
12+
13+
```C#
14+
class #SCRIPTNAME# : CustomPass
15+
{
16+
protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd) {}
17+
18+
protected override void Execute(CustomPassContext ctx) {}
19+
20+
protected override void Cleanup() {}
21+
}
22+
```
23+
24+
The C# Custom Pass template includes the following entry points to code your custom pass:
25+
26+
27+
28+
| **Entry Point** | **Description** |
29+
| --------------- | ------------------------------------------------------------ |
30+
| `Setup` | Use this to allocate all the resources you need to render your pass, such as render textures, materials, and compute buffers. |
31+
| `Execute` | Use this to describe what HDRP renders during the Custom Pass. |
32+
| `Cleanup` | Use this to clear the resources you allocated in the Setup method .Make sure to include every allocated resource to avoid memory leaks. |
33+
34+
The `Setup` and `Execute` methods give you access to a `ScriptableRenderContext` and a `CommandBuffer`. For information on using `CommandBuffers` with a `ScriptableRenderContext`, see [Scheduling and executing commands in the Scriptable Render Pipeline](https://docs.unity3d.com/Manual/srp-using-scriptable-render-context.html).
35+
36+
## **Creating a full-screen Custom Pass in C#**
37+
38+
The following code demonstrates how to create a full-screen Custom Pass that applies an outline effect to an object in your scene.
39+
40+
![A mesh in a scene rendered using this outline effect](images/CustomPass_FrameDebugger.png)
41+
42+
This effect uses a transparent full screen pass with a blend mode that replaces the pixels around the GameObject you assign the script to.
43+
44+
This shader code performs the following steps:
45+
46+
1. Renders the objects in the outline layer to a buffer called `outlineBuffer`.
47+
2. Samples the color in `outlineBuffer`. If the color is below the threshold, then it means that the pixel might be in an outline.
48+
3. Searches neighboring pixels to check if this is the case.
49+
4. If Unity finds a pixel above the threshold, it applies the outline effect.
50+
51+
### Creating a CustomPass script
52+
53+
To create a CustomPass script:
54+
55+
1. Create a new C# script using **Assets > Create > C# Script**.
56+
2. Name your script. In this example, the new script is called “Outline”
57+
3. Enter the following code:
58+
59+
```C#
60+
using UnityEngine;
61+
using UnityEngine.Rendering.HighDefinition;
62+
using UnityEngine.Rendering;
63+
using UnityEngine.Experimental.Rendering;
64+
65+
class Outline : CustomPass
66+
{
67+
public LayerMask outlineLayer = 0;
68+
[ColorUsage(false, true)]
69+
public Color outlineColor = Color.black;
70+
public float threshold = 1;
71+
72+
// To make sure the shader ends up in the build, we keep a reference to it
73+
[SerializeField, HideInInspector]
74+
Shader outlineShader;
75+
76+
Material fullscreenOutline;
77+
RTHandle outlineBuffer;
78+
79+
protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
80+
{
81+
outlineShader = Shader.Find("Hidden/Outline");
82+
fullscreenOutline = CoreUtils.CreateEngineMaterial(outlineShader);
83+
84+
// Define the outline buffer
85+
outlineBuffer = RTHandles.Alloc(
86+
Vector2.one, TextureXR.slices, dimension: TextureXR.dimension,
87+
colorFormat: GraphicsFormat.B10G11R11_UFloatPack32,
88+
// We don't need alpha for this effect
89+
useDynamicScale: true, name: "Outline Buffer"
90+
);
91+
}
92+
93+
protected override void Execute(CustomPassContext ctx)
94+
{
95+
// Render meshes we want to apply the outline effect to in the outline buffer
96+
CoreUtils.SetRenderTarget(ctx.cmd, outlineBuffer, ClearFlag.Color);
97+
CustomPassUtils.DrawRenderers(ctx, outlineLayer);
98+
99+
// Set up outline effect properties
100+
ctx.propertyBlock.SetColor("_OutlineColor", outlineColor);
101+
ctx.propertyBlock.SetTexture("_OutlineBuffer", outlineBuffer);
102+
ctx.propertyBlock.SetFloat("_Threshold", threshold);
103+
104+
// Render the outline buffer fullscreen
105+
CoreUtils.SetRenderTarget(ctx.cmd, ctx.cameraColorBuffer, ClearFlag.None);
106+
CoreUtils.DrawFullScreen(ctx.cmd, fullscreenOutline, ctx.propertyBlock, shaderPassId: 0);
107+
}
108+
109+
protected override void Cleanup()
110+
{
111+
CoreUtils.Destroy(fullscreenOutline);
112+
outlineBuffer.Release();
113+
}
114+
}
115+
```
116+
117+
### Creating a Unity shader
118+
119+
To create a new shader:
120+
121+
1. Create a new Unity shader using **Assets> Create> Shader**
122+
2. Name the new shader source file “Outline”
123+
3. Enter the following code:
124+
125+
```C#
126+
Shader "Hidden/Outline"
127+
{
128+
HLSLINCLUDE
129+
130+
#pragma vertex Vert
131+
132+
#pragma target 4.5
133+
#pragma only_renderers d3d11 playstation xboxone vulkan metal switch
134+
135+
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl"
136+
137+
TEXTURE2D_X(_OutlineBuffer);
138+
float4 _OutlineColor;
139+
float _Threshold;
140+
141+
#define v2 1.41421
142+
#define c45 0.707107
143+
#define c225 0.9238795
144+
#define s225 0.3826834
145+
146+
#define MAXSAMPLES 8
147+
// Neighbour pixel positions
148+
static float2 samplingPositions[MAXSAMPLES] =
149+
{
150+
float2( 1, 1),
151+
float2( 0, 1),
152+
float2(-1, 1),
153+
float2(-1, 0),
154+
float2(-1, -1),
155+
float2( 0, -1),
156+
float2( 1, -1),
157+
float2( 1, 0),
158+
};
159+
160+
float4 FullScreenPass(Varyings varyings) : SV_Target
161+
{
162+
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(varyings);
163+
164+
float depth = LoadCameraDepth(varyings.positionCS.xy);
165+
PositionInputs posInput = GetPositionInput(varyings.positionCS.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
166+
float4 color = float4(0.0, 0.0, 0.0, 0.0);
167+
float luminanceThreshold = max(0.000001, _Threshold * 0.01);
168+
169+
// Load the camera color buffer at the mip 0 if we're not at the before rendering injection point
170+
if (_CustomPassInjectionPoint != CUSTOMPASSINJECTIONPOINT_BEFORE_RENDERING)
171+
color = float4(CustomPassSampleCameraColor(posInput.positionNDC.xy, 0), 1);
172+
173+
// When sampling RTHandle texture, always use _RTHandleScale.xy to scale your UVs first.
174+
float2 uv = posInput.positionNDC.xy * _RTHandleScale.xy;
175+
float4 outline = SAMPLE_TEXTURE2D_X_LOD(_OutlineBuffer, s_linear_clamp_sampler, uv, 0);
176+
outline.a = 0;
177+
178+
// If this sample is below the threshold
179+
if (Luminance(outline.rgb) < luminanceThreshold)
180+
{
181+
// Search neighbors
182+
for (int i = 0; i < MAXSAMPLES; i++)
183+
{
184+
float2 uvN = uv + _ScreenSize.zw * _RTHandleScale.xy * samplingPositions[i];
185+
float4 neighbour = SAMPLE_TEXTURE2D_X_LOD(_OutlineBuffer, s_linear_clamp_sampler, uvN, 0);
186+
187+
if (Luminance(neighbour) > luminanceThreshold)
188+
{
189+
outline.rgb = _OutlineColor.rgb;
190+
outline.a = 1;
191+
break;
192+
}
193+
}
194+
}
195+
196+
return outline;
197+
}
198+
199+
ENDHLSL
200+
201+
SubShader
202+
{
203+
Pass
204+
{
205+
Name "Custom Pass 0"
206+
207+
ZWrite Off
208+
ZTest Always
209+
Blend SrcAlpha OneMinusSrcAlpha
210+
Cull Off
211+
212+
HLSLPROGRAM
213+
#pragma fragment FullScreenPass
214+
ENDHLSL
215+
}
216+
}
217+
Fallback Off
218+
}
219+
```
220+
221+
### Using a C# Custom Pass effect
222+
223+
To enable an effect you created in a shader, assign it to the **FullScreen Material** property of a [Full-screeen Custom Pass](Custom-Pass-Creating.md#Full-Screen-Custom-Pass) component.
224+
225+
## Controlling a Custom Pass Volume component using code
226+
227+
You can retrieve the `CustomPassVolume` in a script using [GetComponent](https://docs.unity3d.com/ScriptReference/GameObject.GetComponent.html) and access most of the things available from the UI like `isGlobal`, `fadeRadius` and `injectionPoint`.
228+
229+
You can also dynamically change the list of Custom Passes executed by modifying the `customPasses` list.
230+
231+
### Scripting the Custom Pass Volume component properties
232+
233+
To customize the properties of a Custom Pass in the Inspector window, you can use a similar pattern to the [CustomPropertyDrawer](https://docs.unity3d.com/ScriptReference/CustomPropertyDrawer.html) MonoBehaviour Editor, but with different attributes.
234+
235+
The following example is a part of the full-screen Custom Pass drawer:
236+
237+
```C#
238+
[CustomPassDrawerAttribute(typeof(FullScreenCustomPass))]
239+
public class FullScreenCustomPassDrawer : CustomPassDrawer
240+
{
241+
protected override void Initialize(SerializedProperty customPass)
242+
{
243+
// Initialize the local SerializedProperty you will use in your pass.
244+
}
245+
246+
protected override void DoPassGUI(SerializedProperty customPass, Rect rect)
247+
{
248+
// Draw your custom GUI using `EditorGUI` calls. Note that the Layout methods don't work here
249+
}
250+
251+
protected override float GetPassHeight(SerializedProperty customPass)
252+
{
253+
// Return the vertical height in pixels that you used in the DoPassGUI method above.
254+
// Can be dynamic.
255+
return (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing) * X;
256+
}
257+
}
258+
```
259+
260+
When you create a Custom Pass drawer, Unity provides a default list of Custom Pass properties. Unity still does this when `DoPassGUI` is empty. These properties are the same properties that Unity provides in the [draw renderers CustomPass Volume](Custom-Pass-Creating.md#Draw-Renderers-Custom-Pass) component by default.
261+
262+
If you don't need all of these settings, you can override the `commonPassUIFlags` property to remove some of them. The following example only keeps the name and the target buffer enum:
263+
264+
```c#
265+
protected override PassUIFlag commonPassUIFlags => PassUIFlag.Name | PassUIFlag.TargetColorBuffer;
266+
```

0 commit comments

Comments
 (0)