Fire material

When I started working on the material in Unreal Engine 3 I had the following goals in mind:

  • Support for different scales, from a single flame to massive fires. On the long run it’s more efficient to maintain and learn to use one material instead of several single purpose ones.
  • High texture resolution with smooth animation. My target is usually 720p@60Hz and that ruled out flipbooks which don’t scale up well resolution and framerate wise.
  • Adjustable complexity, visual quality vs performance tweaking.

Fulfilling these core requirements was simple enough after some experimentation and I ended up spending most of the time on optimizing and making the controlling parameters easy to handle.

At the heart the material is nothing more than the blend of two panning, distorted textures, masked by another distorted image.

 

The emptiness in the middle mask is there so the distortion has room to “tear off” bits of the flame.

The fire texture and masks Fire material Fire material FireShowcase13 300x240

There is an option for factoring in object position when it comes to time sensitive functions, like panners. It’s just (ObjPos.X + ObjPos.Y + ObjPos.X + Time) which makes the same material instance look different at different places.

To keep the node network manageable I relied heavily on custom material functions. Min(), Max(), Sign() and similar easy to make nodes helped a lot. I also made several “Relay” nodes (RelayScalar, RelayVector2, etc)  which don’t do anything only guide links and prevent the crossing connection lines making a mess. (Like the stock “PassThrough” node but that only handles vector 3.)

I batched related scalar parameters into a vector one to decrease clutter in the material instance editor. For example in the vector 4 param called “FireTilingAndSpeed” R and G controls tiling while B and A adjusts speed.

The fire texture is grayscale which has two main advantage: it saves texture memory and the different operations in the material don’t distort the original colors.
The fire is colorized as the very last step, by a color gradient. This way the color adjustment is more intuitive and entirely decoupled from the rest of the fire parameters.

Colored flames Fire material Fire material FireShowcase14 300x242

The opacity mask is a distance field which allows flexible coverage and hardness adjustments. Flames don’t fade out in the classic sense (which looks LDR-ugly) but shrink (coverage decreases).
The fire texture is factored in to opacity to add detail to the very low resolution masks.

The opacity mask can use a second UV, which allows the use of pre-made mask textures instead of mesh specific ones. Since the mask texture is not directly visible the UVs can be quite distorted without degrading visuals.

Wall of flame model Fire material Fire material FireShowcase06 300x101

Since I wanted fine control over material complexity I made sure that the node network is modular. The rule is simple: every single static switch parameter makes the material more expensive and each one is disabled by default.

Vertex noise is implemented in a way that the top of the fire is displaced more than its base. I also made sure that the surface is never pushed inward to prevent it sinking into the burning object the fire envelops.

Some fires on the video have swaying enabled but the effect was toned down. The following is an extreme example where the material is on a simple quad.

The material has another version, to be used in particle systems. The core mechanics are the same but the features which make no sense on a sprite (like vertex displacement or fresnel fade) were removed. Certain parameters can be controlled through vertex color and dynamic parameters.

In its current form the fire material produces acceptable results but there are a few things which need further research, including support for moving bases, DX11 tessellation and more complex, swirly distortions.

With the material done I needed a context for it so I started making assets in Luxology modo.

The high poly candle is sculpted, multi-resolution Pixar SDS while the holder is a more basic SDS model, together weighing 225K polys. The “low” poly mesh is 9K polygons  as polycount was of no concern in this case.
Occlusion (accessibility) layers were used to darken crevasses and brighten edges. Diffuse, normal and SSS maps were baked.

Candle model Fire material Fire material FireShowcase01 300x171

I needed no mesh for the candle flame as it was a particle system (with two particles) so I moved on the next asset, the stone pillar which was meant to provide a base for both the candle and the torch.

Same deal as before, sculpted pSDS (655K), occlusion layers for dirt and edge highlights, 2K polys for the in-game mesh. I used a high resolution stone texture (4Kx4K) as a base, which I stitched together from several photos taken in a quarry nearby.

Stone pillar model Fire material Fire material FireShowcase02 300x219

The torch and its holder are SDS meshes. They ended up being an overkill since the details are not visible on the video…

Torch model Fire material Fire material FireShowcase03 300x137

The torch flame is a very simple tube which will carry the fire material. It is tessellated so the noise vertex displacement has something to work with.

Torch flame model Fire material Fire material FireShowcase05 133x300

The last object I sculpted was the pavement and used a Similar workflow and material to the pillar.

Here too I used modo’s procedural brushes to add indentations to the geometry which then got mixed with the displacement coming from the applied textures.

Cobblestone floor unit model Fire material Fire material FireShowcase04 300x89

I used instances of the base mesh arranged in a 3×3 grid to check proper tiling. They seamlessly connect not just in an even grid but also when a row is offset by half a tile which breaks up repetition a bit. (Thinking back I should have made the blocks in a way that they also match when one is rotated 90 degrees.)

The final piece of the showcase was the medieval house. It was not suited for a high poly baking workflow so I went with the more conventional method: single low poly mesh with a texture atlas on it.

The third image show the convexity/concavity based layers which were baked onto the second (lightmap) UVs. It’s mixed onto the diffuse texture in the unreal material, with an overlay-like blending mode.

Medieval house model Fire material Fire material FireShowcase07 300x124

The fires are separate meshes modeled to fit certain areas of the building, although I reused some of them to add to the inferno.

Fire sheets Fire material Fire material FireShowcase08 244x300

The next step was importing all the assets and setting up the material instances. At the end of that not particularly exciting procedure I had all the building blocks I needed to make the scenes.

The first one had the candle mesh, it’s flame particle system and a few lights.

A bright point light has a big radius and casts dynamic shadows while another acts as a filler to relieve the deep shadows on the candle holder. A dim spot light, embedded in the wall, lightens up the ground.

Since there was a disconnect between the flickering flame and the stationary pointlights I made a RandomMoverActor to carry the lights around. (They are those griffon heads.) The lights also have a light function which vary their brightness.

Candle in editor screenshot Fire material Fire material FireShowcase09 137x300

The torch scene has a very similar structure. The biggest difference is that the bulk of the fire is a mesh with the particle system only providing additional bits flying off the top.

Torch in editor screenshot Fire material Fire material FireShowcase10 163x300

Then I assembled the burning house. Fire meshes, particle sparks, flickering lights and two additional effects, volumetric smoke and world projected scorch decals.

Burning house in editor screenshot Fire material Fire material FireShowcase11 300x290

The volumetric smoke is a FogVolumeSphericalDensityInfo using a custom material:

It’s based on the mix of 3 textures projected on each world axis, with some billowing, panning, tiling and contrast controls. It has the same flicker logic as the light function used by the lights so the two material instances will be in sync if they have the same random seed parameter.
The projection stretch artifacts are clearly visible on the video but they are not that bad with a busy background.

As for the scorch decals, only the mask (a distance field) is affected by the decal projection, the rest is world relative so decals of different size and aspect ratio match up seamlessly. On the right you can see four decals:

Amber decals Fire material Fire material FireShowcase12 300x233

With all the setpieces in order the last thing I needed was the camera control. I used Gavit’s input remapping which made it easy to link camera location and rotation to the same properties of the Hydra controller, right in the editor. After playing around with the smoothing and sensitivity adjustments I ended up with a handheld virtual camera.