Monochrome Full-Screen Shader for Unity URP
A URP full-screen monochrome shader for Unity 6: duotone grey or sepia, an optional 1-bit threshold, and a keep-one-colour mode. Applied with the Full Screen Pass Renderer Feature.
Also called: grayscale effect, black and white shader, sepia filter, noir effect, selective color.
Full-screen effects URP, Unity 6000.0+ beginner MIT licensed
Use it for: Noir and horror looks, flashbacks, a death or pause screen, a one-colour accent like a red coat in a grey world, or a 1-bit retro style.
The preview runs a WebGL copy of the effect over a small test scene. In Unity it applies to everything your camera renders.
What it does
- Duotone: maps brightness between a dark and a light colour, so grey, sepia or any two-tone look is two colour picks
- Contrast control applied in gamma space, so the middle of the slider looks like middle grey
- 1-bit mode: a hard threshold to pure dark and light
- Keep One Color: leaves saturated pixels near a chosen hue in colour
- Intensity slider to fade the effect in and out from a script
- Runs as one full-screen pass with URP's built-in Full Screen Pass Renderer Feature; no C# needed
How it works
The Full Screen Pass Renderer Feature copies the camera's image into _BlitTexture and draws this shader over the whole screen. For each pixel the shader takes its luminance, converts it to gamma space, applies the contrast, and uses the result to blend between the dark and light colours.
In Keep One Color mode the pixel's hue is compared with the hue of the colour to keep, around the colour wheel. Pixels close to it, and saturated enough to have a clear hue, are blended back towards their original colour.
Setup
- Save the file as Monochrome.shader under Assets and create a material that uses Scripts For Unity > Full Screen > Monochrome.
- Select your URP Renderer asset (the Universal Renderer Data your URP asset uses) and click Add Renderer Feature > Full Screen Pass Renderer Feature.
- Assign the material to Pass Material, set Injection Point to After Rendering Post Processing, and keep Requirements on Color.
- To fade it from a script, set the material's _Intensity:
monochromeMaterial.SetFloat("_Intensity", value);
Common mistakes
- Nothing changes: the feature was added to a different Renderer asset than the one your camera uses. Check the URP asset's renderer list and the camera's Renderer setting.
- Only need plain grey? URP's own Color Adjustments override with Saturation at -100 does that without a renderer feature. Use this shader for tints, 1-bit or keep-one-colour.
- UI turns grey too: Screen Space - Overlay canvases draw after the effect and stay in colour; Screen Space - Camera and World Space canvases are part of the image and go grey with it.
Questions
- Can I fade it in and out?
- Yes. Set the material's _Intensity from a script, from 0 (off) to 1 (full).
- How do I apply it to only one camera?
- A renderer feature applies to every camera that uses that Renderer. Add a second Renderer to your URP asset's renderer list, put the feature only on it, and pick that Renderer on the camera.
- Is this the same as Saturation at -100?
- For plain grey, yes: URP's Color Adjustments override does that without a renderer feature. This shader adds a two-colour tint, a 1-bit threshold and keep-one-colour.
Source
Monochrome.shader · 82 lines · ShaderLab and HLSL for the Universal Render Pipeline 17 (Unity 6)
// Monochrome: a full-screen duotone. Grey, sepia, 1-bit or "keep one
// colour", applied with URP's Full Screen Pass Renderer Feature.
// URP 17 (Unity 6). Scripts For Unity, MIT licensed.
Shader "Scripts For Unity/Full Screen/Monochrome"
{
Properties
{
_Intensity ("Intensity", Range(0, 1)) = 1
_DarkColor ("Dark Color", Color) = (0.06, 0.06, 0.08, 1)
_LightColor ("Light Color", Color) = (0.96, 0.94, 0.88, 1)
_Contrast ("Contrast", Range(0, 3)) = 1.15
[Toggle(_ONE_BIT)] _OneBit ("1-Bit (Hard Threshold)", Float) = 0
_Threshold ("Threshold", Range(0, 1)) = 0.5
[Toggle(_KEEP_COLOR)] _KeepColorOn ("Keep One Color", Float) = 0
_KeepColor ("Color To Keep", Color) = (0.85, 0.12, 0.12, 1)
_KeepRange ("Keep Range", Range(0, 0.5)) = 0.06
}
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
ZWrite Off
ZTest Always
Cull Off
Pass
{
Name "Monochrome"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment Frag
#pragma shader_feature_local_fragment _ONE_BIT
#pragma shader_feature_local_fragment _KEEP_COLOR
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
// Blit.hlsl provides Vert, Varyings, _BlitTexture and the global samplers
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
CBUFFER_START(UnityPerMaterial)
float _Intensity;
half4 _DarkColor;
half4 _LightColor;
float _Contrast;
float _OneBit;
float _Threshold;
float _KeepColorOn;
half4 _KeepColor;
float _KeepRange;
CBUFFER_END
half4 Frag(Varyings input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
half4 color = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_LinearClamp, input.texcoord);
// Brightness in gamma space, so the middle of every slider looks like middle grey
float luma = LinearToSRGB(saturate(Luminance(color.rgb)).xxx).x;
luma = saturate((luma - 0.5) * _Contrast + 0.5);
#if defined(_ONE_BIT)
luma = step(_Threshold, luma);
#endif
float3 mono = lerp(_DarkColor.rgb, _LightColor.rgb, luma);
float amount = _Intensity;
#if defined(_KEEP_COLOR)
// Leave saturated pixels near the chosen hue in colour
float3 hsv = RgbToHsv(saturate(color.rgb));
float keepHue = RgbToHsv(saturate(_KeepColor.rgb)).x;
float hueDistance = abs(hsv.x - keepHue);
hueDistance = min(hueDistance, 1.0 - hueDistance);
float keep = (1.0 - smoothstep(_KeepRange, _KeepRange + 0.04, hueDistance)) * smoothstep(0.15, 0.35, hsv.y);
amount *= 1.0 - keep;
#endif
return half4(lerp(color.rgb, mono, amount), color.a);
}
ENDHLSL
}
}
}
More shaders
- Edge Outline Full-Screen ShaderA URP full-screen outline shader for Unity 6 that draws lines on every silhouette and crease from the depth and normals textures, with an ink-on-paper mode. No per-object setup.
- Pixelate Full-Screen ShaderA URP full-screen pixel art shader for Unity 6: large pixels, a limited number of colour steps and ordered dithering for a retro look, as one Full Screen Pass Renderer Feature material.
- Toon Lit Shader with OutlineA URP toon shader for Unity 6: two-band cel shading that receives shadows, a hard specular highlight, a rim light and an inverted-hull outline, in one .shader file.
- Dissolve ShaderA URP dissolve shader for Unity 6 that burns a mesh away along procedural noise with a glowing HDR edge. No noise texture needed, and its shadow dissolves with it.