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. Dismiss Notice

Resolved Calculating world-space position of pixel within a compute shader

Discussion in 'Shaders' started by TriceHelix, Aug 6, 2023.

  1. TriceHelix

    TriceHelix

    Joined:
    Mar 6, 2019
    Posts:
    34
    Thanks to @INedelcu 's open-source project, a neat and easy solution has been found.

    In order to convert camera depth into a world-space position within a compute shader, use this function in HLSL:
    Code (HLSL):
    1. // Convert camera depth to world-space position.
    2. // This should work for all render pipelines regardless of graphics API.
    3. // ---
    4. // cameraToWorld: use built-in "unity_cameraToWorld" property
    5. // zBufferParams: use built-in "_ZBufferParams" property
    6. // depthToViewParams: compute in C# and pass to your kernel as property
    7. // screenPos: screen position in pixels at which the depth was sampled (add 0.5 to the integer value to use the exact center of the pixel)
    8. // rawDepth: unmodified depth value retrieved from buffer (use built-in "_CameraDepthTexture" property in a custom pass, for example)
    9. float3 DepthToWorldPos(float4x4 cameraToWorld, float4 zBufferParams, float4 depthToViewParams, float2 screenPos, float rawDepth)
    10. {
    11.     // linear eye depth in range [0..far]
    12.     float ed = 1.0 / mad(zBufferParams.z, rawDepth, zBufferParams.w);
    13.  
    14.     // convert eye depth to view-space position
    15.     float4 view = float4(mad(screenPos, depthToViewParams.xy, depthToViewParams.zw) * ed, ed, 1.0);
    16.  
    17.     // transform from view-space to world-space
    18.     float4 world = mul(cameraToWorld, view);
    19.  
    20.     // Done!
    21.     return world.xyz;
    22. }
    And compute "depthToViewParams" using this function in C#:
    Code (CSharp):
    1. public static Vector4 ComputeDepthToViewParams(Camera camera)
    2. {
    3.     float w = camera.pixelWidth;
    4.     float h = camera.pixelHeight;
    5.     float invHalfTanFOV = 1.0f / math.tan(camera.fieldOfView * 0.00872664625f); // 1f / math.tan(math.radians(camera.fieldOfView) * 0.5f);
    6.     float aspectRatio = h / w;
    7.  
    8.     // z and w are negated in order to use a MAD (multiply & add) instruction in the shader
    9.     return new Vector4(
    10.         x: 2f / (invHalfTanFOV * w * aspectRatio),
    11.         y: 2f / (invHalfTanFOV * h),
    12.         z: -1f / (invHalfTanFOV * aspectRatio),
    13.         w: -1f / invHalfTanFOV);
    14. }
    Many thanks again to INedelcu for helping out.
    To achieve this in a regular vertex/fragment shader using Unity's built-in functions, view kripto289's reply to the thread.

    Here is the original post/question:
    Dear shader experts,

    The title says it all. What I have access to in the shader is the camera's depth buffer and inverse view matrix. The matrix is computed liked this:
    Code (CSharp):
    1. Matrix4x4 view = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false) * camera.worldToCameraMatrix;
    2. Matrix4x4 invView = Matrix4x4.Inverse(view);
    In the compute shader I have a function like this:
    Code (CSharp):
    1. // ZBuffer: Camera Depth Buffer obtained in Custom Pass (needs to be a texture array because of mip pyramid)
    2. // InverseViewMatrix: The same matrix as computed above in C#
    3. // Pixel: The pixel's coordinates/index
    4. // ScreenDims: The screen's width and height (matches dimensions of depth buffer)
    5. float3 DepthToWorldPos(Texture2DArray<float> ZBuffer, float4x4 InverseViewMatrix, uint2 Pixel, uint2 ScreenDims)
    6. {
    7.     float2 uv = (Pixel + 1.0) / (ScreenDims + 1.0) * 2.0 - 1.0; // -1..1 range
    8.     uv.y = -uv.y;
    9.  
    10.     float d = 1.0 - ZBuffer[uint3(Pixel, 0)]; // DirectX reversed Z
    11.     float4 clip = float4(uv, d, 1.0);
    12.     float4 view = mul(InverseViewMatrix, clip);
    13.  
    14.     return view.xyz / view.w; // perspective adjustment
    15. }
    Unfortunately, the function returns absolutely nonsensical values, regardless of how I tweak it.
    The code is based on similar threads where someone is reconstructing the world position from a depth texture, but within a fragment shader. My issues may not even be related to this specific part of the code, but I am trying to eliminate potential errors one by one. Am I doing this the right way?

    Feedback would be greatly appreciated.
     
    Last edited: Aug 8, 2023
  2. kripto289

    kripto289

    Joined:
    Feb 21, 2013
    Posts:
    466
    My compute shader for urp/hdrp.
    For standard rendering you can just open source code and copy "ComputeWorldSpacePosition" method

    Code (CSharp):
    1.  
    2.  
    3. void RenderHash_kernel(uint3 id : SV_DispatchThreadID)
    4. {
    5.     float2 screenUV = id.xy * _RTSize.zw;
    6.  
    7.     float depth = GetSceneDepth(screenUV);
    8.     float3 posWS = ComputeWorldSpacePosition(screenUV, depth, UNITY_MATRIX_I_VP)
    9.     float3 absoluteWS = GetAbsolutePositionWS(posWS); //this fix for HDRP camera relative rendering, in urp you can use posWS
    10.  
    11. }
     
  3. TriceHelix

    TriceHelix

    Joined:
    Mar 6, 2019
    Posts:
    34
    @kripto289 Thanks for your reply, however that's not really what I'm looking for. If you take a look at my code, I am already doing everything the utility functions do in your case. (Again, I do not have access to the functions in a compute shader and have to mimic their logic manually.)
    It has occurred to me that this line:
    ...should really be corrected to this:
    float d = ZBuffer[uint3(Pixel, 0)];

    Because as far as I am aware, the depth values are already in the correct format required for normalized device coordinates (at least on DirectX).

    I am also adding the camera's world-space position to the function's return value to adjust for HDRP's camera-relative rendering. Still - the results are incorrect.
     
  4. INedelcu

    INedelcu

    Unity Technologies

    Joined:
    Jul 14, 2015
    Posts:
    158
    TriceHelix likes this.
  5. TriceHelix

    TriceHelix

    Joined:
    Mar 6, 2019
    Posts:
    34
    @INedelcu Thank you so much, that looks promising! I will update my original post with a nice drop-in solution as soon as I'm done reworking my shader and C# code, aswell as testing everything thoroughly.