Search Unity

Resolved Proper way to calculate world position ---> depth value

Discussion in 'Shaders' started by b1gry4n, Sep 29, 2020.

  1. b1gry4n

    b1gry4n

    Joined:
    Sep 11, 2013
    Posts:
    146
    If I have access to the world position in a shader...what is the proper way to calculate its depth so that it can later be compared to the scene depth?

    Right now I am doing:

    depth = distance(worldPosition,_WorldSpaceCameraPos) / _ProjectionParams.z


    Is this correct? Doing this:

    LinearEyeDepth(depth,_ZBufferParams);


    Is not giving me the results I expect, so I am doing something wrong.
     
  2. b1gry4n

    b1gry4n

    Joined:
    Sep 11, 2013
    Posts:
    146
    I solved this by just reconstructing the world position for the depth value/scene depth and comparing them that way
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    The distance between the surface and the camera is the distance between the surface and the camera, not the depth. The depth is the distance from the camera to the view plane that point on the surface is on.
    That's also different from the non-linear depth that's in the depth buffer & camera depth texture, which is different from the "linear 01" depth, which is different from the linear (aka linear eye) depth.

    The way you get the linear depth from a world position, is by converting a world space into view space and getting the negative z, like this:
    Code (csharp):
    1. float linearDepth = -UnityWorldToViewPos(worldPostion).z;
    Negative z, because a quirk of the OpenGL style view matrix Unity uses is that forward is negative Z.

    If you want to get the value that's equivalent to what's in the depth texture, you need to convert the world position into clip space.
    Code (csharp):
    1. float4 clipPos = UnityWorldToClipPos(worldPosition);
    2. float depth = clipPos.z / clipPos.w;
    As a quirk of how projection matrices work,
    clipPos.w
    is equal to the
    linearDepth
    above, if you have a perspective camera.

    But, depending on what exactly you're doing, comparing the
    linearDepth
    value from the above to the
    LinearEyeDepth(sceneDepth)
    is probably your best bet.

    One side note, you used the SRP form of the
    LinearEyeDepth
    function that also takes the _ZBufferParams as an input. If you're writing this for one of the SRPs, you should use
    -TransformWorldToView(worldPostion).z
    instead of the above function. Both functions are equivalent to:
    Code (csharp):
    1. mul(UNITY_MATRIX_V, float4(worldPosition, 1.0))
     
    forestrf, lilacsky824, dccoo and 2 others like this.
  4. b1gry4n

    b1gry4n

    Joined:
    Sep 11, 2013
    Posts:
    146
    Thanks for the detailed response. The way I "solved" this was a little hacky and does this barely good enough. I will try to implement what youve posted.
     
  5. dccoo

    dccoo

    Joined:
    Oct 3, 2015
    Posts:
    161
    I cannot thank you enough for this answer. I've trying all sort of maths to evaluate the depth. In my last attempt, I was trying to project the (vertex world position - camera world position) vector into the camera direction vector and use the magnitude of the projected vector as the depth (obviously remapping from (near, far) to (1, 0)), and it looked good but was not perfect in every single pixel, but your answer just made it all clear.
     
    Last edited: Nov 3, 2022
  6. FedoraKirb

    FedoraKirb

    Joined:
    Apr 20, 2021
    Posts:
    6
    Sorry to necropost, but I can't find anything about
    UnityWorldToViewPos
    in the docs. This seems to be exactly what I need to solve one of my own problems, though.
     
  7. pavelkx

    pavelkx

    Joined:
    Nov 19, 2016
    Posts:
    2
    If you use SRP you need to use TransformWorldToView