Category Archives: Cameras

Description

Position Reconstruction

There are many occasions when the fragment position in world space needs to be reconstructed from a texture holding the scene depth (depth texture). One example of use is in deferred rendering when trying to decrease memory usage by not saving the position but instead only the depth. This will result in one channel of data, instead of three channels needed when saving the whole position.

 

Viewspace scene depth

There are different ways to save the depth. The most popular are view space depth and screen space depth. Saving depth in view space instead of screen space gives two advantages. It’s faster, and it gives better precision because it’s linear in view space.

This is how screen space depth can be rendered in HLSL:

struct VS_OUTPUT
{
   float4 Pos: POSITION;
   float4 posInProjectedSpace: TEXCOORD0;
};
 
// vertex shader
VS_OUTPUT vs_main( float4 Pos: POSITION )
{
   VS_OUTPUT Out = (VS_OUTPUT) 0;
   Out.Pos = mul(Pos,matWorldViewProjection);
   Out.posInProjectedSpace = Out.Pos;
   return Out;
}
 
// pixel shader
float4 ps_main( VS_OUTPUT Input ) : COLOR
{
   float depth = Input.posInProjectedSpace.z / Input.posInProjectedSpace.w;
   return depth;
}

The HLSL pixel shader below shows how the position can be reconstructed from the depth map stored with the code above. Although this is one of the slowest ways of doing position reconstruction since it requires a matrix multiplication.

float4 ps_main(float2 vPos : VPOS;) : COLOR0
{
   float depth = tex2D(depthTexture,vPos*fInverseViewportDimensions + fInverseViewportDimensions*0.5).r;
 
   // scale it to -1..1 (screen coordinates)
   float2 projectedXY = vPos*fInverseViewportDimensions*2-1;
   projectedXY.y = -projectedXY.y;
 
   // create the position in screen space
   float4 pos = float4(projectedXY,depth,1);
 
   // transform position into world space by multiplication with the inverse view projection matrix
   pos = mul(pos,matViewProjectionInverse); 
 
   // make it homogeneous
   pos /= pos.w; // result will be (x,y,z,1) in world space
 
   return pos; // for now, just render it out
}

To reconstruct depth from view space, a ray from the camera position to the frustum far plane is needed. For a full screen quad, this ray can be precalculated for the four corners and passed to the shader. This is how the computer game Crysis did it [1] . But for arbitrary geometry, as needed in deferred rendering, the ray must be calculated in the shaders [2] .

[1] “Finding next gen: CryEngine 2″
http://ati.amd.com/developer/gdc/2007/mittring-finding_nextgen_cryengine2(siggraph07).pdf

[2] “Reconstructing Position From Depth, Continued”
http://mynameismjp.wordpress.com/2009/05/05/reconstructing-position-from-depth-continued/

Screen Space

The last step of transformations (world to screen) when rendering is to get the rendered objects to screen space. This is a 2D space with the origin in the middle of the screen. After the vertex shader but before the fragment shader the fragments will be normalized device coordinates and will automatically be transformed to screen space. The fragments x,y coordinates will be mapped to the x and y coordinates on the screen (the exact mapping is set by viewportbounds). And the z coordinates will be mapped to the depth buffer (z-buffer) as the following [-1] -> [near clip plane limit] and [1] -> [far clip plane limit].

The viewport mapping is the following [-1,1] in X is mapped to the left to right of the viewport range. The [-1,1] in Y is mapped to the bottom to top of the viewport range

Framents will automatically be transformed to screen space before the fragment shader, you just need to set the viewport mapping with the following OpenGL call.

glViewport(0, screenHeight/2, screenWidth/2, screenHeight/2);

You can access the current fragment depth in a fragment shader with the variable gl_FragDepth which in default is the same as gl_FragCoord.z. The screen coordinates can be accessed in the variable gl_FragCoord.xy.

Information about pixel shaders in DirectX (most is the same in OpenGL):
http://www.gamedev.net/columns/hardcore/dxshader3/

A quick reference guide to GLSL:
http://www.opengl.org/sdk/libs/OpenSceneGraph/glsl_quickref.pdf

Description of the mapping in DirectX:
http://msdn.microsoft.com/en-us/library/bb219690.aspx

Normalized Device Coordinates

After projection to clip space the points will no longer be normalized. This can be restored by dividing all components of the vertex by the fourth component (w). This will (together with the projection transformation) result in that the projection volume will map to a cube with minimum corner in (-1,-1,-1) and maximum corner in (1,1,1). For example in OpenGL, the near clip plane will map to -1 ( in DirectX it will be mapped to 0) and the far clip plane will map to 1.

The division by w (homogenization) is performed automatically between the vertex shader and the fragment shader in the procedure called Rasterization.