Creating a Depth Texture
Demonstrates how to create a texture that contains depth information for a scene using a render target and a custom effect.
To render a depth texture, also called a shadow map, create a RenderTarget2D and render your scene with an effect that saves pixel depth instead of pixel color.
The Complete Sample
The code in this topic shows you the technique. You can download a complete code sample for this topic, including full source code and any additional supporting files required by the sample.
Download DepthTexture_Sample.zip.
Creating a Depth Texture
To create a depth texture
Declare a render target variable using a RenderTarget2D.
RenderTarget2D shadowRenderTarget;
Create the render target.
shadowRenderTarget = new RenderTarget2D(GraphicsDevice, graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height);
Create a depth-stencil object using a DepthStencilState.
DepthStencilState depthStencilState = new DepthStencilState();
Set up depth testing to preserve depth data by setting the DepthBufferFunction to CompareFunction.LessEqual.
depthStencilState.DepthBufferFunction = CompareFunction.LessEqual; GraphicsDevice.DepthStencilState = depthStencilState;
Prepare to render the scene to your render target by calling GraphicsDevice.SetRenderTarget.
GraphicsDevice.SetRenderTarget(shadowRenderTarget);
In the Draw method, use a SpriteBatch to draw the depth texture.
// Render the depth texture Rectangle rect = new Rectangle( graphics.GraphicsDevice.Viewport.X, graphics.GraphicsDevice.Viewport.Y, graphics.GraphicsDevice.Viewport.Width, graphics.GraphicsDevice.Viewport.Height); spriteBatch.Begin(); spriteBatch.Draw(map, rect, Color.White); spriteBatch.End();
Reset the graphics device to draw to the back buffer by calling GraphicsDevice.SetRenderTarget.
GraphicsDevice.SetRenderTarget(null);
Rendering Depth in a Custom Effect
To render depth in a custom effect
Use a custom effect to render depth values in HLSL. Set the following effect states, and create a technique that will call a custom vertex shader and a custom pixel shader.
technique ShadowMapRender { pass P0 { CullMode = NONE; ZEnable = TRUE; ZWriteEnable = TRUE; AlphaBlendEnable = FALSE; VertexShader = compile vs_2_0 RenderShadowMapVS(); PixelShader = compile ps_2_0 RenderShadowMapPS(); } } technique ShadowMapRender { pass P0 { CullMode = NONE; ZEnable = TRUE; ZWriteEnable = TRUE; AlphaBlendEnable = FALSE; VertexShader = compile vs_2_0 RenderShadowMapVS(); PixelShader = compile ps_2_0 RenderShadowMapPS(); } }
The custom vertex shader generates position and depth values.
The vertex shader returns two values to the pixel shader. The first value is a POSITION that transforms the incoming POSITION into the view and projection space of the light source. The second value is the depth value of the transformed POSITION. The depth is calculated by dividing the z coordinate by the w coordinate to calculate depth between 0 and 1. The depth value is subtracted from 1 to get more precision using a floating point format. The depth is packed into a TEXCOORD semantic (in this case, TEXCOORD0).
struct VS_SHADOW_OUTPUT { float4 Position : POSITION; float Depth : TEXCOORD0; }; struct VS_SHADOW_OUTPUT { float4 Position : POSITION; float Depth : TEXCOORD0; }; VS_SHADOW_OUTPUT RenderShadowMapVS(float4 vPos: POSITION) { VS_SHADOW_OUTPUT Out; Out.Position = GetPositionFromLight(vPos); // Depth is Z/W. This is returned by the pixel shader. // Subtracting from 1 gives us more precision in floating point. Out.Depth.x = 1-(Out.Position.z/Out.Position.w); return Out; } VS_SHADOW_OUTPUT RenderShadowMapVS(float4 vPos: POSITION) { VS_SHADOW_OUTPUT Out; Out.Position = GetPositionFromLight(vPos); // Depth is Z/W. This is returned by the pixel shader. // Subtracting from 1 gives us more precision in floating point. Out.Depth.x = 1-(Out.Position.z/Out.Position.w); return Out; }
The custom pixel shader renders depth to the render target.
The pixel shader returns the depth of each pixel in the TEXCOORD0 semantic. The depth is returned in the red component. For a SurfaceFormat.Single, each pixel stores a 32-bit floating point value. The greater number of bits per pixel that you use, the smoother the shadow will be; this shader will work using almost any SurfaceFormat.
float4 RenderShadowMapPS( VS_SHADOW_OUTPUT In ) : COLOR { // The depth is Z divided by W. We return // this value entirely in a 32-bit red channel // using SurfaceFormat.Single. This preserves the // floating-point data for finer detail. return float4(In.Depth.x,0,0,1); } float4 RenderShadowMapPS( VS_SHADOW_OUTPUT In ) : COLOR { // The depth is Z divided by W. We return // this value entirely in a 32-bit red channel // using SurfaceFormat.Single. This preserves the // floating-point data for finer detail. return float4(In.Depth.x,0,0,1); }