├── README.md ├── Scripts ├── PixelizeFeature.cs └── PixelizePass.cs └── Shaders └── Pixelize.shader /README.md: -------------------------------------------------------------------------------- 1 | # unity-simple-URP-pixelation 2 | 3 | Custom renderer feature to pixelate the screen 4 | Code Walkthrough: [youtube video](https://youtu.be/nvIyQHbhTJE) 5 | 6 | ### Disclaimer 7 | I'm experimenting with this effect to not have to use two separate cameras as in my previous [approach](https://github.com/itsPeetah/PixelatedCamera). 8 | The code might not be optimized since I don't really know how the RendererFeatures operate in depth. 9 | 10 | ## Usage 11 | To use this, go to your renderer settings and add the PixelizeFeature. Set your target height and you're on :) 12 | 13 | Commented code in the shader and in the PixelizePass class is for alternate versions of this effect that either: 14 | - Don't use the shader 15 | - Don't down scale the render texture in the first blit pass 16 | 17 | Un-comment at your own risk lmao 18 | 19 | ## Resources 20 | - Custom render passes (by @alexanderameye): https://alexanderameye.github.io/notes/scriptable-render-passes/ 21 | - Custom render passes: https://learn.unity.com/tutorial/custom-render-passes-with-urp 22 | - Create a custom renderer feature: https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@12.1/manual/containers/create-custom-renderer-feature-1.html 23 | - SamplerStates in shader lab: https://docs.unity3d.com/Manual/SL-SamplerStates.html 24 | 25 | -------------------------------------------------------------------------------- /Scripts/PixelizeFeature.cs: -------------------------------------------------------------------------------- 1 | 2 | 3 | using UnityEngine; 4 | using UnityEngine.Rendering; 5 | using UnityEngine.Rendering.Universal; 6 | 7 | public class PixelizeFeature : ScriptableRendererFeature 8 | { 9 | [System.Serializable] 10 | public class CustomPassSettings 11 | { 12 | public RenderPassEvent renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing; 13 | public int screenHeight = 144; 14 | } 15 | 16 | [SerializeField] private CustomPassSettings settings; 17 | private PixelizePass customPass; 18 | 19 | public override void Create() 20 | { 21 | customPass = new PixelizePass(settings); 22 | } 23 | public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) 24 | { 25 | 26 | #if UNITY_EDITOR 27 | if (renderingData.cameraData.isSceneViewCamera) return; 28 | #endif 29 | renderer.EnqueuePass(customPass); 30 | } 31 | } 32 | 33 | 34 | -------------------------------------------------------------------------------- /Scripts/PixelizePass.cs: -------------------------------------------------------------------------------- 1 | 2 | using UnityEngine; 3 | using UnityEngine.Rendering; 4 | using UnityEngine.Rendering.Universal; 5 | 6 | public class PixelizePass : ScriptableRenderPass 7 | { 8 | private PixelizeFeature.CustomPassSettings settings; 9 | 10 | private RenderTargetIdentifier colorBuffer, pixelBuffer; 11 | private int pixelBufferID = Shader.PropertyToID("_PixelBuffer"); 12 | 13 | //private RenderTargetIdentifier pointBuffer; 14 | //private int pointBufferID = Shader.PropertyToID("_PointBuffer"); 15 | 16 | private Material material; 17 | private int pixelScreenHeight, pixelScreenWidth; 18 | 19 | public PixelizePass(PixelizeFeature.CustomPassSettings settings) 20 | { 21 | this.settings = settings; 22 | this.renderPassEvent = settings.renderPassEvent; 23 | if (material == null) material = CoreUtils.CreateEngineMaterial("Hidden/Pixelize"); 24 | } 25 | 26 | public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData) 27 | { 28 | colorBuffer = renderingData.cameraData.renderer.cameraColorTarget; 29 | RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor; 30 | 31 | //cmd.GetTemporaryRT(pointBufferID, descriptor.width, descriptor.height, 0, FilterMode.Point); 32 | //pointBuffer = new RenderTargetIdentifier(pointBufferID); 33 | 34 | pixelScreenHeight = settings.screenHeight; 35 | pixelScreenWidth = (int)(pixelScreenHeight * renderingData.cameraData.camera.aspect + 0.5f); 36 | 37 | material.SetVector("_BlockCount", new Vector2(pixelScreenWidth, pixelScreenHeight)); 38 | material.SetVector("_BlockSize", new Vector2(1.0f / pixelScreenWidth, 1.0f / pixelScreenHeight)); 39 | material.SetVector("_HalfBlockSize", new Vector2(0.5f / pixelScreenWidth, 0.5f / pixelScreenHeight)); 40 | 41 | descriptor.height = pixelScreenHeight; 42 | descriptor.width = pixelScreenWidth; 43 | 44 | cmd.GetTemporaryRT(pixelBufferID, descriptor, FilterMode.Point); 45 | pixelBuffer = new RenderTargetIdentifier(pixelBufferID); 46 | } 47 | 48 | public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData) 49 | { 50 | CommandBuffer cmd = CommandBufferPool.Get(); 51 | using (new ProfilingScope(cmd, new ProfilingSampler("Pixelize Pass"))) 52 | { 53 | // No-shader variant 54 | //Blit(cmd, colorBuffer, pointBuffer); 55 | //Blit(cmd, pointBuffer, pixelBuffer); 56 | //Blit(cmd, pixelBuffer, colorBuffer); 57 | 58 | Blit(cmd, colorBuffer, pixelBuffer, material); 59 | Blit(cmd, pixelBuffer, colorBuffer); 60 | } 61 | 62 | context.ExecuteCommandBuffer(cmd); 63 | CommandBufferPool.Release(cmd); 64 | } 65 | 66 | public override void OnCameraCleanup(CommandBuffer cmd) 67 | { 68 | if (cmd == null) throw new System.ArgumentNullException("cmd"); 69 | cmd.ReleaseTemporaryRT(pixelBufferID); 70 | //cmd.ReleaseTemporaryRT(pointBufferID); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /Shaders/Pixelize.shader: -------------------------------------------------------------------------------- 1 | 2 | 3 | Shader "Hidden/Pixelize" 4 | { 5 | Properties 6 | { 7 | _MainTex ("Texture", 2D) = "white" 8 | } 9 | 10 | SubShader 11 | { 12 | Tags 13 | { 14 | "RenderType"="Opaque" "RenderPipeline" = "UniversalPipeline" 15 | } 16 | 17 | HLSLINCLUDE 18 | #pragma vertex vert 19 | #pragma fragment frag 20 | 21 | #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" 22 | 23 | struct Attributes 24 | { 25 | float4 positionOS : POSITION; 26 | float2 uv : TEXCOORD0; 27 | }; 28 | 29 | struct Varyings 30 | { 31 | float4 positionHCS : SV_POSITION; 32 | float2 uv : TEXCOORD0; 33 | }; 34 | 35 | TEXTURE2D(_MainTex); 36 | float4 _MainTex_TexelSize; 37 | float4 _MainTex_ST; 38 | 39 | //SAMPLER(sampler_MainTex); 40 | //Texture2D _MainTex; 41 | //SamplerState sampler_MainTex; 42 | 43 | SamplerState sampler_point_clamp; 44 | 45 | uniform float2 _BlockCount; 46 | uniform float2 _BlockSize; 47 | uniform float2 _HalfBlockSize; 48 | 49 | 50 | Varyings vert(Attributes IN) 51 | { 52 | Varyings OUT; 53 | OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz); 54 | OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex); 55 | return OUT; 56 | } 57 | 58 | ENDHLSL 59 | 60 | Pass 61 | { 62 | Name "Pixelation" 63 | 64 | HLSLPROGRAM 65 | half4 frag(Varyings IN) : SV_TARGET 66 | { 67 | float2 blockPos = floor(IN.uv * _BlockCount); 68 | float2 blockCenter = blockPos * _BlockSize + _HalfBlockSize; 69 | 70 | float4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_point_clamp, blockCenter); 71 | //return float4(IN.uv,1,1); 72 | 73 | return tex; 74 | } 75 | ENDHLSL 76 | } 77 | 78 | 79 | } 80 | } --------------------------------------------------------------------------------