Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question How to draw normals to a second render texture during the forward depth pass? (BIRP/CommandBuffers)

Discussion in 'Shaders' started by LazloBonin, May 12, 2024.

  1. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    822
    I'm using the built-in render pipeline and command buffers and I'm familiar with all the those setups.

    My setup:
    I am rendering in forward with a single directional light with shadows. Because of that, Unity starts by rendering a depth pass, no matter what setting I choose, because it needs it for shadows. In that setup, the frame debug tool says Unity renders the depth during DepthPass.Job which uses the ShadowCaster pass of the material with the SHADOWS_DEPTH keyword. However, for some reason, the actual return value of the function seems irrelevant -- SHADOW_CASTER_FRAGMENT always returns 0 if we are simply rendering a directional light. Thus the return value is not the depth. I'm not sure where the depth value comes from.

    What I want:
    I'd like to get high-definition normals data, but I also want to avoid enabling DepthNormals, because that causes Unity to render the scene a third time, and I can't live with the performance cost.

    I'd also like to avoid normals reconstruction from depth in post, because it causes too many artifacts. This is what I currently do, but it causes noticeable faceting on smooth surfaces and edge artifacts on sharp depth changes.

    My attempt: (Doesn't work)
    I tried to render normals in the ShadowCaster pass, which is according to the debugger the hook where Unity renders the depth, by setting up a secondary render target using command buffers.

    To do so, I put this in CameraEvent.BeforeDepthTexture:
    Code (CSharp):
    1. myCommandBuffer.SetRenderTarget(new RenderTargetIdentifier[] { BuiltinRenderTextureType.Depth, myNormalsTexture }, BuiltinRenderTextureType.Depth);
    And in my shader, roughly:
    Code (csharp):
    1.             struct f2a
    2.             {
    3.                 float4 depth : SV_TARGET0;
    4.                 fixed4 normals : SV_TARGET1;
    5.             };
    6.  
    7.             f2a frag(v2f i)
    8.             {
    9.                 f2a o;
    10.                 o.depth = 0; // SHADOW_CASTER_FRAGMENT(i) returns 0 anyway for directional
    11.                 o.normals = fixed4(0,1,1,1); // Test, can't see this being rendered in the frame debugger
    12.                 return o;
    13.             }
    14.  
    First, the command buffer call registers as a clear, and I'm not too sure why:
    Unity_vS4pLaYJYK.png

    Then, and main problem, the render targets don't seem to actually have been assigned during DepthPass.Job:
    Unity_LjNIO9PMOk.png

    Can I make this work somehow? Should I envision a different approach completely?
     
  2. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    822
    If it turns out overriding this specific pass is impossible, is there a way I could render my own depth pass manually (with a second render texture for normals), then feed it to the shadow collection?

    To render, I imagine the easiest way would be to set a global define keyword before rendering the camera, and revert it after, this way I can keep all the code per-shader.

    But to feed it to the shadow collection pass, I have no idea how to proceed.
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,396
    The short answer is: No. You cannot do what you're trying to do.

    The BIRP does not allow you to override the render target(s) for any built in rendering pass, with the exception of the main camera render pass. If you want that kind of control you must write a custom SRP.

    If you want a screen space normal texture for BIRP, your options are:
    • Reconstruct from depth
    • Use the existing separate pass
    • Use deferred rendering
     
    LazloBonin likes this.
  4. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    822
    Ah, thank you for the confirmation, it is the worst answer to get from you in particular but at least it's a good sign to stop wasting my time. ;)

    Do you see any potential in the second approach I was describing in my reply? Getting rid of the built-in Unity depth pass and somehow replicating what it does to provide the necessary data for shadow casting? Or is this a fool's errand too?
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,396
    It used to be possible by forcing some settings intended for mobile on for desktop projects, or by overriding the screen space shadow shader override to nothing and then handling the shadow sampling manually to get soft shadows back. But they've since "fixed" these so they just result in no directional shadow maps being rendered at all.

    Many VR related projects asked Unity for the option to remove the depth pass and either extract it from the main render pass. But they never did. They even added some functionality for this to the URP and HDRP, but don't use it.

    If you want this kind of functionality, you need to use a custom SRP.
     
    LazloBonin likes this.
  6. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    822
    I should specify I'm still on Unity 2020.3 LTS. Is this pre-"fixing" this? Switching to SRP is not an option for us.
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,396
    They fixed it before the switch to Unity versions being labeled by year.
     
    LazloBonin likes this.
  8. LazloBonin

    LazloBonin

    Joined:
    Mar 6, 2015
    Posts:
    822
    I see. Such a shame, no high-quality normals for our game then, depth reconstruction it is.

    Thanks for sharing your insight! You saved me days of banging my head against the wall to solve an impossible problem.