Creating Procedural Smoke in GLSL

in #codingyesterday

Smoke behaves very differently from fire.

Fire is energetic.

Smoke drifts slowly.

It spreads outward.

It fades into the air.

Instead of sharp movements, smoke relies on soft transitions and gradual changes.

Fortunately, procedural shaders are excellent at producing exactly these kinds of effects.

What Makes Smoke Look Like Smoke?

Smoke is built from a few simple ideas.

Smooth noise.

Slow movement.

Soft edges.

Transparent layers.

Every one of these can be created mathematically.

Starting with UV Coordinates

As always, we'll begin with the UV coordinates.

vec2 uv = vUv;

These coordinates will be animated over time.

Moving the Smoke

Smoke rises slowly.

Instead of moving quickly like fire, we'll use a gentle speed.

uv.y -=

uTime * 0.1;

The movement is much slower.

This immediately changes the feeling of the animation.

Creating the Base Pattern

Generate a smooth FBM texture.

float smoke =

fbm(

uv * 3.0

);

The result already resembles soft cloudy shapes.

Distorting the Coordinates

Real smoke curls as it rises.

Add a small distortion.

uv.x +=

fbm(

uv * 2.0

) * 0.15;

The smoke now drifts from side to side.

Small distortions usually look better than large ones.

Making the Bottom Stronger

Smoke is usually denser near its source.

Create a vertical gradient.

float fade =

1.0 -

vUv.y;

The bottom becomes brighter.

The top gradually fades away.

Combining Everything

Multiply the smoke by the fade.

smoke *= fade;

Now the smoke naturally disappears into the sky.

Softening the Edges

Smoke rarely has hard boundaries.

Use smoothstep().

smoke =

smoothstep(

0.2,

0.8,

smoke

);

The edges become much softer.

The overall effect feels more realistic.

Complete Shader

#ifdef GL_ES
precision mediump float;
#endif

uniform float uTime;
varying vec2 vUv;

void main(){

    vec2 uv = vUv;

    uv.y -= uTime * 0.1;

    uv.x += fbm(uv * 2.0) * 0.15;

    float smoke = fbm(uv * 3.0);

    float fade = 1.0 - vUv.y;

    smoke *= fade;

    smoke = smoothstep(0.2,0.8,smoke);

    gl_FragColor = vec4(vec3(smoke),1.0);

}

Even in grayscale, the result resembles drifting smoke.

Adding Smoke Colours

Smoke doesn't have to be pure gray.

vec3 color = mix(

vec3(0.1,0.1,0.12),

vec3(0.8,0.8,0.85),

smoke

);

Dark gray creates dense smoke.

Light gray represents thinner areas.

Creating Colored Smoke

Nothing says smoke has to be gray.

Try blue.

vec3(0.2,0.3,0.8)

Or purple.

vec3(0.6,0.3,0.8)

These work well for magical or fantasy effects.

Creating Thick Smoke

Increase the FBM frequency.

fbm(

uv * 5.0

);

The smoke gains more detail.

Lower the frequency for large soft clouds.

Layering Multiple Smoke Fields

Professional shaders often combine several smoke layers.

Each layer moves at a different speed.

Each has a different scale.

Together they create depth and richness.

Where Is Procedural Smoke Used?

Smoke shaders appear in many projects.

  • Campfires.
  • Steam.
  • Fog.
  • Clouds.
  • Explosions.
  • Dust.
  • Magical effects.
  • Atmospheric backgrounds.
  • Video games.
  • Motion graphics.

Many of these effects differ only in color and movement speed.

Try These Experiments

Slow the animation.

Speed it up.

Create darker smoke.

Create blue magical smoke.

Layer several FBM patterns.

Add domain warping.

Observe how subtle changes dramatically affect the final result.

A Small Challenge

Can you create these effects?

  • Campfire smoke.
  • Steam from hot water.
  • Thick industrial smoke.
  • Magical mist.
  • A drifting fog bank.

All of them begin with the same procedural techniques.

Today we created procedural smoke using animated FBM, coordinate distortion, gradients, and smooth transitions.

Although the shader shares many ideas with the fire shader, slowing the movement and softening the edges completely changes the final appearance.

This demonstrates how flexible procedural graphics can be.

Posted Using INLEO