Add a 2D custom render pass to the frame rendering loop in URP
Add and inject a custom render pass, and learn about the specific APIs you use in a 2D project.
Read time 3 minutesLast updated 20 days ago
A custom render pass is a way to change how the Universal Render Pipeline (URP) renders a scene or the objects within a 2D scene. A custom render pass contains your own rendering code, which you insert into the rendering pipeline at an injection point.
To add a custom render pass in a 2D project, refer to the documentation for 3D projects in Custom render pass workflow in URP.
In a 2D project, make the following changes:
- When you write a render pass, use the class instead of the
ScriptableRenderPass2Dclass.ScriptableRenderPass - When you inject a render pass using a Scriptable Renderer Feature, use the class instead of the
ScriptableRendererFeature2Dclass.ScriptableRendererFeature - To inject the render pass, use the property to set a
renderPassEvent2D, instead of theRenderPassEvent2Dproperty. For a list of 2D injection points, refer to Injection points reference for 2D in URP.renderPassEvent
To access the texture resources used by the URP 2D renderer, including the active 2D light and shadow textures, use the API. For more information, refer to Frame data in the render graph system in URP.
Universal2DResourceDataExample
The following 2D Scriptable Renderer Feature contains an example render pass that creates a texture and clears it to yellow. For more information about adding the render pass to the render pipeline, refer to Add a Renderer Feature to a URP Renderer.
Use the Frame Debugger to check the texture the render pass adds.
using UnityEngine;using UnityEngine.Rendering.Universal;using UnityEngine.Rendering.RenderGraphModule;using UnityEngine.Rendering;// Create a 2D Scriptable Renderer Featurepublic class CreateYellowTextureFeature2D : ScriptableRendererFeature2D{ CreateYellowTexture customPass; public override void Create() { customPass = new CreateYellowTexture(); // Inject the render pass at a 2D injection point injectionPoint2D = RenderPassEvent2D.AfterRenderingPostProcessing; customPass.renderPassEvent2D = injectionPoint2D; customPass.renderPassSortingLayerID = sortingLayerID; } public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) { renderer.EnqueuePass(customPass); } // Create a 2D render pass class CreateYellowTexture : ScriptableRenderPass2D { class PassData { public TextureHandle cameraColorTexture; } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameContext) { using (var builder = renderGraph.AddRasterRenderPass<PassData>("Create yellow texture", out var passData)) { // Get the frame data UniversalResourceData frameData = frameContext.Get<UniversalResourceData>(); // Create texture properties that match the screen size TextureDesc textureDesc = frameData.activeColorTexture.GetDescriptor(renderGraph); textureDesc.msaaSamples = MSAASamples.None; // Create a temporary texture passData.cameraColorTexture = renderGraph.CreateTexture(textureDesc); // Set the texture as the render target builder.SetRenderAttachment(passData.cameraColorTexture, 0, AccessFlags.Write); // Make sure the render graph system keeps the render pass, even if it's not used in the final frame. // Don't use this in production code, because it prevents the render graph system from removing the render pass if it's not needed. builder.AllowPassCulling(false); builder.SetRenderFunc(static (PassData data, RasterGraphContext context) => ExecutePass(data, context)); } } static void ExecutePass(PassData data, RasterGraphContext context) { // Clear the render target to yellow context.cmd.ClearRenderTarget(true, true, Color.yellow); } }}