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.
  2. Dismiss Notice

How to write to depth buffer in Custom Pass?

Discussion in 'High Definition Render Pipeline' started by yzhao105, Jun 17, 2021.

  1. yzhao105

    yzhao105

    Joined:
    Sep 15, 2017
    Posts:
    28
    I am creating a sandstorm effect (looks like the one in the latest Forza Horizon trailer) using the volumetric cloud approach. I used Post Process Volume at first, but ran into an issue where the sandstorm draws on top of the fog and particle effects. I looked into the diagram (HDRP-frame-graph-diagram.png (3250×1026) (unity3d.com)) and thought it might be solved by switching to full screen custom pass and use an earlier injection point so that both the fog and particle effect draw on top of the sandstorm. But I don't know how to overwrite the depth buffer so that sky and fog can properly depth test against the sandstorm. Any help would be appreciated.
     
    Last edited: Jun 17, 2021
  2. yzhao105

    yzhao105

    Joined:
    Sep 15, 2017
    Posts:
    28
    It's also stated here Custom Pass | High Definition RP | 7.1.8 (unity3d.com) in the description of AfterOpaqueDepthAndNormal injection point: "Buffers will contain all opaque objects. Here you can modify the normal, roughness and depth buffer, it will be taken in account in the lighting and the depth pyramid. Note that normals and roughness are in the same buffer, you can use DecodeFromNormalBuffer and EncodeIntoNormalBuffer functions to read/write normal and roughness data."
    So I assume there is also a way to write custom values to depth buffer like the mentioned functions for normals, but I couldn't find anywhere a function that looks like EncodeIntoDepthBuffer.
     
  3. antoinel_unity

    antoinel_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    236
    Hello,

    You don't need an EncodeIntoDepthBuffer function to write to the depth buffer, just the SV_DEPTH semantic. There is an example of this in the custom pass sample repository:

    https://github.com/alelievr/HDRP-Cu...Tests/Tests_SeeThrough/DepthWrite0.shader#L65

    In this example it's used to write the depth of a transparent object to calculate it's thickness (used in translucancy effect).

    Also, note that you can't both write and read to the same depth buffer in a shader due to platform limitation (except in compute shader).
     
    unity_IpxdANggCs1roQ likes this.
  4. yzhao105

    yzhao105

    Joined:
    Sep 15, 2017
    Posts:
    28
    Thank you! I will try and see if that works in my scenario and if the fog would be applied correctly.
     
  5. yzhao105

    yzhao105

    Joined:
    Sep 15, 2017
    Posts:
    28
    @antoinel_unity Hi Antoinel, I ran into another problem. The given example only writes 0 depth. I don't really know what to write to the SV_DEPTH. For example, if the world position of the pixel I want to overwrite is (0, 100, 1000), what should the value to be written into the depth buffer be?
     
  6. antoinel_unity

    antoinel_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    236
    Yeah, the depth value needs to be encoded in the depth buffer format. The simplest way to achieve that would be to use the ComputeNormalizedDeviceCoordinatesWithZ function in Common.hlsl that takes a position world space in param and output in the z the "device depth" (depth in the format of the depth buffer, so it can directly be passed to SV_DEPTH).

    So it would look like that:

    Code (CSharp):
    1.  
    2.             float4 Frag(PackedVaryingsToPS packedInput, out float outputDepth : SV_Depth) : SV_Target
    3.             {
    4.                 UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(packedInput);
    5.                 FragInputs input = UnpackVaryingsMeshToFragInputs(packedInput.vmesh);
    6.  
    7.                 // input.positionSS is SV_Position
    8.                 PositionInputs posInput = GetPositionInput(input.positionSS.xy, _ScreenSize.zw, input.positionSS.z, input.positionSS.w, input.positionRWS);
    9.  
    10.                 // Calculate your depth offset
    11.                 float depthOffset = 0;
    12.  
    13.                 float3 viewDir = GetWorldSpaceNormalizeViewDir(input.positionRWS);
    14.                 posInput.positionWS += depthOffset * viewDir;
    15.                 outputDepth = ComputeNormalizedDeviceCoordinatesWithZ(posInput.positionWS, GetWorldToHClipMatrix()).z;
    16.  
    17.                 return float4(0, 0, 0, 0);
    18.             }
    19.  
    Also, be sure to put the "#define VARYINGS_NEED_POSITION_WS" before including "VertMesh.hlsl", otherwise you won't have access to the "positionWS" field in PositionInputs.

    With this example, you just have to set the "depthOffset" to a certain value in meter and it will work (the offset is in eye space).
     
    yzhao105 likes this.
  7. yzhao105

    yzhao105

    Joined:
    Sep 15, 2017
    Posts:
    28
    Thank you so much!
     
    antoinel_unity likes this.
  8. yzhao105

    yzhao105

    Joined:
    Sep 15, 2017
    Posts:
    28
    I've tried this. But I'm still not getting the result I want.

    In the custom pass C# script, I wrote this:
    Code (CSharp):
    1.         // get scene color and depth buffer
    2.         RTHandle source;
    3.         RTHandle depthBuffer;
    4.         if(targetColorBuffer == TargetBuffer.Camera)
    5.         {
    6.             GetCameraBuffers(out source, out depthBuffer);
    7.         }
    8.         else
    9.         {
    10.             GetCustomBuffers(out source, out depthBuffer);
    11.         }
    12.  
    13.         // shader pass 2, overwrite depth
    14.         HDUtils.DrawFullScreen(cmd, material, _temp, depthBuffer, matProperties, 2);
    15.        
    16.         //test depth
    17.         HDUtils.DrawFullScreen(cmd, material, source, matProperties, 3);
    And in the shader, I wrote this (to visualize if the depth got overwritten correctly):
    Code (CSharp):
    1.  
    2. // shader pass 2
    3. float4 WriteToDepth(PackedVaryingsToPS packedInput, out float outputDepth : SV_Depth) : SV_Target
    4. {
    5. UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(packedInput);
    6. FragInputs input = UnpackVaryingsMeshToFragInputs(packedInput.vmesh);
    7.  
    8. // input.positionSS is SV_Position
    9. PositionInputs posInput = GetPositionInput(input.positionSS.xy, _ScreenSize.zw, input.positionSS.z, input.positionSS.w, input.positionRWS);
    10.  
    11. float3 rayPos = _WorldSpaceCameraPos;
    12. float3 rayDir = normalize(posInput.positionWS);
    13. float2 rayToContainerInfo = rayBoxDst(_boundsMin, _boundsMax, rayPos, 1/rayDir);
    14. float dstToBox = rayToContainerInfo.x;
    15. float dstInsideBox = rayToContainerInfo.y;
    16. float3 entryPoint = rayPos + rayDir * dstToBox;
    17.  
    18. outputDepth = ComputeNormalizedDeviceCoordinatesWithZ(entryPoint, GetWorldToHClipMatrix()).z;
    19.  
    20. return float4(0,0,0,0);
    21. }
    22.  
    23. // shader pass 3
    24. float4 ViewDepth (Varyings varyings) : SV_Target
    25. {
    26.     UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(varyings);
    27.    
    28.     float nonlin_depth = LoadCameraDepth(varyings.positionCS.xy);
    29.     float depth = Linear01Depth(nonlin_depth, _ZBufferParams);
    30.     return depth;
    31. }
    And no matter what world space position value I give to it, the depth buffer always remains unchanged (just showing the original depth of the scene).

    Is there anything wrong with the way I did it?
     
  9. yzhao105

    yzhao105

    Joined:
    Sep 15, 2017
    Posts:
    28
    I don't know anyone else to turn to for help, so @antoinel_unity sensei, please help.
     
  10. antoinel_unity

    antoinel_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    236
    Hum, if nothing changes then I recommend you to fetch directly the depth to fetch the value directly with a Load or Sample instead of using the LoadCameraDepth function. That way you can see if you change the correct depth buffer.

    You can use something like this to fetch the depth directly:

    Code (CSharp):
    1. uint2 pixelCoords = uint2(uv * _ScreenSize.xy);
    2. LOAD_TEXTURE2D_X_LOD(_YourDepthTexture, pixelCoords, 0).r
    Otherwise, for debugging, you can use RenderDoc, it's probably the best tool to debug graphics stuff in Unity, there are a lot of markers in the HDRP code with all the pass names too so it's pretty easy to navigate
     
    yzhao105 likes this.
  11. yzhao105

    yzhao105

    Joined:
    Sep 15, 2017
    Posts:
    28
    This is probably too complex for me :(. I went for another way of rendering the sandstorm. I first raymarch the sandstorm to a render texture in a compute shader, which is dispatched in a custom pass to ensure the depth is correct. This way I can control the render ratio of the sandstorm to enhance performance. And then I use a huge quad placed very far away to sample this render texture. And since I can enable "Receive fog" on this quad, fog is also applied correctly. The sandstorm rendered this way looks good enough for my purpose.

    (I ran into another problem with TAA though. I will post in another thread.)
     
    Last edited: Jul 28, 2021