Volumes: smoke, fire and liquids

A volume is a box of voxels rather than a surface: a smoke plume, a flame, an explosion, water. Pixor reads .vdb files from Blender, EmberGen and Houdini, and has a few of its own for when you have no simulation tool.

A volume has no geometry. It is drawn by walking a ray through it for every pixel, on the processor, and what comes out goes into the same picture the model does — so a flame sits in front of a hand, a character stands inside smoke, and the bands, lines and cleanup treat it exactly as they treat anything else.

Adding one

In the Scene step, the Outliner’s + menu, under A volume:

Puff a round puff of smoke
Flame hot and narrow below, cool and wide above
Cloud wide, flat underneath, lumpy on top
Mist thin, low and even
From a .vdb file… a simulation from Blender, EmberGen or Houdini

A volume is a Volume node of the Scene graph, and its row in the Outliner is the same node: a Row after it is a row of volumes, each placed by its copy, and one wired into nothing is not in the scene. Its settings are on the node too (pxr project scene game.pixor --set flame.voxels=48, by the volume’s name).

On the command line: --volume flame:0.5,0.8,0.5:0,0.55,0, where the numbers are the size and then the position, in metres. A path to a .vdb works in the same place: --volume smoke.vdb:1.2.

Carried by a bone, a volume goes where the bone goes in every clip — a torch’s flame in a hand — and stays upright, as fire does; its position is then measured from the bone. On the command line, @BONE at the end: --volume flame:0.25:0,0.05,0@hand.r.

Carried by an effect’s emitter, a volume is part of that effect: it is drawn on the effect’s own rows, from the effect’s first frame, wherever the emitter is — on its bone, or where a clip’s keys move it — and on no other row. The smoke of an explosion, the fire of a spell. Its position is then measured from the emitter. On the command line, @effect:N for the project’s Nth effect: --effect explosion --volume puff:1.5@effect:1.

How it is drawn

These are under How it is drawn in the volume’s Inspector.

  • Thickness is how much volume a pixel needs before it is drawn at all. Higher gives a tighter, harder shape; lower a wispier one.
  • Soft edge is how wide the fraying edge is. The edge is dithered away in an ordered pattern anchored to the volume itself, never blended, so every pixel stays on the palette and the pattern does not crawl as the volume turns.
  • Self-shadow is how much the volume darkens where the light has to come through it.
  • Colour is the volume’s own, and its ramp is built from it. Hot colour is the colour of the part that burns; setting it to the same as the colour means nothing burns, which is what smoke wants. Glows over says how hot a part has to be before it takes the hot colour whatever the light.
  • Voxels is how finely the volume is cut up along its longest side. A sprite pixel wants one or two voxels: more only shimmers, because a different voxel wins the pixel every frame. A 256³ simulation is cut down to this on load, which is why a plume at 32 px stays still.
  • Samples is how many points on a ray are looked at per voxel. Two is enough at sprite sizes; more is smoother and slower.

Volumes take no glint: a cloud has none, and the density they are shaded from is bumpy enough to sparkle if they were allowed one.

Sequences: making it move

One .vdb is one still volume. A simulation writes a file a frame — fire_0001.vdb, fire_0002.vdb, and so on — and pointing Pixor at the first of them plays the rest.

In the volume’s Sequence section:

Frames how many files to play, counting on from the one you opened
Every take one file in N: a simulation is usually written at more frames than a sprite wants
Close the loop over cross-fade this many frames from the end back into the start, so the sprite plays round and round without a jump. Those files become the fade and do not play: five files with a fade of one are four frames

On the command line the sequence follows the size and the place: --volume fire_0001.vdb:1.5::24,2,4 — 24 files, every second one, the last four faded back into the first.

A sequence plays along with whatever motion the sheet already has: put a fire beside a walk cycle and the fire’s frames are the walk’s frames. When there is nothing else moving, the sequence is the motion and gets an action of its own, called volume.

Every frame is placed by the same transform, taken from the box the whole sequence fills, so a plume that grows does not shrink the picture back each frame. That also means Size is the size of the whole sequence, not of its first frame.

A sequence is never in memory whole: each file is read once to find that box, and then again when the sheet or the preview reaches its frame, with the last three kept. A long simulation costs time to read, not memory.

What Pixor reads

.vdb files holding float or vector grids in the usual tree, with values stored plainly, zipped, or packed with Blosc-LZ4 (split into a stream per byte or not, as Blosc 1.14 and later write it), all of them or only the active ones — which is what Blender, EmberGen, Houdini and OpenVDB’s own tools write. Pixelates for a grid named density, smoke, fog or d, then temperature, flame, heat or fire for the hot part, then velocity for the motion the anti-flicker pass uses. A liquid’s surface (a level set) is read as a thickness with a hard edge, solid all the way through.

Anything Pixor cannot read says so by name rather than coming out wrong: another compressor, another kind of tree, a sheared transform. pxr inspect fire.vdb prints a file’s grids, their size and their voxel size.

What is not there yet

  • Liquids are read and drawn as a volume, with no foam and no reflection. A sheet of water with both is the world’s Water node (Worlds).

What this page describes is in the full version; the free browser demo keeps to the default look and the PNG sheet.

Try in browser Get Pixor