Search Unity

Question How to set _CameraDepthTexture as render target in URP 13?

Discussion in 'Universal Render Pipeline' started by Kichang-Kim, May 11, 2022.

  1. Kichang-Kim

    Kichang-Kim

    Joined:
    Oct 19, 2010
    Posts:
    1,012
    Hi. I found that Unity 2022.1 (URP 13) has non-documented breaking changes related to _CameraDepthTexture.

    In my project (Unity 2021.3, URP 12), I implemented my own ScriptableRendererFeature which renders some objects to _CameraDepthTexture directly. It used ConfigureTarget method like this:
    ScriptableRenderPass.ConfigureTarget("_CameraDepthTexture", "_CameraDepthTexture");

    and works without any problems.

    But in Unity 2022.1, it shows warnings like this:
    and does not work anymore.

    Any ideas for setting _CameraDepthTexture as render target in URP 13?

    Thanks.
     
  2. Kichang-Kim

    Kichang-Kim

    Joined:
    Oct 19, 2010
    Posts:
    1,012
    URP's internal renderer feature (like DecalRendererFeature) used UniversalRenderer.m_DepthTexture for setting render target. But surprisingly m_DepthTexture is internal and cannot be used from user code. Unity, please make it as public.
     
  3. fleity

    fleity

    Joined:
    Oct 13, 2015
    Posts:
    345
    not sure if this still works but you could change the output of the shader you are using to target depth specifically
    https://www.cyanilux.com/tutorials/depth/
    Code (CSharp):
    1. struct FragOut {
    2.     half4 color : SV_Target;
    3.     float depth : SV_Depth;
    4. };
     
  4. Kichang-Kim

    Kichang-Kim

    Joined:
    Oct 19, 2010
    Posts:
    1,012
    SV_Depth is destination for current render target. _CameraDepthTexture may be framebuffer or temporary texture (related to renderer settings or order of ScriptableRenderPass) so I need to set actual _CameraDepthTexture to render target .
     
  5. wwWwwwW1

    wwWwwwW1

    Joined:
    Oct 31, 2021
    Posts:
    769
    Bump, does anybody know how to set the render target to "_CameraDepthTexture" or "_CameraNormalsTexture" in URP 13(+)?
     
  6. wwWwwwW1

    wwWwwwW1

    Joined:
    Oct 31, 2021
    Posts:
    769
    Looks like we should reallocate these render textures before using them.

    Haven't tested on URP12 (and seems that URP 12 does not use RTHandle).

    Use previous method in URP 12:
    ScriptableRenderPass.ConfigureTarget("_CameraDepthTexture", "_CameraDepthTexture");


    Set "_CameraDepthTexture" as render target:

    The settings of "depthDescriptor" can be found in "URP-package\Runtime\UniversalRenderer.cs"
    Code (CSharp):
    1. RTHandle m_DepthTexture;
    2.  
    3. //...
    4.  
    5. RenderingUtils.ReAllocateIfNeeded(ref m_DepthTexture, depthDescriptor, FilterMode.Point, TextureWrapMode.Clamp, name: "_CameraDepthTexture");
    6.  
    7. //...
    8.  
    9. ScriptableRenderPass.ConfigureTarget(m_DepthTexture, m_DepthTexture);
    10.  
    11. //...
    Set "_CameraNormalsTexture" as render target:

    The settings of "normalDescriptor" can be found in "URP-package\Runtime\UniversalRenderer.cs"
    Code (CSharp):
    1. RTHandle m_NormalsTexture;
    2.  
    3. //...
    4.  
    5. RenderingUtils.ReAllocateIfNeeded(ref m_NormalsTexture, normalDescriptor, FilterMode.Point, TextureWrapMode.Clamp, name: "_CameraNormalsTexture");
    6.  
    7. //...
    8. // Edited: cameraDepthTargetHandle is camera's depth attachment.
    9. ScriptableRenderPass.ConfigureTarget(m_NormalsTexture, cameraDepthTargetHandle);
    10.  
    11. //...
     
    Last edited: Jul 14, 2022
  7. Kichang-Kim

    Kichang-Kim

    Joined:
    Oct 19, 2010
    Posts:
    1,012
    My temporary solution is that getting m_DepthTexture from UniversalRenderer by using reflection.

    Code (CSharp):
    1. private readonly static FieldInfo depthTextureFieldInfo = typeof(UniversalRenderer).GetField("m_DepthTexture", BindingFlags.NonPublic | BindingFlags.Instance);
    2.  
    3. public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    4. {
    5. #if UNITY_2022_1_OR_NEWER
    6.     var renderer = renderingData.cameraData.renderer as UniversalRenderer;
    7.     var depthTextureHandle = depthTextureFieldInfo.GetValue(renderer) as RTHandle;
    8.  
    9.     if (depthTextureHandle != null)
    10.     {
    11.         this.ConfigureTarget(depthTextureHandle, depthTextureHandle);
    12.     }
    13. #else
    14.     this.ConfigureTarget("_CameraDepthTexture", "_CameraDepthTexture");
    15. #endif
    16. }
    17.  
     
  8. Wayne-wyj

    Wayne-wyj

    Joined:
    Dec 16, 2020
    Posts:
    41
    Does this really work? I can get handle for _DepthTexture and _DepthAttachment.However,both are able to be loaded but not to be written by Custom RenderFeature.
    Is there anyone can help?
     
  9. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    289
    I use the following for writing to the depth buffer:

    First I set some variables up to store my targets:
    Code (CSharp):
    1. #if URP_13
    2. ...
    3.             RTHandle cameraDepthTarget = renderingData.cameraData.renderer.cameraDepthTargetHandle;
    4. #else
    5. ...
    6.             RenderTargetIdentifier cameraDepthTarget = renderingData.cameraData.renderer.cameraDepthTarget;
    7. #endif
    where URP_13 is a define in my asmdef that checks for Universal Render Pipeline 13.0+. Then for the actual writing of the depth buffer:

    Code (CSharp):
    1. #if URP_13
    2.                 //// Blit pixelised depth into the used depth texture
    3.                 /// (2022.1b and beyond)
    4.                 if (renderingData.cameraData.cameraType != CameraType.Preview)
    5.                     Blit(buffer, _CameraDepthAttachmentTemp, cameraDepthTarget, Materials.CopyDepth);
    6. #else
    7.             // 2020 LTS: required to fix depth buffer in scene view.
    8.             if (!isOverlay)
    9.                     Blit(buffer, _CameraDepthAttachmentTemp, _CameraDepthTexture, Materials.CopyDepth);
    10.                 Blit(buffer, _CameraDepthAttachmentTemp, _CameraDepthAttachment, Materials.CopyDepth);
    11.  
    12.             if (!isOverlay)
    13.                 buffer.SetGlobalTexture("_CameraDepthTexture", _CameraDepthTexture);
    14. #endif
    where isOverlay describes if the camera is an overlay camera:
    Code (CSharp):
    1. bool isOverlay = renderingData.cameraData.camera.GetUniversalAdditionalCameraData().renderType == CameraRenderType.Overlay;
    and _CameraDepthAttachmentTemp is the depth buffer I've prepared and want to blit into the _camera depth (you probably have your own target, or set of render instructions here).

    My CopyDepth material is identical to Unity's copy depth material, but for one line which sets it to be able to write to depth (because Unity's material cannot)

    Code (csharp):
    1. // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
    2.  
    3. // This file is included in ProPixelizer to address a bug in the existing BlitCopyDepth - that is, that it can't write to depth!
    4.  
    5. Shader "Hidden/ProPixelizer/SRP/BlitCopyDepth" {
    6.     Properties{ _MainTex("Texture", any) = "" {} }
    7.         SubShader{
    8.             Pass {
    9.                 ZTest Always Cull Off ZWrite On
    10.  
    11.                 CGPROGRAM
    12.                 #pragma vertex vert
    13.                 #pragma fragment frag
    14.                 #pragma target 2.0
    Bit of a code dump and apologies I can't put the full program, I hope this is enough for you to get it working. I left all the defines in to reflect the changing requirements as the API has evolved over the years.
     
  10. Wayne-wyj

    Wayne-wyj

    Joined:
    Dec 16, 2020
    Posts:
    41
    Thanks for your reply.I think the idea is that copy depth to another texture,modify it .,and write it back to depthTexutre.
    But I don't know what does the "_CameraDepthAttachmentTemp" mean.
    I use one pixelize shader to take sample.
    Code (CSharp):
    1.  var desc = renderingData.cameraData.cameraTargetDescriptor;
    2.             cmd.GetTemporaryRT(_temporaryColorTexture.id, desc, FilterMode.Point);
    3.  
    4.             Blit(cmd, "_CameraDepthTexture", _temporaryColorTexture.Identifier(), m_BlitMaterial);
    5.             cmd.SetGlobalTexture("_CameraDepthTexture",_temporaryColorTexture.Identifier());
    6.             Blit(cmd,"_CameraDepthTexture",  renderer.cameraColorTarget);
    For first blit,it looks like this.
    upload_2022-11-9_22-36-28.png upload_2022-11-9_22-37-32.png

    Then set the RT to "_CameraDepthTexture"

    Finally, copy depthTexture to ColorTarget to show whether it works or not.
    Unfortunately,it looks like this :
    upload_2022-11-9_22-39-48.png upload_2022-11-9_22-40-3.png

    Do I make any mistake?

    PS : URP version is 12.1.7
     
    Last edited: Nov 10, 2022
  11. jjxtra

    jjxtra

    Joined:
    Aug 30, 2013
    Posts:
    1,464
    It's possible _CameraDepthTexture has a long suffix, similar to the camera color texture and screen space shadow texture. This was a breaking change in URP 13. In URP 12, the texture names were constant without random suffixes. In URP 13, they contain format, resolution and other variables for some strange reason.
     
  12. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    289
    This thread still comes up in search, and I had to recently tackle this for 2022 in more detail.

    Pre-2022 you could fairly reliably write to CameraDepthTexture by binding by name and blitting to it. Post 2022, as noted, the names now contain format and size and it's no longer possible to bind reliably by name. Also, the RTHandle is private, so it's not easy to get to. You can use reflection, as per this post, but that is less than ideal.

    When creating the depth texture, URP uses a CopyDepth pass to copy the CameraDepthAttachment target into CameraDepthTexture. Hence, a more reliable way from 2022 onwards is to perform your modifications on the CameraDepthAttachment RTHandle, which you can access via
    renderingData.cameraData.renderer.cameraDepthTargetHandle.

    Note that if your pass uses ConfigureInput(ScriptableRenderPassInput.Depth), URP will interpret this as you declaring that you require CameraDepthTexture to be prepared in time for your pass, and it will move the CopyDepth pass before your pass. So don't do that!

    Obviously this fix is situational and won't work for every ocassion that you could do pre-2022, but at least in some cases you can insert your pass AfterOpaques, change the depth texture mode to AfterTransparents and then your changes to CameraDepthAttachment will be reflected in CameraDepthTexture.
     
  13. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    289
    There's a curious quirk that the above fix works for the Game tab but not for the Scene tab in 2022. In 2022, the scene tab does not run the CopyDepth pass that copies _CameraDepthAttachment into _CameraDepthTarget. There is also an editor-only pass that UniversalRenderer performs to copy _CameraDepthTarget into the backbuffer:

    Code (CSharp):
    1. #if UNITY_EDITOR
    2.             if (isSceneViewOrPreviewCamera || (isGizmosEnabled && lastCameraInTheStack))
    3.             {
    4.                 // Scene view camera should always resolve target (not stacked)
    5.                 m_FinalDepthCopyPass.Setup(m_DepthTexture, k_CameraTarget);
    6.                 m_FinalDepthCopyPass.CopyToDepth = true;
    7.                 m_FinalDepthCopyPass.MssaSamples = 0;
    8.                 EnqueuePass(m_FinalDepthCopyPass);
    9.             }
    10. #endif
    (source: https://github.com/Unity-Technologi....universal/Runtime/UniversalRenderer.cs#L1289)

    However, _CameraDepthTarget in editor is drawn from the DepthPrepass, which always runs for scene and preview cameras:

    Code (CSharp):
    1.             // Depth prepass is generated in the following cases:
    2.             // - If game or offscreen camera requires it we check if we can copy the depth from the rendering opaques pass and use that instead.
    3.             // - Scene or preview cameras always require a depth texture. We do a depth pre-pass to simplify it and it shouldn't matter much for editor.
    4.             // - Render passes require it
    5.             bool requiresDepthPrepass = (requiresDepthTexture || cameraHasPostProcessingWithDepth) && (!CanCopyDepth(ref renderingData.cameraData) || forcePrepass);
    6.             requiresDepthPrepass |= isSceneViewOrPreviewCamera;
    7.             requiresDepthPrepass |= isGizmosEnabled;
    8.             requiresDepthPrepass |= isPreviewCamera;
    9.             requiresDepthPrepass |= renderPassInputs.requiresDepthPrepass;
    10.             requiresDepthPrepass |= renderPassInputs.requiresNormalsTexture;
    (source: https://github.com/Unity-Technologi...al/Runtime/UniversalRenderer.cs#L651C1-L651C2). Note the comment taunting us.

    I have yet to find a way to get _CameraDepthAttachment into _CameraDepthTarget for the scene tab in 2022, although in a push you could probably use reflection to access the private property on UniversalRenderer.
     
  14. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    289
    Here is what I settled on, needed for 2021 and 2022 but not 2020. The private variable changed in 2022 from RenderTargetHandle to RTHandle, which have different APIs, so you need two further preprocessor directives.

    Code (CSharp):
    1.             // Modifying the scene tab's depth:
    2.             // When URP renders the scene tab under UNITY_EDITOR, a final depth pass is enqued.
    3.             // This pass applies CopyDepth of the m_DepthTexture into k_CameraTarget.
    4.             // https://github.com/Unity-Technologies/Graphics/blob/1e4487f95a63937cd3a67d733bd26af31870c77d/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs#L1289
    5.             // So we do need to get m_DepthTexture...
    6.             #if UNITY_2021_3_OR_NEWER
    7.             if (renderingData.cameraData.cameraType == CameraType.SceneView || renderingData.cameraData.cameraType == CameraType.Preview)
    8.             {
    9.                 var universalRenderer = renderingData.cameraData.renderer as UniversalRenderer;
    10.                 if (universalRenderer != null)
    11.                 {
    12. #if UNITY_2021
    13.                     var cameraDepthTexture = (RenderTargetHandle)CameraDepthTextureGetter.GetValue(universalRenderer);
    14. #else
    15.                     var cameraDepthTexture = (RTHandle)CameraDepthTextureGetter.GetValue(universalRenderer);
    16. #endif
    17.  
    18.                     if (!isOverlay && cameraDepthTexture != null)
    19.                     {
    20. #if UNITY_2021
    21.                         Blit(buffer, _CameraDepthAttachmentTemp.nameID, cameraDepthTexture.Identifier(), Materials.CopyDepth);
    22. #else
    23.                         Blit(buffer, _CameraDepthAttachmentTemp, cameraDepthTexture, Materials.CopyDepth);
    24. #endif
    25.  
    26.                     }
    27.                 }
    28.             }
    Somewhere else in your file:
    Code (CSharp):
    1. #if UNITY_2021_3_OR_NEWER
    2.         readonly static FieldInfo CameraDepthTextureGetter = typeof(UniversalRenderer).GetField("m_DepthTexture", BindingFlags.Instance | BindingFlags.NonPublic);
    3. #endif
    The depth texture I want to copy to scene depth is _CameraDepthAttachmentTemp. This seems to work across editor versions.
     
  15. tatoforever

    tatoforever

    Joined:
    Apr 16, 2009
    Posts:
    4,369
    This change also broke an optimization i had with my game. I guess I won't upgrade it to 2022. Will keep using 2019-2021.