3D texture format#

This section describes the 3D texture format used by Gaia Sky to represent volumes.

A volume is a raw dump of a voxel grid, optionally gzip-compressed, together with an optional plain-text sidecar that declares the grid resolution and the box the grid occupies in world space. Gaia Sky binds the voxels as a 3D texture, so any 3D tool that can write an RGBA8 grid can produce one. Volumes are loaded by the VolumeTextureLoader.

File pair#

A volume is two files that share a base name:

<base>_rgba.raw.gz     the voxels
<base>_metadata.txt    the grid resolution and the world-space box

The voxel file is gzipped if its extension is .gz, and read as-is otherwise. The recognised suffixes, which the loader strips to recover <base>, are _density, _color and _rgba, each followed by .raw or .raw.gz. The same suffix also terminates the metadata file name.

Voxel format#

Voxels are stored as unsigned bytes, contiguously, without padding or a header. The number of bytes per voxel selects the format:

Suffix

Bytes/voxel

GL format

Density byte

_density

1

GL_R8

0 (.r)

_color

3

GL_RGB8

none

_rgba

4

GL_RGBA8

3 (.a)

The decision is taken from the file length, not from the suffix: the byte count divided by the number of voxels must be 1, 3 or 4.

Each voxel is written in the order R, G, B, A. In a colour+density volume the alpha channel carries the density, which is the quantity the renderer ray-marches and integrates. The galaxy volumes use the alpha channel as brightness and the RGB channels as the chromaticity of the light, so the emitted radiance is the product RGB * A. An occlusive volume (dust) uses the same layout, with A holding the normalised density.

Voxels are written in C order, (W, H, D), so the third axis varies fastest: the row of D voxels along that axis is contiguous, and the whole file is the concatenation of the W * H such rows.

Metadata file#

The sidecar is plain text, one key: value per line. Lines without a colon are ignored, so # comments and free-form extra keys are harmless. Only two keys are read:

  • Grid size: — the grid resolution. Either a single edge, which means a cube (400), or three extents separated by x, * or a comma (400x400x40, 400 * 400 * 40, 400, 400, 40).

  • Volume bounds: (or World bounds:) — the box in world space, as [x0, y0, z0] to [x1, y1, z1]. It is the box the shader intersects, and the texture coordinate of a point is its position normalised over it, per axis.

A typical file:

# Generated by particles-to-volume.py
Volume: emissive
Grid size: 399x40x400
Galactic grid (x, y, z): 399x400x40
World size: [0.998040, 0.099562, 1.000000]
Volume bounds: [-0.473301, -0.048570, -0.475536] to [0.477213, 0.046252, 0.476845]
Voxel size: 0.00250000 (cubic; the largest box side is 1, in units of the object's size)

Data bounds: is the extent of the source particles rather than of the box, and is ignored by the loader. The Galactic grid and World size lines above are informational.

Note

The grid and the bounds are in the file’s own (W, H, D) order, i.e. the order the voxels are written in. The loader reverses the three extents and the three bounds components together when it hands them to OpenGL, so a file is described entirely in the axis order it is dumped in. This only matters for a grid that is not a cube. Declaring Grid size: is what makes a non-cubic grid possible; without it, the resolution can only be inferred as a cube.

When the sidecar is missing or declares no grid, the resolution is inferred from the file length as a cube: n voxels per side for a density volume, n for a colour volume (3 * n^3 bytes) and n for a colour+density one (4 * n^3 bytes). The box defaults to the unit cube centred on the origin, [-0.5, -0.5, -0.5] to [0.5, 0.5, 0.5].

Loading#

On load, Gaia Sky crops the grid to the range of voxels that hold any density, plus one voxel of padding, and builds a brick occupancy grid from it, both of which speed up the ray march. Neither changes the format, and the cropped grid is mapped onto the same world-space box, so writing the empty shell in the file costs file size and load time only.

The galaxy volumes shipped with Gaia Sky are produced from particle data by particles-to-volume.py, in the core repository under scripts/other/volumes/.