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

Question HDRP Custom Pass - Depth Issues

Discussion in 'High Definition Render Pipeline' started by robunity93, Jan 7, 2020.

  1. robunity93

    robunity93

    Joined:
    Oct 11, 2016
    Posts:
    3
    Hey Guys,

    I've been having some trouble with custom passes with the new HDRP and was hoping I could get some help making sense of it. I'm trying to do a fullscreen shader pass in an attempt to create a pixel perfect selection feature. I've managed to get the feature working but the rendertexture that is being created by the buffer, that the shader is being applied to, doesn't seem to have per pixel depth sorting, which messes with the selection. I'm not sure if the issue is related to the shader, where the shader pass itself doesn't include depth, or if its the buffer not including depth when created.

    Below is the code used for the custom pass as well as the code for the highlight shader, much of this code is just modified versions of the HDRP custom pass examples. I've also attached screenshots of the rendertextures being created by the buffer to show the depth issue. As you can see, when zoomed out the objects render incorrectly and when zoomed in they seem to render correctly.

    Apologises, the code is pretty messy as I've been constantly fiddling with it.

    Custom Pass Code:

    Code (CSharp):
    1. class HighlightPass : CustomPass {
    2.     [SerializeField] private LayerMask interactionLayers;
    3.     [SerializeField] private Material replacementMat;
    4.  
    5.     [ColorUsage(false, true)]
    6.     public Color        outlineColor = Color.black;
    7.     public float        thickness = 4;
    8.  
    9.     Material                fullscreenOutline;
    10.     MaterialPropertyBlock   outlineProperties;
    11.     ShaderTagId[]           shaderTags;
    12.     RTHandle                buffer;
    13.     [SerializeField] private RenderTexture renderTexture;
    14.  
    15.  
    16.     protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
    17.     {
    18.         fullscreenOutline = CoreUtils.CreateEngineMaterial(Shader.Find("FullScreen/Highlight"));
    19.         outlineProperties = new MaterialPropertyBlock();
    20.         //
    21.         // List all the materials that will be replaced in the frame
    22.         shaderTags = new ShaderTagId[3]
    23.         {
    24.             new ShaderTagId("Forward"),
    25.             new ShaderTagId("ForwardOnly"),
    26.             new ShaderTagId("SRPDefaultUnlit")
    27.            
    28.         };
    29.  
    30.         buffer = RTHandles.Alloc(
    31.             Vector2.one, TextureXR.slices, dimension: TextureXR.dimension, enableRandomWrite: true,
    32.             colorFormat: GraphicsFormat.R8G8B8A8_SRGB,          
    33.             useDynamicScale: true, name: "Buffer"
    34.         );
    35.  
    36.         renderTexture = buffer.rt;
    37.     }
    38.  
    39.     void DrawOutlineMeshes(ScriptableRenderContext renderContext, CommandBuffer cmd, HDCamera hdCamera, CullingResults cullingResult, RenderTargetIdentifier buffer)
    40.     {
    41.         var result = new RendererListDesc(shaderTags, cullingResult, hdCamera.camera)
    42.         {
    43.             // We need the lighting render configuration to support rendering lit objects
    44.             rendererConfiguration = PerObjectData.LightProbe | PerObjectData.LightProbeProxyVolume | PerObjectData.Lightmaps,
    45.             renderQueueRange = RenderQueueRange.all,
    46.             sortingCriteria = SortingCriteria.BackToFront,
    47.             excludeObjectMotionVectors = false,
    48.            // overrideMaterial = replacementMat,
    49.             //overrideMaterialPassIndex = 3,
    50.             layerMask = interactionLayers
    51.         };
    52.        
    53.         CoreUtils.SetRenderTarget(cmd, buffer, ClearFlag.Color);
    54.         HDUtils.DrawRendererList(renderContext, cmd, RendererList.Create(result));
    55.     }
    56.  
    57.     protected override void Execute(ScriptableRenderContext renderContext, CommandBuffer cmd, HDCamera camera, CullingResults cullingResult)
    58.     {
    59.         DrawOutlineMeshes(renderContext, cmd, camera, cullingResult, buffer);
    60.         SetCameraRenderTarget(cmd);
    61.  
    62.         // if(InputManager.Instance == null) {
    63.         //     return;
    64.         // }
    65.  
    66.         //Color selectionColour = InputManager.Instance.SelectionColor;
    67.  
    68.         //outlineProperties.SetVector("_MousePosition", Input.mousePosition);
    69.         //outlineProperties.SetColor("_Identifier", selectionColour);
    70.         //outlineProperties.SetColor("_OutlineColor", outlineColor);
    71.        // outlineProperties.SetFloat("_Thickness", thickness);
    72.         outlineProperties.SetTexture("_Buffer", buffer);
    73.  
    74.         CoreUtils.DrawFullScreen(cmd, fullscreenOutline, outlineProperties, shaderPassId: 0);
    75.     }
    76.  
    77.     Texture2D ToTexture2D(RenderTexture rTex) {
    78.         Texture2D tex = new Texture2D(rTex.width, rTex.height);
    79.         RenderTexture.active = rTex;
    80.         tex.ReadPixels(new Rect(0, 0, rTex.width, rTex.height), 0, 0);
    81.         tex.Apply();
    82.         return tex;
    83.     }
    84.  
    85.     protected override void Cleanup()
    86.     {
    87.         CoreUtils.Destroy(fullscreenOutline);
    88.         buffer.Release();
    89.     } // Cleanup code
    90.    
    91. }
    Highlight Shader Code:

    Code (CSharp):
    1. Shader "FullScreen/Highlight"
    2. {
    3.     HLSLINCLUDE
    4.  
    5.     #pragma vertex Vert
    6.  
    7.     #pragma target 4.5
    8.     #pragma only_renderers d3d11 ps4 xboxone vulkan metal switch
    9.  
    10.     #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl"
    11.  
    12.     TEXTURE2D_X(_Buffer);
    13.     float2 _MousePosition;
    14.     //float3 _Identifier;
    15.     float4 _OutlineColor;
    16.     int _Thickness;
    17.  
    18.  
    19.     #define v2 1.41421
    20.     #define c45 0.707107
    21.     #define c225 0.9238795
    22.     #define s225 0.3826834
    23.  
    24.     #define MAXSAMPLES 8
    25.     // Neighbour pixel positions
    26.     static float2 samplingPositions[MAXSAMPLES] =
    27.     {
    28.         float2( 1,  1),
    29.         float2( 0,  1),
    30.         float2(-1,  1),
    31.         float2(-1,  0),
    32.         float2(-1, -1),
    33.         float2( 0, -1),
    34.         float2( 1, -1),
    35.         float2( 1, 0),
    36.     };
    37.  
    38.     bool CompareFloat1(float1 a, float1 b) {
    39.         float1 MIN_NORMAL = 1.17549435E-38;
    40.         float1 epsilon = 0.9999999999999999999999999999999999999999999999999999999999999999999999999999999;
    41.         float1 absA = abs(a);
    42.         float1 absB = abs(b);
    43.         float1 diff = abs(a - b);
    44.  
    45.         if (a == b) { // shortcut, handles infinities
    46.             return true;
    47.         } else if (a == 0 || b == 0 || absA + absB < MIN_NORMAL) {
    48.             // a or b is zero or both are extremely close to it
    49.             // relative error is less meaningful here
    50.             return diff < (epsilon * MIN_NORMAL);
    51.         } else { // use relative error
    52.             return diff / (absA + absB) < epsilon;
    53.         }
    54.  
    55.     }
    56.  
    57.     bool CompareFloat3(float3 a, float3 b) {
    58.         if (CompareFloat1(a.r, b.r)) {
    59.            if(CompareFloat1(a.g, b.g)){
    60.                if(CompareFloat1(a.b, b.b)){
    61.                    return true;
    62.                }
    63.            }
    64.         }
    65.         return false;
    66.     }
    67.  
    68.     // The PositionInputs struct allow you to retrieve a lot of useful information for your fullScreenShader:
    69.     // struct PositionInputs
    70.     // {
    71.     //     float3 positionWS;  // World space position (could be camera-relative)
    72.     //     float2 positionNDC; // Normalized screen coordinates within the viewport    : [0, 1) (with the half-pixel offset)
    73.     //     uint2  positionSS;  // Screen space pixel coordinates                       : [0, NumPixels)
    74.     //     uint2  tileCoord;   // Screen tile coordinates                              : [0, NumTiles)
    75.     //     float  deviceDepth; // Depth from the depth buffer                          : [0, 1] (typically reversed)
    76.     //     float  linearDepth; // View space Z coordinate                              : [Near, Far]
    77.     // };
    78.  
    79.     // To sample custom buffers, you have access to these functions:
    80.     // But be careful, on most platforms you can't sample to the bound color buffer. It means that you
    81.     // can't use the SampleCustomColor when the pass color buffer is set to custom (and same for camera the buffer).
    82.     // float4 SampleCustomColor(float2 uv);
    83.     // float4 LoadCustomColor(uint2 pixelCoords);
    84.     // float LoadCustomDepth(uint2 pixelCoords);
    85.     // float SampleCustomDepth(float2 uv);
    86.  
    87.     // There are also a lot of utility function you can use inside Common.hlsl and Color.hlsl,
    88.     // you can check them out in the source code of the core SRP package.
    89.     float4 FullScreenPass(Varyings varyings) : SV_Target
    90.     {
    91.         UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(varyings);
    92.         float depth = LoadCameraDepth(varyings.positionCS.xy);
    93.         PositionInputs posInput = GetPositionInput(varyings.positionCS.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
    94.         float3 viewDirection = GetWorldSpaceNormalizeViewDir(posInput.positionWS);
    95.         float4 color = float4(0.0, 0.0, 0.0, 0.0);
    96.  
    97.         // Load the camera color buffer at the mip 0 if we're not at the before rendering injection point
    98.         if (_CustomPassInjectionPoint != CUSTOMPASSINJECTIONPOINT_BEFORE_RENDERING)
    99.             color = float4(CustomPassSampleCameraColor(posInput.positionNDC.xy, 0), 1);
    100.  
    101.         // Add your custom pass code here
    102.         float2 uv = posInput.positionNDC.xy * _RTHandleScale.xy;
    103.        
    104.         //float4 buffer = SAMPLE_TEXTURE2D_X_LOD(_Buffer, s_linear_clamp_sampler, uv, 0);
    105.  
    106.         PositionInputs mouseInput = GetPositionInput(_MousePosition.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
    107.  
    108.         float4 _Identifier = SAMPLE_TEXTURE2D_X_LOD(_Buffer, s_linear_clamp_sampler, mouseInput.positionNDC.xy, 0);
    109.  
    110.         float4 buffer = SAMPLE_TEXTURE2D_X_LOD(_Buffer, s_linear_clamp_sampler, posInput.positionNDC.xy, 0);
    111.  
    112.         if(_Identifier.r==0 && _Identifier.g==0 && _Identifier.b==0) {
    113.             return buffer.a = 0;
    114.             return buffer;
    115.         }
    116.  
    117.         //buffer.rgb = test.rgb;
    118.         buffer.a = 0;
    119.  
    120.         if(buffer.r == _Identifier.r && buffer.g == _Identifier.g && buffer.b == _Identifier.b){
    121.            
    122.             for(int x=0; x<_Thickness+1; x++) {
    123.            
    124.                 float3 o = float3(_ScreenSize.zw, 0);
    125.  
    126.                 for (int i = 0; i < MAXSAMPLES; i++)
    127.                 {
    128.                     float2 uvN = posInput.positionNDC.xy + _ScreenSize.zw * samplingPositions[i] * x;
    129.                     float4 neighbour = SAMPLE_TEXTURE2D_X_LOD(_Buffer, s_linear_clamp_sampler, uvN, 0);
    130.                    
    131.                     half3 delta = abs(neighbour.rgb - _Identifier.rgb);
    132.                     if (length(delta) > 0.01)
    133.                     {
    134.                         buffer.rgb = _OutlineColor.rgb;
    135.                         buffer.a = 1;
    136.                         break;
    137.                     }
    138.                 }
    139.             }
    140.  
    141.         }
    142.  
    143.         // return float4(0,0,0,0);
    144.  
    145.         // //buffer.a = 1;
    146.        
    147.         // float3 delta = abs(buffer.rgb - _Identifier.rgb);
    148.    
    149.         // if(length(delta) < 0.01568628) {
    150.  
    151.         //     for(int x=0; x<_Thickness+1; x++) {
    152.            
    153.         //         float3 o = float3(_ScreenSize.zw, 0);
    154.  
    155.         //         for (int i = 0; i < MAXSAMPLES; i++)
    156.         //         {
    157.         //             float2 uvN = uv + _ScreenSize.zw * samplingPositions[i] * x;
    158.         //             float4 neighbour = SAMPLE_TEXTURE2D_X_LOD(_Buffer, s_linear_clamp_sampler, uvN, 0);
    159.                    
    160.         //             delta = abs(neighbour.rgb - _Identifier.rgb);
    161.         //             if (length(delta) > 0.01568628)
    162.         //             {
    163.         //                 buffer.rgb = _OutlineColor.rgb;
    164.         //                 buffer.a = 1;
    165.         //                 break;
    166.         //             }
    167.         //         }
    168.         //     }
    169.         // }
    170.        
    171.         // if(buffer.r == _Identifier.r && buffer.g == _Identifier.g && buffer.b == _Identifier.b){
    172.         //     buffer.rgb = float4(0,0,1,1);
    173.         //     buffer.a = 1;
    174.         // } else {
    175.         //     buffer.a = 0;
    176.         // }
    177.        
    178.  
    179.         // if (Luminance(buffer.rgb) < 0.01)
    180.         // {
    181.         //     buffer.a = 1;
    182.         // }
    183.        
    184.         return buffer;
    185.     }
    186.  
    187.  
    188.  
    189.     ENDHLSL
    190.  
    191.     SubShader
    192.     {
    193.         Pass
    194.         {
    195.             Name "Custom Pass 0"
    196.  
    197.             ZWrite On
    198.             ZTest Always
    199.             Blend SrcAlpha OneMinusSrcAlpha
    200.             Cull Off
    201.  
    202.             HLSLPROGRAM
    203.                 #pragma fragment FullScreenPass
    204.             ENDHLSL
    205.         }
    206.     }
    207.     Fallback Off
    208. }
    209.  

    Screen Shots:

    When zoomed out renders with depth issue:
     

    Attached Files:

  2. antoinel_unity

    antoinel_unity

    Unity Technologies

    Joined:
    Jan 7, 2019
    Posts:
    267
    Hello,

    You indeed don't have any depth buffer bound when you render the objects you want to outline, at this line:
    Code (CSharp):
    1.         CoreUtils.SetRenderTarget(cmd, buffer, ClearFlag.Color);
    2.  
    This only binds the color buffer so the shader can't perform any depth tests. This is fine for most of the outline stuff because they are usually visible behind other objects but if you want it, you just have to add a depth buffer in the SetRenderTarget. There is an example on how to do it here: https://github.com/alelievr/HDRP-Cu...r/Assets/CustomPasses/Blur/SlightBlur.cs#L131
     
    SKoptev likes this.
  3. robunity93

    robunity93

    Joined:
    Oct 11, 2016
    Posts:
    3
    Thank you so much for the response, really appreciate it! Added in the depth buffer and it worked! For those interested these were the changes that enabled depth to work in our build.

    Firstly created the depth buffer in the setup method;

    Code (CSharp):
    1. depthBuffer = RTHandles.Alloc(
    2.             Vector2.one,
    3.             colorFormat: GraphicsFormat.R16_UInt, useDynamicScale: true,
    4.             name: "Blur Depth Mask", depthBufferBits: DepthBits.Depth16
    5.         );
    Then added a stateblock within the RenderListDesc

    Code (CSharp):
    1. stateBlock = new RenderStateBlock(RenderStateMask.Depth){ depthState = new DepthState(true, CompareFunction.LessEqual)}
    Finally, a key component was also setting the ClearFlag to ClearFlag.All.
     
    Egad_McDad and antoinel_unity like this.