Pixelate Full-Screen Shader for Unity URP
A 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.
Also called: pixel art filter, retro shader, posterize effect, dither shader, low-res effect.
Full-screen effects URP, Unity 6000.0+ beginner MIT licensed
Use it for: Retro and lo-fi 3D games, a PS1 or handheld look, transitions, or a stylized pause screen.
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
- Pixel Size in screen pixels, so the look is the same at any resolution
- Colour Steps per channel, quantised in gamma space for even banding
- Ordered 4x4 Bayer dithering with a strength slider
- Runs after post-processing, so it pixelates the final, tonemapped image
How it works
The shader divides the screen into blocks of Pixel Size pixels and samples the camera image once at each block's centre with point filtering, so the whole block takes one colour.
It then rounds each colour channel to the nearest of Colour Steps levels. With dithering on, a value from a 4x4 Bayer pattern nudges each block before rounding, which mixes neighbouring levels into a pattern instead of a hard band.
Setup
- Save the file as Pixelate.shader under Assets and create a material that uses Scripts For Unity > Full Screen > Pixelate.
- On your URP Renderer asset, click Add Renderer Feature > Full Screen Pass Renderer Feature and assign the material to Pass Material.
- Keep Injection Point on After Rendering Post Processing and Requirements on Color.
Common mistakes
- Pixels shimmer when the camera moves: that is how screen-space pixelation behaves. For a stable look, also move the camera in whole steps of a pixel, or render to a low-resolution Render Texture instead.
- Text becomes unreadable: put UI on a Screen Space - Overlay canvas, which draws after this effect.
Questions
- Is it pixel-perfect for 2D games?
- For 2D pixel art, use the 2D Pixel Perfect Camera. This shader is a stylistic filter for 3D scenes or transitions.
- Why do pixels shimmer when the camera moves?
- Screen-space pixelation snaps to screen blocks, so moving content crosses block edges. Moving the camera in whole-pixel steps, or rendering to a low-resolution Render Texture, gives a steadier look.
- How do I keep the UI sharp?
- Use a Screen Space - Overlay canvas, which is drawn after this effect.
Source
Pixelate.shader · 75 lines · ShaderLab and HLSL for the Universal Render Pipeline 17 (Unity 6)
// Pixelate: big pixels, a limited number of colour steps and optional
// ordered dithering for a retro look. Applied with URP's Full Screen Pass
// Renderer Feature. URP 17 (Unity 6). Scripts For Unity, MIT licensed.
Shader "Scripts For Unity/Full Screen/Pixelate"
{
Properties
{
_PixelSize ("Pixel Size", Range(1, 32)) = 4
_ColorSteps ("Color Steps", Range(2, 64)) = 12
[Toggle(_DITHER)] _Dither ("Dither", Float) = 1
_DitherStrength ("Dither Strength", Range(0, 1)) = 0.6
}
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
ZWrite Off
ZTest Always
Cull Off
Pass
{
Name "Pixelate"
HLSLPROGRAM
#pragma vertex Vert
#pragma fragment Frag
#pragma shader_feature_local_fragment _DITHER
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
CBUFFER_START(UnityPerMaterial)
float _PixelSize;
float _ColorSteps;
float _Dither;
float _DitherStrength;
CBUFFER_END
float SfuBayer2(float2 a)
{
a = floor(a);
return frac(a.x * 0.5 + a.y * a.y * 0.75);
}
// 0..15/16 in a 4x4 Bayer pattern
float SfuBayer4(float2 a)
{
return SfuBayer2(0.5 * a) * 0.25 + SfuBayer2(a);
}
half4 Frag(Varyings input) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
// Snap to the centre of a block of Pixel Size screen pixels
float2 cells = max(_ScreenParams.xy / _PixelSize, 1.0);
float2 cell = floor(input.texcoord * cells);
half4 color = SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, (cell + 0.5) / cells);
// Quantise in gamma space so the steps look even
float3 c = LinearToSRGB(saturate(color.rgb));
float steps = max(_ColorSteps - 1.0, 1.0);
#if defined(_DITHER)
c += (SfuBayer4(cell) + 1.0 / 32.0 - 0.5) * _DitherStrength / steps;
#endif
c = floor(c * steps + 0.5) / steps;
color.rgb = SRGBToLinear(saturate(c));
return color;
}
ENDHLSL
}
}
}
More shaders
- Monochrome Full-Screen ShaderA 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.
- 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.
- 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.