Skip to content

Commit 9a7f1a7

Browse files
committed
Added a tutorial.
1 parent 5036cc5 commit 9a7f1a7

5 files changed

Lines changed: 151 additions & 1 deletion

File tree

18.5 KB
Loading
12.2 KB
Loading
21.9 KB
Loading
2.34 MB
Loading

README.md

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,157 @@ The package contains 8 example effect which are included as a sample. Samples ca
3838

3939
## Tutorial
4040

41-
**TODO**
41+
First, we need a c# shader and a script for our custom effect. We will create a grayscale effect for the sake of simplicity.
42+
43+
First, lets create the C# script. We will call it `GrayScaleEffect.cs`. The name of the file must match the volume component class name to comply with Unity Serialization rules. In the file write the following:
44+
45+
```csharp
46+
using UnityEngine;
47+
using UnityEngine.Rendering;
48+
using UnityEngine.Rendering.Universal;
49+
using UnityEngine.Rendering.Universal.PostProcessing;
50+
51+
// Define the Volume Component for the custom effect
52+
[System.Serializable, VolumeComponentMenu("CustomPostProcess/Grayscale")]
53+
public class GrayscaleEffect : VolumeComponent
54+
{
55+
[Tooltip("Controls the effect strength")]
56+
public ClampedFloatParameter blend = new ClampedFloatParameter(0, 0, 1);
57+
}
58+
59+
// Define the renderer for the custom post processing effect
60+
[CustomPostProcess("Grayscale", CustomPostProcessInjectPoint.AfterPostProcess)]
61+
public class GrayscaleEffectRenderer : CustomPostProcessRenderer
62+
{
63+
// A variable to hold a reference to the corresponding volume component.
64+
private GrayscaleEffect m_VolumeComponent;
65+
66+
// The postprocessing material.
67+
private Material m_Material;
68+
69+
// The ids of the shader variables
70+
static class ShaderIDs {
71+
internal readonly static int Input = Shader.PropertyToID("_MainTex");
72+
internal readonly static int Blend = Shader.PropertyToID("_Blend");
73+
}
74+
75+
// Whether the effect is visible in the scene view or not.
76+
public override bool visibleInSceneView => true;
77+
78+
// Setup is called once so we use it to create our material
79+
public override void Setup()
80+
{
81+
m_Material = CoreUtils.CreateEngineMaterial("Hidden/PostProcess/Grayscale");
82+
}
83+
84+
// Called once before rendering for each camera.
85+
// Return true if the effect should be rendered.
86+
public override bool SetupCamera(ref RenderingData renderingData)
87+
{
88+
// Get the current volume stack
89+
var stack = VolumeManager.instance.stack;
90+
// Get the corresponding volume component
91+
m_VolumeComponent = stack.GetComponent<GrayscaleEffect>();
92+
// if blend value > 0, then render this effect.
93+
return m_VolumeComponent.blend.value > 0;
94+
}
95+
96+
// The actual rendering execution is done here
97+
public override void Render(
98+
CommandBuffer cmd,
99+
ref RenderingData renderingData,
100+
RenderTargetIdentifier source,
101+
RenderTargetIdentifier destination
102+
){
103+
// set material properties
104+
if(m_Material != null){
105+
m_Material.SetFloat(ShaderIDs.Blend, m_VolumeComponent.blend.value);
106+
}
107+
// set source texture
108+
cmd.SetGlobalTexture(ShaderIDs.Input, source);
109+
// draw a fullscreen triangle to the destination
110+
CoreUtils.DrawFullScreen(cmd, m_Material, destination);
111+
}
112+
}
113+
```
114+
115+
As you can see, the code consists of two classes: the volume component and the renderer. The volume component only holds data and will appear as a volume profile option. The renderer is rensponsible for rendering the effect and read the effect parameters from the volume component. The volume component and renderer is decoupled from each other which opens up many options while implementing your effects. For example:
116+
1. You can have a one-to-one relationship between your volume component and renderer (as we did above).
117+
2. You can have a renderer without any corresponding volume component if it does not need any data from the volumes.
118+
3. You can have a renderer that reads from multiple volume components (see [GrayAndInvertEffect.cs](Samples~/Examples/Scripts/PostProcessing/GrayAndInvertEffect.cs) as an example).
119+
4. You can have multiple renderers read from the same volume component(s).
120+
121+
The option #3 is especially useful for writing uber effect shaders that can do multiple effects in the same blit to enhance performance.
122+
123+
Then we create the shader code. Create a shader file with any name you like (I prefer `GrayScale.shader`) and replace its content with the following code:
124+
125+
```glsl
126+
Shader "Hidden/PostProcess/Grayscale"
127+
{
128+
HLSLINCLUDE
129+
#include "Packages/com.yetman.render-pipelines.universal.postprocess/ShaderLibrary/Core.hlsl"
130+
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
131+
132+
TEXTURE2D_X(_MainTex);
133+
134+
float _Blend;
135+
136+
float4 GrayscaleFragmentProgram (PostProcessVaryings input) : SV_Target
137+
{
138+
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
139+
140+
float2 uv = UnityStereoTransformScreenSpaceTex(input.texcoord);
141+
float4 color = LOAD_TEXTURE2D_X(_MainTex, uv * _ScreenParams.xy);
142+
143+
// Blend between the original and the grayscale color
144+
color.rgb = lerp(color.rgb, Luminance(color.rgb).xxx, _Blend);
145+
146+
return color;
147+
}
148+
ENDHLSL
149+
150+
SubShader
151+
{
152+
Cull Off ZWrite Off ZTest Always
153+
Pass
154+
{
155+
HLSLPROGRAM
156+
#pragma vertex FullScreenTrianglePostProcessVertexProgram
157+
#pragma fragment GrayscaleFragmentProgram
158+
ENDHLSL
159+
}
160+
}
161+
Fallback Off
162+
}
163+
```
164+
165+
Since vertex shaders rarely contain any logic specific to the effect, we use a default vertex shader `FullScreenTrianglePostProcessVertexProgram` which is included in `Packages/com.yetman.render-pipelines.universal.postprocess/ShaderLibrary/Core.hlsl`. The fragment shader is the one reponsible for reading the original pixel color, calculating the grayscale color and blending the two colors together.
166+
167+
Now that we have our custom effect, we need to add a `CustomPostProcess` renderer feature to the foward renderer as seen in the next image. You can also add a `SceneNormals` renderer feature if you want to use scene normals in an effect (such as Edge Detection).
168+
169+
![Add Renderer Feature](Documentation~/tut-add-renderer-feature.png)
170+
171+
The `CustomPostProcess` renderer feature contains 3 lists that represent 3 injection points in the `ScriptableRenderer` as seen in the next image. The three injection points are:
172+
* **After Opaque and Sky** where we can apply effects before the transparent geometry is rendered.
173+
* **Before Post Process** which happens after the transparent geometry is rendered and before the builtin post processing is applied.
174+
* **After Post Process** which happens at the very end before the result is blit to the camera target.
175+
176+
The ordering of the effects in a list is the same order in which they are executed (from top to bottom). You can re-order the effects as you see fit. **Note** that the effect must be added to the renderer feature to be rendered, otherwise, it will be ignored. So we added `GrayScale` to its list `After Post Process`.
177+
178+
![Add GrayScale to After Post Process](Documentation~/tut-add-grayscale-to-after-pp.png)
179+
180+
Then we will create a volume in the scene (or use an existing volume) and add a `GrayScale Effect` volume component to it as seen in the next image.
181+
182+
![Add GrayScale to Volume](Documentation~/tut-add-grayscale-to-volume.png)
183+
184+
Now you can override the `Blend` parameter and see the view becoming grayscale.
185+
186+
![Modify Blend](Documentation~/tut-modify-blend.gif)
187+
188+
Other stuff we didn't explain but can be seen in the samples:
189+
* Merge effects and read from more than one volume component (see [GrayAndInvertEffect.cs](Samples~/Examples/Scripts/PostProcessing/GrayAndInvertEffect.cs) and [GrayAndInvert.shader](Samples~/Examples/Resources/Shaders/PostProcessing/GrayAndInvert.shader)).
190+
* Create temporary render targets inside the renderer (see [StreakEffect.cs](Samples~/Examples/Scripts/PostProcessing/StreakEffect.cs)).
191+
* Create persistent render targets inside the renderer (see [AfterImageEffect.cs](Samples~/Examples/Scripts/PostProcessing/AfterImageEffect.cs)).
42192

43193
## License
44194
[MIT License](LICENSE.md)

0 commit comments

Comments
 (0)