Procedural 3D Product Commercials in the Browser: Anisotropic Metals, Rim Lights, and Camera Beats
Why single-directional-light Three.js scenes look like plastic, and how Baltor composes 4-pass studio lighting rigs, brushed anisotropic clearcoats, and Catmull-Rom camera beat splines.
When an unassisted language model generates a 3D product showcase in Three.js, it almost always drops a MeshStandardMaterial into a black void with one AmbientLight(0xffffff, 0.8) and one DirectionalLight. Without an environment map (PMREMGenerator) or a grazing rim light, metallic surfaces reflect pure black (0x000000) and dielectric reliefs lose all depth contrast. On compare.baltor.ai and assets.baltor.ai, Baltor binds product scenes to a 4-pass studio lighting rig, procedural HDRI reflections, and chordal Catmull-Rom camera beat splines.
The four-pass studio lighting contract
A metallic or glazed ceramic surface derives its visual read from the luminance gradient across its curvature. Baltor's studio lighting component (studio.lighting.four_pass_pbr.v1) enforces four distinct light contributions and verifies their histogram spread:
- Key light (45 deg azimuth, 38 deg elevation): High-CRI warm directional/spot source (
#fff6e8) casting soft PCF percentage-closer shadows (2048x2048shadow map,bias = -0.0003). - Cool fill bounce (-60 deg azimuth, 15 deg elevation): Low-intensity sky-tinted fill (
#cfe4ff) preventing shadow crushing while preserving a3.5:1key-to-fill ratio. - Grazing rim kicker (165 deg azimuth, 22 deg elevation): High-intensity edge light that catches fluted surface geometry and separates the silhouette from the studio cyclorama.
- Procedural PMREM studio environment: Synthesizes softbox strip highlights directly into a cube-map prefiltered mipmap chain so
metalness: 0.9andclearcoat: 0.9surfaces exhibit realistic Fresnel edge roll-off without external HDR network downloads.
// Procedural Studio Softbox Environment via PMREMGenerator
export function buildProceduralStudioEnv(renderer, THREE) {
const pmrem = new THREE.PMREMGenerator(renderer);
const envScene = new THREE.Scene();
envScene.background = new THREE.Color(0x0b0e14);
const stripGeo = new THREE.PlaneGeometry(10, 2.5);
const keyMat = new THREE.MeshBasicMaterial({ color: new THREE.Color(6.0, 5.6, 5.0), side: THREE.DoubleSide });
const keyStrip = new THREE.Mesh(stripGeo, keyMat);
keyStrip.position.set(6, 7, 5);
keyStrip.lookAt(0, 0, 0);
envScene.add(keyStrip);
const rimMat = new THREE.MeshBasicMaterial({ color: new THREE.Color(3.5, 4.8, 6.5), side: THREE.DoubleSide });
const rimStrip = new THREE.Mesh(stripGeo, rimMat);
rimStrip.position.set(-7, 4, -6);
rimStrip.lookAt(0, 0, 0);
envScene.add(rimStrip);
const renderTarget = pmrem.fromScene(envScene, 0.04);
pmrem.dispose();
return renderTarget.texture;
}
Camera beat splines and verifier gates
Commercial product spots require intentional pacing rather than a constant mesh.rotation.y += 0.01 turntable. Baltor structures commercials as a sequence of timed camera beats (Macro Sweep -> Hero Reveal -> Packshot Lock) interpolated via centripetal CatmullRomCurve3 with damped look-at targets, verified by an automated luminance-contrast gate that rejects any frame with clipped highlights or silhouette contrast below 4.8:1.
| Navigation | Post Title | Direct Links |
|---|---|---|
| Previous Post | 60 FPS Motion Graphics: Why Spring Physics and DFT Epicycles Beat CSS Keyframes | HTML · Markdown |
| Next Post | Simulating Backpressure, Consistent Hashing, and Shard Failover Before Writing Production Code | HTML · Markdown |
| Blog Index & Feeds | Return to Baltor Engineering Blog Index (8 Posts) | index.json · feed.xml |