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 Converting a clip-space point to world-space?

Discussion in 'Shaders' started by Squidcor, Sep 28, 2023.

  1. Squidcor

    Squidcor

    Joined:
    Feb 12, 2013
    Posts:
    7
    I am editing the Unity built-in-renderer fog macros to apply a height based fog to every shader that utilises built-in fog. Because I am hooking into UNITY_TRANSFER_FOG to get the vertex position, it is unfortunately in clip-space.

    Is there way to convert the vertex position back to world-space so I can compare the Y value to a known world-space height?

    Alternatively, does anyone have any clever suggestions on how to do the comparison in clip-space instead? Other height fog implementations (deferred etc.) are off the table. It needs to be compiled into every shader.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,238
    Yes, it’s possible. The harder part you’re going to run into is Unity only needs a single float value for fog, and there are lots of places where Unity packs that single value into another vector when transferring it to the fragment.

    What you need to go from clip space to world space is an inverse projection matrix and inverse view matrix. Unity provides an inverse project matrix, but it doesn’t exactly match the “real” view matrix, so some tweaks need to be made to solve that.

    Code (csharp):
    1. float3 ClipToWorldPos(float4 clipPos)
    2. {
    3. #ifdef UNITY_REVERSED_Z
    4.     // unity_CameraInvProjection always in OpenGL matrix form
    5.     // that doesn't match the current view matrix used to calculate the clip space
    6.  
    7.     // transform clip space into normalized device coordinates
    8.     float3 ndc = clipPos.xyz / clipPos.w;
    9.  
    10.     // convert ndc's depth from 1.0 near to 0.0 far to OpenGL style -1.0 near to 1.0 far
    11.     ndc = float3(ndc.x, ndc.y * _ProjectionParams.x, (1.0 - ndc.z) * 2.0 - 1.0);
    12.  
    13.     // transform back into clip space and apply inverse projection matrix
    14.     float3 viewPos =  mul(unity_CameraInvProjection, float4(ndc * clipPos.w, clipPos.w));
    15. #else
    16.     // using OpenGL, unity_CameraInvProjection matches view matrix
    17.     float3 viewPos = mul(unity_CameraInvProjection, clipPos);
    18. #endif
    19.  
    20.     // transform from view to world space
    21.     return mul(unity_MatrixInvV, float4(viewPos, 1.0)).xyz;
    22. }
     
    Last edited: Oct 1, 2023
  3. Squidcor

    Squidcor

    Joined:
    Feb 12, 2013
    Posts:
    7
    Thanks so much for your help bgolus! That's perfect!

    I'm modifying UNITY_FOG_COORDS to add second float (y position in clip space). I ran some tests and couldn't seem to find anywhere in our game where UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE, UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS, or UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC are used. Do you know in what situations these defines are used? Is there somewhere we're gonna be caught out?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,238
    Unity’s Standard shader uses UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC
     
  5. Squidcor

    Squidcor

    Joined:
    Feb 12, 2013
    Posts:
    7
    Good to know. We aren't using the standard shader anywhere so we might be alright. I'll do some more tests to be sure.

    Thanks for your help!