Jump to content
  • entries
    943
  • comments
    5,899
  • views
    924,322

An interesting fix


Josh

1,536 views

 Share

You may have noticed our deferred decals (presently in beta) tend to shift with distance. Something was wrong with the screen space to world space conversion, but it's a hard problem to describe. I wrote a function based on the work Igor did for his SSLR shader, like this:

vec4 ScreenPositionToWorldPosition(in vec2 texCoord)
{
   float x = (texCoord.s / buffersize.x - 0.5) * 2.0;
   float y = (texCoord.t / buffersize.y - 0.5) * 2.0;
   float z = texelFetch(texture5, ivec2(texCoord),gl_SampleID).r;
   vec4 posProj = vec4(x,y,z,1.0);
   vec4 posView = inverse(projectioncameramatrix) * posProj;
   posView /= posView.w;
   posView+=cameraposition;
   return posView;
}

 

OpenGL stores depth values in a non-linear manner, which yields more precision closer to the camera. This allows a 24-bit depth buffer to cover a much greater distance than a linear depth buffer would allow with acceptable visual fidelity. The problem with the above equation is that the exponential value isn't being converted back into a linear value before being multiplied by the inverse camera projection matrix.

 

I found a PDF with a formula I had come across before and started messing around with it: The trick is this:

lineardepth = exponentialdepth / 0.5 - 1.0

 

This equation can change if you have set glDepthRange to anything other than the defaults. (Coincidentally, calling glDepthRange() was what messed up my deferred renderer for iPad a couple years ago and forced me to use an extra floating-point buffer for storing screen depth at my GDC talk.)

 

Plugging this into my function and eliminating the camera position add results in the following code:

vec4 ScreenPositionToWorldPosition(in vec2 texCoord)
{
   float x = (texCoord.s / buffersize.x - 0.5) * 2.0;
   float y = (texCoord.t / buffersize.y - 0.5) * 2.0;
   float z = texelFetch(texture5, ivec2(texCoord),gl_SampleID).r;
   z = z / 0.5 - 1.0;
   vec4 posProj = vec4(x,y,z,1.0);
   vec4 posView = inverse(projectioncameramatrix) * posProj;
   posView /= posView.w;
   return posView;
}

 

This also eliminated a couple of strange divide by twos I had in the texture mapping, which didn't really make sense to me at the time. Below you can see a decal rendered on a surface from a far distance, with no inaccuracies in the texture alignment.

 

blogentry-1-0-94122900-1439710365_thumb.jpg

 

In reality, this was probably about five hours of work with "River Monsters" playing in the background.

  • Upvote 5
 Share

0 Comments


Recommended Comments

There are no comments to display.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...