Make the view transform an operation the photographer can set
FR-DEV-3j gives the view transform two controls, contrast and the white point in stops above middle grey, persisted and held per mask layer like any other setting. A scene-referred pipeline whose white point cannot be moved hands the photographer a shoulder they cannot place. `view_transform` is a hand-written node in `Stage::View`, a new stage the composer emits last and emits whatever the node's state: a neutral operation is otherwise left out of the shader, but a photograph with no view transform is a scan. "Active" keeps meaning "moved from the defaults", so an untouched photograph writes nothing for it and `every_node_starts_neutral` still holds. A caller whose chain holds no view operation gets a default one. The composer's loop becomes an ordered list of steps — camera nodes, the matrix, scene nodes, layer-only nodes, the view — so each is emitted in exactly one place. The base curve could not be a node because it belonged to the camera; the ops README records why that argument went with it (D19). The panel shows it in the Light group as "Tone Mapping". Tests that counted the blocks of a neutral graph now count one, the view transform, and the two chain-wide tests that load a film expect the view transform to be absent, since a stock replaces it.
This commit is contained in:
@@ -293,19 +293,25 @@ in raw pixels is a different photograph on screen and in the exported file.
|
||||
|
||||
## What is not a node, and why
|
||||
|
||||
Three things act on every pixel and are deliberately not in this directory:
|
||||
the as-shot white balance, the camera matrix, and the **view transform**
|
||||
(FR-DEV-3j). They are emitted by [`../src/operation.rs`](../src/operation.rs)
|
||||
into the composed shader around the block of nodes.
|
||||
|
||||
The first two are properties of the *file*, at the same standing as the
|
||||
Two things act on every pixel and are deliberately not in this directory: the
|
||||
as-shot white balance and the camera matrix. They are emitted by
|
||||
[`../src/operation.rs`](../src/operation.rs) into the composed shader around the
|
||||
block of nodes. They are properties of the *file*, at the same standing as the
|
||||
masked-photosite crop (FR-RAW-3) and the stored orientation (FR-DEV-3h): nobody
|
||||
chose the sensor's green sensitivity, and reading the file correctly means
|
||||
undoing it. The view transform is different in kind — it is the one stage that
|
||||
maps scene-linear colour to a display range (D19, ARCH §6.14) — and it runs
|
||||
after every node, because corrections to capture are only meaningful on linear
|
||||
values. It replaced the per-body **base curve**, which was looked up by camera
|
||||
model and flat past 1.0, so it clipped every recovered highlight.
|
||||
undoing it.
|
||||
|
||||
The **view transform** (FR-DEV-3j) *is* a node — `view_transform.yaml`, a
|
||||
`rust:` one — and that is a change of mind worth knowing about. It replaced the
|
||||
per-body base curve, which was kept out of this directory because it belonged
|
||||
to the camera: as a node it would have carried one body's rendering onto
|
||||
another body's file through a shared sidecar. D19 retired the per-body curves,
|
||||
and with them the argument. One view transform serves every body, so its
|
||||
settings are a decision about the picture like any other. What is still
|
||||
special about it is `Stage::View`: the composer emits it at the end of the
|
||||
chain *whatever its state*, because a photograph with no view transform is a
|
||||
scan and not a picture. Its neutral is its defaults, like every other node's,
|
||||
so an untouched photograph writes nothing for it.
|
||||
|
||||
## Stages
|
||||
|
||||
@@ -315,7 +321,10 @@ primaries, scene-referred and unbounded. White balance is the only camera
|
||||
node, because its multipliers scale the sensor's own channels. Everything else
|
||||
belongs in the scene, where a hue or a luminance weight means the same thing
|
||||
whichever body took the frame (D19). The composer emits the camera nodes, then
|
||||
the matrix, then the scene nodes, each group in `order:`.
|
||||
the matrix, then the scene nodes, each group in `order:`, and the view
|
||||
transform last. `stage: view` is not offered to a declaration: a node that
|
||||
maps into a display range is exactly what ARCH §6.14 forbids of everything
|
||||
before the end, and the one that is allowed to is hand-written.
|
||||
|
||||
## Errors
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
id: view_transform
|
||||
order: 200
|
||||
# A `rust:` node publishes its own descriptor; its attributes are on the type
|
||||
# in `../src/ops/view_transform.rs`.
|
||||
rust: ViewTransform
|
||||
|
||||
why_rust: |
|
||||
It is composed at its defaults — a photograph with no view transform is a
|
||||
scan, not a picture — which is `Stage::View`, and a declaration has no way to
|
||||
say it. Its three uniforms are also the solution of two equations rather than
|
||||
expressions over its parameters (`dr_pipeline::view::Sigmoid::new`).
|
||||
|
||||
placement: |
|
||||
Last, after every scene operation and, when there is one, after the detail
|
||||
stage (D19, FR-DEV-3j). It is the one stage allowed to map scene-linear colour
|
||||
to a display range, so anything after it would be working on a rendering.
|
||||
The order here only places it in the panel; the composer puts every
|
||||
`Stage::View` node at the end whatever its number says.
|
||||
Reference in New Issue
Block a user