Render a film stock on the GPU, and let it take over the rendering
The stock model landed in dr-film with no way to see it. This is the pipeline node, the two texture bindings it reads, and the end-to-end test that proves the shader agrees with the model. The design point is that a film simulation is not an adjustment. Every other node changes a picture; this one makes it. A stock's characteristic curve does the camera profile's base curve's job -- from measurements rather than from a curve somebody drew -- so running both renders the scene twice: the camera's rendering, and then a film's rendering of that. It looks like neither, and it reads as a colour-management bug with no colour-management bug to find. So `Operation::renders` is new. A node declaring it takes camera RGB and hands back linear sRGB, and the composer emits neither the base curve nor the conversion out of camera space. Both halves move together, and the composer keeps them as one string precisely so that getting half of it right is impossible. The tables are not parameters, for the reason vignetting's coefficients are not: they are measurements. dr-pipeline declares the layout as a plain struct and keeps its no-dependency property; the two crates share no types on purpose. `EditGraph::set_film_tables` offers them to every node rather than to the one that wants them, because knowing which concrete type is which is what the graph is organised not to know. Bindings 4 and 5 follow the masks precedent: declared unconditionally so one bind group layout serves every generated shader, bound to 1x1 placeholders when no stock is loaded. Both are interpolated by hand with textureLoad -- this pipeline binds no sampler, and adding one for two lookups would cost a binding in every shader. Uploads are keyed on content so an unchanged stock does not push half a megabyte across the bus per frame. The end-to-end test earned its place immediately: it found the density lookup being filled z-fastest while a 3D texture upload wants x-fastest, so the red and blue axes were transposed. Green matched exactly, which is what that bug looks like -- a plausible photograph of the wrong colour, and one that every unit test on either side of the seam passes. dr-film now pins the layout in a test that needs no device, and states it where the field is declared. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -326,6 +326,29 @@ own JPEG. ΔE2000 validation against ColorChecker references applies once DCP su
|
||||
simulation ecosystem at near-zero implementation cost, plus reading the in-RAF film simulation tag
|
||||
to auto-apply a matching render for Fujifilm files.
|
||||
|
||||
**Spectral film simulation, in addition rather than instead** (`dr-film`). Where a stock's
|
||||
measurements exist, simulate the physics instead of replaying a grade: spectral sensitivity exposes
|
||||
three emulsion layers, characteristic curves develop them to densities, dye densities absorb, and a
|
||||
paper profile prints the negative with the enlarger's filtration solved rather than dialled. A
|
||||
scanned negative is therefore orange and inverted, because that is what a negative is.
|
||||
|
||||
Two things this buys that a LUT cannot. The parameters stay **physical** — opening up a stop moves
|
||||
the picture along the film's own curve, shoulder and all, rather than scaling a number baked at one
|
||||
exposure. And the **data cost inverts**: a stock is ~17 kB of published measurements where one
|
||||
HaldCLUT is ~800 kB of one person's grade.
|
||||
|
||||
A film simulation is a *rendering*, not an adjustment, so it replaces the camera profile's base
|
||||
curve and the conversion out of camera space (`Operation::renders`) — applying both would render
|
||||
the scene twice.
|
||||
|
||||
*Acceptance:* a neutral scene printed through a colour negative's own paper renders neutral to
|
||||
within 0.06 in linear sRGB; the baked lookup's interpolation error stays under one 8-bit code
|
||||
value; and the shader agrees with the CPU model, which agrees in turn with an independent
|
||||
reference implementation.
|
||||
|
||||
**Open:** how the chosen stock persists. Sidecar parameters are `f32` and the stock list is
|
||||
data-driven, so neither an index nor a name fits the existing shape.
|
||||
|
||||
**FR-DEV-3g — AI denoise.** Learned denoising operating in the raw domain, ideally jointly with
|
||||
demosaic.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user