Friday, November 15, 2013

World Position from Depth - What NOT to do !!

Ah yes, the ever famous question of Deferred Renderer world: "How do I reconstruct the world position from the depth?" There are lots of blogs and pages about this topic. Following are a few that I read through:

http://www.phasersonkill.com
http://gamedev.stackexchange.com
http://mynameismjp.wordpress.com

Today I'm going to talk a little bit about this. The following is the standard method:

  1. Use texture coords to read depth sample
  2. Convert texture coords [0-1] to clip space [-1 1]
  3. Convert depth sample to linear depth
  4. Calculate View Space coordinates
  5. Use Inverse View Matrix to convert from View Space to World Space
CPU Side:
A = farPlane / (farPlane - nearPlane)
B = (-farPlane * nearPlane) / (farPlane - nearPlane)
AdjustX = (projectionMatrix(0,2) + 1) / projectionMatrix(0,0)
AdjustY = (projectionMatrix(1,2) + 1)/ projectionMatrix(1,1)

In Shader:
linearDepth = B / (depth - A)
viewSpacePosition = float3(clipSpacePos.x * AdjustX, clipSpacePos.y * AdjustY, 1.0f) * linearDepth
worldSpacePosition = mul(viewSpacePosition, InverseViewMatrix)

I wasn't using this initially. I was using something much simpler that turned out to be almost right, without even knowing the math. It was only after chatting with my colleagues that I realized that my method could have been wrong. And so, I decided to implement the above method (mathematically correct) and compare it to my supposedly incorrect method.

Here's what I did initially (WRONG WAY !!):
  1. Use texture coords to read depth sample
  2. Convert texture coords [0-1] to clip space [-1 1]
  3. Use Inverse View Projection Matrix to convert float3(clipSpace.xy, depthSample) to someSpace
  4. Convert from someSpace to worldSpace by dividing each component by w !!!!!!
I somehow hacked away and found that this worked for me. I figured that we multiply WVP matrix with position and then divide by w. If we go backwards and use Inverse WVP, we get some position in some space, and bring it back by dividing by that position's w (feels like by doing this we undo the initial divide by w). I could be totally wrong on the explanation, but the results look pretty similar. I tried a test where both methods calculates their own world space positions and render the difference as the result. By doing that, i really got the picture of why my method was wrong, even though it looked "correct" when used for lighting. Here's an example of the error. It changes based on position/angle.

So, if you're implementing the world space reconstruction from Depth, please use the method suggested on the Top rather than the one at the bottom.

No comments: