Skip to content

Volume Object

The volume object is aimed at smoke simulation, procedural volume generation with modifiers, and importing OpenVDB files from other applications.

Data Structures

  • Object can be of type OB_VOLUME, and then uses a Volume datablock as object data.
  • Volume is datablock that contains a list of volume grids. These grids may either come from a file, or be procedurally generated by a modifier.
  • VolumeGrid contains one OpenVDB grid. For details on this data structure, see the OpenVDB documentation.

OpenVDB grids consist of:

  • Metadata: name and other fields.
  • Transform: linear (or perspective) transform from voxel index to object space.
  • Tree: a tree data structure of nodes, with leaf nodes that contain a grid of 8x8x8 voxels.

File Loading

Volume datablocks have a filepath, and if set the volume will be loaded from that file. The list of grids and their contents is loaded on-demand, on modifier evaluation, drawing, user interface or when Python code needs it.

  • BKE_volume_load() loads the list of grids and their name, metadata and transform.
  • BKE_volume_grid_load() loads the grid tree and voxels into memory.

To share grid memory between original and CoW datablocks, and between volume datablocks using the same filepath, the grid memory is managed by a global volume file cache. Only grids loaded from file are in this cache, procedurally generated grids are not. When the number of users of a grid in the cache drops to zero, it is immediately freed.

Different volume datablocks may need either only the list of grids and their metadata, or the actual grid tree. This is accounted for in the global volume cache, with a distinction between metadata users and tree users. If the number of tree users drops to zero, the grid tree is freed while its metadata may still remain in memory for metadata users.

Modifiers and Nodes

Volume modifiers and nodes may take a Volume as input and output a new Volume. The input volume should be considered read-only. The modifier or node can either create a new Volume with no grids, or make a copy of an existing Volume for editing.

To access the actual OpenVDB grid and its contents, one of the following functions must be used:

  • BKE_volume_grid_openvdb_for_metadata(): for reading metadata and transform only.
  • BKE_volume_grid_openvdb_for_read(): for reading only, ensures grid tree is loaded into memory.
  • BKE_volume_grid_openvdb_for_write(): for read and write, ensures grid tree is loaded into memory and writable.

Volume datablocks use a copy-on-write system. When a copy of a volume datablock is made, both the original and new datablock will reference the same grid in memory. Only when calling BKE_volume_grid_openvdb_for_write() will a deep copy of the grid be made.

Together with the file cache, the copy-on-write system helps to ensure that the same grid is not duplicated in memory, even when the dependency graph and modifier stack make copies of volume datablocks.

Modifiers and nodes must be implemented in C++, since OpenVDB grids are a C++ data structure.

Rendering

In Cycles, the OpenVDB data structure is converted to NanoVDB, which is then used for rendering. A bounding mesh is built to determine when a ray is inside or outside a volume, and the NanoVDB tree is used for sampling.

Eevee and workbench render volumes as a dense 3D texture. This is to be optimized.

Grid Names

Modifiers, nodes and shaders expect the following grid names by default, which matches conventions in some other apps:

  • density: volume density
  • temperature: for fire simulations, temperature with unit Kelvin/1000
  • velocity: for motion blur, 3D velocity vector