I'm trying to render depth texture in XNA 4.0. I'm read few different tutorials several times and realy cannot understand what I'm doing wrong.
Depth shader:
float4x4 WVPMatrix;
struct VertexShaderOutput
{
float4 Position : position0;
float Depth : texcoord0;
};
VertexShaderOutput VertexShader1(float4 pPosition : position0)
{
VertexShaderOutput output;
output.Position = mul(pPosition, WVPMatrix);
output.Depth.x = 1 - (output.Position.z / output.Position.w);
return output;
}
float4 PixelShader1(VertexShaderOutput pOutput) : color0
{
return float4(pOutput.Depth.x, 0, 0, 1);
}
technique Technique1
{
pass Pass1
{
AlphaBlendEnable = false;
ZEnable = true;
ZWriteEnable = true;
VertexShader = compile vs_2_0 VertexShader1();
PixelShader = compile ps_2_0 PixelShader1();
}
}
Drawing:
this.depthRenderTarget = new RenderTarget2D(
this.graphicsDevice,
this.graphicsDevice.PresentationParameters.BackBufferWidth,
this.graphicsDevice.PresentationParameters.BackBufferHeight);
...
public void Draw(GameTime pGameTime, Camera pCamera, Effect pDepthEffect, Effect pOpaqueEffect, Effect pNotOpaqueEffect)
{
this.graphicsDevice.SetRenderTarget(this.depthRenderTarget);
this.graphicsDevice.Clear(Color.CornflowerBlue);
this.DrawChunksDepth(pGameTime, pCamera, pDepthEffect);
this.graphicsDevice.SetRenderTarget(null);
this.spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Opaque, SamplerState.PointClamp, null, null);
this.spriteBatch.Draw(this.depthRenderTarget, Vector2.Zero, Color.White);
this.spriteBatch.End();
}
private void DrawChunksDepth(GameTime pGameTime, Camera pCamera, Effect pDepthEffect)
{
// ...
this.graphicsDevice.RasterizerState = RasterizerState.CullClockwise;
this.graphicsDevice.DepthStencilState = DepthStencilState.Default;
// draw mesh with pDepthEffect
}
As I see output.Position.z always equals output.Position.w, but why?
There are several depth definitions that might be useful. Here are some of them.
The easiest is the z-coordinate in camera space (i.e. after applying world and view transform). It usually has the same units as the world coordinate system and it is linear. However, it is always measured in parallel to the view direction. This means that moving left/right and up/down does not change the distance because you stay at the same plane (parallel to the znear/zfar clipping planes). A slight variation is z/far
which just scales the values to the [0, 1] interval.
If real distances (in the Euclidean metric) are needed, you have to calculate them in the shader. If you just need coarse values, the vertex shader is enough. If the values should be accurate, do this in the pixel shader. Basically, you need to calculate the length of the position vector after applying world and view transforms. Units are equal to world space units.
Depth buffer depth is non-linear and optimized for depth buffering. This is the depth that is returned by the projection transform (and following w-clip). The near clipping plane is mapped to a depth of 0, the far clipping plane to a depth of 1. If you change the location of a very near pixel along the view direction, the depth value changes a lot more than a far pixel that is moved equally. This is because display errors (due to floating point imprecision) at near pixels are a lot more visible than at far pixels. This depth is also measured in parallel to the view direction.