PBR (Cook-Torrance + GGX)
Purpose
PBR reduces material direction from "pick a shininess exponent and a specular color" to two knobs — metalness (is it a metal, yes or no) and roughness (smooth or micro-bumpy) — and every modern game-engine material sits on that grid.
Key insight
Blinn-Phong lets an artist pick any specular color, any shininess, any specular strength — which means most tunings look wrong, because real materials don't have that many degrees of freedom. PBR fixes this by encoding physics. The big split: dielectrics (plastic, wood, skin, paint) have a dim white reflection (~4%) and show their color in the diffuse term. Metals have no diffuse at all and show their color in the specular reflection — a gold bar is gold because its reflection is gold-tinted, not because its "diffuse" is yellow.
That is the metalness knob: 0 for dielectric, 1 for metal, and please don't pick 0.5 (there's no such material). roughness replaces the shininess exponent but inverts it — 0 is a perfect mirror, 1 is unpolished chalk. Underneath, GGX shapes the highlight lobe: sharp star at low roughness, wide glow with a bright core and long tail at high roughness. Fresnel is already inside — no separate rim dial.
Drag metalness through its full range at roughness = 0.3. Watch the sphere's body go from painted plastic (body shows its base color, tiny white highlight) to raw metal (body goes dark, reflection takes the color).
Break it
Metalness 0.5, roughness 0. The classic "wrong answer." The surface looks like shrink-wrapped plastic pretending to be metal — a pure white highlight on a colored body.
Teaches: metalness is binary in the real world. 0.5 only exists at edges between metal flakes and binder (car paint, glitter), and even there it's a blend of the two extremes, not a middle substance. This is the most common material-briefing mistake.
Roughness 1 on a metal (metalness = 1). The sphere becomes an unidentifiable muddy disc. Teaches: without a sharp reflection, metals lose the thing that makes them read as metal. Brushed steel is roughness ~0.5, not 1.
Direct Claude
MeshStandardMaterial)
// PBR is too big to hand-roll in a lesson — every modern engine ships
// the same Cook-Torrance + GGX implementation. In Three.js it's one line:
const material = new THREE.MeshStandardMaterial({
color: 0xc8846a, // base color (acts as diffuse for dielectrics,
// as reflection tint for metals)
metalness: 0.0, // 0 = dielectric (plastic/wood/paint/skin),
// 1 = metal. Keep it binary.
roughness: 0.5, // 0 = perfect mirror, 1 = chalk.
});
// An environment map (sky, indoor lighting, HDRI) is what metals
// actually reflect. Without one, metals look black because there's
// nothing around them to bounce into the camera.
scene.environment = pmremFromGradient(renderer);
// Update from sliders:
material.metalness = metalnessSlider.value;
material.roughness = roughnessSlider.value;