This page runs from a web server, not from a file: in the repository run python3 -m http.server 8000 and open http://localhost:8000/tutorial/.

Part II · What light is · Chapter 5

Measuring light

So far a ray has brought back "light" as a bare number. ShadyTracer's numbers are real units: a lamp in candela per square metre, the sun in lux, every pixel in candela per square metre, so that a picture can be checked against a light meter, and a floor in the sun against a line of arithmetic. This chapter is those units, the two laws that connect them, and the sun.

1Four ways to say how bright

"How bright is this lamp?" has four answers, each a different question, and lighting uses all four. Each counts light the way the eye does: green light counts for more than blue of the same power, because the eye is more sensitive to it (chapter 6 says how much).

  • How much light it gives off in all: its flux, in lumens (lm). A household LED bulb gives about 800.
  • How much it sends one way: its intensity, in candela (cd): lumens per unit of solid angle, the steradian. A lamp of 1 candela in every direction gives 4π lumens in all, since the whole sphere of directions is 4π steradians.
  • How much lands on a surface: its illuminance, in lux (lx): lumens per square metre. A light meter on a desk measures this.
  • How bright a surface looks: its luminance, in candela per square metre (cd/m²): the intensity each square metre of it sends towards you, counting only the area you see (a tile seen at a slant looks smaller). A camera's pixel measures this.

Light lands as illuminance; anything seen, a lamp or a lit wall, has a luminance. A camera ray brings back luminance, so a pixel holds candela per square metre. The figure is a glowing panel, 0.5 m square, of luminance 1000 cd/m², 2 m from the eye. The eye's picture is 60 × 40 pixels. Every pixel wholly on the panel reads 1000; a pixel at its edge reads its share, the average of the many rays through it of which some miss (chapter 1's jitter). Point at the picture to read a pixel. Add up what each pixel brings the eye (its reading, times the small solid angle it covers, times the cosine of its direction from straight ahead, chapter 1's slant) and the panel puts 61.2 lux on the eye.

Drag the panel twice as far, to 4 m. A pixel wholly on it still reads 1000: the panel looks exactly as bright. What changes is how much of the picture it covers: counting each pixel at its edge by the share it sees, 348.2 pixels' worth at 2 m and 87.1 at 4 m, a quarter. With it the light it brings the eye falls to a quarter too: 15.5 lux. (The readout gives both: the pixels wholly or partly on the panel, and the area they add up to.) That is the inverse square law of chapter 0, and it lives in how much of the view the panel fills, not in the light along a ray. Along a ray, luminance does not change: a ray is one line of sight, and what it sees looks as bright from near as from far.

At 8 m the panel is small beside its distance, and the lux follows the simplest formula: the panel's intensity is its luminance times its area, I = 1000 × 0.25 = 250 cd, and E = I/d² = 3.91 lux. The pixels' sum says 3.90; the exact answer for a square this size is 3.90.

This is why a path tracer carries luminance (radiance, in the physicist's units) along its rays: it is the one quantity a ray does not need to change as it travels. Nor does it care how big the scene is: a room twice as big, seen from twice as far, looks exactly the same, pixel for pixel, so a room built in millimetres and the same room in metres give the same picture, which section 5 shows measured.

One quantity holds the rest together, and the next two sections lean on it: how much light a bright patch of sky (or a lamp, or the sun) puts on a floor. The fold-out works it out; its two answers are E = πL for a whole sky of luminance L, and E = πL sin² α for a round lamp straight overhead whose edge is α from its middle.

The mathsThe four quantities, and light from a patch of sky
Φ
flux, lumens: all the light, counted by the eye's sensitivity
I
intensity, candela: flux per steradian, in one direction
E
illuminance, lux: flux per square metre arriving
L
luminance, cd/m²: intensity per square metre of the surface as seen from that direction, its area times the cosine of the angle from its normal

A small lamp of intensity I, d away, its light arriving at an angle θ from the surface's normal:

E = I cos θd2

A large one, or the sky: picture a glass dome of radius 1 over a point of the floor, the bright patches of sky painted on it. Shine a light straight down from above the dome, and each patch casts a shadow on the floor's disc of radius 1 under it. A patch of luminance L lights the point by L times the area of its shadow. Why: a small patch of the dome, of area Ω, is Ω of solid angle (the dome has radius 1), so it would put LΩ on a surface facing it; the floor meets its light at a slant θ from straight up, and the cosine law takes that down to LΩ cos θ. And a small patch tilted by θ from facing straight down casts a shadow cos θ as big: Ω cos θ. The two match. (This is Nusselt's way of seeing it.) Two cases follow:

  • A whole sky of luminance L presses down onto the whole disc, of area π: E = πL.
  • A round lamp straight overhead, of angular radius α (the angle from its middle to its edge), presses down onto a circle of radius sin α (its edge is a ring on the dome α from the top, at height cos α and radius sin α): E = π L sin² α. For a lamp at a slant, the same illuminance falls on a surface turned to face it, and a level floor gets cos θ of it.

Many renderers write a small lamp's illuminance as L times its solid angle instead: the usual shortcut. The solid angle of a round lamp is 2π(1 − cos α) (the area of that cap of the unit sphere, a fact of geometry), which for a small α is very nearly π sin² α, so the shortcut is close; since sin² α = (1 − cos α)(1 + cos α), it is off by (1 − cos α)/(1 + cos α) of the answer, about α²/4 (for small α, cos α is close to 1 − α²/2) (α in radians: the angle measured as the length of arc it cuts from a circle of radius 1, so 180° is π).

Left, from the side: the eye, the edges of its view (faint), the line straight ahead (dotted), and the glowing panel at a distance you drag by its handle; the two lines from the eye to its edges are the angle it fills. Right: the eye's picture, 60 × 40 pixels, each pixel the panel's luminance where it is wholly on the panel and its share at the edge; point at it to read a pixel. The sliders set the distance and the panel's size.

2The sun, in two numbers

ShadyTracer's sun is set by two numbers: the illuminance it puts on a surface facing it, 100,000 lux by default, and how wide its disc looks, 0.53° (). Its luminance follows from those two: E = πL sin² α (section 1: the disc's shadow on the floor's unit disc is a circle of radius sin α), with α half of 0.53°, 0.00463 radians (an angle measured as the arc it cuts from a circle of radius 1: 180° is π), gives L = 1.488 billion cd/m². The figure draws the disc at its true size over a point of the ground, and on the right its luminance for every width, at the same 100,000 lux.

That luminance is a problem for a path tracer. The disc is 0.0000672 steradians, a speck of the sky. A ray bouncing off a matte ground goes in a random direction, more often near straight up (chapter 8 shows how: the chance of a direction goes as the cosine of its angle from straight up, divided by π so that the chances add up to 1), and it finds the disc with the chance 0.0000672 × cos 45° / π, 1 time in 66,111 when the sun is 45° up (45° from straight up as well as from the ground). A few rays that find it bring back a billion; the rest bring back nothing. That is why chapter 14 aims at the sun instead of waiting to hit it.

Widen the disc to 10° across. The illuminance is held at 100,000 lux, so its luminance drops to 4.19 million, and a random bounce finds it 1 time in 186. A bigger sun is a softer shadow, not a brighter picture: the setting's comment says just that. The shortcut L × solid angle is now 0.19% off the exact answer, where at the real sun it was 0.0005%; ShadyTracer uses the exact one at every size.

Small as it is, the real sun's disc is a test of arithmetic. Its 1 − cos α is 0.0000107. The graphics card computes with 32-bit numbers (f32), and near 1 those are kept in steps of about 0.00000006, so cos α is about 180 steps below 1, and 1 − cos α comes out with an error near half a percent. That number is the disc's solid angle (the fold-out), and when the renderer aims a ray at the sun (chapter 14) it says how likely each direction in the disc is; the luminance needs sin² α, as small. So ShadyTracer works both out once, in the program on the main processor, in 64-bit numbers, and hands them to the graphics card (section 4).

Left: a point of the ground, the sky over it (dashed), and the sun's disc at its true size, drawn no smaller than a dot, with the two edges of its cone from the point. Right: the disc's luminance for each width across, at 100,000 lux, on stretched scales: up the side each mark is ten times the last, and along the bottom equal steps are equal ratios too. On such scales the curve is a straight line, because the luminance falls as one over the width squared. The dot is the width the slider sets; the elevation changes only how often a random bounce finds the disc (the readout). Choose the disc's width and its elevation; The real sun puts 0.53° back; point at the curve to read it.

3A white floor, measured

Now everything at once, and checked on a real graphics card. A floor of albedo 0.8, matte (it sends its light out evenly in every direction, so it looks equally bright from anywhere), under the default sun at 45° and the default sky. The sun's 100,000 lux arrive at a slant: on the level floor they give 100,000 × sin 45° = 70,711 lux (the cosine law, with the angle measured from the floor up). The sky adds 15,000, its setting being the illuminance it alone puts on a level surface. In all, 85,711 lux.

The floor sends back 0.8 of it (its albedo, written ρ). A matte surface of luminance L sends out πL lux's worth (section 1's sky, turned round: the floor is a sky for the space above it), so L = ρE/π = 0.8 × 85,711 / π = 21,826 cd/m². ShadyTracer's own test of this rendered the floor on the user's NVIDIA Pascal card, 1188 × 969 pixels, 32 samples a pixel: 21,827.2. The ratio is 1.0001.

The sun alone at 20°: sin 20° × 100,000 = 34,202 lux, so 8,709.5 cd/m², and the card measured 8,709.3. Every row of that test is within 0.07% of its arithmetic, except one with aiming switched off (section 5).

Left, from the side: the floor, the sun's parallel rays at its elevation, the sky over it (dashed), and an eye looking at the floor from the right. Right: the floor's luminance ρE/π for every elevation of the sun (the line), the elevation the slider sets (the dot), and what the user's card measured (the green rings, at albedo 0.8 only). Choose the elevation, the albedo, and which light; point at the chart to read it.

