mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-01 05:58:19 -06:00
Levels: channel controls before RGB, "Off for 32 bpc Color" unclipped in 32 bpc, as After Effects
After Effects 2026 in a 32 bpc project, Levels (Individual Controls) with RGB (0.1, 0.9, gamma 1.5, 0.05, 0.95), Red Gamma 0.8, Green Input White 0.8 and Blue Output Black 0.2 on grey steps 0..255 (case 904 E07): every step matches "each channel's controls, then the RGB controls" with nothing clipped (values below Input Black end below Output Black, above Input White above Output White). EffectCraft ran RGB first and clipped "Off for 32 bpc Color" (it could not see the bit depth): up to 27/255 off on 95 % of the grid; now 0 grid points off, max 1/255, CPU and GPU. EffectEnv gains float_depth (the renderer sets it for 32 bpc projects), so "Off for 32 bpc Color" clips in 8 / 16 bpc projects as before. Unclipped values below 0 take the gamma mirrored through 0. Plain Levels keeps its order (its channel controls were not measured); its clipping follows the same rule. EffectCraft's own projects default to 8 bpc and are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
617b0a578d
commit
515cc3d5f7
@@ -252,6 +252,7 @@ pub(crate) fn chain_key(chain: &[FxStep], buf: &Buf, levels: Option<f32>) -> u12
|
||||
k.u64(e.effect_index as u64);
|
||||
k.debug(&e.working_space);
|
||||
k.u64(e.working_linear as u64);
|
||||
k.u64(e.float_depth as u64);
|
||||
k.debug(&e.shutter);
|
||||
}
|
||||
k.finish()
|
||||
|
||||
@@ -20,6 +20,14 @@ fn fxt_rem360(x: f32) -> f32 {
|
||||
}
|
||||
|
||||
// Levels (Individual Controls) channel `i` (data: 5 × (ib, iw, g, ob, ow, identity)).
|
||||
// color_fx::levels_gamma: unclipped values below 0 mirror the curve.
|
||||
fn fxt_gamma(t: f32, g: f32) -> f32 {
|
||||
if (t >= 0.0) {
|
||||
return powz(t, 1.0 / g);
|
||||
}
|
||||
return -powz(-t, 1.0 / g);
|
||||
}
|
||||
|
||||
fn fxt_lv(i: u32, v: f32, clip_b: bool, clip_w: bool) -> f32 {
|
||||
let o = i * 6u;
|
||||
if (data[o + 5u] != 0.0) {
|
||||
@@ -32,7 +40,7 @@ fn fxt_lv(i: u32, v: f32, clip_b: bool, clip_w: bool) -> f32 {
|
||||
if (clip_w) {
|
||||
t = min(t, 1.0);
|
||||
}
|
||||
return data[o + 3u] + (data[o + 4u] - data[o + 3u]) * powz(max(t, 0.0), 1.0 / max(data[o + 2u], 0.01));
|
||||
return data[o + 3u] + (data[o + 4u] - data[o + 3u]) * fxt_gamma(t, max(data[o + 2u], 0.01));
|
||||
}
|
||||
|
||||
// color_fx::level (data offset o: ib, iw, g, ob, ow, clip black, clip white).
|
||||
@@ -44,8 +52,7 @@ fn fxt_level(o: u32, v: f32) -> f32 {
|
||||
if (data[o + 6u] != 0.0) {
|
||||
t = min(t, 1.0);
|
||||
}
|
||||
t = powz(max(t, 0.0), 1.0 / data[o + 2u]);
|
||||
return data[o + 3u] + (data[o + 4u] - data[o + 3u]) * t;
|
||||
return data[o + 3u] + (data[o + 4u] - data[o + 3u]) * fxt_gamma(t, data[o + 2u]);
|
||||
}
|
||||
|
||||
fn fxt_level_ident(o: u32) -> bool {
|
||||
@@ -177,7 +184,7 @@ fn fxt_point(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let aa = max(a, 0.0);
|
||||
var nc: vec3<f32>;
|
||||
for (var i = 0u; i < 3u; i++) {
|
||||
nc[i] = fxt_lv(i + 1u, fxt_lv(0u, c[i], cb, cw), cb, cw);
|
||||
nc[i] = fxt_lv(0u, fxt_lv(i + 1u, c[i], cb, cw), cb, cw);
|
||||
}
|
||||
let na = clamp(clamp(fxt_lv(4u, aa, cb, cw), 0.0, 1.0), 0.0, 1.0);
|
||||
o = vec4<f32>(nc * na, na);
|
||||
|
||||
@@ -703,7 +703,12 @@ fn pointwise(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
if (P.u[0].z != 0u) {
|
||||
t = min(t, 1.0);
|
||||
}
|
||||
t = powz(max(t, 0.0), 1.0 / g);
|
||||
// color_fx::levels_gamma: unclipped values below 0 mirror the curve.
|
||||
if (t >= 0.0) {
|
||||
t = powz(t, 1.0 / g);
|
||||
} else {
|
||||
t = -powz(-t, 1.0 / g);
|
||||
}
|
||||
o[i] = ob + (ow - ob) * t;
|
||||
}
|
||||
px = vec4<f32>(o * a, a);
|
||||
|
||||
Reference in New Issue
Block a user