Skip to content

Commit 63e82d3

Browse files
committed
fix(background): make the gradient motions visible at a glance
Measured on a port of the shader, the three motions strayed 3 to 9 % from the still gradient and changed by less than 1/255 per frame, so they read as a still background. Motion is now twice as fast and two to ten times wider, depending on the gradient. Aurora blobs and wave crests also lighten or darken a little: most offered gradients pair two neighbouring hues, and moving their own colours alone stayed invisible on them. Drift slides along its axis on top of swinging it, since a rotation alone leaves the centre still.
1 parent 4dfc09e commit 63e82d3

4 files changed

Lines changed: 88 additions & 56 deletions

File tree

‎crates/compositor/src/frame_geometry.rs‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1565,8 +1565,8 @@ fn same_source_path(a: &str, b: &str) -> bool {
15651565
a.eq_ignore_ascii_case(b)
15661566
}
15671567

1568-
/// Période commune des mouvements de fond (s). Chaque période du shader (20, 24, 30, 40, 12,
1569-
/// 120 s) la divise, donc replier le temps dessus ne crée aucun raccord visible — et garde au
1568+
/// Période commune des mouvements de fond (s). Chaque période du shader (6, 10, 12, 15, 20,
1569+
/// 30 s) la divise, donc replier le temps dessus ne crée aucun raccord visible — et garde au
15701570
/// shader un temps borné, là où un `f32` de plusieurs heures perdrait la finesse du bruit.
15711571
pub const GRADIENT_MOTION_PERIOD_S: f32 = 120.0;
15721572

@@ -6752,7 +6752,7 @@ mod tests {
67526752
assert_eq!(gradient_motion_slots(M::Aurora, 0.0, 1.0).0[1], 2.0);
67536753
assert_eq!(gradient_motion_slots(M::Waves, 240.0, 1.0).0, [0.0, 3.0]);
67546754
// Chaque période du shader divise la période de repli : pas de raccord au bouclage.
6755-
for p in [20.0, 24.0, 30.0, 40.0, 12.0, 120.0] {
6755+
for p in [6.0, 10.0, 12.0, 15.0, 20.0, 30.0] {
67566756
assert_eq!(GRADIENT_MOTION_PERIOD_S % p, 0.0, "période {p}");
67576757
}
67586758
}

‎crates/compositor/src/shaders.hlsl‎

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -349,37 +349,47 @@ float3 ramp4(float t, float4 k0, float4 k1, float4 k2, float4 k3)
349349
return c;
350350
}
351351

