mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-25 18:47:07 -06:00
Merge branch 'main' into worktree-agent-a15cecfec57d5290d
# Conflicts: # crates/effects/src/ocio.rs # crates/engine/src/commands/stubs.rs # docs/parity.md
This commit is contained in:
@@ -484,6 +484,41 @@ impl<'g> Enc<'g> {
|
||||
Some(out)
|
||||
}
|
||||
|
||||
/// [`Enc::read_texture`] without waiting for the GPU (the browser's main thread can't): `done`
|
||||
/// gets the bytes once the copy is mapped (from the browser's event loop on the web; from
|
||||
/// a later device poll natively).
|
||||
pub fn read_texture_async(&mut self, texture: &wgpu::Texture, w: u32, h: u32, bpp: u32, done: impl FnOnce(Option<Vec<u8>>) + wgpu::WasmNotSend + 'static) {
|
||||
let row = w * bpp;
|
||||
let padded = row.div_ceil(wgpu::COPY_BYTES_PER_ROW_ALIGNMENT) * wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
|
||||
let buf = self.g.device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("readback-async"),
|
||||
size: padded as u64 * h as u64,
|
||||
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
self.encoder().copy_texture_to_buffer(
|
||||
wgpu::TexelCopyTextureInfo { texture, mip_level: 0, origin: wgpu::Origin3d::ZERO, aspect: wgpu::TextureAspect::All },
|
||||
wgpu::TexelCopyBufferInfo { buffer: &buf, layout: wgpu::TexelCopyBufferLayout { offset: 0, bytes_per_row: Some(padded), rows_per_image: Some(h) } },
|
||||
wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 },
|
||||
);
|
||||
self.submit();
|
||||
let b = buf.clone();
|
||||
buf.map_async(wgpu::MapMode::Read, .., move |r| {
|
||||
let out = r.ok().and_then(|_| {
|
||||
let view = b.get_mapped_range(..).ok()?;
|
||||
let mut out = Vec::with_capacity((row * h) as usize);
|
||||
for y in 0..h as usize {
|
||||
out.extend_from_slice(&view[y * padded as usize..y * padded as usize + row as usize]);
|
||||
}
|
||||
Some(out)
|
||||
});
|
||||
b.unmap();
|
||||
done(out);
|
||||
});
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
let _ = self.g.device.poll(wgpu::PollType::Poll);
|
||||
}
|
||||
|
||||
/// A data buffer for kernels that read tables (curves).
|
||||
pub fn data(&self, v: &[f32]) -> wgpu::Buffer {
|
||||
let mut bytes = Vec::with_capacity(v.len().max(4) * 4);
|
||||
|
||||
+61
-17
@@ -179,20 +179,21 @@ fn glow(e: &mut Enc, ctx: &EffectCtx, mut b: GBuf) -> Option<GBuf> {
|
||||
let use_colors = ctx.params.e("colors") == 1;
|
||||
let ca = ctx.params.color("colorA");
|
||||
let cb = ctx.params.color("colorB");
|
||||
let (kx, ky) = dims_xy(ctx.params.e("glowDimensions"));
|
||||
if !ctx.adjustment {
|
||||
b.pad(e, (radius * 1.5).ceil() as u32 + 2)?;
|
||||
}
|
||||
let mut p = Params::default();
|
||||
p.u[0][0] = use_colors as u32;
|
||||
p.f[0][0] = thr;
|
||||
p.u[0] = [use_colors as u32, (ctx.params.e("based") == 0) as u32, ctx.params.e("colorLooping"), 0];
|
||||
p.f[0] = [thr, ctx.params.f("colorLoops") as f32, (ctx.params.f("colorPhase") / 360.0) as f32, ctx.params.f("abMidpoint") as f32 / 100.0];
|
||||
p.f[1] = ca;
|
||||
p.f[2] = cb;
|
||||
let bright = e.image(b.img.width, b.img.height);
|
||||
e.pixels("glow_bright", &p, &b.img, None, &bright, None);
|
||||
let s = (radius / 2.0).max(0.5);
|
||||
let blurred = gaussian_blur(e, &bright, s, s, false);
|
||||
let blurred = gaussian_blur(e, &bright, s * kx, s * ky, false);
|
||||
let mut p = Params::default();
|
||||
p.u[0][0] = (ctx.params.e("operation") == 1) as u32;
|
||||
p.u[0][0] = ctx.params.e("operation").min(2);
|
||||
p.f[0][0] = intensity;
|
||||
let out = e.image(b.img.width, b.img.height);
|
||||
e.pixels("glow_combine", &p, &b.img, Some(&blurred), &out, None);
|
||||
@@ -240,8 +241,9 @@ fn transform(e: &mut Enc, ctx: &EffectCtx, mut b: GBuf) -> Option<GBuf> {
|
||||
* Mat3::skew_deg(ctx.params.f("skew"), ctx.params.f("skewAxis"))
|
||||
* Mat3::scale(vec2(sw / 100.0, sh / 100.0))
|
||||
* Mat3::translate(vec2(-anchor.0, -anchor.1));
|
||||
let sampling = if ctx.params.e("sampling") == 1 { Sampling::Bicubic } else { Sampling::Bilinear };
|
||||
let empty = e.image(b.img.width, b.img.height);
|
||||
b.img = ops::warp(e, &empty, &b.img, &m, Sampling::Bilinear, effectcraft_color::BlendMode::Normal, opacity, 0, None);
|
||||
b.img = ops::warp(e, &empty, &b.img, &m, sampling, effectcraft_color::BlendMode::Normal, opacity, 0, None);
|
||||
Some(b)
|
||||
}
|
||||
|
||||
@@ -292,8 +294,17 @@ fn pointwise(e: &mut Enc, id: &str, ctx: &EffectCtx, b: GBuf) -> Option<GBuf> {
|
||||
let br = f("brightness") as f32 / 100.0;
|
||||
let ct = f("contrast") as f32 / 100.0;
|
||||
let k = if ct >= 0.0 { 1.0 / (1.0 - ct * 0.99) } else { 1.0 + ct };
|
||||
p.u[0][0] = 2;
|
||||
p.f[0] = [br, k, 0.0, 0.0];
|
||||
let legacy = ctx.params.b("useLegacy");
|
||||
// Mode: 0 = identity, 1 = legacy linear, 2 = tone curves (see `bc_modern`).
|
||||
let mode = if br == 0.0 && ct == 0.0 {
|
||||
0
|
||||
} else if legacy {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
};
|
||||
p.u[0] = [2, mode, 0, 0];
|
||||
p.f[0] = [br, k, ct, 0.0];
|
||||
}
|
||||
"ec.color.huesaturation" => {
|
||||
p.u[0] = [3, ctx.params.b("colorize") as u32, 0, 0];
|
||||
@@ -301,16 +312,28 @@ fn pointwise(e: &mut Enc, id: &str, ctx: &EffectCtx, b: GBuf) -> Option<GBuf> {
|
||||
p.f[1] = [f("colorizeHue") as f32 / 360.0, f("colorizeSaturation") as f32 / 100.0, f("colorizeLightness") as f32 / 100.0, 0.0];
|
||||
}
|
||||
"ec.color.levels" => {
|
||||
p.u[0] = [4, !ctx.params.b("noClip") as u32, 0, 0];
|
||||
let (clip_b, clip_w) = effectcraft_effects::levels_clip(ctx);
|
||||
p.u[0] = [4, clip_b as u32, clip_w as u32, 0];
|
||||
p.f[0] = [f("inBlack") as f32, f("inWhite") as f32, f("gamma").max(0.01) as f32, f("outBlack") as f32];
|
||||
p.f[1][0] = f("outWhite") as f32;
|
||||
}
|
||||
"ec.color.exposure" => {
|
||||
p.u[0][0] = 5;
|
||||
p.f[0] = [2f32.powf(f("exposure") as f32), f("offset") as f32, f("gamma").max(0.01) as f32, 0.0];
|
||||
let s = effectcraft_effects::exposure_settings(ctx);
|
||||
p.u[0] = [5, ctx.params.b("bypassLinearLight") as u32, 0, 0];
|
||||
for i in 0..3 {
|
||||
p.f[0][i] = s[i].0;
|
||||
p.f[1][i] = s[i].1;
|
||||
p.f[2][i] = s[i].2;
|
||||
}
|
||||
}
|
||||
"ec.channel.invert" => {
|
||||
p.u[0] = [6, ctx.params.e("channel"), 0, 0];
|
||||
// The shader does the RGB channels and Alpha; HLS / YIQ inversions run on the CPU.
|
||||
let ch = match ctx.params.e("channel") {
|
||||
c @ 0..=3 => c,
|
||||
effectcraft_effects::INVERT_ALPHA => 4,
|
||||
_ => return None,
|
||||
};
|
||||
p.u[0] = [6, ch, 0, 0];
|
||||
p.f[0][0] = 1.0 - f("blend") as f32 / 100.0;
|
||||
}
|
||||
"ec.generate.fill" => {
|
||||
@@ -330,14 +353,35 @@ fn pointwise(e: &mut Enc, id: &str, ctx: &EffectCtx, b: GBuf) -> Option<GBuf> {
|
||||
p.f[3] = [len2 as f32, f("scatter") as f32 / 512.0, f("blend") as f32 / 100.0, 0.0];
|
||||
}
|
||||
"ec.noise.fractal" => {
|
||||
// The shader covers Soft Linear noise, the Basic / Turbulent Smooth / Turbulent Basic
|
||||
// types, all Overflow modes, independent width / height scaling, Sub Influence and Sub
|
||||
// Scaling, and the None / Normal blending modes; anything else renders on the CPU.
|
||||
let pr = ctx.params;
|
||||
let kind = pr.e("fractalType");
|
||||
let mode = pr.e("blendingMode");
|
||||
let sub_offset = pr.v2("subSettings/subOffset");
|
||||
if pr.e("noiseType") != 2
|
||||
|| kind > 2
|
||||
|| mode > 1
|
||||
|| pr.b("evolutionOptions/cycleEvolution")
|
||||
|| pr.b("transform/perspectiveOffset")
|
||||
|| f("subSettings/subRotation") != 0.0
|
||||
|| (!pr.b("subSettings/centerSubscale") && sub_offset != [0.0, 0.0])
|
||||
{
|
||||
return None;
|
||||
}
|
||||
let octaves = f("complexity").clamp(1.0, 20.0);
|
||||
let (sr, cr) = (f("rotation") as f32).to_radians().sin_cos();
|
||||
let (ox, oy) = b.to_px(ctx.params.v2("offset"));
|
||||
p.u[0] = [10, ctx.params.e("fractalType"), octaves.ceil() as u32, f("seed") as u32 ^ 0x51ed];
|
||||
p.u[1][0] = ctx.params.b("invert") as u32;
|
||||
p.f[0] = [f("contrast") as f32 / 100.0, f("brightness") as f32 / 100.0, (f("scale") * b.scale).max(1.0) as f32, (octaves - octaves.floor()) as f32];
|
||||
let (sr, cr) = (f("transform/rotation") as f32).to_radians().sin_cos();
|
||||
let (ox, oy) = b.to_px(pr.v2("transform/offset"));
|
||||
let s = f("transform/scale");
|
||||
let (sw, sh) = if pr.b("transform/uniformScaling") { (s, s) } else { (f("transform/scaleWidth"), f("transform/scaleHeight")) };
|
||||
let sub = (f("subSettings/subScaling") / 100.0).clamp(0.1, 1.0);
|
||||
p.u[0] = [10, kind, octaves.ceil() as u32, f("evolutionOptions/seed") as u32 ^ 0x51ed];
|
||||
p.u[1] = [pr.b("invert") as u32, pr.e("overflow"), mode, 0];
|
||||
p.f[0] = [f("contrast") as f32 / 100.0, f("brightness") as f32 / 100.0, (sw * b.scale).max(1.0) as f32, (octaves - octaves.floor()) as f32];
|
||||
p.f[1] = [ox as f32, oy as f32, sr, cr];
|
||||
p.f[2] = [f("evolution") as f32 / 360.0, f("blend") as f32 / 100.0, f("opacity") as f32 / 100.0, 0.0];
|
||||
p.f[2] = [f("evolution") as f32 / 360.0, f("blend") as f32 / 100.0, (f("opacity") as f32 / 100.0).clamp(0.0, 1.0), (sh * b.scale).max(1.0) as f32];
|
||||
p.f[3] = [(f("subSettings/subInfluence") as f32 / 100.0).clamp(0.0, 1.0), 1.0 / sub as f32, 0.0, 0.0];
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
@@ -93,6 +93,12 @@ impl Gpu {
|
||||
Enc::new(&self.ctx).read_texture(&f.texture, f.width, f.height, 4)
|
||||
}
|
||||
|
||||
/// [`Gpu::read_display`] without blocking (the browser's main thread): `done` gets the bytes
|
||||
/// once the GPU has them.
|
||||
pub fn read_display_async(&self, f: &DisplayFrame, done: impl FnOnce(Option<Vec<u8>>) + wgpu::WasmNotSend + 'static) {
|
||||
Enc::new(&self.ctx).read_texture_async(&f.texture, f.width, f.height, 4, done);
|
||||
}
|
||||
|
||||
/// Render a frame on the GPU and read it back (`None` = not handled).
|
||||
pub fn render(&self, r: &Renderer, comp: ItemId, t: Tick) -> Option<Image> {
|
||||
if !self.ctx.can_readback() {
|
||||
|
||||
@@ -326,7 +326,29 @@ fn directional(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
|
||||
// ---------------------------------------------------------------- Glow / Drop Shadow
|
||||
|
||||
// Glow bright pass. u[0].x = A & B colours; f[0].x = threshold; f[1] = colour A; f[2] = colour B.
|
||||
// Glow's A & B gradient position (effects::misc::glow_ab_t). f[0].yzw = loops, phase, midpoint;
|
||||
// u[0].z = colour looping.
|
||||
fn glow_ab_t(l: f32) -> f32 {
|
||||
let u = clamp(l, 0.0, 1.0) * max(P.f[0].y, 0.0) + P.f[0].z;
|
||||
var t = u - floor(u);
|
||||
if (u > 0.0 && t <= 0.0) {
|
||||
t = 1.0;
|
||||
}
|
||||
let tri = 1.0 - abs(2.0 * t - 1.0);
|
||||
let lp = P.u[0].z;
|
||||
if (lp == 1u) {
|
||||
t = 1.0 - t;
|
||||
} else if (lp == 3u) {
|
||||
t = 1.0 - tri;
|
||||
} else if (lp != 0u) {
|
||||
t = tri;
|
||||
}
|
||||
let m = clamp(P.f[0].w, 0.01, 0.99);
|
||||
return pow(max(t, 0.0), log(0.5) / log(m));
|
||||
}
|
||||
|
||||
// Glow bright pass. u[0].x = A & B colours; u[0].y = based on alpha; f[0].x = threshold;
|
||||
// f[1] = colour A; f[2] = colour B.
|
||||
@compute @workgroup_size(16, 16)
|
||||
fn glow_bright(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let dims = out_dims();
|
||||
@@ -341,10 +363,13 @@ fn glow_bright(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
return;
|
||||
}
|
||||
let thr = P.f[0].x;
|
||||
let l = luminance(px.xyz / a);
|
||||
var l = luminance(px.xyz / a);
|
||||
if (P.u[0].y != 0u) {
|
||||
l = a;
|
||||
}
|
||||
let k = clamp((l - thr) / max(1.0 - thr, 1e-3), 0.0, 1.0);
|
||||
if (P.u[0].x != 0u) {
|
||||
let t = clamp(l, 0.0, 1.0);
|
||||
let t = glow_ab_t(l);
|
||||
let ca = P.f[1].xyz;
|
||||
let cb = P.f[2].xyz;
|
||||
let c = ca + (cb - ca) * t;
|
||||
@@ -354,7 +379,8 @@ fn glow_bright(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
}
|
||||
}
|
||||
|
||||
// Glow composite: original `src` with the blurred glow `aux`. u[0].x = behind; f[0].x = intensity.
|
||||
// Glow composite: original `src` with the blurred glow `aux`. u[0].x = 0 on top, 1 behind,
|
||||
// 2 none (glow only); f[0].x = intensity.
|
||||
@compute @workgroup_size(16, 16)
|
||||
fn glow_combine(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let dims = out_dims();
|
||||
@@ -364,7 +390,9 @@ fn glow_combine(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
}
|
||||
var o = textureLoad(src, p, 0);
|
||||
let g = textureLoad(aux, p, 0) * P.f[0].x;
|
||||
if (P.u[0].x != 0u) {
|
||||
if (P.u[0].x == 2u) {
|
||||
o = g;
|
||||
} else if (P.u[0].x == 1u) {
|
||||
let k = 1.0 - o.w;
|
||||
o = o + g * k;
|
||||
} else {
|
||||
@@ -518,8 +546,9 @@ fn straight(px: vec4<f32>) -> vec3<f32> {
|
||||
// 8 Curves f[0] = data offsets (rgb, red, green, blue), f[1].x = alpha (−1 = none)
|
||||
// 9 Gradient Ramp f[0] = (sx, sy, ex, ey); f[1] = start colour; f[2] = end colour;
|
||||
// f[3] = (len2, scatter, blend); u[0] = (_, radial, seed)
|
||||
// 10 Fractal Noise f[0] = (contrast, brightness, scale, frac); f[1] = (ox, oy, sr, cr);
|
||||
// f[2] = (evo, blend, opacity); u[0] = (_, kind, n_oct, seed); u[1].x = invert
|
||||
// 10 Fractal Noise f[0] = (contrast, brightness, scale x, frac); f[1] = (ox, oy, sr, cr);
|
||||
// f[2] = (evo, blend, opacity, scale y); f[3] = (sub influence, 1 / sub scaling);
|
||||
// u[0] = (_, kind, n_oct, seed); u[1] = (invert, overflow, normal blend)
|
||||
@compute @workgroup_size(16, 16)
|
||||
fn pointwise(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let dims = out_dims();
|
||||
@@ -546,7 +575,26 @@ fn pointwise(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let c = straight(px);
|
||||
let br = P.f[0].x;
|
||||
let k = P.f[0].y;
|
||||
px = vec4<f32>(max((c + br - 0.5) * k + 0.5, vec3<f32>(0.0)) * a, a);
|
||||
let ct = P.f[0].z;
|
||||
if (P.u[0].y == 1u) {
|
||||
px = vec4<f32>(max((c + br - 0.5) * k + 0.5, vec3<f32>(0.0)) * a, a);
|
||||
} else if (P.u[0].y == 2u) {
|
||||
// Brightness & Contrast tone curves (effects crate bc_modern).
|
||||
var o = c;
|
||||
let pb = exp2(-br);
|
||||
let g = exp2(2.0 * ct);
|
||||
for (var i = 0; i < 3; i++) {
|
||||
if (c[i] < 1.0) {
|
||||
let v = powz(max(c[i], 0.0), pb);
|
||||
if (v < 0.5) {
|
||||
o[i] = 0.5 * powz(2.0 * v, g);
|
||||
} else {
|
||||
o[i] = 1.0 - 0.5 * powz(max(2.0 - 2.0 * v, 0.0), g);
|
||||
}
|
||||
}
|
||||
}
|
||||
px = vec4<f32>(o * a, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
case 3u: {
|
||||
@@ -600,7 +648,10 @@ fn pointwise(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var t = (c[i] - ib) / max(iw - ib, 1e-6);
|
||||
if (P.u[0].y != 0u) {
|
||||
t = clamp(t, 0.0, 1.0);
|
||||
t = max(t, 0.0);
|
||||
}
|
||||
if (P.u[0].z != 0u) {
|
||||
t = min(t, 1.0);
|
||||
}
|
||||
t = powz(max(t, 0.0), 1.0 / g);
|
||||
o[i] = ob + (ow - ob) * t;
|
||||
@@ -613,9 +664,15 @@ fn pointwise(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let c = straight(px);
|
||||
var o = vec3<f32>(0.0);
|
||||
for (var i = 0; i < 3; i++) {
|
||||
let lin = srgb_to_linear(max(c[i], 0.0));
|
||||
let v = powz(max(lin * P.f[0].x + P.f[0].y, 0.0), 1.0 / P.f[0].z);
|
||||
o[i] = linear_to_srgb(v);
|
||||
// Per-channel gain, offset, gamma; negative values are mirrored.
|
||||
if (P.u[0].y != 0u) {
|
||||
let x = c[i] * P.f[0][i] + P.f[1][i];
|
||||
o[i] = sign(x) * powz(abs(x), 1.0 / P.f[2][i]);
|
||||
} else {
|
||||
let lin = srgb_to_linear(max(c[i], 0.0));
|
||||
let x = lin * P.f[0][i] + P.f[1][i];
|
||||
o[i] = sign(x) * linear_to_srgb(powz(abs(x), 1.0 / P.f[2][i]));
|
||||
}
|
||||
}
|
||||
px = vec4<f32>(o * a, a);
|
||||
}
|
||||
@@ -706,6 +763,9 @@ fn pointwise(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let contrast = P.f[0].x;
|
||||
let brightness = P.f[0].y;
|
||||
let scale = P.f[0].z;
|
||||
let scale_y = P.f[2].w;
|
||||
let infl = P.f[3].x;
|
||||
let fmul = P.f[3].y;
|
||||
let frac = P.f[0].w;
|
||||
let ox = P.f[1].x;
|
||||
let oy = P.f[1].y;
|
||||
@@ -718,7 +778,7 @@ fn pointwise(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
let dx = f32(p.x) + 0.5 - ox;
|
||||
let dy = f32(p.y) + 0.5 - oy;
|
||||
let u = (dx * cr + dy * sr) / scale;
|
||||
let v = (-dx * sr + dy * cr) / scale;
|
||||
let v = (-dx * sr + dy * cr) / scale_y;
|
||||
var sum = 0.0;
|
||||
var amp = 1.0;
|
||||
var norm = 0.0;
|
||||
@@ -729,20 +789,40 @@ fn pointwise(@builtin(global_invocation_id) gid: vec3<u32>) {
|
||||
w = frac;
|
||||
}
|
||||
var n = value_noise(u * f, v * f, evo + f32(o) * 7.31, seed + o);
|
||||
let s = n * 2.0 - 1.0;
|
||||
if (kind == 1u) {
|
||||
n = 1.0 - abs(n * 2.0 - 1.0);
|
||||
n = 1.0 - s * s;
|
||||
} else if (kind == 2u) {
|
||||
n = 1.0 - abs(s);
|
||||
}
|
||||
sum += n * amp * w;
|
||||
norm += amp * w;
|
||||
amp *= 0.5;
|
||||
f *= 2.0;
|
||||
amp *= infl;
|
||||
f *= fmul;
|
||||
}
|
||||
var val = sum / max(norm, 1e-6);
|
||||
val = clamp((val - 0.5) * contrast + 0.5 + brightness, 0.0, 1.0);
|
||||
val = (val - 0.5) * contrast + 0.5 + brightness;
|
||||
// Overflow: Clip, Soft Clamp, Wrap Back, Allow HDR Results.
|
||||
let ov = P.u[1].y;
|
||||
if (ov == 0u) {
|
||||
val = clamp(val, 0.0, 1.0);
|
||||
} else if (ov == 1u) {
|
||||
val = clamp(0.5 + 0.5 * tanh(2.0 * (val - 0.5)) / tanh(1.0), 0.0, 1.0);
|
||||
} else if (ov == 2u) {
|
||||
let t = val - 2.0 * floor(val / 2.0);
|
||||
val = select(t, 2.0 - t, t > 1.0);
|
||||
}
|
||||
if (P.u[1].x != 0u) {
|
||||
val = 1.0 - val;
|
||||
}
|
||||
px = put(px, vec3<f32>(val), P.f[2].z, P.f[2].y);
|
||||
let op = P.f[2].z;
|
||||
if (P.u[1].z == 1u) {
|
||||
// Normal: the noise over the layer at Opacity, then Blend With Original.
|
||||
let over = vec4<f32>(vec3<f32>(val) * op, op) + px * (1.0 - op);
|
||||
px = px * P.f[2].y + over * (1.0 - P.f[2].y);
|
||||
} else {
|
||||
px = put(px, vec3<f32>(val), op, P.f[2].y);
|
||||
}
|
||||
}
|
||||
default: {}
|
||||
}
|
||||
|
||||
+43
-4
@@ -434,8 +434,22 @@ fn gpu_effects_match_cpu() {
|
||||
effect_case("ec.blur.directional", &[("direction", n(30.0)), ("length", n(12.0))]);
|
||||
effect_case("ec.stylize.glow", &[("threshold", n(40.0)), ("radius", n(14.0)), ("intensity", n(1.5))]);
|
||||
effect_case("ec.stylize.glow", &[("colors", Value::Enum(1)), ("operation", Value::Enum(1)), ("colorA", c(1.0, 0.8, 0.1))]);
|
||||
effect_case(
|
||||
"ec.stylize.glow",
|
||||
&[
|
||||
("colors", Value::Enum(1)),
|
||||
("colorLooping", Value::Enum(3)),
|
||||
("colorLoops", n(2.5)),
|
||||
("colorPhase", n(40.0)),
|
||||
("abMidpoint", n(30.0)),
|
||||
("glowDimensions", Value::Enum(1)),
|
||||
],
|
||||
);
|
||||
effect_case("ec.stylize.glow", &[("based", Value::Enum(0)), ("operation", Value::Enum(2)), ("threshold", n(20.0))]);
|
||||
effect_case("ec.color.levels", &[("inBlack", n(0.1)), ("inWhite", n(0.8)), ("gamma", n(1.6)), ("outBlack", n(0.05))]);
|
||||
effect_case("ec.color.levels", &[("inWhite", n(0.5)), ("noClip", Value::Bool(true))]);
|
||||
effect_case("ec.color.levels", &[("inBlack", n(0.2)), ("inWhite", n(0.6)), ("clipToOutputBlack", Value::Enum(0))]);
|
||||
effect_case("ec.color.levels", &[("inBlack", n(0.2)), ("inWhite", n(0.6)), ("clipToOutputWhite", Value::Enum(0))]);
|
||||
effect_case(
|
||||
"ec.color.curves",
|
||||
&[("rgb", Value::Str("0,0 0.3,0.45 1,1".into())), ("red", Value::Str("0,0.1 1,0.9".into())), ("alpha", Value::Str("0,0 0.5,0.7 1,1".into()))],
|
||||
@@ -450,15 +464,40 @@ fn gpu_effects_match_cpu() {
|
||||
effect_case("ec.noise.fractal", &[]);
|
||||
effect_case(
|
||||
"ec.noise.fractal",
|
||||
&[("fractalType", Value::Enum(1)), ("complexity", n(4.5)), ("rotation", n(30.0)), ("evolution", n(90.0)), ("scale", n(40.0)), ("blend", n(30.0))],
|
||||
&[
|
||||
("fractalType", Value::Enum(1)),
|
||||
("complexity", n(4.5)),
|
||||
("transform/rotation", n(30.0)),
|
||||
("evolution", n(90.0)),
|
||||
("transform/scale", n(40.0)),
|
||||
("blend", n(30.0)),
|
||||
],
|
||||
);
|
||||
effect_case(
|
||||
"ec.noise.fractal",
|
||||
&[
|
||||
("fractalType", Value::Enum(2)),
|
||||
("overflow", Value::Enum(1)),
|
||||
("contrast", n(250.0)),
|
||||
("transform/uniformScaling", Value::Bool(false)),
|
||||
("transform/scaleWidth", n(60.0)),
|
||||
("transform/scaleHeight", n(25.0)),
|
||||
("subSettings/subInfluence", n(40.0)),
|
||||
("subSettings/subScaling", n(70.0)),
|
||||
("opacity", n(60.0)),
|
||||
],
|
||||
);
|
||||
effect_case("ec.noise.fractal", &[("overflow", Value::Enum(2)), ("contrast", n(300.0)), ("blendingMode", Value::Enum(0)), ("opacity", n(50.0))]);
|
||||
effect_case("ec.perspective.dropshadow", &[("distance", n(6.0)), ("softness", n(8.0)), ("opacity", n(80.0))]);
|
||||
effect_case("ec.perspective.dropshadow", &[("shadowOnly", Value::Bool(true)), ("color", c(0.0, 0.2, 0.6))]);
|
||||
effect_case("ec.color.brightnesscontrast", &[("brightness", n(20.0)), ("contrast", n(35.0))]);
|
||||
effect_case("ec.color.brightnesscontrast", &[("brightness", n(-10.0)), ("contrast", n(-40.0))]);
|
||||
effect_case("ec.color.exposure", &[("exposure", n(1.3)), ("offset", n(0.02)), ("gamma", n(1.2))]);
|
||||
effect_case("ec.color.brightnesscontrast", &[("brightness", n(20.0)), ("contrast", n(35.0)), ("useLegacy", Value::Bool(true))]);
|
||||
effect_case("ec.color.exposure", &[("master/exposure", n(1.3)), ("master/offset", n(0.02)), ("master/gamma", n(1.2))]);
|
||||
effect_case("ec.color.exposure", &[("channels", Value::Enum(1)), ("red/redExposure", n(0.8)), ("green/greenOffset", n(-0.05)), ("blue/blueGamma", n(1.6))]);
|
||||
effect_case("ec.color.exposure", &[("master/exposure", n(0.5)), ("master/offset", n(-0.03)), ("bypassLinearLight", Value::Bool(true))]);
|
||||
effect_case("ec.channel.invert", &[]);
|
||||
effect_case("ec.channel.invert", &[("channel", Value::Enum(4)), ("blend", n(30.0))]);
|
||||
effect_case("ec.channel.invert", &[("channel", Value::Enum(effectcraft_effects::INVERT_ALPHA)), ("blend", n(30.0))]);
|
||||
effect_case("ec.channel.invert", &[("channel", Value::Enum(2))]);
|
||||
effect_case("ec.distort.transform", &[("rotation", n(20.0)), ("scaleHeight", n(80.0)), ("skew", n(10.0)), ("opacity", n(70.0))]);
|
||||
}
|
||||
@@ -470,7 +509,7 @@ fn effect_chains_and_mixed_stacks() {
|
||||
for depth in [BitDepth::Bpc8, BitDepth::Bpc32] {
|
||||
let mut s = Scene::new(depth);
|
||||
let mut l = s.footage(80, 50);
|
||||
s.effect(&mut l, "ec.color.exposure", &[("exposure", n(0.7))]);
|
||||
s.effect(&mut l, "ec.color.exposure", &[("master/exposure", n(0.7))]);
|
||||
s.effect(&mut l, "ec.blur.gaussian", &[("blurriness", n(5.0))]);
|
||||
s.effect(&mut l, "ec.stylize.posterize", &[]);
|
||||
s.effect(&mut l, "ec.stylize.glow", &[]);
|
||||
|
||||
Reference in New Issue
Block a user