camera-dof-bokeh2
camera-dof-bokeh2
Camera depth-of-field primitives — the whole DOF surface, one knob per scene.
Read the rig's notes
THIS IS THE ALL-BOKEH2 TWIN of camera-dof.pug: every camera in every scene
carries dof_pass="bokeh2" on a 42mm lens, so the whole reel is one lens and
the only variables left are the ones each scene names. The mixed reel next
door keeps the default camera_dof pass for scenes 1-7; read them side by
side to see what the pass itself is worth.
DOF is not a <postprocessing> step you add: writing ANY of `focus`,
`aperture` or `max_blur` on a <camera> (or on any camera <kf>) auto-injects
the pass and drives it from camera.userData.dof every frame
(engine/yaml-runtime, "Shallow DOF"). There is nothing else to turn on.
THE THREE AUTHORED VALUES — the only ones that animate
focus the sharp distance. Three spellings, all live:
focus=9.3 a distance in world units
focus="0 0 0.2" a world POINT — whatever sits there
focus="chaser" a NODE id — tracks that node's position
Point/node forms re-resolve every frame against the live
camera pose (lib/focus-target.ts), so a moving camera or a
moving subject needs no hand-computed distances.
aperture how FAST blur grows away from the focal plane. The default
pass ramps over `range = max_blur ÷ aperture` world units and
then flattens, so a bigger aperture = a shorter ramp.
max_blur the blur CEILING, as a fraction of frame width (0.03 ≈ 32px
on a 1080p frame). It is also the numerator of `range`, so
raising the ceiling softens the ramp unless aperture rises
with it.
All three interpolate per <kf> on the camera's <frame> keyframes — that is
what a rack focus IS here.
TWO PASSES — dof_pass=
(default) camera_dof (lib/postprocessing/camera-dof-pass.ts). Smoothstep
CoC, 80-tap Vogel disc with a per-pixel golden-angle phase,
scatter-as-gather weighting so a sharp subject cannot smear a
halo into the soft field around it. No highlight model: an
out-of-focus specular just spreads and dims.
"bokeh2" the three.js webgl_postprocessing_dof2 port (BokehShader2).
A real thin lens: fstop = 0.0072 ÷ aperture, focalLength in
mm, CoC 0.03mm, plus per-channel fringe and a highlight gain
that keeps blown-out points bright as they spread — which is
what makes bokeh balls read as bokeh balls.
BOKEH2-ONLY KNOBS — dof_* camera attrs, read once when the pass is built
(so they are scene-level, not keyframable):
dof_rings dof_samples tap budget of the ring gather (3 / 4)
dof_fstop dof_focal_length override the thin lens directly
dof_pentagon pentagon iris instead of a round one
dof_threshold dof_gain dof_bias the highlight model
dof_fringe chromatic fringing on the gather
dof_vignetting dof_noise dof_dithering rim falloff + grain
dof_showFocus debug tint: orange sharp, blue on ramp
dof_exclude_sharp=false restore stock (halo-prone) gathering
dof_manualdof dof_depthblur dof_shaderFocus the remaining stock flags
THE STAGE · one lens (fov 34 at 0 0.15 9.5) and one light rig in every
scene, and four depth planes that never move:
laptop z=-5.5 → screen at 18.0 ipad z=-2.0 → 11.5
panel z= 0.2 → 9.3 iphone z= 3.6 → 5.9
The laptop is the one to watch: the rig origin is 15.0 out, but an open lid
leans its SCREEN about three units further back, so the plane you focus on
is 18.0 — measured with dof_showFocus, not assumed. The five scattered
accents do sit on their nominal planes (15.0 / 11.5 / 9.3 / 5.9 / 4.9),
which is why the far spheres go soft while the laptop screen is sharp.
Scenes dress those planes three ways — a device desk (laptop · tablet ·
floating panel · phone), a wall of floating UI behind a browser, and the
desk again with a field of blown-out points for bokeh — plus five scattered
primitives, one per plane, so the falloff also reads on plain geometry.
Subjects, haze lobes and accents carry `match` ids the whole way, so
match-move gaps morph the set; the A/B pairs (passes, tap budgets) cut hard
on `transition=none` so nothing but the named attribute differs.
Every piece of type sits on the plane it is talking about, which is why a
readout goes soft the moment its beat ends.
A NOTE ON focus TARGETS · the point and node-id forms resolve per frame on
the CAMERA (scene 5 and scene 6 prove it). Written on a <kf> instead they
come back as the resolver's 0.001 floor in this build, which collapses the
thin lens (focalDepth < focalLength) and the frame goes flat — so a rack
across keyframes uses distances, as scene 14 does.
Preview: /packages/runtime/index.html?project=promos/primitives/camera-dof/camera-dof-bokeh2.promo- Topic
- camera
- Scenes
- 15
- Kind
- reference rig
//
Camera depth-of-field primitives — the whole DOF surface, one knob per scene.
THIS IS THE ALL-BOKEH2 TWIN of camera-dof.pug: every camera in every scene
carries dof_pass="bokeh2" on a 42mm lens, so the whole reel is one lens and
the only variables left are the ones each scene names. The mixed reel next
door keeps the default camera_dof pass for scenes 1-7; read them side by
side to see what the pass itself is worth.
DOF is not a <postprocessing> step you add: writing ANY of `focus`,
`aperture` or `max_blur` on a <camera> (or on any camera <kf>) auto-injects
the pass and drives it from camera.userData.dof every frame
(engine/yaml-runtime, "Shallow DOF"). There is nothing else to turn on.
THE THREE AUTHORED VALUES — the only ones that animate
focus the sharp distance. Three spellings, all live:
focus=9.3 a distance in world units
focus="0 0 0.2" a world POINT — whatever sits there
focus="chaser" a NODE id — tracks that node's position
Point/node forms re-resolve every frame against the live
camera pose (lib/focus-target.ts), so a moving camera or a
moving subject needs no hand-computed distances.
aperture how FAST blur grows away from the focal plane. The default
pass ramps over `range = max_blur ÷ aperture` world units and
then flattens, so a bigger aperture = a shorter ramp.
max_blur the blur CEILING, as a fraction of frame width (0.03 ≈ 32px
on a 1080p frame). It is also the numerator of `range`, so
raising the ceiling softens the ramp unless aperture rises
with it.
All three interpolate per <kf> on the camera's <frame> keyframes — that is
what a rack focus IS here.
TWO PASSES — dof_pass=
(default) camera_dof (lib/postprocessing/camera-dof-pass.ts). Smoothstep
CoC, 80-tap Vogel disc with a per-pixel golden-angle phase,
scatter-as-gather weighting so a sharp subject cannot smear a
halo into the soft field around it. No highlight model: an
out-of-focus specular just spreads and dims.
"bokeh2" the three.js webgl_postprocessing_dof2 port (BokehShader2).
A real thin lens: fstop = 0.0072 ÷ aperture, focalLength in
mm, CoC 0.03mm, plus per-channel fringe and a highlight gain
that keeps blown-out points bright as they spread — which is
what makes bokeh balls read as bokeh balls.
BOKEH2-ONLY KNOBS — dof_* camera attrs, read once when the pass is built
(so they are scene-level, not keyframable):
dof_rings dof_samples tap budget of the ring gather (3 / 4)
dof_fstop dof_focal_length override the thin lens directly
dof_pentagon pentagon iris instead of a round one
dof_threshold dof_gain dof_bias the highlight model
dof_fringe chromatic fringing on the gather
dof_vignetting dof_noise dof_dithering rim falloff + grain
dof_showFocus debug tint: orange sharp, blue on ramp
dof_exclude_sharp=false restore stock (halo-prone) gathering
dof_manualdof dof_depthblur dof_shaderFocus the remaining stock flags
THE STAGE · one lens (fov 34 at 0 0.15 9.5) and one light rig in every
scene, and four depth planes that never move:
laptop z=-5.5 → screen at 18.0 ipad z=-2.0 → 11.5
panel z= 0.2 → 9.3 iphone z= 3.6 → 5.9
The laptop is the one to watch: the rig origin is 15.0 out, but an open lid
leans its SCREEN about three units further back, so the plane you focus on
is 18.0 — measured with dof_showFocus, not assumed. The five scattered
accents do sit on