352+
// Éclaircit vers le blanc (k > 0) ou assombrit vers le noir (k < 0) d'une fraction |k|.
353+
float3 lighten(float3 c, float k)
354+
{
355+
return k > 0.0 ? c + (1.0 - c) * k : c * (1.0 + k);
356+
}
357+
352358
// Mouvements 2 (aurore) et 3 (vagues) du mode 5, sur la rampe de ses stops.
353359
// `gp` 0..1 sur le quad, `dir`/`denom` ceux du dégradé, `time` = temps programme replié sur
354-
// 120 s (toutes les périodes ci-dessous le divisent), `aspect` = w/h de la sortie. Périodes
355-
// longues et contraste bas : le fond ne doit jamais prendre l'attention.
360+
// 120 s (toutes les périodes ci-dessous le divisent), `aspect` = w/h de la sortie. Amples et
361+
// rapides assez pour se voir d'un coup d'œil : réglés plus bas, ils ne se voyaient qu'en
362+
// comparant deux images, et la plupart des dégradés proposés n'ont que deux teintes voisines,
363+
// d'où la lumière que chacun ajoute en plus de déplacer la rampe.
356364
float3 gradient_motion(float2 gp, float2 dir, float denom, float4 k0, float4 k1, float4 k2,
357365
float4 k3, float time, float motion, float aspect)
358366
{
359367
const float TAU = 6.2831853;
360368
float u = dot(gp - 0.5, dir) / denom; // position le long de l'axe, -0.5..0.5
361369
if (motion < 2.5)
362370
{
363-
// Aurore : rampe perturbée par un bruit lent (dont l'origine tourne en 120 s), puis
364-
// trois nappes gaussiennes sur des Lissajous de 20 à 40 s. Coordonnées corrigées de
365-
// l'aspect pour que les nappes restent rondes.
371+
// Aurore : rampe perturbée par un bruit (dont l'origine tourne en 30 s), puis trois
372+
// larges nappes gaussiennes sur des Lissajous de 10 à 20 s. Deux portent la couleur de
373+
// fin éclaircie, une celle du début assombrie. Coordonnées corrigées de l'aspect pour
374+
// que les nappes restent rondes.
366375
float2 p = float2((gp.x - 0.5) * aspect, gp.y - 0.5);
367-
float ph = TAU * time / 120.0;
368-
float n = value_noise(p * 2.5 + 1.5 * float2(cos(ph), sin(ph)));
369-
float3 g = ramp4(saturate(0.5 + u + 0.3 * (n - 0.5)), k0, k1, k2, k3);
370-
float2 b0 = float2(0.35 * aspect * sin(TAU * time / 20.0), 0.25 * sin(TAU * time / 30.0 + 1.0));
371-
float2 b1 = float2(0.30 * aspect * sin(TAU * time / 24.0 + 2.0), 0.22 * cos(TAU * time / 40.0));
372-
float2 b2 = float2(0.25 * aspect * cos(TAU * time / 30.0 + 4.0), 0.28 * sin(TAU * time / 24.0 + 3.0));
373-
g = lerp(g, k3.rgb, 0.45 * exp(-dot(p - b0, p - b0) / 0.08));
374-
g = lerp(g, k0.rgb, 0.45 * exp(-dot(p - b1, p - b1) / 0.06));
375-
g = lerp(g, k3.rgb, 0.35 * exp(-dot(p - b2, p - b2) / 0.05));
376+
float ph = TAU * time / 30.0;
377+
float n = value_noise(p * 1.8 + 1.5 * float2(cos(ph), sin(ph)));
378+
float3 g = ramp4(saturate(0.5 + u + 0.6 * (n - 0.5)), k0, k1, k2, k3);
379+
float2 b0 = float2(0.35 * aspect * sin(TAU * time / 10.0), 0.25 * sin(TAU * time / 15.0 + 1.0));
380+
float2 b1 = float2(0.30 * aspect * sin(TAU * time / 12.0 + 2.0), 0.22 * cos(TAU * time / 20.0));
381+
float2 b2 = float2(0.25 * aspect * cos(TAU * time / 15.0 + 4.0), 0.28 * sin(TAU * time / 12.0 + 3.0));
382+
float3 light = lighten(k3.rgb, 0.15);
383+
g = lerp(g, light, 0.7 * exp(-dot(p - b0, p - b0) / 0.176));
384+
g = lerp(g, lighten(k0.rgb, -0.15), 0.7 * exp(-dot(p - b1, p - b1) / 0.132));
385+
g = lerp(g, light, 0.6 * exp(-dot(p - b2, p - b2) / 0.11));
376386
return g;
377387
}
378-
// Vagues : trois bandes sinus perpendiculaires à l'axe, qui avancent d'une bande en 12 s,
379-
// légèrement ondulées le long des bandes (20 s). Elles décalent la rampe, rien d'autre.
388+
// Vagues : trois bandes sinus perpendiculaires à l'axe, qui avancent d'une bande en 6 s,
389+
// ondulées le long des bandes (10 s). Elles décalent la rampe et éclairent leurs crêtes.
380390
float v = dot(gp - 0.5, float2(-dir.y, dir.x)) / denom;
381-
float w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 20.0)) - time / 12.0));
382-
return ramp4(saturate(0.5 + u + 0.07 * w), k0, k1, k2, k3);
391+
float w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 10.0)) - time / 6.0));
392+
return lighten(ramp4(saturate(0.5 + u + 0.18 * w), k0, k1, k2, k3), 0.07 * w);
383393
}
384394

