Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Question SRP shadow rendering without native lights

Discussion in 'General Graphics' started by Fewes, Nov 3, 2022.

  1. Fewes

    Fewes

    Joined:
    Jul 1, 2014
    Posts:
    256
    Hi there!

    Something I find myself wanting to do often is implement custom shadow rendering outside of what seems to be the intended way of doing it using the SRP API, but which also seems frustratingly close to being possible. Here's an example:

    I have a custom light class to circumvent limitations with using the native light class.
    I want to draw a shadow map for this "light".
    I find ScriptableRenderContext.DrawShadows (and CreateShadowRendererList in 2023 which looks even better). Great! I just need to pass it a ShadowDrawingSettings struct
    Problem 1: ShadowDrawingSettings must be passed a light index or it wont work. I don't have a light index because I am not using native lights. Even if I am using native lights, I can run into problems if I do a prepass over all cameras to find all visible lights with the intention of updating them only once (which leaves me without valid light indices because there is no singular visibleLights list to derive an index from).
    Alright, I guess I'll fall back to using CommandBuffer.DrawRendererList and draw my shadow casters that way instead.
    Problem 2: To gather a RendererList, I need to supply a CullingResult, but there seems to be no way to filter out shadow casters only (and I also need to create a dummy camera because I can't get a CullingResult without one). The only way I found to "fix" this is to filter renderers using Renderer.renderingLayerMask by setting up a layer for shadow casters. This means that enabling/disabling shadows is really user unfriendly though, and also means that the shadow caster flags no longer do anything.

    Question 1: Why does DrawShadows need to know about some light index? What information could it possibly need that I couldn't just pass along myself? I really feel like this hugely limits what kind of render pipeline you can create yourself if it is tied to how native lights (that you cannot modify) work.
    Question 1: Why can't I when I call ScriptableRenderContext.Cull or CommandBuffer.DrawRendererList tell it to only cull or draw shadow casters? This seems like it would be incredibly powerful, letting the user fully utilize the shadow caster flags and render shadow maps which can be used for many other purposes other than just supplying native lights with shadows. FilteringSettings has a field for excluding motion vector objects. It would be awesome if it also had a field for filtering shadow casters (non-shadow casters only, static shadow casters only or all shadow casters for example).
    There is CullingOptions.ShadowCasters which can be set on ScriptableCullingParameters, but it doesn't seem to work in this way because it does not cull non-shadow casters.

    In my mind, any "DrawShadows" function just needs a few things:
    1. A list of things to draw (be it CullingResults or a RendererList derived from CullingResults)
    2. Additional filtering options (already exists)
    3. Shader pass tag override (possible if you use DrawRenderers but not DrawShadows)
    And the rest is set by the user (view/projection matrices, render target etc).
     
    Last edited: Nov 3, 2022
    Arycama likes this.
  2. Arycama

    Arycama

    Joined:
    May 25, 2014
    Posts:
    180
    I've run into this issue too, for now I'm just living with the bad built-in functionality, however would it be possible to use a custom camera like you suggested, with a DrawRenderList command and only draw renderers with the "ShadowCaster" pass?

    You'd have to calculate shadow view/proj matrices yourself instead of using the builtin Directional/Point/Spot shadow matrix functions, but this isn't too complicated once you know how to deal with Unity's view/projection matrix weirdness. (View matrix has 3rd column inverted so Z points backwards, projection has 3rd column inverted to reverse this reversal... and projection matrix also needs [1,1] inverted to flip the Y, or culling will be inverted. (Alternatively you can invert culling..))

    I've managed to get better directional shadows by calculating the matrices manually. You can use code/techniques from any shadow mapping technique, but I do mine by splitting the camera frustum into 4 sections, and transforming the 8 corners of each section into light space, and calculating the view/proj matrices from that. (Requires a bit of extra logic to snap texels to avoid excessive shimmering, but resolution is much better than the built in function) I'm still using context.DrawShadows for now, but hoping to replace it with render list soon.
     
  3. Fewes

    Fewes

    Joined:
    Jul 1, 2014
    Posts:
    256
    That is indeed possible, and it is the best way I have found to render shadows. The problem however is that even though you are filtering by the ShadowCaster pass, this does not take renderer's ShadowCastingMode into account, meaning all renderers using shaders with a ShadowCaster pass get rendered, even if they have shadows set to off.
     
    Arycama likes this.