Advanced shaders · lesson 7 of 19 · Advanced · about 5 min
Clouds 3: light and shadow inside the cloud
You will build: Light the cloud: for every step inside the cloud, sample the density towards the sun to get a shadow, and add the lit color to the pixel.
A flat white cloud looks like a cutout. What makes clouds look deep is that the light also has to travel through them: the side facing the sun is bright, the inside and the far side are in shadow, and thin edges glow.
To know how much sunlight reaches a point inside the cloud, we ask the density once more, a little way towards the sun: sh = dens(pos + sun * 0.5). If there is lots of cloud between the point and the sun, sh is large and the point is dark. This is a cheap approximation of a real light march (the real thing takes 6-8 samples; one is enough to look good).
The color of the light at that point is a blend from a cool, bluish shadow color to warm white: lerp(shadow, sunlit, saturate(1.0 - sh * 1.5)).
Now each step adds light as well as blocking it. A step's contribution to the pixel is its color times the light that survived to reach the camera (T) times how much it blocks (a):
float sh = dens(pos + sun * 0.5); float3 lit = lerp(float3(0.5, 0.6, 0.78), float3(1.0, 0.97, 0.9), saturate(1.0 - sh * 1.5)); float a = 1.0 - exp(-d * 0.4); L += T * a * lit; // light collected on this step T *= 1.0 - a; // and it blocks what is behind // at the end: col = sky * T + L;
Skipping the density-towards-the-sun lookup in empty air (if (d > 0.01)) keeps this fast: most steps are in clear sky.
The numbers you need
Everything the picture depends on is given, so the challenge is the code, not guessing.
- Setup
as in Clouds 2, but the sun is sun = normalize(float3(0.5, 0.7, 0.4))- Skip empty air
only if d > 0.01- Shadow
sh = dens(pos + sun * 0.5)- Light color
lit = lerp(float3(0.5, 0.6, 0.78), float3(1.0, 0.97, 0.9), saturate(1.0 - sh * 1.5))- Step
a = 1.0 - exp(-d * 0.4); L += T * a * lit; T *= 1.0 - a (L starts at float3(0.0, 0.0, 0.0))- Final color
sky * T + L
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 coding