4How ShadyTracer does it

The units, as the file that turns every light setting into numbers for the graphics card states them:


    

The sun's disc, in 64-bit numbers on the main processor. sunCone keeps everything the graphics card needs about the disc; discLuminance is the fold-out's E = πL sin² α turned round:


    

On the graphics card, every way of reaching the sun asks one function whether a direction is inside the disc, so they all agree where its edge is:


    

A lamp can be given by its flux in lumens instead of its luminance. A matte face of area A and luminance L gives off πLA lumens (the fold-out again), so the lumens become a luminance by dividing by πA; a face that sends its light into a narrower beam has a smaller factor in place of π:


    

And the check of section 3, in the test that ran it on the user's card:


    

Numbers in the comments, checked. The file's header says the solar disc "is 0.00465 rad across" and 1 − cos of it is 1.08 × 10⁻⁵: at 0.53° across, 0.00463 is the angle from its middle to its edge, and 1 − cos of that is 1.07 × 10⁻⁵, as sunCone works out. discLuminance's comment says the shortcut is "1% off at a 10-degree" sun: for a disc 10° across, as the setting means it, it is 0.19%, and 1% at about 23° across. Several comments elsewhere give the sun in a mirror as 1.6 × 10⁹ cd/m²; at 100,000 lux it is 1.49 × 10⁹.

5Tricks and trade-offs

Each is stamped with how it is known; point at a stamp to see what it means.

The units hold, to a tenth of a percent. The test of section 3 (docs/TESTING.md, "Physical units: third run", the user's Pascal card, 1188 × 969, 32 samples): sun and sky 1.0001, sun alone 1.0000, sky alone 0.9993, a 10° sun 1.0000, the same floor in millimetres 0.9999. One row is further off, 0.9984: sun and sky with aiming at the sun switched off and a 10° disc, so that only random bounces find it (section 2). Its noise was ten times the other rows', and it sits within that. The two sky-only rows, both 0.9993, are the one steady remainder (the scene in metres and in millimetres, both listed in the document); its first suspect is the sky's own measurement (next), which is inferred.

The sky is measured, then scaled. The sky's colours come from a model of sunlight scattered by air, with no units of its own. The graphics card measures the illuminance that model puts on a level surface, and the sky is multiplied by the setting over that measurement, so it gives exactly its 15,000 lux. Until the first measurement arrives the sky is dark, "rather than wrongly bright" (the comment above derive in photometry.js).

A setting sun goes out by its top edge. The sun is off once its whole disc is below the horizon (elevation + α < 0), not when its middle is ().

6What went wrong before

  • No units at all. The sun was an "intensity" of 1, its disc 0.1 radians from middle to edge (21 times the real sun's), the sky a multiplier on a sky made to look like the one before it, and glowing materials were 5: each number was set against the one before it, nothing could be measured, and the sun gave 3% of the sky's light (the comment above the sun's settings in uniforms.schema.js, and photometry.js's header).
  • Two edges to one sun. The first run of section 3's test found the rows with the real sun at 142 to 352 times the right mean, with their median right (docs/TESTING.md, "Physical units: the first absolute check", Pascal). A path reaches the sun two ways: by aiming a ray at it (chapter 14), and by a bounce that happens to go there. Each asks whether a direction is inside the disc, and they asked differently: the aiming by dot(L, S) ≥ cos α, the bounce by section 4's cross product. Near the sun the cosine is about 180 steps below 1, so the two disagreed on a thin ring at the rim. A bounce into that ring was taken as one the aiming could never make, so it counted the whole disc's luminance divided by a bounce's tiny chance of finding the sun (a rare find counts for more: chapter 8), about 10⁵ too much, in a few pixels. Since then there is one test, inSunDisc, and the other was deleted so a second edge cannot come back.
  • A matte floor that was not quite matte. After that fix every row read 0.975, sun and sky, metres and millimetres alike. The light was right; the floor was not: its material sent 4% of its light off as a shine and reflected like an albedo of about 0.78. With a pure matte floor, the third run's 1.0001.
  • A soft edge that lost light. The sun's disc used to fade over its outer 2%, which every way of reaching it saw alike but which took about 1% off its illuminance: a number that is a setting now, and has to come out as set (the comment above sunDiscRadiance in sdf.js).
  • A sun under the floor. Below the horizon the sun used to keep shining, through the ground of any scene that had none modelled (the comment in derive).

7Try it

  • In the first figure, drag the panel out to 10 m. Does a pixel wholly on it read any less? How many are left, and what does the lux do?
  • In the second, widen the disc until L × solid angle is 1% off (section 4 says about 23°), then find where it is 0.1% off. Does the error grow as the width, or as its square?
  • In the third, put the sun at 0°, then choose Sky only. Why does the line go flat?
  • In ShadyTracer, choose View › Tests and run Physical units: does a white floor come out at rho E / pi? on your own card. Then switch the picture to lux on surfaces (one of the three view buttons over the picture) and point at a sunlit floor.

Words from this chapter