Onion tint: screen-blend instead of multiply (so outlines tint too)
Multiplicative tint left blacks black, so outlines (the main thing to ghost in line art) never picked up the warm/cool color. Switched the canvas-blit tint to a screen blend (out = base + tint - base*tint): black → tint color, white unchanged, and a clean no-op at tint=(0,0,0) for all normal blits. Reverted the default .w slots to 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
53cae1bfe5
commit
4dc937f9c0
|
|
@ -2030,16 +2030,17 @@ impl BlitTransform {
|
||||||
// Column-major 3×3: col0=(a,b,0), col1=(c,d,0), col2=(e,f,1)
|
// Column-major 3×3: col0=(a,b,0), col1=(c,d,0), col2=(e,f,1)
|
||||||
let [a, b, c, d, e, f] = combined.as_coeffs();
|
let [a, b, c, d, e, f] = combined.as_coeffs();
|
||||||
// The .w slots are unused by the 3×3 matrix; the shader reads them as an RGB
|
// The .w slots are unused by the 3×3 matrix; the shader reads them as an RGB
|
||||||
// tint (1,1,1 = no tint — the default for all normal blits).
|
// screen-blend tint ((0,0,0) = no tint — the default for all normal blits).
|
||||||
Self {
|
Self {
|
||||||
col0: [a as f32, b as f32, 0.0, 1.0],
|
col0: [a as f32, b as f32, 0.0, 0.0],
|
||||||
col1: [c as f32, d as f32, 0.0, 1.0],
|
col1: [c as f32, d as f32, 0.0, 0.0],
|
||||||
col2: [e as f32, f as f32, 1.0, 1.0],
|
col2: [e as f32, f as f32, 1.0, 0.0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Multiply the sampled color by an RGB tint (for onion-skin ghosts). Packed into
|
/// Screen-blend the sampled color toward an RGB tint (for onion-skin ghosts), so
|
||||||
/// the matrix uniform's `.w` slots, so no extra binding is needed.
|
/// blacks/outlines pick up the tint. Packed into the matrix uniform's `.w` slots,
|
||||||
|
/// so no extra binding is needed.
|
||||||
pub fn with_tint(mut self, r: f32, g: f32, b: f32) -> Self {
|
pub fn with_tint(mut self, r: f32, g: f32, b: f32) -> Self {
|
||||||
self.col0[3] = r;
|
self.col0[3] = r;
|
||||||
self.col1[3] = g;
|
self.col1[3] = g;
|
||||||
|
|
|
||||||
|
|
@ -67,8 +67,11 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
let mask = textureSample(mask_tex, mask_sampler, canvas_uv).r;
|
let mask = textureSample(mask_tex, mask_sampler, canvas_uv).r;
|
||||||
let masked_a = c.a * mask;
|
let masked_a = c.a * mask;
|
||||||
let inv_a = select(0.0, 1.0 / c.a, c.a > 1e-6);
|
let inv_a = select(0.0, 1.0 / c.a, c.a > 1e-6);
|
||||||
// RGB tint packed into the matrix uniform's unused .w slots (1,1,1 = no tint).
|
// RGB tint packed into the matrix uniform's unused .w slots ((0,0,0) = no tint).
|
||||||
// Used by onion-skin ghosts (warm = past, cool = future).
|
// Screen-blended so it recolors blacks too (outlines → warm/cool ghosts) and
|
||||||
|
// leaves white alone: out = base + tint - base*tint. Used by onion-skin ghosts.
|
||||||
let tint = vec3<f32>(transform.col0.w, transform.col1.w, transform.col2.w);
|
let tint = vec3<f32>(transform.col0.w, transform.col1.w, transform.col2.w);
|
||||||
return vec4<f32>(c.r * inv_a * tint.r, c.g * inv_a * tint.g, c.b * inv_a * tint.b, masked_a);
|
let base = vec3<f32>(c.r * inv_a, c.g * inv_a, c.b * inv_a);
|
||||||
|
let tinted = base + tint - base * tint;
|
||||||
|
return vec4<f32>(tinted, masked_a);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue