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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question How to predict depth value of a nearby pixel

Discussion in 'Shaders' started by jbklsny, Oct 21, 2022.

  1. jbklsny

    jbklsny

    Joined:
    Jul 18, 2020
    Posts:
    4
    I have a normal and depth of a pixel. I want to predict depths of 4 pixels around it. I can't cope with the math. Can someone explain me how to achieve this?

    EDIT: Simple visualisation - I want to know depths of orange pixels having depth of black and its normal values.
    upload_2022-10-21_2-0-0.png
     
  2. Przemyslaw_Zaworski

    Przemyslaw_Zaworski

    Joined:
    Jun 9, 2017
    Posts:
    314
    I assume, you read depth values from depth map, so this is an example how to read values of nearby pixels (variables a,b,d,e):

    Code (CSharp):
    1. Shader "DepthTest"
    2. {
    3.     Subshader
    4.     {
    5.         Pass
    6.         {
    7.             CGPROGRAM
    8.             #pragma vertex VSMain
    9.             #pragma fragment PSMain
    10.             #pragma target 5.0
    11.  
    12.             Texture2D _CameraDepthTexture;
    13.  
    14.             float4 VSMain (float4 vertex : POSITION) : SV_POSITION
    15.             {
    16.                 return UnityObjectToClipPos(vertex);
    17.             }
    18.  
    19.             float4 PSMain (float4 vertex : SV_POSITION) : SV_Target
    20.             {
    21.                 int2 texcoord = int2(vertex.xy);
    22.                 float a = _CameraDepthTexture.Load(int3(texcoord + int2( 0.0, -1.0), 0)).r;
    23.                 float b = _CameraDepthTexture.Load(int3(texcoord + int2(-1.0,  0.0), 0)).r;
    24.                 float c = _CameraDepthTexture.Load(int3(texcoord + int2( 0.0,  0.0), 0)).r;
    25.                 float d = _CameraDepthTexture.Load(int3(texcoord + int2( 1.0,  0.0), 0)).r;
    26.                 float e = _CameraDepthTexture.Load(int3(texcoord + int2( 0.0,  1.0), 0)).r;              
    27.                 float average = (a + b + c + d + e) / 5.0;
    28.                 return average.xxxx;
    29.             }
    30.             ENDCG
    31.         }
    32.     }
    33. }
     
  3. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,497
    I believe they're talking about a situation where they are running in the fragment on some geometry, and they will naturally know the depth and normal of that fragment, but they would like to derive what the depth of the adjacent fragments of that mesh would be by using that information.

    One solution would be to use screen-space derivatives to get the difference in depth either on the horizontal or vertical direction,
    ddx(depth)
    and
    ddy(depth)
    will return that difference value on a given direction and then you can simply add the result to original depth value to get the value of the adjacent pixel's depth.

    The only issue there is it's not guaranteed whether this will be the pixel to the left or right (ddx) and the top or the bottom (ddy). To determine that you could do another ddx() or ddy() using that component (.x or .y) of the current clip-space position, and see whether it's negative or positive, then you'll know which direction the derivative looked in and can subtract the depth-derivative value instead of adding to get the value of the other side pixel (since the fragment program is operating on triangles, depth change should be mostly the same on either side). (also, I would recommend bundling depth and that position component into a float2() that you do a single ddx/ddy with to avoid doing 2 of those expensive operations (though the compiler may be smart enough to bundle them regardless, but best to force it))

    Derivatives can be a bit expensive on some platforms though, especially mobile. But other options for calculating this without them might be just as expensive if not more, such as shifting screen-space position over by a pixel, calculating the world-space plane of the surface and projecting that offset position to world space and then onto that plane surface, and then that resulting position transformed back to clip-space for the depth value.

    There might be an even smarter approach that adjusts the depth value by the slope of the normal in some way but I can't think up the math for that and it may still end up being more expensive.

    (Sidenote: This will only ever get you the values for the current triangle, if the adjacent pixel lays outside the triangle, it returns the derivative of the opposite-side pixel. And a region of 2x2 pixels will always run even on the thinnest triangle strip, it's not going to return values of the touching triangles)
     
    Last edited: Oct 21, 2022
  4. jbklsny

    jbklsny

    Joined:
    Jul 18, 2020
    Posts:
    4
    I am doing 1 pixel edge in 3d pixelart shader. I've heard the best approach is to use normal and depth and use math. I don't have to know how far is a pixel because I am going to adjust it just by slider and make it work by trial and error. I just need how to do it using maths.

    I just want to extrapolate depth from pixel and if it doesn't match actual depth value + some value it marks it as edge.
     
  5. Invertex

    Invertex

    Joined:
    Nov 7, 2013
    Posts:
    1,497
    In that case, what you're looking for is a screen-space post-effect outline shader. I would avoid writing your own unless you're doing it specifically for learning, as there are already plenty of free post-process outline shaders.

    Some examples:




    https://github.com/UnityTechnologies/open-project-1