1
votes

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
}

Result: enter image description here

As I see output.Position.z always equals output.Position.w, but why?

1
Your shader is almost fine. It's probably just that your znear and zfar clipping planes are so far apart that every pixel has a depth-value of almost 1. And you should perform the w-clip in the pixe shader. Otherwise, you won't get correct perspective interpolation.Nico Schertler
@NicoSchertler, I'm also thought about that, because far plan was set to 1000. But nothing changed when I put 100 or less. I passed far plane as parameter and replaced z / w on z / FarPlane. However, it is interesting in which case z / w can work? In fact it should work claim 3 or 4 tutorial that I read, including "Creating a Depth Texture - MSDN - Microsoft".user4632995
z / w is correct. The depth buffer uses a non-linear map, which is probably why you get almost 1 everywhere. Try to increase znear. Have you checked that it's not a problem with pixel output? I.e. have you tried outputting constant 0.5 or the like?Nico Schertler
@NicoSchertler, z / 100 or w / 100 give me correct and exactly same result. In my case z = w. Near plane was 0.001, but I also tried few greater values. I think, there should be some option that turn on correct behavior, or some like that. I'm don't now...user4632995
It all depends on what kind of depth you want. If you want a simple linear depth, leave the projection matrix away and use z/far of the result (vertexPosition * world * view).Nico Schertler

1 Answers

0
votes

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.