385395
// Couverture d'une pastille (disque) adoucie sur ~1.5 px, pour la barre de titre du mode 14.
@@ -2493,21 +2503,24 @@ float4 ps_main(VSOut i) : SV_Target
24932503
if (mode > 4.5)
24942504
{
24952505
float2 dir = fx.xy;
2506+
float slide = 0.0;
24962507
if (fx.w > 0.5 && fx.w < 1.5)
24972508
{
2498-
// Dérive : l'axe respire de ±15° (0.2617994 rad) en 20 s.
2499-
float da = 0.2617994 * sin(6.2831853 * fx.z / 20.0);
2509+
// Dérive : l'axe balance de ±30° (0.5235988 rad) en 20 s, et le dégradé glisse le
2510+
// long de lui de ±20 % en 15 s. Une rotation seule laisse le centre immobile.
2511+
float da = 0.5235988 * sin(6.2831853 * fx.z / 20.0);
25002512
float sa = sin(da);
25012513
float ca = cos(da);
25022514
dir = float2(dir.x * ca - dir.y * sa, dir.x * sa + dir.y * ca);
2515+
slide = 0.2 * sin(6.2831853 * fx.z / 15.0);
25032516
}
25042517
float denom = max(abs(dir.x) + abs(dir.y), 1e-4);
25052518
// Paramétré sur le QUAD dès qu'il en a un (la bulle webcam), sinon sur la sortie. Pour le
25062519
// fond plein cadre les deux coïncident ; pour une bulle dans un coin, `pout` ne montrerait
25072520
// que la tranche du dégradé plein cadre qui passe dessous, jamais la rampe complète que
25082521
// le sélecteur affiche.
25092522
float2 gp = (quad_px.x > 0.0 && quad_px.y > 0.0) ? (i.local / quad_px) : i.pout;
2510-
float t = saturate(0.5 + dot(gp - 0.5, dir) / denom);
2523+
float t = saturate(0.5 + dot(gp - 0.5, dir) / denom + slide);
25112524
float3 g = ramp4(t, color, src_prev, dst_prev, src);
25122525
if (fx.w > 1.5)
25132526
{

‎crates/compositor/src/shaders.metal‎

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,12 @@ inline float3 ramp4(float t, float4 k0, float4 k1, float4 k2, float4 k3)
463463
return c;
464464
}
465465

466+
// Éclaircit vers le blanc (k > 0) ou assombrit vers le noir (k < 0) d'une fraction |k|.
467+
inline float3 lighten(float3 c, float k)
468+
{
469+
return k > 0.0 ? c + (1.0 - c) * k : c * (1.0 + k);
470+
}
471+
466472
// Mouvements 2 (aurore) et 3 (vagues) du mode 5. Miroir ligne pour ligne de
467473
// `gradient_motion` côté HLSL (commentaires complets là-bas).
468474
inline float3 gradient_motion(float2 gp, float2 dir, float denom, float4 k0, float4 k1,
@@ -472,23 +478,24 @@ inline float3 gradient_motion(float2 gp, float2 dir, float denom, float4 k0, flo
472478
float u = dot(gp - 0.5, dir) / denom; // position le long de l'axe, -0.5..0.5
473479
if (motion < 2.5)
474480
{
475-
// Aurore : rampe perturbée par un bruit lent, puis trois nappes gaussiennes.
481+
// Aurore : rampe perturbée par un bruit, puis trois larges nappes gaussiennes.
476482
float2 p = float2((gp.x - 0.5) * aspect, gp.y - 0.5);
477-
float ph = TAU * time / 120.0;
478-
float n = value_noise(p * 2.5 + 1.5 * float2(cos(ph), sin(ph)));
479-
float3 g = ramp4(clamp(0.5 + u + 0.3 * (n - 0.5), 0.0, 1.0), k0, k1, k2, k3);
480-
float2 b0 = float2(0.35 * aspect * sin(TAU * time / 20.0), 0.25 * sin(TAU * time / 30.0 + 1.0));
481-
float2 b1 = float2(0.30 * aspect * sin(TAU * time / 24.0 + 2.0), 0.22 * cos(TAU * time / 40.0));
482-
float2 b2 = float2(0.25 * aspect * cos(TAU * time / 30.0 + 4.0), 0.28 * sin(TAU * time / 24.0 + 3.0));
483-
g = mix(g, k3.rgb, 0.45 * exp(-dot(p - b0, p - b0) / 0.08));
484-
g = mix(g, k0.rgb, 0.45 * exp(-dot(p - b1, p - b1) / 0.06));
485-
g = mix(g, k3.rgb, 0.35 * exp(-dot(p - b2, p - b2) / 0.05));
483+
float ph = TAU * time / 30.0;
484+
float n = value_noise(p * 1.8 + 1.5 * float2(cos(ph), sin(ph)));
485+
float3 g = ramp4(clamp(0.5 + u + 0.6 * (n - 0.5), 0.0, 1.0), k0, k1, k2, k3);
486+
float2 b0 = float2(0.35 * aspect * sin(TAU * time / 10.0), 0.25 * sin(TAU * time / 15.0 + 1.0));
487+
float2 b1 = float2(0.30 * aspect * sin(TAU * time / 12.0 + 2.0), 0.22 * cos(TAU * time / 20.0));
488+
float2 b2 = float2(0.25 * aspect * cos(TAU * time / 15.0 + 4.0), 0.28 * sin(TAU * time / 12.0 + 3.0));
489+
float3 light = lighten(k3.rgb, 0.15);
490+
g = mix(g, light, 0.7 * exp(-dot(p - b0, p - b0) / 0.176));
491+
g = mix(g, lighten(k0.rgb, -0.15), 0.7 * exp(-dot(p - b1, p - b1) / 0.132));
492+
g = mix(g, light, 0.6 * exp(-dot(p - b2, p - b2) / 0.11));
486493
return g;
487494
}
488-
// Vagues : trois bandes sinus perpendiculaires à l'axe (12 s), ondulées (20 s).
495+
// Vagues : trois bandes sinus perpendiculaires à l'axe (6 s), ondulées (10 s), crêtes éclairées.
489496
float v = dot(gp - 0.5, float2(-dir.y, dir.x)) / denom;
490-
float w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 20.0)) - time / 12.0));
491-
return ramp4(clamp(0.5 + u + 0.07 * w, 0.0, 1.0), k0, k1, k2, k3);
497+
float w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 10.0)) - time / 6.0));
498+
return lighten(ramp4(clamp(0.5 + u + 0.18 * w, 0.0, 1.0), k0, k1, k2, k3), 0.07 * w);
492499
}
493500

494501
// ============ Curseur MODÉLISÉ (mode 15) ============
@@ -2274,13 +2281,16 @@ fragment float4 ps_main(VSOut i [[stage_in]],
22742281
if (layer.mode > 4.5 && layer.mode < 5.5)
22752282
{
22762283
float2 dir = layer.fx.xy;
2284+
float slide = 0.0;
22772285
if (layer.fx.w > 0.5 && layer.fx.w < 1.5)
22782286
{
2279-
// Dérive : l'axe respire de ±15° (0.2617994 rad) en 20 s.
2280-
float da = 0.2617994 * sin(6.2831853 * layer.fx.z / 20.0);
2287+
// Dérive : l'axe balance de ±30° (0.5235988 rad) en 20 s, le dégradé glisse le long
2288+
// de lui de ±20 % en 15 s.
2289+
float da = 0.5235988 * sin(6.2831853 * layer.fx.z / 20.0);
22812290
float sa = sin(da);
22822291
float ca = cos(da);
22832292
dir = float2(dir.x * ca - dir.y * sa, dir.x * sa + dir.y * ca);
2293+
slide = 0.2 * sin(6.2831853 * layer.fx.z / 15.0);
22842294
}
22852295
float denom = max(abs(dir.x) + abs(dir.y), 1e-4);
22862296
// Paramétré sur le QUAD dès qu'il en a un (la bulle webcam), sinon sur la sortie. Pour le
@@ -2290,7 +2300,7 @@ fragment float4 ps_main(VSOut i [[stage_in]],
22902300
float2 gp = (layer.quad_px.x > 0.0 && layer.quad_px.y > 0.0)
22912301
? (i.local / layer.quad_px)
22922302
: i.pout;
2293-
float t = clamp(0.5 + dot(gp - 0.5, dir) / denom, 0.0, 1.0);
2303+
float t = clamp(0.5 + dot(gp - 0.5, dir) / denom + slide, 0.0, 1.0);
22942304
float3 g = ramp4(t, layer.color, layer.src_prev, layer.dst_prev, layer.src);
22952305
if (layer.fx.w > 1.5)
22962306
{

‎crates/compositor/src/vk_shaders/layer.wgsl‎

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -424,30 +424,36 @@ fn ramp4(t: f32, k0: vec4<f32>, k1: vec4<f32>, k2: vec4<f32>, k3: vec4<f32>) ->
424424
return c;
425425
}
426426

427+
// Eclaircit vers le blanc (k > 0) ou assombrit vers le noir (k < 0) d'une fraction |k|.
428+
fn lighten(c: vec3<f32>, k: f32) -> vec3<f32> {
429+
return select(c * (1.0 + k), c + (vec3<f32>(1.0) - c) * k, k > 0.0);
430+
}
431+
427432
// Mouvements 2 (aurore) et 3 (vagues) du mode 5. Miroir ligne pour ligne de
428433
// `gradient_motion` cote HLSL (commentaires complets la-bas).
429434
fn gradient_motion(gp: vec2<f32>, dir: vec2<f32>, denom: f32, k0: vec4<f32>, k1: vec4<f32>,
430435
k2: vec4<f32>, k3: vec4<f32>, time: f32, motion: f32, aspect: f32) -> vec3<f32> {
431436
let TAU = 6.2831853;
432437
let u = dot(gp - vec2<f32>(0.5), dir) / denom; // position le long de l'axe, -0.5..0.5
433438
if motion < 2.5 {
434-
// Aurore : rampe perturbee par un bruit lent, puis trois nappes gaussiennes.
439+
// Aurore : rampe perturbee par un bruit, puis trois larges nappes gaussiennes.
435440
let p = vec2<f32>((gp.x - 0.5) * aspect, gp.y - 0.5);
436-
let ph = TAU * time / 120.0;
437-
let n = value_noise(p * 2.5 + 1.5 * vec2<f32>(cos(ph), sin(ph)));
438-
var g = ramp4(clamp(0.5 + u + 0.3 * (n - 0.5), 0.0, 1.0), k0, k1, k2, k3);
439-
let b0 = vec2<f32>(0.35 * aspect * sin(TAU * time / 20.0), 0.25 * sin(TAU * time / 30.0 + 1.0));
440-
let b1 = vec2<f32>(0.30 * aspect * sin(TAU * time / 24.0 + 2.0), 0.22 * cos(TAU * time / 40.0));
441-
let b2 = vec2<f32>(0.25 * aspect * cos(TAU * time / 30.0 + 4.0), 0.28 * sin(TAU * time / 24.0 + 3.0));
442-
g = mix(g, k3.rgb, 0.45 * exp(-dot(p - b0, p - b0) / 0.08));
443-
g = mix(g, k0.rgb, 0.45 * exp(-dot(p - b1, p - b1) / 0.06));
444-
g = mix(g, k3.rgb, 0.35 * exp(-dot(p - b2, p - b2) / 0.05));
441+
let ph = TAU * time / 30.0;
442+
let n = value_noise(p * 1.8 + 1.5 * vec2<f32>(cos(ph), sin(ph)));
443+
var g = ramp4(clamp(0.5 + u + 0.6 * (n - 0.5), 0.0, 1.0), k0, k1, k2, k3);
444+
let b0 = vec2<f32>(0.35 * aspect * sin(TAU * time / 10.0), 0.25 * sin(TAU * time / 15.0 + 1.0));
445+
let b1 = vec2<f32>(0.30 * aspect * sin(TAU * time / 12.0 + 2.0), 0.22 * cos(TAU * time / 20.0));
446+
let b2 = vec2<f32>(0.25 * aspect * cos(TAU * time / 15.0 + 4.0), 0.28 * sin(TAU * time / 12.0 + 3.0));
447+
let light = lighten(k3.rgb, 0.15);
448+
g = mix(g, light, 0.7 * exp(-dot(p - b0, p - b0) / 0.176));
449+
g = mix(g, lighten(k0.rgb, -0.15), 0.7 * exp(-dot(p - b1, p - b1) / 0.132));
450+
g = mix(g, light, 0.6 * exp(-dot(p - b2, p - b2) / 0.11));
445451
return g;
446452
}
447-
// Vagues : trois bandes sinus perpendiculaires a l'axe (12 s), ondulees (20 s).
453+
// Vagues : trois bandes sinus perpendiculaires a l'axe (6 s), ondulees (10 s), cretes eclairees.
448454
let v = dot(gp - vec2<f32>(0.5), vec2<f32>(-dir.y, dir.x)) / denom;
449-
let w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 20.0)) - time / 12.0));
450-
return ramp4(clamp(0.5 + u + 0.07 * w, 0.0, 1.0), k0, k1, k2, k3);
455+
let w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 10.0)) - time / 6.0));
456+
return lighten(ramp4(clamp(0.5 + u + 0.18 * w, 0.0, 1.0), k0, k1, k2, k3), 0.07 * w);
451457
}
452458

