# 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.

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](/engine/6000.6/manual/render-pipelines/universal-render-pipeline/customizing-urp/custom-rendering-pass-workflow-in-urp.md).

In a 2D project, make the following changes:

1. When you [write a render pass](/engine/6000.6/manual/render-pipelines/universal-render-pipeline/customizing-urp/render-graph/write-render-pass.md), use the `ScriptableRenderPass2D` class instead of the `ScriptableRenderPass` class.
2. When you [inject a render pass using a Scriptable Renderer Feature](/engine/6000.6/manual/render-pipelines/universal-render-pipeline/customizing-urp/inject-a-render-pass/inject-a-pass-using-a-scriptable-renderer-feature.md), use the `ScriptableRendererFeature2D` class instead of the `ScriptableRendererFeature` class.
3. To inject the render pass, use the `renderPassEvent2D` property to set a `RenderPassEvent2D`, instead of the `renderPassEvent` property. For a list of 2D injection points, refer to [Injection points reference for 2D in URP](/engine/6000.6/manual/unity2d/2d-urp/scriptable-renderer-features-2d/injection-points-2d.md).

To access the texture resources used by the URP 2D renderer, including the active 2D light and shadow textures, use the [`Universal2DResourceData`](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@latest/index.html?subfolder=/api/UnityEngine.Rendering.Universal.Universal2DResourceData.html) API. For more information, refer to [Frame data in the render graph system in URP](/engine/6000.6/manual/render-pipelines/universal-render-pipeline/customizing-urp/render-graph/frame-data.md).

## Example

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](/engine/6000.6/manual/render-pipelines/universal-render-pipeline/customizing-urp/urp-renderer-feature/feature.md).

Use the [Frame Debugger](/engine/6000.6/manual/analysis/graphics-performance-profiling/profiling/frame-debugger-window.md) to check the texture the render pass adds.

```lang-cs
using UnityEngine;
using UnityEngine.Rendering.Universal;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering;

// Create a 2D Scriptable Renderer Feature
public 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);            
        }
    }

}
```

## Additional resources

* [Introduction to Scriptable Render Passes in URP](/engine/6000.6/manual/render-pipelines/universal-render-pipeline/customizing-urp/intro-to-scriptable-render-passes.md).
* [Render graph system in URP](/engine/6000.6/manual/render-pipelines/universal-render-pipeline/customizing-urp/render-graph.md)
