This Houdini procedural explosion VFX setup is built as a deterministic, layer-based system designed for simulation accuracy and export flexibility.
What I find genuinely interesting in VFX work is not any single effect — it's the patterns underneath. Most effects, when you look closely enough, share the same structural logic. Find that logic, build a system around it, and you get something reusable, predictable, and fast to iterate.
This breakdown is about that process — using a procedural explosion as the example. Not because explosions are unique, but because they're a good stress test: clear physical reference, multiple simultaneous layers, and they need to work across very different rendering contexts. Getting all of that into one coherent system requires thinking about architecture first and aesthetics second.
The techniques covered here — layered point animation, velocity field construction, Pyro sourcing, VAT export, Six Point Lighting — are not explosion-specific. The same approach applies to any effect where organic behavior and pipeline reusability both matter. One tool. Predictable behavior. Any engine.
The first thing I do before touching any node is establish ground truth — physically correct scene scale and a solid visual reference.
Scale matters more than most people realize. In Houdini's geometry context (SOPs), units are dimensionless — one unit can mean anything. But the moment you move into simulation (DOPs), everything changes. Pyro, RBD, and FLIP solvers operate on MKS units: meters, kilograms, seconds. Gravity, viscosity, density, buoyancy — all calculated against real physical values.
The industry standard is simple: 1 unit = 1 meter. Break that convention and your simulation misbehaves before you've changed a single solver parameter.
For reference, I used real footage of a 122mm high-explosive artillery shell detonation. Any VFX effect is a combination of static shape and animation. Break it into layers and you get: flame, smoke, debris, displaced ground, shockwave, crater. There's an impact, a detonation, an active expansion phase, and a decay.
| PHASE | TIMING | CHARACTER | LAYERS ACTIVE |
|---|---|---|---|
| Impact | F0 | Sharp collision, debris ejection | Debris, crater mask |
| Detonation | F0–F3 | Explosive pressure wave origin | Main burst, shockwave |
| Expansion | F3–F20 | Spherical outward growth | All layers + pump |
| Dissipation | F20+ | Slow rise, density decay | Curl, pump column |
These patterns — extracted from reference — became the architecture of the tool.
I built the system as two connected HDAs to separate geometry generation from heavy simulation data. This creates a clean, debuggable workflow.
At a high level, an explosion is smoke, fire, and fragments. Each layer is independent and operates on its own geometry, velocity field, and timing:
| LAYER | TYPE | GEOMETRY | FUNCTION |
|---|---|---|---|
| Layer 1 | Main Burst | Sphere scale (1,1,1) | Primary expansion — symmetric force origin |
| Layer 2 | Curl Noise | Sphere + curl field | Divergence-free rotation — organic billowing |
| Layer 3 | Shockwave | Flattened disc (large X/Z, small Y) | Horizontal atmospheric pressure wave |
| Layer 4 | Pump | Vertical column (small X/Z, large Y) | Rising impulse — mushroom column formation |
| Debris | Fragment | Scattered points per fragment | Geometry fragments with Curl trajectory + spin |
The graph stays closed — only essential parameters are exposed, grouped into a clean, intuitive interface. The Python auto-configuration means adding a new layer in HDA 1 automatically propagates the interface change to HDA 2.
Everything starts with two objects: a sphere defines the shape and scale of the explosion, and a point defines the direction. These are the two inputs to the first HDA. From there, the configuration is managed via three primary tabs.
This tab controls the base geometry from which velocity is calculated. A turbulent Perlin noise is applied to the sphere, creating the characteristic spikes radiating from the epicenter visible in the first frames of any real explosion.
The noise is masked through the dot product of the mesh normals: near the ground, the shape automatically smooths out. This is physically correct — the blast wave flattens as it meets surface resistance.
A separate parameter — Point Direction Interpolation (Bias) — interpolates between the original point position and the reference direction point (second input). At Bias = 0, the explosion is symmetric. Offset it and you get a directional blast matching the shell's angle of entry. No manual geometry editing required.
This tab controls how the explosion lives in time.
| PARAMETER | FUNCTION |
|---|---|
| Burst Duration | Total length of the burst phase in frames |
| Min Life / Max Life | Randomized per-point lifetime range — organic dissolution, no hard cutoff |
| Burst Time Remap | Curve remapping animation time progress — main tool for "character" of the blast |
| Death Rate | Separate decay curve, independent of duration — full temporal shape control |
This is where the core logic lives. The current setup has 4 layers, each independent.
| LAYER | SCALE | KEY SETUP |
|---|---|---|
| 1 — Main Burst | (1,1,1) | Two independent Jitter passes — large-scale spread + fine detail = two-level point distribution |
| 2 — Curl Noise | (1,1,1) | Divergence-free animated Curl Noise — only rotation in velocity field, no sources/sinks → organic billowing |
| 3 — Shockwave | Large X/Z, compressed Y, -90° rot | Sphere → flat disc → horizontal outward pressure wave |
| 4 — Pump | Small X/Z, extended Y | "Layer is Pump" ON + 2-frame Time Shift delay → vertical mushroom column impulse |
The debris section is separate from the main layers. Points scatter per fragment, additional vertical velocity is added, Curl Noise controls trajectory variation, and a Spinning Force range drives per-fragment random rotation.
No simulation has run yet. All of this is deterministic and procedural — recalculates in seconds when any parameter changes. HDA 1 output: point cloud layers with velocity and animation, pump layer, debris points with trails, and a crater mask.
The second HDA receives the data from the first. On initialization, a Python script reads the layer count and types from HDA 1 and auto-configures the interface — the designer gets a ready-made interface that matches whatever was set up upstream.
The system programmatically builds the DOP network, ensuring simulation setup is always in sync with geometry sourcing:
# =============================================================================
# DOP NODE CREATION — auto-builds Volume Source nodes from HDA 1 layer config
# =============================================================================
def create_dop_volume_source_nodes():
"""Creates and configures all Volume Source nodes in the DOP network."""
log("\n--- Updating DOP Volume Source Nodes ---")
node = hou.pwd()
try:
dop_network = node.node("sim_explosion")
if not dop_network:
log("DOP Network 'sim_explosion' not found.", "ERROR")
return
merge_node = dop_network.node("merge1")
switch_node = dop_network.node("switch1") # may be None
_configure_smoke_object(dop_network)
# Iterate DOP_CONFIGS — one entry per layer type (burst/debris/pump)
for config in DOP_CONFIGS:
config_func = globals()[config["config_func"]]
_create_or_update_dop_sources(
parent_node = node,
dop_network = dop_network,
merge_node = merge_node,
switch_node = switch_node,
count_parm_name= config["count_parm"],
source_prefix = config["source_prefix"],
null_prefix = config["null_prefix"],
config_func = config_func
)
dop_network.layoutChildren()
except Exception as e:
log(f"Error in create_dop_volume_source_nodes: {e}", "ERROR")
import traceback
traceback.print_exc()
Dynamic Synchronization: Change the layer count in HDA 1 — HDA 2 interface updates automatically on next cook. No manual DOP network editing required.
Per-layer noise settings for density and temperature — type, frequency, detail. This is where the character of the smoke is defined: how lumpy, how uniform, how the edges behave.
For the main burst layer: Alligator noise — it produces the characteristic bumpy structure close to how real smoke behaves at cloud boundaries. The Debris layer has a separate Velocity tab with optional Curl Noise for additional swirl along fragment trajectories.
Controls how the point cloud is converted into a VDB volume. Each layer has a Particle Scale — the influence radius of each point during rasterization.
Too large and you lose detail. Too small and you get discretization artifacts. The debris layer uses a small particle scale by design — fragments are point sources, leaving a minimal volumetric trail.
Pyro solver settings. Voxel Size balances detail against simulation time. Per-layer multipliers for Density, Temperature, and Velocity determine the weight of each layer in the final simulation. The shockwave and curl layers get a higher velocity multiplier to amplify their influence on smoke behavior.
Verification is critical in a procedural pipeline. Before locking the HDA logic, I ran iterative tests to validate velocity field behavior, layer blending, and rendering fidelity in Arnold.
Validating procedural noise distribution before rasterization. This test ensures the velocity vectors match the desired explosive expansion patterns — if they don't, fixing it here costs seconds, not hours of sim cache.
Testing the interaction between independent layers (Burst, Curl, Shockwave, Pump). The system must handle volume density blending at the boundaries to prevent clipping or artifacting.
Final validation. Using a 1 Camera AA pass, we verify that the VDB volumes generated by the simulation hold up to light absorption, scattering, and motion blur in the renderer.
After simulation, the output is exported in whatever format the pipeline requires. The engine doesn't matter because all the physics and shape were determined at the point animation stage — by the time you export, it's data, not logic.
| FORMAT | TARGET | USE CASE |
|---|---|---|
| VDB | Cinematic renderer | Karma, Arnold, Mantra — full volumetric rendering |
| Vector Fields | Game engine | Real-time particle simulation — drives GPU particles in UE/Unity |
| VAT | Game engine | Playback in Unity/Unreal without runtime simulation — flipbook-based |
| Mesh / Cache | Any | Static or animated geometry — debris, crater, displaced ground |
The same setup delivers cinematic renders and real-time game effects. The fundamentals are always the same. Only the output format changes.
Camera configuration is often overlooked but it directly affects both render time and export quality — and the setup is completely different depending on your target.
Getting the camera wrong at this stage means redoing the render. Lock it down before the simulation cache is written.
Motion Vector texture stores the per-pixel direction and magnitude of movement between frames. In a game engine, the shader uses this data to interpolate between flipbook frames at runtime — effectively faking in-between frames that were never rendered.
Without Motion Vectors, a flipbook plays as a hard cut between frames — stutter is clearly visible, especially at low frame counts. With Motion Vectors enabled, the engine blends between frames based on actual movement direction, so 16 or 24 rendered frames can look as smooth as 60. This directly reduces texture memory and render time without sacrificing perceived quality.
Motion Vector Influence: keep it low — typically between 0.0005 and 0.001. Too high and the interpolation overshoots, causing the effect to smear or swim. Find the minimum value that eliminates stutter and stop there.
Standard sprite or flipbook effects are lit as flat surfaces — they receive light from one direction and ignore everything else. This looks fine in a static scene but breaks the moment a dynamic light source moves, or the effect is placed near colored environment lighting.
Six Point Lighting solves this. During the Houdini render, six separate cameras capture the volume from six directions — Top, Left, Right, Bottom, Back, Front. The resulting two textures (TLR and BBF) store baked illumination for each voxel from all six axes.
| TEXTURE | CHANNELS | DIRECTIONS BAKED |
|---|---|---|
| TLR map | RGB | Top / Left / Right |
| BBF map | RGB | Bottom / Back / Front |
In-engine, the shader reconstructs approximate volumetric lighting by blending these six samples based on the current light direction. The explosion responds correctly to any light in the scene — a muzzle flash nearby, rotating sunlight, colored fill from an environment — without any volumetric rendering, without runtime simulation.
This is the key difference between a VFX asset that integrates into a scene and one that sits on top of it. Six Point Lighting is what makes a flipbook explosion feel like it belongs in the world rather than being composited over it.