How to Create a Simple Scriptable Renderer in URP17?

I have a question about creating a minimal Scriptable Renderer in URP17.

Below is the code I wrote for Unity 2022.3.8f1 with URP14. It implements a minimal Scriptable Renderer that draws opaque objects:

SimpleRenderer.cs (URP14)

using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class SimpleRenderer : ScriptableRenderer
{
    private DrawOpaquePass m_DrawOpaquePass;
    public SimpleRenderer(ScriptableRendererData data) : base(data)
    {
        m_DrawOpaquePass = new DrawOpaquePass();
    }

    public override void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        EnqueuePass(m_DrawOpaquePass);
    }
}

DrawOpaquePass.cs (URP14)

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class DrawOpaquePass : ScriptableRenderPass
{
    public DrawOpaquePass()
    {
        renderPassEvent = RenderPassEvent.BeforeRenderingOpaques;
    }

    public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
    {
        ConfigureTarget(k_CameraTarget);
        ConfigureClear(ClearFlag.All, clearColor);
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        SortingSettings sortingSettings = new SortingSettings(renderingData.cameraData.camera)
        {
            criteria = SortingCriteria.CommonOpaque
        };
        DrawingSettings drawingSettings = new DrawingSettings(new ShaderTagId("SRPDefaultUnlit"), sortingSettings);
        FilteringSettings filteringSettings = new FilteringSettings(RenderQueueRange.opaque);

        context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filteringSettings);
    }

}

I’m now trying to port this to Unity6 with URP17. I attempted to write the following code using RenderGraph:

SimpleRenderer.cs (URP17)

using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class SimpleRenderer : ScriptableRenderer
{
    private DrawOpaquePass m_DrawOpaquePass;
    public SimpleRenderer(ScriptableRendererData data) : base(data)
    {
        m_DrawOpaquePass = new DrawOpaquePass();
    }

    [System.Obsolete]
    public override void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
    {
    }

    public override void OnBeginRenderGraphFrame()
    {
        EnqueuePass(m_DrawOpaquePass);
    }
}

DrawOpaquePass.cs (URP17)

using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.Universal;
public class DrawOpaquePass : ScriptableRenderPass
{
    class PassData
    {
        internal RendererListHandle rendererList;
    }

    public DrawOpaquePass()
    {
        renderPassEvent = RenderPassEvent.BeforeRenderingOpaques;
    }

    public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
    {
        var renderingData = frameData.Get<UniversalRenderingData>();
        var cameraData = frameData.Get<UniversalCameraData>();
        var lightData = frameData.Get<UniversalLightData>();
        var resourceData = frameData.Get<UniversalResourceData>();

        using (var builder = renderGraph.AddRasterRenderPass<PassData>("DrawOpaquePass", out var passData))
        {
            var drawingSettings = CreateDrawingSettings(new ShaderTagId("SRPDefaultUnlit"), renderingData, cameraData, lightData, SortingCriteria.CommonOpaque);
            var filteringSettings = new FilteringSettings(RenderQueueRange.opaque);
            var rendererListParams = new RendererListParams(renderingData.cullResults, drawingSettings, filteringSettings);
            passData.rendererList = renderGraph.CreateRendererList(rendererListParams);

            builder.UseRendererList(passData.rendererList);

            builder.SetRenderAttachment(resourceData.cameraColor, 0);
            builder.SetRenderAttachmentDepth(resourceData.cameraDepth);

            builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
            {
                context.cmd.ClearRenderTarget(true, true, clearColor);
                context.cmd.DrawRendererList(data.rendererList);
            });
        }
    }
}

However, it doesn’t work as expected, and no objects are rendered. This is my first time working with a system like RenderGraph, and I find the available documentation and resources to be very limited, making it hard to figure out what I might be missing. How can I correctly implement a simple Scriptable Renderer in URP17?

Any guidance or advice would be greatly appreciated. Thank you!

Hi :slight_smile:

You can find all the learning materials that we have provided so far in this post.

Can you have a look at these if they help you find the right answer? Especially the Package Manager samples should be relevant, like the RendererList one.

If you still have issues, feel free to post here and we can dig deeper into the code.

Best,
Oliver

Thank you for your response and the suggestion to look into the provided learning materials and Package Manager samples. They were helpful in understanding the basics of RenderGraph and RendererLists.

I was able to get my render pass to work when implemented as a RendererFeature. However, my goal is to implement this functionality directly within a ScriptableRenderer, as I was doing in URP14.

In URP14, I used the Setup method of ScriptableRenderer to enqueue my custom render pass, and it worked perfectly. For example:

public class SimpleRenderer : ScriptableRenderer
{
    private DrawOpaquePass m_DrawOpaquePass;
    public SimpleRenderer(ScriptableRendererData data) : base(data)
    {
        m_DrawOpaquePass = new DrawOpaquePass();
    }

    public override void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        EnqueuePass(m_DrawOpaquePass);
    }
}

In URP17, the Setup method has been marked as obsolete. I replaced it with the OnBeginRenderGraphFrame method to enqueue my pass, but it didn’t produce any visible rendering:

public class SimpleRenderer : ScriptableRenderer
{
    private DrawOpaquePass m_DrawOpaquePass;
    public SimpleRenderer(ScriptableRendererData data) : base(data)
    {
        m_DrawOpaquePass = new DrawOpaquePass();
    }

    [System.Obsolete]
    public override void Setup(ScriptableRenderContext context, ref RenderingData renderingData)
    {
    }

    public override void OnBeginRenderGraphFrame()
    {
        EnqueuePass(m_DrawOpaquePass);
    }
}

Is this the correct way to enqueue passes in a ScriptableRenderer in URP17, or am I missing some required configuration?
Any advice or clarification would be greatly appreciated.