Volumetric Smoke Grenade
TGA specialization — 7 weeks at 50% speed — DX11 / C++
view on github →Overview
For my specialization at The Game Assembly — 7 weeks at 50% pace (~4 hours/day) — I recreated Counter-Strike 2's volumetric smoke grenade in my own DX11 engine. The work is based on Acerola's video on the subject, with Valve's original implementation as the reference target. The goal was to understand and reproduce the technique from first principles, not just port code.
Final result — smoke propagating around level geometry, shaded by directional light.
Voxel Grid
The level geometry is baked into a uniform voxel grid via SAT intersection tests, run on a compute shader. Each cell is flagged occupied or empty; propagation and raymarching both query this structure to avoid geometry. Density is sampled using manual trilinear interpolation across the 8 surrounding voxel corners to smooth out the discrete grid.
The voxel grid overlaid on the scene.
Mesh voxelization process.
The scene represented as solid and empty voxels.
Propagation
Smoke expansion is driven by a flood-fill algorithm running on a compute shader. When a smoke grenade lands, a seed voxel is set with an initial fuel value. Each tick, every voxel checks its neighbors for empty cells and fills them, while cells occupied by geometry are skipped. The fill rate is tied to an interpolating function, producing the organic “climbing” motion around obstacles.
Smoke propagating outward from its seed point, wrapping around obstacles.
Raymarching
Rays are cast from the camera at uniform step intervals, sampling density at each position. Ray origins are jittered per-pixel per-frame using Interleaved Gradient Noise to suppress temporal banding. Beer’s Law gives transmittance, refined via a Riemann sum over each sample’s density × step size. The result composites as an alpha mask into the scene.
The raymarch is rendered into a lower-resolution render target at a configurable scale factor — e.g. 1⁄4 resolution — then upscaled to fit the screen before compositing. This keeps raymarching cost proportional to the reduced pixel count rather than full screen resolution.
Scattering is computed with a selectable phase function — Henyey-Greenstein, Mie, or Rayleigh — switchable at runtime.
Density (transmittance alpha)
Albedo & shadow color
Composited alpha mask
Lighting & Shadows
Shadow transmittance is marched from each sample point toward the directional light using the same Beer’s Law approximation. Shadow transmittance × base transmittance × sun color gives the shaded albedo — no separate shadow pass. Output is tonemapped through ACES filmic and a Sony S-Gamut3 Cine color transform (school pipeline).
Worley Noise
A 3D Worley noise texture is generated on a compute shader and layered into the density field to break up the flood-fill's uniformity and add cloud-like detail.
Worley noise contributing density variation.
Results
Visual variations — different lighting and density settings.
Reflection
One of the most enjoyable things I’ve done at TGA. On the technical side: I got hands-on with compute shaders, deepened my understanding of DX11 resource management, and learned how voxels can collapse to a single uint array — just the minimum data you actually need. The biggest hardware lesson: I only profiled on school machines, which have solid GPUs. Running on weaker hardware earlier would have pushed me to optimize the raymarching sooner.
The project management side was unexpectedly harder than the technical side — setting a schedule, following it, and knowing when to adapt rather than push through.
Future Work
- Per-smoke fuel values instead of a single shared fuel-per-voxel
- Multiple simultaneous smoke instances
- Network synchronization (deterministic smoke across clients)
- Curl noise or bitangent noise for more realistic shape detail
- Bullet hole support via SDFs or decals
- Delta tracking as an alternative to raymarching
- Per-smoke grid instantiation to reduce memory footprint
- Loading mesh voxelization from file (required for larger scenes)
- Further artistic controls for appearance tuning
- Performance profiling on lower-end hardware