Documentation

Unity Engine


Unity Engine


Get the current depth buffer from GPU memory in URP

Use a depth input attachment to read the current pixel depth from GPU memory instead of sampling a depth texture.
Read time 4 minutesLast updated a month ago

To read the depth value at the current pixel from on-chip memory in a custom Universal Render Pipeline (URP) shader, use the
FetchSceneDepth
method with a depth input attachment.
A render pass can read the depth buffer that a previous render pass wrote to on-chip memory, instead of writing it to a separate texture and sampling it back. In some graphics APIs, this is called an input attachment. For more information, refer to Get the current framebuffer from GPU memory in URP.
Reading depth using an input attachment reduces memory bandwidth in your project, especially on mobile devices that use tile-based deferred rendering (TBDR). This is useful to optimize effects like custom on-tile depth-based fog and depth-based smoke.
Note
If you use Shader Graph, use the Fetch Scene Depth node instead of writing HLSL.

Requirements

Depth input attachments are supported only in URP, and only on DirectX 12 and Vulkan. You can check whether your target platform supports this at runtime by using
SystemInfo.supportsDepthAttachmentAsInputAttachment
.

Read the current pixel depth

To read depth from on-chip memory in your shader, do the following:
  1. In your URP Renderer, add a Render Objects Renderer Feature.
  2. In the Render Objects Renderer Feature, enable Depth and Set As Input Attachment.
  3. In the Render Objects Renderer Feature, set Event to After Rendering Opaques or later. This is necessary because depth data is available only after the opaque pass.
  4. In the
    HLSLPROGRAM
    in your shader file, add an
    #include
    directive for the
    DeclareDepthTexture.hlsl
    file.
    For example:
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
  5. Add a
    multi_compile
    pragma for the input attachment keywords, so Unity can enable the correct keyword for the pass. URP sets these keywords automatically at runtime when a render pass uses depth as an input attachment.
    For example:
    // URP sets these keywords automatically at runtime; you don't set them manually.#pragma multi_compile _ _DEPTH_AS_INPUT_ATTACHMENT _DEPTH_AS_INPUT_ATTACHMENT_MSAA
  6. In your fragment shader, call
    FetchSceneDepth
    to read the depth value at the current pixel. For more information about the overloads, refer to FetchSceneDepth overloads.
    For example:
    #if defined(_DEPTH_AS_INPUT_ATTACHMENT) // Reads depth directly from on-chip memory instead of sampling a depth texture. float depth = FetchSceneDepth(input.positionCS.xy);#elif defined(_DEPTH_AS_INPUT_ATTACHMENT_MSAA) float depth = FetchSceneDepth(input.positionCS.xy, 0);#endif
  7. Create a material from your shader, and assign it to objects that your Render Objects Renderer Feature affects.
The material now reads current depth values from on-chip memory instead of a separate depth texture sample.

FetchSceneDepth overloads

FetchSceneDepth
has the following overloads.

Syntax

Description

float FetchSceneDepth(float2 fragCoord)
Returns the depth at the current pixel. Use this overload when the
_DEPTH_AS_INPUT_ATTACHMENT
keyword is defined.
float FetchSceneDepth(float2 fragCoord, int sampleIndex)
Returns the depth at the current pixel for the given MSAA sample index. Use this overload when the
_DEPTH_AS_INPUT_ATTACHMENT_MSAA
keyword is defined.

Full code example

The following example shader code reads the current pixel depth and writes it to the red channel of the output color.
Shader "CustomFetchDepth"{ SubShader { Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" } Pass { Name "CustomFetchDepth" ZWrite On ZTest LEqual Cull Off HLSLPROGRAM #pragma vertex Vert #pragma fragment FragDepthVisualization #pragma target 4.5 // URP sets these keywords automatically at runtime; you don't set them manually. #pragma multi_compile _ _DEPTH_AS_INPUT_ATTACHMENT _DEPTH_AS_INPUT_ATTACHMENT_MSAA #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl" struct Attributes { float4 positionOS : POSITION; UNITY_VERTEX_INPUT_INSTANCE_ID }; struct Varyings { float4 positionCS : SV_POSITION; UNITY_VERTEX_OUTPUT_STEREO }; Varyings Vert(Attributes input) { Varyings output; UNITY_SETUP_INSTANCE_ID(input); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); output.positionCS = TransformObjectToHClip(input.positionOS.xyz); return output; } #if defined(_DEPTH_AS_INPUT_ATTACHMENT) float4 FragDepthVisualization(Varyings input) : SV_Target0 { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); // Reads depth directly from on-chip memory instead of sampling a depth texture. float depth = FetchSceneDepth(input.positionCS.xy); return float4(depth, 0.0f, 0.0f, 1.0); } #elif defined(_DEPTH_AS_INPUT_ATTACHMENT_MSAA) float4 FragDepthVisualization(Varyings input) : SV_Target0 { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); // Reads depth directly from on-chip memory for the given MSAA sample, instead of sampling a depth texture. float depth = FetchSceneDepth(input.positionCS.xy, 0); return float4(depth, 0.0f, 0.0f, 1.0); } #else float4 FragDepthVisualization(Varyings input) : SV_Target0 { UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); // Unity doesn't provide an automatic fallback to depth texture sampling here. float depth = 0; return float4(depth, 0.0f, 0.0f, 1.0); } #endif ENDHLSL } }}

Additional resources