Advanced shaders · lesson 15 of 19 · Advanced · about 5 min
Fire 2: a campfire at night
You will build: Make it a campfire: the flame lights the ground around it with a flickering glow, and 14 embers rise and fade.
A flame in a black void looks like a sticker. What makes it feel there is that it lights the world around it, and that it throws off sparks.
Light on the ground. For rays that point down we find where they hit the ground (y = 0), and add warm light that fades with distance from the fire: 1.1 / (1.0 + dot(g, g) * 1.2), where g is the spot on the ground. Multiply it by the flicker so the glow breathes with the flame.
Embers. Each spark is a tiny glowing point that rises and fades. To place 14 of them we again use no memory: spark j has a random speed and start offset from hash21, its life frac(time * speed + offset) goes 0 to 1 and then starts over, its height is 0.2 + life * 2.6, and it wobbles with a sine. How bright is it? Depends on how close the ray passes to the spark. The distance from a point sp to a ray is length(cross(sp - ro, rd)): the same line-distance trick as the bullet holes. A narrow glow exp(-dist * dist * 900.0) makes it a bright dot, and (1 - life) fades it as it rises.
float life = frac(time * (0.35 + 0.25 * hash21(float2(fj, 1.0))) + hash21(float2(fj, 2.0))); float3 sp = float3((hash21(float2(fj, 3.0)) - 0.5) * 0.8 + 0.25 * sin(life * 6.0 + fj), 0.2 + life * 2.6, (hash21(float2(fj, 4.0)) - 0.5) * 0.8); float dist = length(cross(sp - ro, rd)); col += float3(1.0, 0.6, 0.2) * exp(-dist * dist * 900.0) * (1.0 - life) * 1.5;
The numbers you need
Everything the picture depends on is given, so the challenge is the code, not guessing.
- Ground light
for rays with rd.y < -0.01: tg = (0.0 - ro.y) / rd.y; g = (ro + rd * tg).xz; col += float3(1.0, 0.45, 0.12) * flick * 1.1 / (1.0 + dot(g, g) * 1.2) (before the flame march)- Sparks
14 sparks, after the flame: j = 0 to 13, fj = float(j)- Life
life = frac(time * (0.35 + 0.25 * hash21(float2(fj, 1.0))) + hash21(float2(fj, 2.0)))- Position
sp = float3((hash21(float2(fj, 3.0)) - 0.5) * 0.8 + 0.25 * sin(life * 6.0 + fj), 0.2 + life * 2.6, (hash21(float2(fj, 4.0)) - 0.5) * 0.8)- Glow
dist = length(cross(sp - ro, rd)); col += float3(1.0, 0.6, 0.2) * exp(-dist * dist * 900.0) * (1.0 - life) * 1.5- Everything else
as in Fire 1
Try it in your own language
The lesson is written once and shown in every language. Pick yours: Unity (Built-in), Unity (URP), Unreal, Shadertoy / WebGL, OpenGL (LWJGL, raylib), three.js, GameMaker, LÖVE (Lua), Godot, Apple (iOS, macOS), WebGPU / Bevy.
Start codingGo deeper
Part of the learning path: Game effects: smoke, fire and snow.