rect-area-light
rect-area-light
RECT AREA LIGHT — a one-scene port of three.js `webgl_lights_rectarealight`, kept faithful on purpose: it is the acceptance test for the area light, the torus-knot primitive and the procedural checker roughness map.
Read the rig's notes
Everything is the example's numbers × 100, because the authoring unit here is
the MILLIMETRE (packages/core/units.ts) and the example's unit is arbitrary.
Area-light irradiance is a solid angle, so a uniform scale of the whole
scene — lights, subject, camera — leaves the exposure untouched.
three.js here
RectAreaLight(0xff0000, 5, 4, 10) light(type=area color=#ff0000
intensity=5 width=400 height=1000)
RectAreaLightHelper(light) helper=true (the light's own emissive
rectangle — colour × intensity)
TorusKnotGeometry(1.5,0.5,200,16) torusknot(r=150 tube=50
tubular_segments=200
radial_segments=16)
roughnessMap = checker(400) roughness_map="checker:400"
roughness_map_filter=nearest
rectLight.rotation.y += ±delta a linear rotation.y track
The example spins its lights off the frame clock forever (-1, +0.5 and +1
rad/s); a reel has a length, so the same rates are baked into 12s of linear
keyframes: -12 rad = -687.55°, +6 rad = +343.77°, +12 rad = +687.55°.
NOTE ON THE LOOK: there is no ambient, no environment and no haze anywhere in
this reel, and that is the whole demonstration — every photon on every subject
and on the floor comes off the three rectangles. Tone mapping is off (`None`) so the frame
matches the example's untone-mapped output rather than the reel default.
SCENES 2–6 · THE SAME RIG, A REAL DEVICE ON THE PLINTH
The knot is a test object; a device is the thing this engine actually shoots.
Each of the five swaps the subject and keeps the composition —
but a device renders at its TRUE PHYSICAL SIZE (an iPhone is 159.5mm tall, an
iMac 461mm), so the soft-box rig is scaled to the subject exactly the way a
photographer picks a soft-box for what is on the table. One factor k per
scene multiplies every length in the rig — light size, light position, camera
distance, subject height, floor, check count — so all six scenes are the SAME
photograph at six sizes:
s1 knot k = 1.00 400mm subject s4 macbook k = 0.90 354.8mm
s2 iphone k = 0.40 159.5mm s5 chrome k = 1.20 480mm
s3 ipad k = 0.70 280.6mm s6 imac k = 1.35 547mm
Devices face +Z out of the box, so each one is yawed 180° to present its
screen to the -Z side of the rig — the side the example's camera shoots from.
BACK → FRONT. Scene 1 keeps the example's fixed camera, because it is a port
and the port has to stay honest. Every device scene instead ORBITS 155°: it
opens behind the device on the LIT side (the soft-boxes face -Z, so it is the
rear shell they rake — the iPhone's camera island, the iMac's back and stand),
swings out through a three-quarter and a full side profile, and lands dead
front on the screen. One second of hold at each end, constant angular rate in
between. The arc is authored as CHORDS — eight position keyframes sampling the
circle — because a position track interpolates in a straight line, and two
keyframes from back to front would fly the camera through the middle of the
device. `look_at` has to be a track too: a static look_at attr is read once at
build, so an animated camera with one would slide sideways still facing its
original heading.
`side=double` on the shared material is there for scene 5 alone. A <chrome>
window is not a body, it is two flat planes — and a plane is single-sided, so
without it the browser window is simply GONE for the whole first half of its
orbit. The GLB devices are closed surfaces and cannot tell the difference.
ONE MATERIAL, SIX SUBJECTS. `standard#subject` is declared at the top of this
file and nowhere else; the knot resolves it as `material(ref=subject)` and
every device as `body-material=subject`. They are not two lookalike materials
— they are the same definition, so the phone is shaded by exactly the surface
the knot is, and re-tinting that one line re-tints all six.
It is also what makes these scenes work at all. Shipped as-authored, an iPhone
GLB is a BLACK device: 37 materials whose base colours run 0.00–0.06, most of
them metal at roughness 0.001. A 2% albedo returns 2% of any light you put on
it, and a metal has no diffuse term at all — it renders only what it reflects,
so in a black room with three small rectangles it is a black cutout no matter
how hard you light it. The device's own glass still survives the swap: a
whole-shell override keeps the replaced material's transparency, or the iMac's
Screen_Glass_0 sheet would fuse into a white plate over the display.
NO SCENE HERE HAS AN ENVIRONMENT — the port doesn't, so the device scenes
don't either. That is the whole reason the subjects go from bright on the back
half of each orbit to near-black at the front: an environment map is the only
thing that would deliver light from the camera's side of the room, and there
isn't one. A #ffffff albedo returns everything it receives and nothing is
still nothing. The five scenes carried `environment(0.18)` while the casings
were the GLB's black metal and needed something to reflect; `subject` is
metalness 0, so that reason retired with them.- Topic
- lights
- Scenes
- 6
- Kind
- reference rig
//
RECT AREA LIGHT — a one-scene port of three.js `webgl_lights_rectarealight`,
kept faithful on purpose: it is the acceptance test for the area light, the
torus-knot primitive and the procedural checker roughness map.
Everything is the example's numbers × 100, because the authoring unit here is
the MILLIMETRE (packages/core/units.ts) and the example's unit is arbitrary.
Area-light irradiance is a solid angle, so a uniform scale of the whole
scene — lights, subject, camera — leaves the exposure untouched.
three.js here
RectAreaLight(0xff0000, 5, 4, 10) light(type=area color=#ff0000
intensity=5 width=400 height=1000)
RectAreaLightHelper(light) helper=true (the light's own emissive
rectangle — colour × intensity)
TorusKnotGeometry(1.5,0.5,200,16) torusknot(r=150 tube=50
tubular_segments=200
radial_segments=16)
roughnessMap = checker(400) roughness_map="checker:400"
roughness_map_filter=nearest
rectLight.rotation.y += ±delta a linear rotation.y track
The example spins its lights off the frame clock forever (-1, +0.5 and +1
rad/s); a reel has a length, so the same rates are baked into 12s of linear
keyframes: -12 rad = -687.55°, +6 rad = +343.77°, +12 rad = +687.55°.
NOTE ON THE LOOK: there is no ambient, no environment and no haze anywhere in
this reel, and that is the whole demonstration — every photon on every subject
and on the floor comes off the three rectangles. Tone mapping is off (`None`) so the frame
matches the example's untone-mapped output rather than the reel default.
SCENES 2–6 · THE SAME RIG, A REAL DEVICE ON THE PLINTH
The knot is a test object; a device is the thing this engine actually shoots.
Each of the five swaps the subject and keeps the composition —
but a device renders at its TRUE PHYSICAL SIZE (an iPhone is 159.5mm tall, an
iMac 461mm), so the soft-box rig is scaled to the subject exactly the way a
photographer picks a soft-box for what is on the table. One factor k per
scene multiplies every length in the rig — light size, light position, camera
distance, subject height, floor, check count — so all six scenes are the SAME
photograph at six sizes:
s1 knot k = 1.00 400mm subject s4 macbook k = 0.90 354.8mm
s2 iphone k = 0.40 159.5mm s5 chrome k = 1.20 480mm
s3 ipad k = 0.70 280.6mm s6 imac k = 1.35 547mm
Devices face +Z out of the box, so each one is yawed 180° to present its
screen to the -Z side of the rig — the side the example's camera shoots from.
BACK → FRONT. Scene 1 keeps the example's fixed camera, because it is a port
and the port has to stay honest. Every device scene instead ORBITS 155°: it
opens behind the device on the LIT side (the soft-boxes face -Z, so it is the
rear shell they rake — the iPhone's camera island, the iMac's back and stand),
swings out through a three-quarter and a full side profile, and lands dead
front on the screen. One second of hold at each end, constant angular rate in
between. The arc is authored as CHORDS — eight position keyframes sampling the
circle — because a position track interpolates in a straight line, and two
keyframes from back to front would fly the camera through the middle of the
device. `look_at` has to be a track too: a static look_at attr is read once at
build, so an animated camera with one would slide sideways still facing its
original heading.
`side=double` on the shared material is there for scene 5 alone. A <chrome>
window is not a body, it is two flat planes — and a plane is single-sided, so
without it the browser wi