Convert to the working space before the edits, not after
Every point operation ran in camera RGB and the camera matrix came after them all, against the order ARCH §5.2 draws. So `luminance()` applied Rec.709 weights to a body's own primaries, a band in the colour mixer was a different hue on every make of sensor, and the vibrance skin guard tested channel order in a space where skin does not have one. Operations now declare a `Stage`. White balance is the only camera-stage node — its multipliers scale the sensor's channels, and after a matrix that mixes them the same numbers are a different correction — and says so with `stage: camera` in its YAML, a key both the build-time generator and the load-time declared op read. The composer emits the camera nodes, then the matrix, then the rest, each group in graph order; an empty chain still gets the matrix. Film simulation stops converting out of camera space itself, since it is now handed working-space colour like every other scene node. The base curve stays where it was, after the operations, and so now acts on working-space colour; the next commit replaces it (D19).
This commit is contained in:
@@ -271,6 +271,32 @@ pub struct Declaration {
|
||||
/// Boxed so the rare node that declares one does not widen every
|
||||
/// declaration by the size of a presentation it does not have.
|
||||
pub presentation: Option<Box<PresentationDef>>,
|
||||
/// Whether the node runs in camera RGB, ahead of the camera matrix —
|
||||
/// `stage: camera`. See [`read_stage`].
|
||||
pub camera_stage: bool,
|
||||
}
|
||||
|
||||
/// TRACES: FR-DEV-3e | FR-DEV-2
|
||||
/// Where in the chain a declared node's colour comes from: `stage: camera` or
|
||||
/// `stage: scene`, the default.
|
||||
///
|
||||
/// Camera RGB is where white balance's multipliers are defined, and it is the
|
||||
/// only thing that belongs there (D19): every other operation is handed
|
||||
/// working-space colour, so that a hue or a luminance weight means the same
|
||||
/// thing whichever body took the frame. `view` is not offered. The view
|
||||
/// transform is hand-written, and a declared node that clipped into a display
|
||||
/// range would be exactly what ARCH §6.14 forbids of every node before it.
|
||||
fn read_stage(root: &Mapping) -> Result<bool, String> {
|
||||
match root.get("stage") {
|
||||
None => Ok(false),
|
||||
Some(v) => match as_str(v, "stage")? {
|
||||
"camera" => Ok(true),
|
||||
"scene" => Ok(false),
|
||||
other => Err(format!(
|
||||
"unknown stage {other:?}; expected \"camera\" or \"scene\""
|
||||
)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl Declaration {
|
||||
@@ -512,6 +538,7 @@ pub fn read_node(text: &str, ctx: &str, shared: &BTreeSet<&str>) -> Result<Node,
|
||||
"define",
|
||||
"label",
|
||||
"attributes",
|
||||
"stage",
|
||||
] {
|
||||
if root.contains_key(key) {
|
||||
return Err(format!(
|
||||
@@ -564,6 +591,7 @@ pub fn read_node(text: &str, ctx: &str, shared: &BTreeSet<&str>) -> Result<Node,
|
||||
.collect();
|
||||
let tests = read_tests(root, ¶ms, &uniform_names, &helper_names)?;
|
||||
let presentation = read_presentation(root, ¶m_names)?;
|
||||
let camera_stage = read_stage(root)?;
|
||||
|
||||
Ok(Node::Declared(Box::new(Declaration {
|
||||
id,
|
||||
@@ -580,6 +608,7 @@ pub fn read_node(text: &str, ctx: &str, shared: &BTreeSet<&str>) -> Result<Node,
|
||||
active,
|
||||
tests,
|
||||
presentation,
|
||||
camera_stage,
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,8 @@ pub struct DeclaredOp {
|
||||
helpers: Vec<Helper>,
|
||||
presentation: Option<Presentation>,
|
||||
order: i64,
|
||||
/// See `decl::read_stage`.
|
||||
camera_stage: bool,
|
||||
}
|
||||
|
||||
/// One uniform: the name the fragment reads it by, and how to compute it.
|
||||
@@ -208,6 +210,7 @@ impl DeclaredOp {
|
||||
wgsl: declaration.wgsl_body(),
|
||||
helpers,
|
||||
presentation: declaration.presentation.as_deref().map(presentation),
|
||||
camera_stage: declaration.camera_stage,
|
||||
order: declaration.order,
|
||||
})
|
||||
}
|
||||
@@ -292,6 +295,14 @@ impl Operation for DeclaredOp {
|
||||
fn presentation(&self) -> Option<Presentation> {
|
||||
self.presentation.clone()
|
||||
}
|
||||
|
||||
fn stage(&self) -> crate::operation::Stage {
|
||||
if self.camera_stage {
|
||||
crate::operation::Stage::Camera
|
||||
} else {
|
||||
crate::operation::Stage::Scene
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A declared parameter as the descriptor the panel reads.
|
||||
|
||||
Reference in New Issue
Block a user