M13.22: GPU CC light family (CC Light Rays, CC Light Burst 2.5, CC Light Sweep, CC Light Wipe)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
081ce79de3
commit
f7927c8f9f
|
|
@ -931,6 +931,11 @@ pub const GPU_EFFECTS: &[&str] = &[
|
|||
"ec.perspective.bevelalpha",
|
||||
"ec.perspective.beveledges",
|
||||
"ec.obsolete.gaussianlegacy",
|
||||
// effectcraft-gpu fx_light (the CC light family)
|
||||
"ec.generate.cclightrays",
|
||||
"ec.generate.cclightburst",
|
||||
"ec.generate.cclightsweep",
|
||||
"ec.transition.cclightwipe",
|
||||
];
|
||||
|
||||
/// Effects whose output depends on [`EffectCtx::time`] directly (not only through animated
|
||||
|
|
|
|||
|
|
@ -314,6 +314,7 @@ impl GpuContext {
|
|||
include_str!("shaders/fx_tone.wgsl"),
|
||||
include_str!("shaders/fx_warp.wgsl"),
|
||||
include_str!("shaders/fx_extra.wgsl"),
|
||||
include_str!("shaders/fx_light.wgsl"),
|
||||
]
|
||||
.concat();
|
||||
let module =
|
||||
|
|
@ -373,6 +374,7 @@ impl GpuContext {
|
|||
.chain(crate::fx_tone::KERNELS)
|
||||
.chain(crate::fx_warp::KERNELS)
|
||||
.chain(crate::fx_extra::KERNELS)
|
||||
.chain(crate::fx_light::KERNELS)
|
||||
.map(|e| (e, &layout))
|
||||
.chain(EXT_ENTRIES.iter().map(|e| (e, &layout_ext)))
|
||||
.map(|(e, layout)| {
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@ fn apply(e: &mut Enc, id: &str, ctx: &EffectCtx, b: GBuf) -> Option<GBuf> {
|
|||
_ if crate::fx_tone::IDS.contains(&id) => crate::fx_tone::apply(e, id, ctx, b),
|
||||
_ if crate::fx_warp::IDS.contains(&id) => crate::fx_warp::apply(e, id, ctx, b),
|
||||
_ if crate::fx_extra::IDS.contains(&id) => crate::fx_extra::apply(e, id, ctx, b),
|
||||
_ if crate::fx_light::IDS.contains(&id) => crate::fx_light::apply(e, id, ctx, b),
|
||||
_ => pointwise(e, id, ctx, b),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
//! GPU effects, the CC light family (kernels in `shaders/fx_light.wgsl`, every entry point
|
||||
//! prefixed `flt_`): CC Light Rays, CC Light Burst 2.5, CC Light Sweep and CC Light Wipe, with
|
||||
//! the CPU effects' exact steps. Centres, radii, the wipe's corner distance and ramp are
|
||||
//! computed on the CPU in f64 as the CPU effect does; the kernels work relative to the centre.
|
||||
//! Light Sweep's softened alpha plane is the CPU's `gauss_plane` (box passes, edges repeated).
|
||||
|
||||
use effectcraft_effects::EffectCtx;
|
||||
|
||||
use crate::context::{Enc, GpuImage, Params};
|
||||
use crate::effects::{GBuf, gaussian_blur};
|
||||
|
||||
/// Compute entry points in `fx_light.wgsl`.
|
||||
pub(crate) const KERNELS: &[&str] = &["flt_rays", "flt_burst", "flt_sweep", "flt_wipe"];
|
||||
|
||||
/// Effect ids implemented here.
|
||||
pub(crate) const IDS: &[&str] = &["ec.generate.cclightrays", "ec.generate.cclightburst", "ec.generate.cclightsweep", "ec.transition.cclightwipe"];
|
||||
|
||||
/// Run effect `id` (one of [`IDS`]); `None` = this parameter combination runs on the CPU.
|
||||
pub(crate) fn apply(e: &mut Enc, id: &str, ctx: &EffectCtx, b: GBuf) -> Option<GBuf> {
|
||||
match id {
|
||||
"ec.generate.cclightrays" => light_rays(e, ctx, b),
|
||||
"ec.generate.cclightburst" => light_burst(e, ctx, b),
|
||||
"ec.generate.cclightsweep" => light_sweep(e, ctx, b),
|
||||
"ec.transition.cclightwipe" => light_wipe(e, ctx, b),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A same-size per-pixel kernel over the buffer.
|
||||
fn run(e: &mut Enc, entry: &str, p: &Params, mut b: GBuf, aux: Option<&GpuImage>) -> Option<GBuf> {
|
||||
let out = e.scratch(b.img.width, b.img.height);
|
||||
e.pixels(entry, p, &b.img, aux, &out, None);
|
||||
b.img = out;
|
||||
Some(b)
|
||||
}
|
||||
|
||||
fn rgb(c: [f32; 4]) -> [f32; 4] {
|
||||
[c[0], c[1], c[2], 0.0]
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- CC Light Rays / Burst (generate2.rs)
|
||||
|
||||
fn light_rays(e: &mut Enc, ctx: &EffectCtx, b: GBuf) -> Option<GBuf> {
|
||||
let pr = ctx.params;
|
||||
let c = b.to_px(pr.v2("center"));
|
||||
let k = (pr.f("intensity") / 100.0 * 0.5).clamp(0.0, 0.95);
|
||||
let gain = (pr.f("intensity") / 100.0 * 2.0) as f32;
|
||||
let rad = (pr.f("radius") * b.scale).max(0.5);
|
||||
let ws = (pr.f("warpSoftness") / 50.0).max(0.0);
|
||||
let mut p = Params::default();
|
||||
p.u[0] = [(pr.e("shape") == 1) as u32, pr.b("colorFromSource") as u32, pr.e("transferMode"), 0];
|
||||
p.f[0] = [c.0 as f32, c.1 as f32, k as f32, gain];
|
||||
p.f[1] = [rad as f32, (rad * (1.0 + ws)) as f32 + 1e-3, 0.0, 0.0];
|
||||
p.f[2] = rgb(pr.color("color"));
|
||||
run(e, "flt_rays", &p, b, None)
|
||||
}
|
||||
|
||||
fn light_burst(e: &mut Enc, ctx: &EffectCtx, b: GBuf) -> Option<GBuf> {
|
||||
let pr = ctx.params;
|
||||
let c = b.to_px(pr.v2("center"));
|
||||
let inten = (pr.f("intensity") / 100.0) as f32;
|
||||
let k = (pr.f("rayLength") / 100.0).clamp(0.0, 1.0);
|
||||
let set_color = pr.b("setColor");
|
||||
if k <= 0.0 && (inten - 1.0).abs() < 1e-6 && !set_color {
|
||||
return Some(b);
|
||||
}
|
||||
let mut p = Params::default();
|
||||
p.u[0] = [pr.e("burst"), set_color as u32, 0, 0];
|
||||
p.f[0] = [c.0 as f32, c.1 as f32, k as f32, inten];
|
||||
p.f[1] = rgb(pr.color("color"));
|
||||
run(e, "flt_burst", &p, b, None)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- CC Light Sweep (generate2.rs)
|
||||
|
||||
fn light_sweep(e: &mut Enc, ctx: &EffectCtx, b: GBuf) -> Option<GBuf> {
|
||||
let pr = ctx.params;
|
||||
let c = b.to_px(pr.v2("center"));
|
||||
let dir = pr.f("direction").to_radians();
|
||||
let half = (pr.f("width") * b.scale * 0.5).max(0.5);
|
||||
let edge_t = pr.f("edgeThickness") * b.scale;
|
||||
// The alpha plane (x), softened by util::gauss_plane (edges repeated) when thick enough.
|
||||
let alpha = e.scratch(b.img.width, b.img.height);
|
||||
e.pixels("fxe_alpha", &Params::default(), &b.img, None, &alpha, None);
|
||||
let soft = if edge_t > 0.05 { gaussian_blur(e, &alpha, edge_t * 0.5, edge_t * 0.5, true) } else { alpha };
|
||||
let mut p = Params::default();
|
||||
p.u[0] = [pr.e("shape"), pr.e("lightReceptionMode"), 0, 0];
|
||||
p.f[0] = [c.0 as f32, c.1 as f32, dir.cos() as f32, dir.sin() as f32];
|
||||
p.f[1] = [half as f32, (pr.f("sweepIntensity") / 100.0) as f32, (pr.f("edgeIntensity") / 100.0) as f32, 0.0];
|
||||
p.f[2] = rgb(pr.color("lightColor"));
|
||||
run(e, "flt_sweep", &p, b, Some(&soft))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- CC Light Wipe (transition.rs)
|
||||
|
||||
fn light_wipe(e: &mut Enc, ctx: &EffectCtx, b: GBuf) -> Option<GBuf> {
|
||||
let pr = ctx.params;
|
||||
let done = (pr.f("completion") / 100.0).clamp(0.0, 1.0);
|
||||
if done <= 0.0 {
|
||||
return Some(b);
|
||||
}
|
||||
let c = b.to_px(pr.v2("center"));
|
||||
let shape = pr.e("shape");
|
||||
let (s, co) = pr.f("direction").to_radians().sin_cos();
|
||||
let reverse = pr.b("reverse");
|
||||
let metric = |dx: f64, dy: f64| {
|
||||
let (qx, qy) = (dx * co + dy * s, -dx * s + dy * co);
|
||||
match shape {
|
||||
0 => qx.abs(),
|
||||
2 => qx.abs().max(qy.abs()),
|
||||
_ => qx.hypot(qy),
|
||||
}
|
||||
};
|
||||
// transition::max_corner_dist.
|
||||
let (w, h) = (b.img.width as f64, b.img.height as f64);
|
||||
let maxd = [(0.0, 0.0), (w, 0.0), (0.0, h), (w, h)].iter().map(|(x, y)| metric(x - c.0, y - c.1)).fold(1e-6, f64::max);
|
||||
let edge = if reverse { (1.0 - done) * maxd } else { done * maxd };
|
||||
let band = (maxd * 0.06).max(2.0);
|
||||
let ramp = (done * 20.0).min(1.0) as f32 * ((1.0 - done) * 20.0).min(1.0) as f32;
|
||||
let mut p = Params::default();
|
||||
p.u[0] = [shape, pr.b("colorFromSource") as u32, reverse as u32, 0];
|
||||
p.f[0] = [c.0 as f32, c.1 as f32, s as f32, co as f32];
|
||||
p.f[1] = [edge as f32, band as f32, pr.f("intensity") as f32 / 100.0 * ramp, 0.0];
|
||||
p.f[2] = rgb(pr.color("color"));
|
||||
run(e, "flt_wipe", &p, b, None)
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ mod fx_distort;
|
|||
mod fx_extra;
|
||||
mod fx_generate;
|
||||
mod fx_key;
|
||||
mod fx_light;
|
||||
mod fx_noise;
|
||||
mod fx_stylize;
|
||||
mod fx_tone;
|
||||
|
|
@ -209,6 +210,8 @@ mod tests_fx_generate;
|
|||
#[cfg(test)]
|
||||
mod tests_fx_key;
|
||||
#[cfg(test)]
|
||||
mod tests_fx_light;
|
||||
#[cfg(test)]
|
||||
mod tests_fx_noise;
|
||||
#[cfg(test)]
|
||||
mod tests_fx_stylize;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,231 @@
|
|||
// GPU effects, the CC light family: see src/fx_light.rs. CC Light Rays, CC Light Burst 2.5,
|
||||
// CC Light Sweep and CC Light Wipe, each mirroring the CPU effect operation for operation.
|
||||
// Coordinates are pixel centres relative to the effect's centre, so f32 keeps its precision.
|
||||
|
||||
fn flt_inside(p: vec2<i32>) -> bool {
|
||||
let d = out_dims();
|
||||
return p.x < d.x && p.y < d.y;
|
||||
}
|
||||
|
||||
fn flt_rel(p: vec2<i32>, c: vec2<f32>) -> vec2<f32> {
|
||||
return vec2<f32>(f32(p.x) + 0.5, f32(p.y) + 0.5) - c;
|
||||
}
|
||||
|
||||
// util::smoothstep.
|
||||
fn flt_smoothstep(e0: f32, e1: f32, x: f32) -> f32 {
|
||||
if (abs(e1 - e0) < 1e-9) {
|
||||
return select(1.0, 0.0, x < e0);
|
||||
}
|
||||
let t = clamp((x - e0) / (e1 - e0), 0.0, 1.0);
|
||||
return t * t * (3.0 - 2.0 * t);
|
||||
}
|
||||
|
||||
// util::unpremul's colour.
|
||||
fn flt_unpremul(p: vec4<f32>) -> vec3<f32> {
|
||||
if (p.w > 1e-6) {
|
||||
return p.xyz / p.w;
|
||||
}
|
||||
return vec3<f32>(0.0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- CC Light Rays (generate2::light_rays)
|
||||
// u[0] = (square, colour from source, transfer mode); f[0] = (cx, cy, k, gain);
|
||||
// f[1] = (radius, outer radius + 1e-3); f[2] = colour.
|
||||
|
||||
fn flt_ray_mask(q: vec2<f32>) -> f32 {
|
||||
let d = abs(q);
|
||||
var r = length(d);
|
||||
if (P.u[0].x != 0u) {
|
||||
r = max(d.x, d.y);
|
||||
}
|
||||
return 1.0 - flt_smoothstep(P.f[1].x, P.f[1].y, r);
|
||||
}
|
||||
|
||||
@compute @workgroup_size(16, 16)
|
||||
fn flt_rays(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let p = vec2<i32>(gid.xy);
|
||||
if (!flt_inside(p)) {
|
||||
return;
|
||||
}
|
||||
let c = P.f[0].xy;
|
||||
let rel = flt_rel(p, c);
|
||||
var acc = vec4<f32>(0.0);
|
||||
for (var i = 0u; i < 32u; i++) {
|
||||
let s = 1.0 - P.f[0].z * f32(i) / 32.0;
|
||||
let q = rel * s;
|
||||
let m = flt_ray_mask(q);
|
||||
if (m <= 0.0) {
|
||||
continue;
|
||||
}
|
||||
acc += sample_bilinear(src, c.x + q.x, c.y + q.y) * m;
|
||||
}
|
||||
var ray = acc / 32.0 * P.f[0].w;
|
||||
if (P.u[0].y == 0u) {
|
||||
let l = luminance(flt_unpremul(ray)) * ray.w;
|
||||
ray = vec4<f32>(P.f[2].xyz * l, ray.w);
|
||||
}
|
||||
let la = min(ray.w, 1.0);
|
||||
let px = textureLoad(src, p, 0);
|
||||
var o: vec4<f32>;
|
||||
switch P.u[0].z {
|
||||
case 1u: {
|
||||
o = vec4<f32>(max(px.xyz, ray.xyz), max(px.w, la));
|
||||
}
|
||||
case 2u: {
|
||||
let r4 = vec4<f32>(ray.xyz, la);
|
||||
o = px + r4 - px * r4;
|
||||
}
|
||||
case 3u: {
|
||||
o = vec4<f32>(ray.xyz, la);
|
||||
}
|
||||
default: {
|
||||
let a = clamp(la, 0.0, 1.0);
|
||||
o = vec4<f32>(px.xyz + ray.xyz, clamp(px.w + a * (1.0 - px.w), 0.0, 1.0));
|
||||
}
|
||||
}
|
||||
textureStore(out, p, o);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- CC Light Burst 2.5 (generate2::light_burst)
|
||||
// u[0] = (burst, set colour); f[0] = (cx, cy, ray length k, intensity); f[1] = colour.
|
||||
|
||||
@compute @workgroup_size(16, 16)
|
||||
fn flt_burst(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let p = vec2<i32>(gid.xy);
|
||||
if (!flt_inside(p)) {
|
||||
return;
|
||||
}
|
||||
let c = P.f[0].xy;
|
||||
let rel = flt_rel(p, c);
|
||||
let mode = P.u[0].x;
|
||||
var acc = vec4<f32>(0.0);
|
||||
var wsum = 0.0;
|
||||
var best = vec4<f32>(0.0);
|
||||
var best_l = -1.0;
|
||||
for (var i = 0u; i < 32u; i++) {
|
||||
let f = f32(i) / 32.0;
|
||||
let s = 1.0 - P.f[0].z * f;
|
||||
let q = rel * s;
|
||||
let v = sample_bilinear(src, c.x + q.x, c.y + q.y);
|
||||
if (mode == 0u) {
|
||||
let l = luminance(v.xyz) + v.w * 1e-3;
|
||||
if (l > best_l) {
|
||||
best_l = l;
|
||||
best = v;
|
||||
}
|
||||
} else if (mode == 1u) {
|
||||
let w = 1.0 - f;
|
||||
acc += v * w;
|
||||
wsum += w;
|
||||
} else {
|
||||
acc += v;
|
||||
wsum += 1.0;
|
||||
}
|
||||
}
|
||||
var o = best;
|
||||
if (mode != 0u) {
|
||||
o = acc / max(wsum, 1e-6);
|
||||
}
|
||||
if (P.u[0].y != 0u) {
|
||||
let a = max(o.w, 0.0);
|
||||
let l = luminance(flt_unpremul(o)) * a;
|
||||
o = vec4<f32>(P.f[1].xyz * l, a);
|
||||
}
|
||||
let k = P.f[0].w;
|
||||
textureStore(out, p, vec4<f32>(o.xyz * k, clamp(o.w * k, 0.0, 1.0)));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- CC Light Sweep (generate2::light_sweep)
|
||||
// aux = the (softened) alpha plane in x. u[0] = (shape, reception mode); f[0] = (cx, cy, nx, ny);
|
||||
// f[1] = (half width, sweep, edge intensity); f[2] = light colour.
|
||||
|
||||
@compute @workgroup_size(16, 16)
|
||||
fn flt_sweep(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let p = vec2<i32>(gid.xy);
|
||||
if (!flt_inside(p)) {
|
||||
return;
|
||||
}
|
||||
let px = textureLoad(src, p, 0);
|
||||
let rel = flt_rel(p, P.f[0].xy);
|
||||
let d = abs(rel.x * P.f[0].z + rel.y * P.f[0].w);
|
||||
let half = P.f[1].x;
|
||||
let mode = P.u[0].y;
|
||||
var band: f32;
|
||||
switch P.u[0].x {
|
||||
case 0u: {
|
||||
band = max(1.0 - d / half, 0.0);
|
||||
}
|
||||
case 2u: {
|
||||
band = clamp(half - d + 0.5, 0.0, 1.0);
|
||||
}
|
||||
default: {
|
||||
band = flt_smoothstep(half, 0.0, d);
|
||||
}
|
||||
}
|
||||
if (band <= 0.0) {
|
||||
textureStore(out, p, select(px, vec4<f32>(0.0), mode == 2u));
|
||||
return;
|
||||
}
|
||||
let gx = tex_get_clamped(aux, p.x + 1, p.y).x - tex_get_clamped(aux, p.x - 1, p.y).x;
|
||||
let gy = tex_get_clamped(aux, p.x, p.y + 1).x - tex_get_clamped(aux, p.x, p.y - 1).x;
|
||||
let edge = min(sqrt(gx * gx + gy * gy) * 2.0, 1.0);
|
||||
let light = band * P.f[1].y + band * edge * P.f[1].z;
|
||||
let lc = P.f[2].xyz;
|
||||
let a = px.w;
|
||||
var o: vec4<f32>;
|
||||
if (mode == 1u) {
|
||||
let la = min(band * P.f[1].y, 1.0);
|
||||
o = vec4<f32>(px.xyz + lc * light, min(a + la * (1.0 - a), 1.0));
|
||||
} else if (mode == 2u) {
|
||||
let la = min(light * a, 1.0);
|
||||
o = vec4<f32>(lc * la, la);
|
||||
} else {
|
||||
o = vec4<f32>(px.xyz + lc * light * a, a);
|
||||
}
|
||||
textureStore(out, p, o);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- CC Light Wipe (transition::light_wipe)
|
||||
// u[0] = (shape, colour from source, reverse); f[0] = (cx, cy, sin, cos);
|
||||
// f[1] = (edge, band, intensity × ramp); f[2] = colour.
|
||||
|
||||
@compute @workgroup_size(16, 16)
|
||||
fn flt_wipe(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let p = vec2<i32>(gid.xy);
|
||||
if (!flt_inside(p)) {
|
||||
return;
|
||||
}
|
||||
var px = textureLoad(src, p, 0);
|
||||
let d = flt_rel(p, P.f[0].xy);
|
||||
let s = P.f[0].z;
|
||||
let co = P.f[0].w;
|
||||
let q = vec2<f32>(d.x * co + d.y * s, -d.x * s + d.y * co);
|
||||
var m: f32;
|
||||
switch P.u[0].x {
|
||||
case 0u: {
|
||||
m = abs(q.x);
|
||||
}
|
||||
case 2u: {
|
||||
m = max(abs(q.x), abs(q.y));
|
||||
}
|
||||
default: {
|
||||
m = length(q);
|
||||
}
|
||||
}
|
||||
let edge = P.f[1].x;
|
||||
let visible = select(m > edge, m < edge, P.u[0].z != 0u);
|
||||
if (!visible) {
|
||||
textureStore(out, p, vec4<f32>(0.0));
|
||||
return;
|
||||
}
|
||||
let z = (m - edge) / P.f[1].y;
|
||||
let g = exp(-(z * z)) * P.f[1].z;
|
||||
if (g > 0.0) {
|
||||
var lc = P.f[2].xyz;
|
||||
if (P.u[0].y != 0u) {
|
||||
lc = flt_unpremul(px);
|
||||
}
|
||||
px = vec4<f32>(px.xyz + lc * g * px.w, px.w);
|
||||
}
|
||||
textureStore(out, p, px);
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
//! GPU CC light effects (CC Light Rays, CC Light Burst 2.5, CC Light Sweep, CC Light Wipe) vs
|
||||
//! the CPU effects (the oracle): direct on a buffer at full and half resolution, as
|
||||
//! adjustment, and composited at 8 and 32 bpc.
|
||||
|
||||
use effectcraft_keyframe::Value;
|
||||
|
||||
use crate::tests::{c, effect_case, n};
|
||||
|
||||
fn e(v: u32) -> Value {
|
||||
Value::Enum(v)
|
||||
}
|
||||
|
||||
fn on() -> Value {
|
||||
Value::Bool(true)
|
||||
}
|
||||
|
||||
fn pt(x: f64, y: f64) -> Value {
|
||||
Value::Vec2([x, y])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn light_rays() {
|
||||
effect_case("ec.generate.cclightrays", &[("center", pt(30.0, 20.0))]);
|
||||
effect_case(
|
||||
"ec.generate.cclightrays",
|
||||
&[("center", pt(40.0, 18.0)), ("intensity", n(250.0)), ("radius", n(15.0)), ("shape", e(1)), ("transferMode", e(1))],
|
||||
);
|
||||
effect_case(
|
||||
"ec.generate.cclightrays",
|
||||
&[("intensity", n(80.0)), ("colorFromSource", Value::Bool(false)), ("color", c(1.0, 0.6, 0.2)), ("transferMode", e(2)), ("warpSoftness", n(120.0))],
|
||||
);
|
||||
effect_case("ec.generate.cclightrays", &[("intensity", n(150.0)), ("transferMode", e(3)), ("radius", n(25.0))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn light_burst() {
|
||||
effect_case("ec.generate.cclightburst", &[("center", pt(30.0, 20.0)), ("burst", e(1))]);
|
||||
effect_case("ec.generate.cclightburst", &[("rayLength", n(80.0)), ("burst", e(2)), ("intensity", n(150.0))]);
|
||||
effect_case("ec.generate.cclightburst", &[("rayLength", n(30.0)), ("setColor", on()), ("color", c(0.3, 0.7, 1.0)), ("burst", e(1))]);
|
||||
effect_case("ec.generate.cclightburst", &[("rayLength", n(40.0))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn light_sweep() {
|
||||
effect_case("ec.generate.cclightsweep", &[("center", pt(30.0, 20.0))]);
|
||||
effect_case("ec.generate.cclightsweep", &[("shape", e(0)), ("width", n(30.0)), ("edgeThickness", n(4.0)), ("lightReceptionMode", e(1))]);
|
||||
effect_case("ec.generate.cclightsweep", &[("shape", e(2)), ("direction", n(60.0)), ("lightReceptionMode", e(2)), ("lightColor", c(1.0, 0.8, 0.4))]);
|
||||
effect_case("ec.generate.cclightsweep", &[("edgeThickness", n(0.0)), ("sweepIntensity", n(120.0))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn light_wipe() {
|
||||
effect_case("ec.transition.cclightwipe", &[("completion", n(40.0)), ("center", pt(30.0, 20.0))]);
|
||||
effect_case("ec.transition.cclightwipe", &[("completion", n(25.0)), ("shape", e(0)), ("direction", n(30.0)), ("colorFromSource", on())]);
|
||||
effect_case("ec.transition.cclightwipe", &[("completion", n(60.0)), ("shape", e(2)), ("reverse", on()), ("color", c(0.2, 0.9, 0.5))]);
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
EffectCraft ships 306 effects with 3009 parameters in total, grouped into the same categories as the Effects & Presets panel. Every effect is our own implementation, written from public behaviour descriptions and standard image-processing literature. Parameter names, order, popup options, units, defaults and ranges follow the reference application so that projects, expressions and muscle memory carry over.
|
||||
|
||||
- **GPU**: 166 effects also run on the GPU compositor with identical results.
|
||||
- **GPU**: 170 effects also run on the GPU compositor with identical results.
|
||||
- **32**: 306 effects process 32-bit float (HDR, overbright) pixels without clamping.
|
||||
- **Status**: 306 implemented in full, 0 partial (what is missing is listed).
|
||||
|
||||
|
|
@ -194,9 +194,9 @@ Effects are addressed by id (`ec.<category>.<name>`) or display name in commands
|
|||
| Audio Waveform | `ec.generate.audiowaveform` | 17 | | 32 | Implemented |
|
||||
| Beam | `ec.generate.beam` | 11 | | 32 | Implemented |
|
||||
| CC Glue Gun | `ec.generate.ccgluegun` | 7 | | 32 | Implemented |
|
||||
| CC Light Burst 2.5 | `ec.generate.cclightburst` | 6 | | 32 | Implemented |
|
||||
| CC Light Rays | `ec.generate.cclightrays` | 8 | | 32 | Implemented |
|
||||
| CC Light Sweep | `ec.generate.cclightsweep` | 9 | | 32 | Implemented |
|
||||
| CC Light Burst 2.5 | `ec.generate.cclightburst` | 6 | GPU | 32 | Implemented |
|
||||
| CC Light Rays | `ec.generate.cclightrays` | 8 | GPU | 32 | Implemented |
|
||||
| CC Light Sweep | `ec.generate.cclightsweep` | 9 | GPU | 32 | Implemented |
|
||||
| CC Threads | `ec.generate.ccthreads` | 8 | | 32 | Implemented |
|
||||
| Cell Pattern | `ec.generate.cellpattern` | 14 | GPU | 32 | Implemented |
|
||||
| Checkerboard | `ec.generate.checkerboard` | 10 | GPU | 32 | Implemented |
|
||||
|
|
@ -395,7 +395,7 @@ Effects are addressed by id (`ec.<category>.<name>`) or display name in commands
|
|||
| CC Grid Wipe | `ec.transition.ccgridwipe` | 7 | | 32 | Implemented |
|
||||
| CC Image Wipe | `ec.transition.ccimagewipe` | 7 | | 32 | Implemented |
|
||||
| CC Jaws | `ec.transition.ccjaws` | 6 | | 32 | Implemented |
|
||||
| CC Light Wipe | `ec.transition.cclightwipe` | 8 | | 32 | Implemented |
|
||||
| CC Light Wipe | `ec.transition.cclightwipe` | 8 | GPU | 32 | Implemented |
|
||||
| CC Line Sweep | `ec.transition.cclinesweep` | 5 | | 32 | Implemented |
|
||||
| CC Radial ScaleWipe | `ec.transition.ccradialscalewipe` | 3 | | 32 | Implemented |
|
||||
| CC Scale Wipe | `ec.transition.ccscalewipe` | 3 | | 32 | Implemented |
|
||||
|
|
|
|||
Loading…
Reference in New Issue