Search Unity

Reconstruct world pos from planar reflection depth texture

Discussion in 'Image Effects' started by MuckSponge, Dec 30, 2017.

  1. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    I'm working on a water shader post effect (using the post processing stack) with planar reflections. Everything works great except I'm unable to get the right shading for underwater internal reflections. That is, where the light penetrates the water, bounces off an object, reflects back off the water surface and reaches the camera. In order to do this calculation I render a planar reflection into a colour texture and depth texture. I need to reconstruct the world position of the reflected fragment from the depth value so that I can calculate the distance light must travel to reach it through the water volume (exact same calculation I use for non-reflected fragments).

    Sounds good? Well, unfortunately my knowledge of projection matrices is not great so I don't actually know how to transform the planar reflection depth value into a world space position; I was hoping someone here could nudge me in the right direction.

    Here's what I do to reconstruct the world position of a non-reflected fragment (adapted from here):

    Code (CSharp):
    1. // in script
    2. var p = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false);
    3. p[2, 3] = p[3, 2] = 0.0f;
    4. p[3, 3] = 1.0f;
    5. var clipToWorld = Matrix4x4.Inverse(p * camera.worldToCameraMatrix) * Matrix4x4.TRS(new Vector3(0, 0, -p[2, 2]), Quaternion.identity, Vector3.one);
    6. material.SetMatrix("_ClipToWorld", clipToWorld);
    7.  
    8. // in shader
    9. struct Varyings
    10. {
    11.     float4 vertex : SV_POSITION;
    12.     float2 texcoord : TEXCOORD0;
    13.     float2 texcoordStereo : TEXCOORD1;
    14.     float3 worldDirection : TEXCOORD2;
    15. };
    16.  
    17. Varyings Vert(AttributesDefault v)
    18. {
    19.     VaryingsGreyscale o;
    20.     o.vertex = float4(v.vertex.xy, 0.0, 1.0);
    21.     o.texcoord = TransformTriangleVertexToUV(v.vertex.xy);
    22.  
    23.     #if UNITY_UV_STARTS_AT_TOP
    24.         o.texcoord = o.texcoord * float2(1.0, -1.0) + float2(0.0, 1.0);
    25.     #endif
    26.  
    27.     o.texcoordStereo = TransformStereoScreenSpaceTex(o.texcoord, 1.0);
    28.  
    29.     float4 clip = float4(o.vertex.xy, 0.0, 1.0);
    30.     o.worldDirection = mul(_ClipToWorld, clip).xyz - _WorldSpaceCameraPos.xyz;
    31.  
    32.     return o;
    33. }
    34.  
    35. // frag
    36. float sceneDepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture, texcoord);
    37. sceneDepth = LinearEyeDepth(sceneDepth); // in world units
    38.  
    39. // scene pos
    40. float3 worldSpaceScenePos = i.worldDirection * sceneDepth + _WorldSpaceCameraPos;
    I've tried the same method for the planar reflection texture (but using the reflection camera's position and matrices) but this doesn't seem to work.

    Here are a few WIP screenshots:
    water-3.png water-2.png water-1.png water-0.png
     
  2. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Might be just translation error on my part, are you talking about the underwater water surface reflection?
     
  3. MuckSponge

    MuckSponge

    Joined:
    Jul 11, 2015
    Posts:
    41
    Yes that is correct. It is a planar reflection of what is under the water surface and the context is when the camera is below the water level as opposed to above it (where the reflections would be of things above the water surface). The first screenshot I posted exhibits the effect. The colour of the reflection is slightly off due to the fact that I don't know how to calculate its world position (you'll notice it is a bit darker).

    Since I created this thread I realised I could render the planar reflection with a custom shader which would calculate those colours without needing to transform to the scene camera space. Because this is integrated into the Post processing stack pipeline, applying the effect for other arbitrary cameras is probably a bit tricky, haven't looked into it yet.