Search Unity

Reading depth buffer in vertex shader

Discussion in 'Shaders' started by SeaRelic, Sep 17, 2016.

  1. SeaRelic

    SeaRelic

    Joined:
    Sep 17, 2016
    Posts:
    3
    I was trying to do an effect where i need the vertex shader to read the depth buffer around the vertex(9 pixels surrounding vertex) and move the vertex to where ever is height in local area. Is there a way to do this?
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    You can use tex2Dlod() to sample textures in the vertex shader, otherwise it's basically the same code as sampling the depth texture from the pixel shader. Something like:

    Code (CSharp):
    1. o.pos = UnityObjectToClipPos(v.vertex);
    2. float4 screenPos = ComputeScreenPos(o.pos);
    3. float2 depthUV = screenPos.xy / screenPos.w;
    4. float3 uOffsets = float3(-1,0,1) * _CameraDepthTexture_TexelSize.x;
    5. float3 vOffsets = float3(-1,0,1) * _CameraDepthTexture_TexelSize.y;
    6.  
    7. float depthAverage = 0.0;
    8. depthAverage += tex2Dlod(_CameraDepthTexture, float4(depthUV + float2(uOffsets.x, vOffset.x), 0.0, 0.0)) / 9.0;
    9. depthAverage += tex2Dlod(_CameraDepthTexture, float4(depthUV + float2(uOffsets.x, vOffset.y), 0.0, 0.0)) / 9.0;
    10. depthAverage += tex2Dlod(_CameraDepthTexture, float4(depthUV + float2(uOffsets.x, vOffset.z), 0.0, 0.0)) / 9.0;
    11. depthAverage += tex2Dlod(_CameraDepthTexture, float4(depthUV + float2(uOffsets.y, vOffset.x), 0.0, 0.0)) / 9.0;
    12. depthAverage += tex2Dlod(_CameraDepthTexture, float4(depthUV + float2(uOffsets.y, vOffset.y), 0.0, 0.0)) / 9.0;
    13. depthAverage += tex2Dlod(_CameraDepthTexture, float4(depthUV + float2(uOffsets.y, vOffset.z), 0.0, 0.0)) / 9.0;
    14. depthAverage += tex2Dlod(_CameraDepthTexture, float4(depthUV + float2(uOffsets.z, vOffset.x), 0.0, 0.0)) / 9.0;
    15. depthAverage += tex2Dlod(_CameraDepthTexture, float4(depthUV + float2(uOffsets.z, vOffset.y), 0.0, 0.0)) / 9.0;
    16. depthAverage += tex2Dlod(_CameraDepthTexture, float4(depthUV + float2(uOffsets.z, vOffset.z), 0.0, 0.0)) / 9.0;
    That does the average, you could do the min, or max, or whatever kind of weighting you want. The harder part is moving the vertex to someplace that makes sense. The easiest would be to calculate the view position of each vertex then move the .z value to match the average and convert that to clip space like this:

    Code (CSharp):
    1. float4 viewPos = mul(UNITY_MATRIX_MV, v.vertex);
    2. viewPos.z = LinearEyeDepth(averageDepth);
    3. o.pos  = mul(UNITY_MATRIX_P, viewPos);
    Alternatively you could try something like this:
    Code (CSharp):
    1. o.pos.z = averageDepth;
    Though I don't think that quite works and I can't remember the code off the top of my head to convert the depth to clip space z.
     
  3. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    What effect are you trying to achieve?

    Technically no, vertex are used to rasterized the depth buffer, so the buffer is something that happen after the vertex pass.

    EDIT: ninja'ed by BGOLUS
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    Some notes: In the forward rendering path the depth texture is created before the scene is rendered to the screen so you can access the depth texture on opaque or transparent geometry. In deferred the depth texture is created when the gbuffers are rendered, which means it's not accessible for opaque geometry, but is for transparent geometry (specifically shaders with "Queue"="Transparent" or a render queue of 2500 or higher).