Extra Main Light Shadow Caster Pass

Hey everyone, I am working on a feature where I am compositing 2 different unity layers into one full screen effect. I am currently using 2 separate cameras that I keep in sync and the extra cam is set to render to a texture that I then use for the compositing.

I recently did some profiling and was thinking I should be able to add an extra pass and avoid having to run the entire pipeline twice (for each of the cameras). So I made a custom RendererFeature and ScriptableRenderPass and I managed to achieve my effect with context.DrawRenderers but my issue is that the objects on layer 2 are still receiving shadows from the objects on layer 1… which makes sense since the MainLightShadow pass ran at the beginning of the pipeline and used the layer of the camera.

So the question is, how can I run a shadow caster pass a second time, for the objects on layer 2? Since we are late in the pipeline (Before Rendering Post Processing) I should be able to reuse the same shadow maps, by just clearing and rendering again in the texture (“_MainLightShadowmapTexture”). Documentation feels very limited and by digging through MainLightShadowCasterPass.cs I was not able to reproduce it as it uses a number of internal fields and properties. Also overriding the CullingResults feels extremely hacky for an API user as it needs ScriptableRenderContext.Cull, instead of the layers being configurable through the constructor or Setup.

Here is my best attempt at hijacking MainLightShadowCasterPass, with partial results. It only works if the RenderPassEvent is ‘BeforeRenderingShadows’ and it renders the shadows of layer 2, but then the natural shadow pass renders again layer 2… which means my first pass is interfering with the regular one. If I try to enqueue the path for later, there is no actual draw with the shadow map as a target in the Frame Debugger… besides a Clear, where there should be a full draw.

private class BlueprintShadowPass : MainLightShadowCasterPass
{
    LayerMask m_Mask;

    public BlueprintShadowPass(RenderPassEvent renderPassEvent, LayerMask mask) : base(renderPassEvent)
    {
        m_Mask = mask;
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        RenderingData data = renderingData;

        int maskCache = data.cameraData.camera.cullingMask;
        data.cameraData.camera.cullingMask = m_Mask;
        data.cameraData.camera.TryGetCullingParameters(out ScriptableCullingParameters cullingParameters);
        CullingResults cullResults = context.Cull(ref cullingParameters);
        data.cameraData.camera.cullingMask = maskCache;

        data.cullResults = cullResults;
        base.Execute(context, ref data);
    }
}
1 Like

Hey I have exact same situation!
Do you have any success?

Hi. Sadly I concluded that the shadow passes are not flexible enough in the render pipeline to modify freely. So I had to resort to 2 separate cameras that run full render pipelines, meaning they have their dedicated layers with their dedicated objects, and the shadows that go with them.

In short, I could not make a set of passes handle fully an object layer, had to use 2 cameras.

PS. I recently came across Light Layers in URP, and they might be the answer to filtering shadows.

1 Like

Yes this is actually renderingLayerMask

With this mask you can

  • add/remove to shadowmap of specific light
  • enable/disable lighting calculation of specific light

You cannot:

  • disable rendering for specific rendering layer mask

There is also separate rendering layer mask for shadowmap generation so
only shadows will be rendered if rendering layers matched

I have some progress of splitting additional shadowmap generation.
The approach will modify urp package.
Will post if succeed