Files
DarkRoom/core/dr-pipeline/ops/vignetting.yaml
dtourolleandClaude Opus 5 e235e99cce Move the film to Effect in the descriptor that is actually read
An earlier commit claimed to move `film_sim` from `[tone, colour]` to
`[effect]` and did not. It edited `ops/film_sim.yaml`, where `attributes:` is
read, validated against the vocabulary, and then dropped: a `rust:` node
publishes its own descriptor, and the type still said tone and colour. The
stock went on appearing in the Light group beside exposure and again in Colour
beside white balance, exactly as before, and every test passed.

Nothing caught it because nothing could. The declaration parsed, the parity
tests compare ids rather than attributes, and an operation filed under the
wrong groups renders perfectly. It surfaced only on screen, as a missing
Effects tab — which is indistinguishable from a category that genuinely has
nothing in it, and is precisely how `Optics` looked for as long as it was
empty.

So three changes rather than one:

`FilmSim`'s descriptor declares `Attribute::Effect`, which is the move the
earlier commit described.

`attributes:` joins the keys a `rust:` node may not carry, beside `params`,
`uniforms`, `wgsl`, `helpers`, `define` and `label`. The rule was already
written — "its descriptor comes from the type" — and attributes were the one
field that slipped past it. A key that is silently ignored is worse than one
that is rejected, because it reads as though it worked; the eight hand-written
declarations lose a line that never did anything.

And a test asserts that every attribute the chain carries reaches the tab
strip. That is the property that was actually broken, and its failure mode is
invisible from every direction: the controls exist, they are in the shader,
and there is no way to filter to them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 18:28:59 +02:00

40 lines
1.9 KiB
YAML

# A hand-written node, and the first thing in `Attribute::Optics`.
#
# Written, tested and unreferenced until now: `src/ops/vignetting.rs` has
# existed with a full descriptor and a working polynomial, and without an entry
# here it was never in `chain()` — so it reached no photograph and no panel.
# This file is the whole of what was missing, which is the point of `ops/`
# being the one place the pipeline's order is written down.
id: vignetting
order: 5
rust: Vignetting
why_rust: |
It carries a lens profile's `pa` coefficients, which are not parameters: they
come from the body and lens that took the photograph, not from the
photographer, and no `uniforms:` expression could produce them. The one
parameter that *is* theirs — the manual trim — is composed with `k1` in Rust,
because the profile and the trim have to reach the shader as a single
polynomial rather than as two the fragment would have to add up.
placement: |
First in the chain, ahead of white balance and exposure.
Vignetting is what the lens did to the light before the sensor measured it,
so undoing it belongs with reading the file rather than with editing the
picture — everything downstream is then working on the frame the lens would
have delivered had it been even.
The ordering is load-bearing rather than tidy. Correcting a corner means
*dividing* by an attenuation below one, which pushes those pixels up: a fast
prime wide open needs about two stops there. Run after the tonal stages, that
recovery happens once the highlights have already been rolled off and
clipped, so it lifts values that no longer have anywhere to go and the
corners posterise instead of brightening. Run here, the headroom to hold them
still exists (ARCH §5.2).
Before distortion and chromatic aberration in intent, though those are
`lens::Warp`s rather than nodes and compose ahead of the fetch, so no `order:`
relates the two.