Volumetric textures

When I was researching possible propulsion methods for what later became the Ion Pulse engine I found ion thrusters. I really loved the look and I wanted to recreate the volumetric effect as closely as possible. I remembered that in the “Infiltrator” demo they used GPU particles to display a volumetric explosion and decided to do something similar.

Volumetric ion thruster effect Volumetric textures Volumetric textures IonPulseTurntable

First I made a two simple shapes in Modo (an outer ring and an inner beam) and set up volumetric rendering for them. While tweaking the material values and the mesh they were colored white to clearly see everything. However when I was happy with the look and feel they became red and green so I could tweak their colors individually later in the Unreal material.

The rendering of the volume Volumetric textures Volumetric textures VolumeRenderShape 300x201

The next step was rendering a slice of the volumetric mesh. Modo’s render boolean made it easy: Two boxes with a small gap between them is subtracted from the volumetric mesh. The boxes are animated, they scan the volume in 32 frames. Those frames are rendered in 64×64 resolution and collected in a single 2048×64 image.

The volumetric models in Modo Volumetric textures Volumetric textures VolumeRenderBoolean 300x100
How the slices are rendered Volumetric textures Volumetric textures VolumeRenderAnim

In Unreal that texture feeds the material displaying the volume. The core of the texture lookup is simple: X and Z coordinates correspond to texels within a tile from the atlas texture while Y values pick the tile.

This logic is implemented as a material function because it’s also used with decal based fake ambient occlusion (see bellow).


Dimensions: The desired dimensions of the volume.

Adjusted World Position: The transformed world coordinates of the pixel. There are two parameters, Center and Rotation, which are used to calculate these final coordinates. Those two parameters are updated every frame in the material instance, so they are in sync with the carrier medium (particle system component, decal projector, etc).

Tile Count: The number of tiles in the volume texture.


UV1/UV2: The UVs of the two neighboring tiles in the atlas which affect the final color of the pixel.

Blend Mask: The alpha to blend between the two tiles.

Volume Mask: Helps to limit the effect to the given volume. It’s necessary because the polygons carrying this material might be partially outside that volume. (Like GPU particle quads near the edge.)

The VolumeProjection material node Volumetric textures Volumetric textures VolumeProjectionNode 300x160

The material also has optional 3D noise which disturbs the world coordinates. Right now it’s only used in the material for the jet engine flame to make it look more fierce.

The jet engine's flame Volumetric textures Volumetric textures JetFlameTurntable

The last step was creating the GPU particle system and tweaking the particle count and quad size. ~400 particles were enough to adequately cover the 6x6x12cm volume so the main worry here is overdraw. Good thing that the volumes never get too big on screen to really impact performance, at least on my machine.

Heavy overdraw Volumetric textures Volumetric textures Overdraw 300x204

The other main use of volume textures is the fake decal based medium range ambient occlusion for the hero drone. The effect is easy to see on this test level:

Decal based fake ambient occlusion Volumetric textures Volumetric textures DecalAO

The actor carries a decal component with a material displaying a volume texture. The whole idea is pretty similar to what I described above, the biggest difference is the way the atlas texture is rendered.

In Modo the mesh I want to calculate the occlusion of is imported as a reference along with two sets of 64 planes around it. In one group the planes face up and in the other they face down. The reason for this is that I don’t want the planes to “see” each other, only the subject mesh in the middle, so the occlusion material layer works as expected: darkens the surface depending how close the drone is. The group where the planes face down can capture the occlusion volume above the drone, the other group is for the bottom area. The planes are arranged in their UV set next to each other so when baked the results are two atlas textures:

Volumetric textures Volumetric textures Up A 300x5
Volumetric textures Volumetric textures Up B 300x5
The planes for baking AO Volumetric textures Volumetric textures AOBakingPlanes 300x296

I made a simple Filter Forge filter which combines these and also allows quick tweaking of the gamma while factoring in tile distance: The correction is the strongest at the center tiles and gets weaker outward.


The 4096×64 texture stores a volume of 64x64x64 voxels which is enough for AO. To make the low resolution and compression artifacts less visible a screen space noise is used to scatter the samples a bit. The temporal anti-aliasing doesn’t just smooth the result but also mixes it with the color of the underlying surface which is great since modulated decals don’t work on lightmapped surfaces so I had to use a plain translucent decal mode.