我的 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。如果沿视图方向更改非常近的像素的位置,则深度值的变化比远像素的变化大得多同样移动。这是因为近像素处的显示错误(由于浮点不精确)比远像素处的显示错误更明显。这个深度也是平行于观察方向测量的。
我正在尝试在 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。如果沿视图方向更改非常近的像素的位置,则深度值的变化比远像素的变化大得多同样移动。这是因为近像素处的显示错误(由于浮点不精确)比远像素处的显示错误更明显。这个深度也是平行于观察方向测量的。