public RenderPassEvent injectionPoint = RenderPassEvent.AfterRenderingOpaques;
/*[Header("Output / Sharing")]
public Color clearColor = Color.black;
public bool setGlobalTexture = true;
public string globalTextureName = "_OverrideOpaquesTex";
public bool copyBackToCamera = false; // optional composite*/
public struct OverrideGroup {
public LayerMask layerMask;
public Material material;
public Settings settings = new Settings();
public OverrideGroup[] _groups;
public override void Create()
// Create the render pass that draws the objects, and pass in the override material
materialOverlayPass = new MaterialOverlayPass(settings, _groups);
// Insert render passes after URP's post-processing render pass
materialOverlayPass.renderPassEvent = materialOverlayPass.injectionPoint;// RenderPassEvent.AfterRenderingOpaques;
// You can request URP color texture and depth buffer as inputs by uncommenting the line below,
// URP will ensure copies of these resources are available for sampling before executing the render pass.
// Only uncomment it if necessary, it will have a performance impact, especially on mobiles and other TBDR GPUs where it will break render passes.
//m_ScriptablePass.ConfigureInput(ScriptableRenderPassInput.Color | ScriptableRenderPassInput.Depth);
// You can request URP to render to an intermediate texture by uncommenting the line below.
// Use this option for passes that do not support rendering directly to the backbuffer.
// Only uncomment it if necessary, it will have a performance impact, especially on mobiles and other TBDR GPUs where it will break render passes.
//m_ScriptablePass.requiresIntermediateTexture = true;
// Here you can inject one or multiple render passes in the renderer.
// This method is called when setting up the renderer once per-camera.
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
renderer.EnqueuePass(materialOverlayPass);
class MaterialOverlayPass : ScriptableRenderPass
public RenderPassEvent injectionPoint;
private readonly OverrideGroup[] groups;
//private Material materialToUse;
//public OverrideGroup[] groups = Array.Empty<OverrideGroup>();
public MaterialOverlayPass( Settings settings, OverrideGroup[] overrideGroups/*Material overrideMaterial*/)
// Set the pass's local copy of the override material
//materialToUse = overrideMaterial;
injectionPoint = s.injectionPoint;
//globalId = Shader.PropertyToID(string.IsNullOrEmpty(s.globalTextureName) ? "_OverrideOpaquesTex" : s.globalTextureName);
// This class stores the data needed by the RenderGraph pass.
// It is passed as a parameter to the delegate function that executes the RenderGraph pass.
// Create a field to store the list of objects to draw
//public RendererListHandle rendererListHandle;
public RendererListHandle[] lists;
//public TextureHandle colorOut;
// This static method is passed as the RenderFunc delegate to the RenderGraph render pass.
// It is used to execute draw commands.
static void ExecutePass(PassData data, RasterGraphContext context)
// RecordRenderGraph is where the RenderGraph handle can be accessed, through which render passes can be added to the graph.
// FrameData is a context container through which URP resources can be accessed and managed.
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
if (groups == null || groups.Length == 0) return;
var camData = frameData.Get<UniversalCameraData>();
var urpData = frameData.Get<UniversalRenderingData>();
var light = frameData.Get<UniversalLightData>();
// Off-screen color + private depth (same as before)
var colorDesc = camData.cameraTargetDescriptor;
colorDesc.msaaSamples = 1;
colorDesc.depthBufferBits = 0;
var colorTex = UniversalRenderer.CreateRenderGraphTexture(renderGraph, colorDesc, "_RG_MultiOverrideColor", false);
var depthDesc = colorDesc;
depthDesc.graphicsFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.None;
depthDesc.depthBufferBits = 32;
var depthTex = UniversalRenderer.CreateRenderGraphTexture(renderGraph, depthDesc, "_RG_MultiOverrideDepth", false);
// Build a renderer list per group (opaque range + opaque sorting)
var tag = new ShaderTagId("UniversalForward");
var sort = camData.defaultOpaqueSortFlags; // CommonOpaque
var lists = new System.Collections.Generic.List<RendererListHandle>(groups.Length);
foreach (var g in groups)
if (g.material == null) continue;
var draw = RenderingUtils.CreateDrawingSettings(tag, urpData, camData, light, sort);
draw.overrideMaterial = g.material;
draw.overrideMaterialPassIndex = g.materialPass;
var filter = new FilteringSettings(RenderQueueRange.opaque, g.layerMask);
var rlp = new RendererListParams(urpData.cullResults, draw, filter);
lists.Add(renderGraph.CreateRendererList(rlp));
// One raster pass: bind attachments once, draw all lists
using (var builder = renderGraph.AddRasterRenderPass<PassData>("Multi-Override Opaques (RG)", out var pass))
// Fill the data object that RG will pass to your render function
pass.lists = lists.ToArray(); // 'lists' is your List<RendererListHandle>
pass.clearColor = Color.black;
// Declare dependencies so RG can schedule/resources correctly
foreach (var rl in pass.lists) builder.UseRendererList(rl);
foreach (var rl in lists) builder.UseRendererList(rl);
builder.SetRenderAttachment(colorTex, 0, AccessFlags.Write);
builder.SetRenderAttachmentDepth(depthTex, AccessFlags.Write);
// The render function now takes PassData (a class)
builder.SetRenderFunc((PassData data, RasterGraphContext ctx) =>
ctx.cmd.ClearRenderTarget(true, true, data.clearColor);
foreach (var rl in data.lists)
ctx.cmd.DrawRendererList(rl);
/*const string passName = "Apply Line Noise Pass";
// This adds a raster render pass to the graph, specifying the name and the data type that will be passed to the ExecutePass function.
using (var builder = renderGraph.AddRasterRenderPass<PassData>(passName, out var passData))
// Use this scope to set the required inputs and outputs of the pass and to
// setup the passData with the required properties needed at pass execution time.
// Make use of frameData to access resources and camera data through the dedicated containers.
// UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
// Get the data needed to create the list of objects to draw
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
// Setup pass inputs and outputs through the builder interface.
// builder.UseTexture(sourceTexture);
// TextureHandle destination = UniversalRenderer.CreateRenderGraphTexture(renderGraph, cameraData.cameraTargetDescriptor, "Destination Texture", false);
// This sets the render target of the pass to the active color texture. Change it to your own render target as needed.
builder.SetRenderAttachment(resourceData.activeColorTexture, 0);
// Assigns the ExecutePass function to the render pass delegate. This will be called by the render graph when executing the pass.
builder.SetRenderFunc((PassData data, RasterGraphContext context) => ExecutePass(data, context));