我的 HLSL 深度着色器有什么问题?

What's wrong with my HLSL depth shader?

我正在尝试在 XNA 4.0 中渲染深度纹理。我读了好几遍不同的教程,但真的无法理解我做错了什么。

深度着色器:

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();
    }
}

绘图:

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
}

结果:

如我所见,output.Position.z 总是等于 output.Position.w,但为什么呢?

有几个可能有用的深度定义。这是其中的一些。

最简单的是相机中的 z 坐标 space(即在应用世界和视图变换之后)。它通常与世界坐标系具有相同的单位,并且是线性的。但是,它总是平行于观察方向进行测量。这意味着移动 left/right 和 up/down 不会改变距离,因为您处于同一平面(平行于 znear/zfar 裁剪平面)。一个细微的变化是 z/far,它只是将值缩放到 [0, 1] 区间。

如果需要实际距离(在欧几里德度量中),您必须在着色器中计算它们。如果你只需要粗略的值,顶点着色器就足够了。如果值应该准确,请在像素着色器中执行此操作。基本上,您需要在应用世界和视图变换后计算位置向量的长度。单位等于世界 space 单位。

深度缓冲深度是非线性的并且针对深度缓冲进行了优化。这是投影变换(以及随后的 w-clip)返回的深度。近裁剪平面映射到深度 0,远裁剪平面映射到深度 1。如果沿视图方向更改非常近的像素的位置,则深度值的变化比远像素的变化大得多同样移动。这是因为近像素处的显示错误(由于浮点不精确)比远像素处的显示错误更明显。这个深度也是平行于观察方向测量的。