Volumetric Fog Rendering with DirectCompute 3D Textures
This article explores how modern volumetric fog techniques utilize 3D textures within DirectX DirectCompute to achieve physically-based atmospheric lighting. By discretizing the camera frustum into a three-dimensional grid of voxels—often called "froxels"—graphics pipelines can decouple fog complexity from geometry density and screen resolution. The following sections break down how DirectCompute allocates these 3D textures, injects lighting and medium properties, performs parallel accumulation, and blends the final atmospheric volume into the scene.
Frustum-Aligned 3D Textures (Froxels)
Rather than storing volumetric data in world-space grids, DirectCompute implementations typically allocate a 3D texture aligned directly to the view frustum.
- X and Y Dimensions: Mapped linearly across the screen's horizontal and vertical coordinates, often at a reduced fraction of the display resolution (e.g., \(160 \times 90\)).
- Z Dimension: Mapped non-linearly to depth. Because perspective projection compresses distance, depth slices are distributed exponentially or logarithmically. This grants high precision near the camera where visual details matter most, while coarser steps cover distant scenery.
In DirectX, these textures are initialized as Texture3D
resources and bound to compute pipelines as Unordered Access Views
(UAVs) using RWTexture3D<float4>.
Step 1: Density and Media Property Injection
The first compute pass determines the physical attributes of the participating medium at each voxel location:
- A compute shader maps its 3D dispatch thread ID
(
SV_DispatchThreadID) to the corresponding froxel coordinate \((X, Y, Z)\). - The shader converts the grid index into view-space coordinates using inverse projection transforms.
- Media properties, such as uniform fog density, height-based fog falloff, or 3D animated noise (e.g., Perlin-Worley noise), are evaluated.
- The output is written to a 3D texture containing absorption and scattering coefficients, often accompanied by phase function parameters (such as the Henyey-Greenstein asymmetry factor \(g\)).
Step 2: Light Scattering Evaluation
With medium properties established, a subsequent DirectCompute pass evaluates incoming radiance from scene light sources:
- Shadow Sampling: For directional, point, and spot lights, the compute shader transforms the froxel's position into shadow map spaces to determine visibility.
- Phase Function Application: The shader computes the angle between the camera view vector and the incident light direction, applying the phase function to determine how much light scatters forward toward the viewer.
- In-Scattering Storage: The accumulated in-scattered
light multiplied by the scattering coefficient is written to an
intermediate
RWTexture3D<float4>, where the RGB channels store scattered radiance and the Alpha channel typically stores extinction.
Step 3: Ray Marching and Accumulation via DirectCompute
Once each froxel contains raw scattering and extinction values, the volume must be integrated from the near plane to the far plane along each view ray:
- Thread Allocation: Compute shaders assign thread groups where each thread processes a single \((X, Y)\) froxel column and iterates sequentially through the \(Z\) slices, or they utilize parallel scan algorithms in group shared memory (LDS).
- Numerical Integration: The shader marches through depth, accumulating in-scattered light while tracking optical thickness (transmittance) using Beer-Lambert's law: \[\text{Transmittance}(d) = e^{-\sigma_t \cdot \Delta d}\]
- Output: The final accumulated radiance and total
transmittance are written to a final integration
Texture3D. DirectCompute makes this pass efficient due to fast cache reuse when marching along continuous memory layouts.
Step 4: Final Scene Integration
The final step incorporates the volumetric fog into the fully rendered opaque and transparent scene buffers:
- During a fullscreen pass or post-processing compute pass, the shader reads the scene's depth buffer.
- The linear scene depth and screen UVs are converted into the corresponding 3D texture UVW coordinate.
- Hardware trilinear filtering natively interpolates between the discrete froxel cells, eliminating stepping artifacts without the need for expensive per-pixel ray marching.
- The sampled in-scattering is added to the scene color, while the sampled transmittance scales the background scene luminance.