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

Question Question about Custom Pass FPS Foreground

Discussion in 'High Definition Render Pipeline' started by ryanflees, Oct 18, 2022.

  1. ryanflees

    ryanflees

    Joined:
    Nov 15, 2014
    Posts:
    59
    Hi, I've been studying how to create a final solution for FPS camera in HDRP with custom pass.

    And here's the general thought I get from the HDRP custom pass samples (https://github.com/alelievr/HDRP-Custom-Passes)correct me if I understand it wrong: FPS hands/weapons will be in a FPS foreground layer, and we use custom pass to nuke the depth of the envirnoment wich get covered by the FPS hands/weapons, then draw the FPS hands/weapons's color and depth over the environment by layermask. Then the FPS hands/weapons will not be clipped into environment because they were drawn on the top. Also with a overwritten depth it should takes the shadow from environments correctly, and post processing should work correctly as well.

    But the sample doesn't seem to work very practically for that. I've tried different branch of the samples of unity 2020, 2021, 2022.

    Code (CSharp):
    1.  protected override void Execute(CustomPassContext ctx)
    2.     {
    3.         // Disable it for scene view because it's horrible
    4.         if (ctx.hdCamera.camera.cameraType == CameraType.SceneView)
    5.             return;
    6.  
    7.         var currentCam = ctx.hdCamera.camera;
    8.  
    9.         // Copy settings of our current camera
    10.         foregroundCamera.transform.SetPositionAndRotation(currentCam.transform.position, currentCam.transform.rotation);
    11.         foregroundCamera.CopyFrom(ctx.hdCamera.camera);
    12.         // Make sure the camera is disabled, we don't want it to render anything.
    13.         foregroundCamera.enabled = false;
    14.         foregroundCamera.fieldOfView = fov;
    15.         foregroundCamera.cullingMask = foregroundMask;
    16.  
    17.         var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth)
    18.         {
    19.             depthState = new DepthState(false, CompareFunction.LessEqual),
    20.         };
    21.  
    22.         // TODO: Nuke the depth in the after depth and normal injection point
    23.         // Override depth to 0 (avoid artifacts with screen-space effects)
    24.         ctx.cmd.SetRenderTarget(trueDepthBuffer, 0, CubemapFace.Unknown, 0); // TODO: make it work in VR
    25.         RenderFromCameraDepthPass(ctx, foregroundCamera, null, null, ClearFlag.None, foregroundMask, overrideMaterial: depthClearMaterial, overrideMaterialIndex: 0);
    26.  
    27.         // Render the object color or normal + depth depending on the injection point
    28.         if (injectionPoint == CustomPassInjectionPoint.AfterOpaqueDepthAndNormal)
    29.             RenderFromCameraDepthPass(ctx, foregroundCamera, ctx.cameraColorBuffer, ctx.cameraDepthBuffer, ClearFlag.None, foregroundMask, overrideRenderState: depthTestOverride);
    30.         else
    31.             CustomPassUtils.RenderFromCamera(ctx, foregroundCamera, ctx.cameraColorBuffer, ctx.cameraDepthBuffer, ClearFlag.None, foregroundMask, overrideRenderState: depthTestOverride);
    32.     }
    here's the FPS Foreground code from HDRP-Custom-Passes sample of unity 2022 version.
    It injects into AfterOpaqueDepthAndNormal and BeforeTransparent.

    QQ截图20221018174900.png
    I copy the cameraDepthBuffer to a render texture as debug in After Post Process.
    It seems that the FPSForeground.cs doesn't write into depth buffer. It does nuke the default depth buffer over the FPS wepon.And with this part of depth missing. The post processing get wrong.

    QQ截图20221018175150.png
    QQ截图20221018175548.png
    Enable DOF the weapon part of screen get wrong. (By default the weapon part is totally black.)
    Raise the Far Blur-> Sample Count a little and it gets shown, and it's taken as very far depth, even the near range is 0 and far range starts at 14.81 ( where range between 0 and 14.81 should be not blurred, and the weapon's real postion is within the range).

    I tried to alter the code a little.

    Code (CSharp):
    1.     var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth)
    2.         {
    3.             depthState = new DepthState(false, CompareFunction.LessEqual),
    4.         };
    5.  
    Code (CSharp):
    1.     var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth)
    2.         {
    3.             depthState = new DepthState(true, CompareFunction.LessEqual),
    4.         };
    5.  
    I changed the write depth from false to true.


    QQ图片20221018181955.png
    The depth is written into depth buffer.
    DOF works correctly now. But there's another problem that AO works wrong. It seems that it take the old depth buffer into account. And as the write depth == false ealier, the FPS weapons part of screen has no depth value so AO must have thought it's empty here and didn't make any change.

    I tried different experiment and finally get to this.
    Code (CSharp):
    1.  
    2. protected override void Execute(CustomPassContext ctx)
    3. {
    4.     // Disable it for scene view because it's horrible
    5.     if (ctx.hdCamera.camera.cameraType == CameraType.SceneView)
    6.         return;
    7.  
    8.     var currentCam = ctx.hdCamera.camera;
    9.  
    10.     // Copy settings of our current camera
    11.     foregroundCamera.transform.SetPositionAndRotation(currentCam.transform.position, currentCam.transform.rotation);
    12.     foregroundCamera.CopyFrom(ctx.hdCamera.camera);
    13.     // Make sure the camera is disabled, we don't want it to render anything.
    14.     foregroundCamera.enabled = false;
    15.     foregroundCamera.fieldOfView = fov;
    16.     foregroundCamera.cullingMask = foregroundMask;
    17.  
    18.     // TODO: Nuke the depth in the after depth and normal injection point
    19.     // Override depth to 0 (avoid artifacts with screen-space effects)
    20.     //ctx.cmd.SetRenderTarget(trueDepthBuffer, 0, CubemapFace.Unknown, 0); // TODO: make it work in VR
    21.  
    22.     ShaderTagId[] depthTags = { HDShaderPassNames.s_DepthForwardOnlyName, HDShaderPassNames.s_DepthOnlyName };
    23.  
    24.     // Render the object color or normal + depth depending on the injection point
    25.     if (injectionPoint == CustomPassInjectionPoint.AfterOpaqueDepthAndNormal)
    26.     {  
    27.         var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth)
    28.         {
    29.             depthState = new DepthState(false, CompareFunction.LessEqual),
    30.         };        
    31.         using (new CustomPassUtils.DisableSinglePassRendering(ctx))
    32.         {
    33.             using (new CustomPassUtils.OverrideCameraRendering(ctx, foregroundCamera))
    34.             { // using (new ProfilingScope(ctx.cmd, renderFromCameraSampler))
    35.  
    36.                 CustomPassUtils.DrawRenderers(ctx, depthTags, foregroundMask, CustomPass.RenderQueueType.All,
    37.                     depthClearMaterial, 0);
    38.             }
    39.         }
    40.  
    41.         using (new CustomPassUtils.DisableSinglePassRendering(ctx))
    42.         {
    43.             using (new CustomPassUtils.OverrideCameraRendering(ctx, foregroundCamera))
    44.             {
    45.                 // using (new ProfilingScope(ctx.cmd, renderFromCameraSampler))
    46.                 CustomPassUtils.DrawRenderers(ctx, depthTags, foregroundMask, CustomPass.RenderQueueType.All, overrideRenderState: depthTestOverride);
    47.             }
    48.         }
    49.     }
    50.     else
    51.     {
    52.         var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth)
    53.         {
    54.             depthState = new DepthState(true, CompareFunction.LessEqual),
    55.         };
    56.         CustomPassUtils.RenderFromCamera(ctx, foregroundCamera, ctx.cameraColorBuffer, ctx.cameraDepthBuffer,
    57.             ClearFlag.None, foregroundMask, overrideRenderState: depthTestOverride);
    58.     }
    59. }
    60.  
    I commented the
    Code (CSharp):
    1. //ctx.cmd.SetRenderTarget(trueDepthBuffer, 0, CubemapFace.Unknown, 0); // TODO: make it work in VR
    because I don't know what it does.
    And expand the RenderFromCameraDepthPass method. Set the write depth false for AfterOpaqueDepthAndNormal and true for BeforeTransparent.
    QQ截图20221018181219.png
    The final picture looks corret. DOF works right, AO didn't draw on the weapon. Weapon takes shadow corretly.
    But I don't understand why the final depth texture is missing the weapon part.
    And I still don't understand how the code works.
    Can someone explain how the last chunk of code works? Like why there should be 2 injection point and why the depth texture didn't get written. And with a wrong depth texture how DOF can work right?
     
  2. ryanflees

    ryanflees

    Joined:
    Nov 15, 2014
    Posts:
    59
    Some followups.

    QQ截图20221019145014.png
    If I add any custom post processing to the game injected to After Post Process.
    And enable the post in the scene.
    QQ截图20221020152843.png
    Magically, once the Debug Depth post is enabled (the post process just returns _MainTex color and do nothing).
    The left-up corner's copied depth texture is a merged depth of environment and hands.
    (The copy depth texture up left is done very similarly to the CopyPass example in the project injected at After Post Process)

    QQ截图20221020153733.png
    As a comparision when the Debug Depth post is off. Left-up depth texture is only the environment's depth.

    And for the FPS Foreground I do some test with transparent objects (for example you probably need an optical sight on the gun). I use 3 pass to draw it together, the opaque and transparent objects are drawn separately.
    I suppose the transparent objects on FPS hands shouldn't be affected by the blur in post process.

    Code (CSharp):
    1.     protected override void Execute(CustomPassContext ctx)
    2.     {
    3.         // Disable it for scene view because it's horrible
    4.         if (ctx.hdCamera.camera.cameraType == CameraType.SceneView)
    5.             return;
    6.  
    7.         var currentCam = ctx.hdCamera.camera;
    8.  
    9.         // Copy settings of our current camera
    10.         foregroundCamera.transform.SetPositionAndRotation(currentCam.transform.position, currentCam.transform.rotation);
    11.         foregroundCamera.CopyFrom(ctx.hdCamera.camera);
    12.         // Make sure the camera is disabled, we don't want it to render anything.
    13.         foregroundCamera.enabled = false;
    14.         foregroundCamera.fieldOfView = fov;
    15.         foregroundCamera.cullingMask = foregroundMask;
    16.  
    17.         // TODO: Nuke the depth in the after depth and normal injection point
    18.         // Override depth to 0 (avoid artifacts with screen-space effects)
    19.         //ctx.cmd.SetRenderTarget(trueDepthBuffer, 0, CubemapFace.Unknown, 0); // TODO: make it work in VR
    20.      
    21.         ShaderTagId[] depthTags = { HDShaderPassNames.s_DepthForwardOnlyName, HDShaderPassNames.s_DepthOnlyName };
    22.      
    23.         // Render the object color or normal + depth depending on the injection point
    24.         if (injectionPoint == CustomPassInjectionPoint.AfterOpaqueDepthAndNormal)
    25.         {
    26.             var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth)
    27.             {
    28.                 depthState = new DepthState(false, CompareFunction.LessEqual),
    29.             };      
    30.             using (new CustomPassUtils.DisableSinglePassRendering(ctx))
    31.             {
    32.                 using (new CustomPassUtils.OverrideCameraRendering(ctx, foregroundCamera))
    33.                 { // using (new ProfilingScope(ctx.cmd, renderFromCameraSampler))
    34.  
    35.                     CustomPassUtils.DrawRenderers(ctx, depthTags, foregroundMask, CustomPass.RenderQueueType.AllOpaque,
    36.                         depthClearMaterial, 0);
    37.                 }
    38.             }
    39.  
    40.             using (new CustomPassUtils.DisableSinglePassRendering(ctx))
    41.             {
    42.                 using (new CustomPassUtils.OverrideCameraRendering(ctx, foregroundCamera))
    43.                 {
    44.                     // using (new ProfilingScope(ctx.cmd, renderFromCameraSampler))
    45.                     CustomPassUtils.DrawRenderers(ctx, depthTags, foregroundMask, CustomPass.RenderQueueType.All, overrideRenderState: depthTestOverride);
    46.                 }
    47.             }
    48.         }
    49.         else if (injectionPoint == CustomPassInjectionPoint.BeforeTransparent)
    50.         {
    51.             var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth)
    52.             {
    53.                 depthState = new DepthState(true, CompareFunction.LessEqual),
    54.             };
    55.             CustomPassUtils.RenderFromCamera(ctx, foregroundCamera, ctx.cameraColorBuffer, ctx.cameraDepthBuffer,
    56.                  ClearFlag.None, foregroundMask, RenderQueueType.AllOpaque, overrideRenderState: depthTestOverride);
    57.         }
    58.         else if (injectionPoint == CustomPassInjectionPoint.AfterPostProcess)
    59.         {
    60.             var depthTestOverride = new RenderStateBlock(RenderStateMask.Depth)
    61.             {
    62.                 depthState = new DepthState(false, CompareFunction.LessEqual),
    63.             };
    64.             CustomPassUtils.RenderFromCamera(ctx, foregroundCamera, ctx.cameraColorBuffer, ctx.cameraDepthBuffer,
    65.                 ClearFlag.None, foregroundMask, RenderQueueType.AllTransparent, overrideRenderState: depthTestOverride);
    66.         }
    67.     }
    QQ截图20221020154550.png
    The transparent crosshair draws over the background's particles and DOF blurs.
    3 pass and injection points are shown on the right.
    (There's another crosshair quad I directly put in the scenes, it gets blured by environment DOF even it's not in the DOF blur range)
     
  3. iamarugin

    iamarugin

    Joined:
    Dec 17, 2014
    Posts:
    863
    Agreed, I don't understand how most Custom Passes work in HDRP-Custom-Passes. There are simply not enough docs about it.
     
    Ghosthowl, seoyeon222222 and PutridEx like this.