Part III · What surfaces do · Chapter 10
Glass
Glass, water, acrylic and diamond let light through, bend it as it crosses their surface, and reflect a little of it, more at a slant. ShadyTracer has an optics bench for LED and luminaire design, where all of that has to be exact. This chapter is the two laws involved (Snell's and Fresnel's), what happens inside a slab and a ball, how the renderer knows which material a ray is inside, and how coloured glass absorbs.
1Bending and reflecting
Light slows down in glass. How much is its index of refraction n: 1 for air, 1.33 for water, about 1.5 for glass, 2.42 for diamond. Crossing into a slower material, a ray bends towards the normal, by Snell's law: n1 sin θ1 = n2 sin θ2, every angle measured from the normal. Here light from the air meets glass at 45° (drag it) and bends to 28.13°. Some reflects instead: 5.0% here. How much is Fresnel's law, the chart on the right: about 4% straight on, rising to all of it at grazing (light skimming along the surface, near 90°).
Light is a wave that vibrates across its direction, and the two ways it can vibrate (across the plane of the drawing, s, and in it, p) reflect differently: s 9.2%, p 0.8%. Sunlight and lamplight mix both equally, so a renderer can use their average (unpolarised, the bold curve). At one angle, Brewster's angle, p does not reflect at all (marked "p = 0"), so light reflected off water near that angle is almost all s. Polarising sunglasses block s light, and with it most of the glare.
From inside the glass out, at 30°, the ray bends away from the normal, to 48.59°.
At 50° there is no angle to bend to: sin θ2 would have to be 1.15. Past the critical angle all the light reflects: total internal reflection. The critical angle is where the bent ray would just skim the surface, θ2 = 90°: Snell gives sin θc = 1/n, 41.81° for glass. It keeps light inside an optical fibre and makes the far side of an aquarium a mirror.
The mathsFresnel's two reflectances
With η = n1/n2, the cosine of the incoming angle c1, and of the refracted one c2 = √(1 − η²(1 − c1²)). That is Snell's law written for cosines: sin θ2 = η sin θ1, and since sin² + cos² = 1, cos θ2 = √(1 − η² sin²θ1) = √(1 − η²(1 − c1²)).
Fresnel's equations give how much of the wave's amplitude (its height) reflects, r; the light it carries goes as the amplitude squared, so the share of light reflected is r²:
rs = η c1 − c2η c1 + c2, rp = c1 − η c2c1 + η c2, R = ½(rs² + rp²)
When 1 − η²(1 − c1²) is negative there is no c2: total internal reflection, R = 1. Straight on, both are ((n − 1)/(n + 1))², 0.04 for n = 1.5. rp is 0 where c1 = η c2, which works out to tan θ1 = n2/n1: Brewster's angle, 56.3° from air into glass.
2A pane, every bounce counted
A pane of glass has two surfaces, and light reflects at each, back and forth inside. At 45° each face reflects 5.0%: 90.2% of the light gets through on its first pass, and each later pass adds a little, to 90.4% in all, which is (1 − R)/(1 + R) (the fold-out adds the passes up).
The renderer never adds them up. Each path (one ray followed from bounce to bounce, chapter 8) takes one branch at each face: it reflects with chance R or passes with chance 1 − R. By chapter 8's rule it divides what each branch carries by that branch's chance: R/R = 1 for the reflection, (1 − R)/(1 − R) = 1 for the pass. So every path carries all its light one way or the other, and the share of many paths that come out below is the share of the light that does: the sum, worked out by counting. At one face: of 1000 paths about 50 reflect and 950 pass, each carrying its full light, so the counts are the shares, 5% and 95%.
Both faces reflect the same 5.0%, though the bottom one is met from inside at 28.13°: that is the same crossing as the top's, run the other way (28.13° inside bends back out to 45°), and Fresnel's law gives one crossing the same share from either side. (Figure 1's 30° from inside is a different crossing.) On top the figure also shows 4.5% coming back up: what reflected off the bottom face and got out through the top, 0.95 × 0.05 × 0.95.
One shortcut: the renderer averages s and p afresh at every surface. Real light keeps its
polarisation from one face to the next, so the exact answer works the series out for s light and for p light,
each with its own reflectance, and averages the two results: 90.7% here. A note in ShadyTracer's optics code
(optics/fresnel.js) gives the shortcut's error: none straight on, 0.3% at 45°, 3% at 70°.
The mathsA pane's passes, added up
Into the glass goes 1 − R; out of the bottom face, (1 − R) of that: the first pass brings (1 − R)². What the bottom face reflects goes up, reflects off the top with R again, and comes down: the second pass is (1 − R)² R², the third (1 − R)² R⁴, each R² of the one before. A series that shrinks by the same factor each time adds up to its first term over (1 − that factor):
T = (1 − R)²1 − R² = (1 − R)²(1 − R)(1 + R) = 1 − R1 + R
At 45°, R = 0.050: (0.950)² = 90.2% the first time, 0.950/1.050 = 90.4% in all.
At 78° each face reflects 32.6%, and the later passes matter: 45.5% through on the first, 50.9% in all.
Coloured glass absorbs as light crosses it, more the further it goes: Beer–Lambert's law. Each equal stretch of glass keeps the same share of what reaches it, so the shares multiply. ShadyTracer gives a coloured material its colour and a colour depth, the distance after which exactly that colour is left: of each channel (red, green and blue, each kept separately), after one depth the colour's share, after two depths its square. With a colour of 0.5 at a depth of one pane's thickness, a ray straight down would keep 50%. The slanted path across is longer than the thickness, 1/cos 28.13° = 1.134 of it, so it keeps 0.51.134 = 45.6%. Through the pane, 41.1% gets out the first time, and the later passes, absorbed twice as much on each round trip, add almost nothing: 41.1% in all, and 52.9% of the light is absorbed.
3A glass ball
Parallel light through a glass ball, as from the sun: each ray bends in at the front and out at the back, towards the axis (the faint dashed line through its centre). A ball's normal at any point points straight out from its centre, along the radius. Rays near the axis meet 1.5 radii from the centre, half a radius behind the ball (the dot); the outer ones bend more and cross nearer, at 1.104 for a ray 0.9 out. That spread is why a ball makes a blurred focus, and on a floor under it a bright caustic ring and spot (chapter 18 is how the renderer finds those).
Water bends less: its focus is 2.015 radii out.
Diamond bends so much that the rays cross the axis inside the ball, 0.704 radii past the centre for those near it (the dot), and leave spreading apart: there is no focus behind the ball at all.
And no ray is ever trapped inside, however strongly the ball bends: a ray bent to an angle θ2 on the way in meets the far side at that same angle (the two radii to its ends make an isosceles triangle with it), and since sin θ2 = sin θ1/n is less than 1/n, it is always below the critical angle. Total internal reflection needs a flatter far side, as in a prism (chapter 11).
4How ShadyTracer does it
The fold-out's Fresnel, as the graphics card computes it at every crossing:
At a smooth glass surface a path reflects with the chance ps, the Fresnel at its angle, or refracts with the rest; each branch divides by its own chance, as section 2 says:
Which index is on the other side? The ray carries the index of the material it is in (Ray.n), and a
surface it meets says nothing about what lies behind it: glass in water, a bubble in glass. So a transparent
surface asks the scene, at a point a hair beyond it, which material is there:
An opaque surface does not ask: it uses its own index, and never less than the material the ray is in (), since asking cost a scene evaluation at every hit, and once made a mirror of an ordinary surface under water (section 6).
And the absorption of section 2, applied where a ray arrives, for the whole distance it travelled in the material:
5Tricks and trade-offs
Each is stamped with how it is known; point at a stamp to see what it means.
Refraction as the textbook says. The optics trace against optics/fresnel.js's
answers (the optics-refraction test, the user's Pascal card, 2026-09-27, 400,000 rays a row): a slab
at 0, 45 and 70° read 0.9991, 0.9999 and 1.0007 of the model; a source inside the slab 0.9990, a glass ball
0.9997; total internal reflection exactly 1; a 20° wedge's exit angle to 2 millionths of a degree. Against the
polarised answer: 0.9991, 0.9966, 0.9721, as predicted (docs/TESTING.md).
Smooth glass in a white furnace (chapter 8): 0.9996 at index 1.5, 0.9997 at 2.4. Rough glass
loses light a single reflection cannot give back (chapter 9): 0.9587 against a reference of 0.9468 computed on
the main processor for the same model; later runs read 0.9872 and 0.9440, not yet explained
(roadmap/SURFACES.md).
A gap thinner than the step off a surface. Two slabs 0.5 mm apart at the automatic tolerance read as one slab, 0.9228 where two give 0.857. The test is there to show where the renderer's limit lies, and that row fails at the automatic tolerance: the step a ray takes off a surface is longer than the gap. At a tolerance of 10 µm it passes.
6What went wrong before
- Every glass reflected 4%. Fresnel came from a "specular" slider, never from the index: "water,
glass and diamond all reflected 4%, and total internal reflection jumped from 4% to 100%"
(
microfacet.js's header). - Rough glass that never let light out past the critical angle. It chose to reflect by the
Fresnel at the surface's overall angle, 1 past the critical angle, though tilted facets still let light through:
a rough glass sphere lost 8% in the furnace. It now draws a facet first and decides by that facet's Fresnel
(Walter et al., 2007; the comment in
lobesSample). - A mirror under water. An opaque surface used to ask the scene what was beyond it too, which "turned an IOR-1 default material met from inside water into a total-internal-reflection mirror" (section 4's comment).
- Thick and thin glass alike. Before colour depth (version 0.37.0), a coloured material tinted a
ray at every crossing, so "two boxes of one transmissive material, one thicker, transmitted the same"
(
CHANGELOG.md). A material with no colour depth still does: the old rule, kept for it (the comment where a material's lobes are built). - A glass ball with a plain shadow. The sun through glass was counted by neither of chapter 15's two ways (its story there).
7Try it
- In the first figure, from inside the glass, set the index to 1.33 (water). What is the critical angle now?
- Find Brewster's angle from the air for glass, and check tan θ = 1.5.
- In the pane, make the colour 0.2 and the depth 4 thicknesses, then the depth 0.25 thicknesses. Which loses more light, and how much of the first pass survives?
- In ShadyTracer, put a glass sphere on a floor under the sun, and look under it for the caustic.