453459
// Couverture d'une pastille (disque) adoucie sur ~1.5 px, pour la barre de titre du mode 14.
@@ -1869,12 +1875,15 @@ fn fs_main(i: VsOut) -> @location(0) vec4<f32> {
18691875
// Fond anime : fx.z = temps programme (s, replie sur 120), fx.w = mouvement (0 immobile,
18701876
// 1 derive, 2 aurore, 3 vagues), mb.x = aspect w/h. 0 rend le degrade d'avant a l'octet.
18711877
var dir = layer.fx.xy;
1878+
var slide = 0.0;
18721879
if layer.fx.w > 0.5 && layer.fx.w < 1.5 {
1873-
// Derive : l'axe respire de +-15 deg (0.2617994 rad) en 20 s.
1874-
let da = 0.2617994 * sin(6.2831853 * layer.fx.z / 20.0);
1880+
// Derive : l'axe balance de +-30 deg (0.5235988 rad) en 20 s, le degrade glisse le
1881+
// long de lui de +-20 % en 15 s.
1882+
let da = 0.5235988 * sin(6.2831853 * layer.fx.z / 20.0);
18751883
let sa = sin(da);
18761884
let ca = cos(da);
18771885
dir = vec2<f32>(dir.x * ca - dir.y * sa, dir.x * sa + dir.y * ca);
1886+
slide = 0.2 * sin(6.2831853 * layer.fx.z / 15.0);
18781887
}
18791888
let denom = max(abs(dir.x) + abs(dir.y), 1e-4);
18801889
// Parametre sur le QUAD des qu'il en a un (la bulle webcam), sinon sur la sortie. Pour
@@ -1884,7 +1893,7 @@ fn fs_main(i: VsOut) -> @location(0) vec4<f32> {
18841893
if layer.quad_px.x > 0.0 && layer.quad_px.y > 0.0 {
18851894
gp = i.local / layer.quad_px;
18861895
}
1887-
let t = clamp(0.5 + dot(gp - vec2<f32>(0.5), dir) / denom, 0.0, 1.0);
1896+
let t = clamp(0.5 + dot(gp - vec2<f32>(0.5), dir) / denom + slide, 0.0, 1.0);
18881897
rgb = ramp4(t, layer.color, layer.src_prev, layer.dst_prev, layer.src);
18891898
if layer.fx.w > 1.5 {
18901899
rgb = gradient_motion(gp, dir, denom, layer.color, layer.src_prev, layer.dst_prev,

0 commit comments

Comments
 (0)