Search Unity

Problem with Reconstructing Position From Depth Buffer

Discussion in 'Shaders' started by Chimera3D, Feb 10, 2015.

  1. Chimera3D

    Chimera3D

    Joined:
    Jan 27, 2012
    Posts:
    73
    After trying several methods that all had some sort of limitation or problem I found a method of reconstructing world positions from a depth buffer that works but is only accurate when the camera x and z rotation is set to zero, but it's the most flexible method I've found so far (can be applied to non-post processing shaders). Here's the reconstruction function:

    Code (CSharp):
    1. float3 DepthToWorld(float2 screenPos, float depth)
    2.             {
    3.                 float3 cameraPos = _WorldSpaceCameraPos;
    4.                 float3 localX = _FrustumCorners[1] - _FrustumCorners[0];
    5.                 float3 localY = _FrustumCorners[3] - _FrustumCorners[0];
    6.                
    7.                 float3 ray = screenPos.x * localX + screenPos.y * localY + _FrustumCorners[0];
    8.                
    9.                 float3 pos = cameraPos + (-ray) * depth;
    10.                 return pos;
    11.             }
    and the code that calls it:

    Code (CSharp):
    1. float3 worldPos = DepthToWorld(i.uv, depth);
    and the code that creates the frustum corners matrix:

    Code (CSharp):
    1.         float CAMERA_NEAR = camera.nearClipPlane;
    2.         float CAMERA_FAR = camera.farClipPlane;
    3.         float CAMERA_FOV = camera.fieldOfView;
    4.         float CAMERA_ASPECT_RATIO = camera.aspect;
    5.        
    6.         Matrix4x4 frustumCorners = Matrix4x4.identity;      
    7.        
    8.         float fovWHalf = CAMERA_FOV * 0.5f;
    9.        
    10.         Vector3 toRight = camera.transform.right * CAMERA_NEAR * Mathf.Tan (fovWHalf * Mathf.Deg2Rad) * CAMERA_ASPECT_RATIO;
    11.         Vector3 toTop = camera.transform.up * CAMERA_NEAR * Mathf.Tan (fovWHalf * Mathf.Deg2Rad);
    12.        
    13.         Vector3 topLeft = (camera.transform.forward * CAMERA_NEAR - toRight + toTop);
    14.         float CAMERA_SCALE = topLeft.magnitude * CAMERA_FAR/CAMERA_NEAR;  
    15.        
    16.         topLeft.Normalize();
    17.         topLeft *= CAMERA_SCALE;
    18.        
    19.         Vector3 topRight = (camera.transform.forward * CAMERA_NEAR + toRight + toTop);
    20.         topRight.Normalize();
    21.         topRight *= CAMERA_SCALE;
    22.        
    23.         Vector3 bottomRight = (camera.transform.forward * CAMERA_NEAR + toRight - toTop);
    24.         bottomRight.Normalize();
    25.         bottomRight *= CAMERA_SCALE;
    26.        
    27.         Vector3 bottomLeft = (camera.transform.forward * CAMERA_NEAR - toRight - toTop);
    28.         bottomLeft.Normalize();
    29.         bottomLeft *= CAMERA_SCALE;
    30.  
    31.         frustumCorners.SetRow (0, topLeft);
    32.         frustumCorners.SetRow (1, topRight);      
    33.         frustumCorners.SetRow (2, bottomRight);
    34.         frustumCorners.SetRow (3, bottomLeft);
    35.  
    36.         mat.SetMatrix ("_FrustumCorners", frustumCorners);
    and where I originally found this method: https://www.synapsegaming.com/forums/p/1165/13827.aspx (the last post on the page). As I said it seems to work really well but when the camera rotates on the x or z axis the reconstruction doesn't account for the rotation (but does for the y axis). Does anyone what could be going wrong?
     
  2. Chimera3D

    Chimera3D

    Joined:
    Jan 27, 2012
    Posts:
    73
    Am I supposed to be doing a simple matrix multiplication somewhere? Everything seems to check out so I wouldn't think that it would be necessary.
     
  3. Chimera3D

    Chimera3D

    Joined:
    Jan 27, 2012
    Posts:
    73
    Nevermind I figured it out, a few simple errors, the ray variable should not be negative in the line:
    Code (CSharp):
    1. float3 pos = cameraPos + (-ray) * depth;
    and the y component of screenPos in the line:
    Code (CSharp):
    1. float3 ray = screenPos.x * localX + screenPos.y * localY + _FrustumCorners[0];
    has to be inverted and the line should read:
    Code (CSharp):
    1. float3 ray = screenPos.x * localX + (1-screenPos.y) * localY + _FrustumCorners[0];
    and now it works perfectly. :)