Shadow mapping is a technique used in 3D computer graphics to add shadows to a scene. It involves rendering the scene from the perspective of the light source to create a depth map, which is then used to determine which areas of the scene are in shadow when rendering from the camera's perspective.
Key Concepts
- Depth Map: A texture that stores the depth information from the light's perspective.
- Light Space Transformation: Transforming world coordinates to the light's view space.
- Shadow Map Sampling: Comparing the depth of a fragment to the depth stored in the shadow map to determine if it is in shadow.
Steps to Implement Shadow Mapping
- Render the Scene from the Light's Perspective
First, you need to render the scene from the light's point of view to create the depth map.
// Set up the light's view and projection matrices XMMATRIX lightViewMatrix = XMMatrixLookAtLH(lightPosition, lightTarget, upVector); XMMATRIX lightProjectionMatrix = XMMatrixOrthographicLH(lightWidth, lightHeight, nearPlane, farPlane); // Render the scene to the depth texture RenderSceneToDepthTexture(lightViewMatrix, lightProjectionMatrix);
- Create and Bind the Depth Texture
Create a texture to store the depth information and bind it as the render target.
// Create the depth texture D3D11_TEXTURE2D_DESC depthTextureDesc = {}; depthTextureDesc.Width = textureWidth; depthTextureDesc.Height = textureHeight; depthTextureDesc.MipLevels = 1; depthTextureDesc.ArraySize = 1; depthTextureDesc.Format = DXGI_FORMAT_R32_TYPELESS; depthTextureDesc.SampleDesc.Count = 1; depthTextureDesc.Usage = D3D11_USAGE_DEFAULT; depthTextureDesc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE; ID3D11Texture2D* depthTexture; device->CreateTexture2D(&depthTextureDesc, nullptr, &depthTexture); // Create the depth stencil view D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc = {}; dsvDesc.Format = DXGI_FORMAT_D32_FLOAT; dsvDesc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; dsvDesc.Texture2D.MipSlice = 0; ID3D11DepthStencilView* depthStencilView; device->CreateDepthStencilView(depthTexture, &dsvDesc, &depthStencilView); // Bind the depth stencil view context->OMSetRenderTargets(0, nullptr, depthStencilView);
- Render the Scene from the Camera's Perspective
Render the scene from the camera's perspective, using the depth map to determine shadows.
// Set up the camera's view and projection matrices XMMATRIX cameraViewMatrix = XMMatrixLookAtLH(cameraPosition, cameraTarget, upVector); XMMATRIX cameraProjectionMatrix = XMMatrixPerspectiveFovLH(fov, aspectRatio, nearPlane, farPlane); // Render the scene with shadow mapping RenderSceneWithShadows(cameraViewMatrix, cameraProjectionMatrix, lightViewMatrix, lightProjectionMatrix, depthTexture);
- Sample the Shadow Map in the Shader
In the pixel shader, sample the shadow map to determine if a fragment is in shadow.
Texture2D<float> shadowMap : register(t0); SamplerState shadowSampler : register(s0); float4 mainPS(VertexOut input) : SV_Target { // Transform the fragment position to light space float4 lightSpacePos = mul(input.worldPos, lightViewProjectionMatrix); lightSpacePos /= lightSpacePos.w; // Sample the shadow map float shadowDepth = shadowMap.Sample(shadowSampler, lightSpacePos.xy).r; // Compare the fragment depth with the shadow map depth float shadowFactor = (lightSpacePos.z > shadowDepth + shadowBias) ? 0.0f : 1.0f; // Calculate the final color float4 color = input.color * shadowFactor; return color; }
Practical Exercise
Exercise: Implement Basic Shadow Mapping
- Setup: Create a DirectX project and set up the necessary shaders and resources.
- Depth Map Creation: Implement the code to render the scene from the light's perspective and create a depth map.
- Shadow Map Sampling: Modify the pixel shader to sample the shadow map and apply shadows to the scene.
Solution
- Setup: Follow the steps outlined in the "Setting Up the Development Environment" section of Module 1.
- Depth Map Creation: Use the provided code snippets to create and bind the depth texture, and render the scene from the light's perspective.
- Shadow Map Sampling: Implement the provided HLSL code in your pixel shader to sample the shadow map and apply shadows.
Common Mistakes and Tips
- Depth Precision: Ensure that the depth texture format and depth comparison in the shader are precise enough to avoid shadow artifacts.
- Shadow Acne: Use a small bias when comparing depths to prevent shadow acne, where surfaces incorrectly self-shadow.
- Performance: Optimize the shadow map resolution and sampling to balance quality and performance.
Conclusion
Shadow mapping is a powerful technique for adding realistic shadows to a 3D scene. By understanding and implementing the steps to create and use a depth map, you can enhance the visual quality of your DirectX applications. In the next module, we will explore more advanced rendering techniques to further improve your graphics programming skills.
DirectX Programming Course
Module 1: Introduction to DirectX
- What is DirectX?
- Setting Up the Development Environment
- Understanding the DirectX API
- Creating Your First DirectX Application
Module 2: Direct3D Basics
Module 3: Working with Shaders
Module 4: Advanced Rendering Techniques
Module 5: 3D Models and Animation
Module 6: Performance Optimization
- Profiling and Debugging
- Optimizing Rendering Performance
- Memory Management
- Multithreading in DirectX