Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit65083fe8f2

README.md

lensOptics

The render passes behind the LensFx component and the lensFx module.

Three compute passes after lighting.

lfx_bright reduces the frame to the parts bright enough to have an optical response, at quarter resolution. The block each texel stands for is averaged rather than point-sampled, so a single bright pixel still contributes. The threshold has a soft knee below it: a hard cut makes a bright object's flare pop on and off as it crosses the boundary, and the knee rolls the contribution in so the response is continuous.

lfx_features builds the flare. A flare is not painted at the light — it is what the lens does with the light. Each air-glass interface reflects a little of the incoming cone back down the barrel, and those reflections land on the sensor at positions mirrored through the optical axis. That is the whole geometry of the effect: sampling the bright buffer at points reflected through the centre of the frame. It is why the ghosts sweep across the frame as a light moves rather than sitting on top of it, and why a source near the axis produces ghosts bunched near the axis.

Each feature carries a share of its source, and that share is the bound on the whole effect: a reflection returns a fraction of the cone that entered, so a ghost lands about a hundredth of the light its source put in, the halo less (the same reflection spread around a ring) and the streak less again (one smeared along a whole axis). The bright buffer holds light, so a share of it is light — a lamp at 20 flares ten times as hard as one at 2. What a feature lands is its share times flareIntensity, so at the lens as built (intensity 1, the largest share a hundredth) a source flares at a fraction of what it shines.

The chain marches from the mirrored source along that axis, each ghost a step further, fading as it approaches the frame edge where the reflected cone would be vignetted by the barrel. Each ghost pulls its three channels to slightly different radii, because glass disperses and a real ghost carries colour fringing rather than being a tinted copy. The halo is the widest of those reflections wrapping into a ring at a fixed radius, which is why a bright light near the edge throws an arc rather than another disc. The streak is a cylindrical element spreading a highlight along one axis, gathered at each pixel's own position because it belongs to the source rather than to its reflections.

lfx_composite adds the flare back and lays down the grain. The flare is read bilinearly out of the quarter-resolution buffer so its edges stay soft instead of stepping in blocks of four pixels.

The dirt mask modulates the flare, not the scene: a clean lens shows the same picture, just without the bloom-driven grime lighting up. It is a two-octave value noise field, so it needs no texture asset and follows dirtScale continuously.

The grain is drawn against a clock the module advances, so it is a fresh field every frame. Grain size is in pixels because silver halide crystals are a fixed physical size and do not scale with what is in front of the lens. Its amplitude peaks in the midtones and falls away at both ends — shadows hold too little exposed silver to vary and highlights are saturated — which is why film grain reads as texture in skin and skies rather than as uniform noise over the frame.

Parameters arrive through the lens_fx_params buffer, packed by the lensFx module.