Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/compositor/src/frame_geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1565,8 +1565,8 @@ fn same_source_path(a: &str, b: &str) -> bool {
a.eq_ignore_ascii_case(b)
}

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

Expand Down Expand Up @@ -6722,7 +6722,7 @@ mod tests {
assert_eq!(gradient_motion_slots(M::Aurora, 0.0, 1.0).0[1], 2.0);
assert_eq!(gradient_motion_slots(M::Waves, 240.0, 1.0).0, [0.0, 3.0]);
// Chaque période du shader divise la période de repli : pas de raccord au bouclage.
for p in [20.0, 24.0, 30.0, 40.0, 12.0, 120.0] {
for p in [6.0, 10.0, 12.0, 15.0, 20.0, 30.0] {
assert_eq!(GRADIENT_MOTION_PERIOD_S % p, 0.0, "période {p}");
}
}
Expand Down
55 changes: 34 additions & 21 deletions crates/compositor/src/shaders.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -349,37 +349,47 @@ float3 ramp4(float t, float4 k0, float4 k1, float4 k2, float4 k3)
return c;
}

// Éclaircit vers le blanc (k > 0) ou assombrit vers le noir (k < 0) d'une fraction |k|.
float3 lighten(float3 c, float k)
{
return k > 0.0 ? c + (1.0 - c) * k : c * (1.0 + k);
}

// Mouvements 2 (aurore) et 3 (vagues) du mode 5, sur la rampe de ses stops.
// `gp` 0..1 sur le quad, `dir`/`denom` ceux du dégradé, `time` = temps programme replié sur
// 120 s (toutes les périodes ci-dessous le divisent), `aspect` = w/h de la sortie. Périodes
// longues et contraste bas : le fond ne doit jamais prendre l'attention.
// 120 s (toutes les périodes ci-dessous le divisent), `aspect` = w/h de la sortie. Amples et
// rapides assez pour se voir d'un coup d'œil : réglés plus bas, ils ne se voyaient qu'en
// comparant deux images, et la plupart des dégradés proposés n'ont que deux teintes voisines,
// d'où la lumière que chacun ajoute en plus de déplacer la rampe.
float3 gradient_motion(float2 gp, float2 dir, float denom, float4 k0, float4 k1, float4 k2,
float4 k3, float time, float motion, float aspect)
{
const float TAU = 6.2831853;
float u = dot(gp - 0.5, dir) / denom; // position le long de l'axe, -0.5..0.5
if (motion < 2.5)
{
// Aurore : rampe perturbée par un bruit lent (dont l'origine tourne en 120 s), puis
// trois nappes gaussiennes sur des Lissajous de 20 à 40 s. Coordonnées corrigées de
// l'aspect pour que les nappes restent rondes.
// Aurore : rampe perturbée par un bruit (dont l'origine tourne en 30 s), puis trois
// larges nappes gaussiennes sur des Lissajous de 10 à 20 s. Deux portent la couleur de
// fin éclaircie, une celle du début assombrie. Coordonnées corrigées de l'aspect pour
// que les nappes restent rondes.
float2 p = float2((gp.x - 0.5) * aspect, gp.y - 0.5);
float ph = TAU * time / 120.0;
float n = value_noise(p * 2.5 + 1.5 * float2(cos(ph), sin(ph)));
float3 g = ramp4(saturate(0.5 + u + 0.3 * (n - 0.5)), k0, k1, k2, k3);
float2 b0 = float2(0.35 * aspect * sin(TAU * time / 20.0), 0.25 * sin(TAU * time / 30.0 + 1.0));
float2 b1 = float2(0.30 * aspect * sin(TAU * time / 24.0 + 2.0), 0.22 * cos(TAU * time / 40.0));
float2 b2 = float2(0.25 * aspect * cos(TAU * time / 30.0 + 4.0), 0.28 * sin(TAU * time / 24.0 + 3.0));
g = lerp(g, k3.rgb, 0.45 * exp(-dot(p - b0, p - b0) / 0.08));
g = lerp(g, k0.rgb, 0.45 * exp(-dot(p - b1, p - b1) / 0.06));
g = lerp(g, k3.rgb, 0.35 * exp(-dot(p - b2, p - b2) / 0.05));
float ph = TAU * time / 30.0;
float n = value_noise(p * 1.8 + 1.5 * float2(cos(ph), sin(ph)));
float3 g = ramp4(saturate(0.5 + u + 0.6 * (n - 0.5)), k0, k1, k2, k3);
float2 b0 = float2(0.35 * aspect * sin(TAU * time / 10.0), 0.25 * sin(TAU * time / 15.0 + 1.0));
float2 b1 = float2(0.30 * aspect * sin(TAU * time / 12.0 + 2.0), 0.22 * cos(TAU * time / 20.0));
float2 b2 = float2(0.25 * aspect * cos(TAU * time / 15.0 + 4.0), 0.28 * sin(TAU * time / 12.0 + 3.0));
float3 light = lighten(k3.rgb, 0.15);
g = lerp(g, light, 0.7 * exp(-dot(p - b0, p - b0) / 0.176));
g = lerp(g, lighten(k0.rgb, -0.15), 0.7 * exp(-dot(p - b1, p - b1) / 0.132));
g = lerp(g, light, 0.6 * exp(-dot(p - b2, p - b2) / 0.11));
return g;
}
// Vagues : trois bandes sinus perpendiculaires à l'axe, qui avancent d'une bande en 12 s,
// légèrement ondulées le long des bandes (20 s). Elles décalent la rampe, rien d'autre.
// Vagues : trois bandes sinus perpendiculaires à l'axe, qui avancent d'une bande en 6 s,
// ondulées le long des bandes (10 s). Elles décalent la rampe et éclairent leurs crêtes.
float v = dot(gp - 0.5, float2(-dir.y, dir.x)) / denom;
float w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 20.0)) - time / 12.0));
return ramp4(saturate(0.5 + u + 0.07 * w), k0, k1, k2, k3);
float w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 10.0)) - time / 6.0));
return lighten(ramp4(saturate(0.5 + u + 0.18 * w), k0, k1, k2, k3), 0.07 * w);
}

// Couverture d'une pastille (disque) adoucie sur ~1.5 px, pour la barre de titre du mode 14.
Expand Down Expand Up @@ -2493,21 +2503,24 @@ float4 ps_main(VSOut i) : SV_Target
if (mode > 4.5)
{
float2 dir = fx.xy;
float slide = 0.0;
if (fx.w > 0.5 && fx.w < 1.5)
{
// Dérive : l'axe respire de ±15° (0.2617994 rad) en 20 s.
float da = 0.2617994 * sin(6.2831853 * fx.z / 20.0);
// Dérive : l'axe balance de ±30° (0.5235988 rad) en 20 s, et le dégradé glisse le
// long de lui de ±20 % en 15 s. Une rotation seule laisse le centre immobile.
float da = 0.5235988 * sin(6.2831853 * fx.z / 20.0);
float sa = sin(da);
float ca = cos(da);
dir = float2(dir.x * ca - dir.y * sa, dir.x * sa + dir.y * ca);
slide = 0.2 * sin(6.2831853 * fx.z / 15.0);
}
float denom = max(abs(dir.x) + abs(dir.y), 1e-4);
// Paramétré sur le QUAD dès qu'il en a un (la bulle webcam), sinon sur la sortie. Pour le
// fond plein cadre les deux coïncident ; pour une bulle dans un coin, `pout` ne montrerait
// que la tranche du dégradé plein cadre qui passe dessous, jamais la rampe complète que
// le sélecteur affiche.
float2 gp = (quad_px.x > 0.0 && quad_px.y > 0.0) ? (i.local / quad_px) : i.pout;
float t = saturate(0.5 + dot(gp - 0.5, dir) / denom);
float t = saturate(0.5 + dot(gp - 0.5, dir) / denom + slide);
float3 g = ramp4(t, color, src_prev, dst_prev, src);
if (fx.w > 1.5)
{
Expand Down
42 changes: 26 additions & 16 deletions crates/compositor/src/shaders.metal
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,12 @@ inline float3 ramp4(float t, float4 k0, float4 k1, float4 k2, float4 k3)
return c;
}

// Éclaircit vers le blanc (k > 0) ou assombrit vers le noir (k < 0) d'une fraction |k|.
inline float3 lighten(float3 c, float k)
{
return k > 0.0 ? c + (1.0 - c) * k : c * (1.0 + k);
}

// Mouvements 2 (aurore) et 3 (vagues) du mode 5. Miroir ligne pour ligne de
// `gradient_motion` côté HLSL (commentaires complets là-bas).
inline float3 gradient_motion(float2 gp, float2 dir, float denom, float4 k0, float4 k1,
Expand All @@ -472,23 +478,24 @@ inline float3 gradient_motion(float2 gp, float2 dir, float denom, float4 k0, flo
float u = dot(gp - 0.5, dir) / denom; // position le long de l'axe, -0.5..0.5
if (motion < 2.5)
{
// Aurore : rampe perturbée par un bruit lent, puis trois nappes gaussiennes.
// Aurore : rampe perturbée par un bruit, puis trois larges nappes gaussiennes.
float2 p = float2((gp.x - 0.5) * aspect, gp.y - 0.5);
float ph = TAU * time / 120.0;
float n = value_noise(p * 2.5 + 1.5 * float2(cos(ph), sin(ph)));
float3 g = ramp4(clamp(0.5 + u + 0.3 * (n - 0.5), 0.0, 1.0), k0, k1, k2, k3);
float2 b0 = float2(0.35 * aspect * sin(TAU * time / 20.0), 0.25 * sin(TAU * time / 30.0 + 1.0));
float2 b1 = float2(0.30 * aspect * sin(TAU * time / 24.0 + 2.0), 0.22 * cos(TAU * time / 40.0));
float2 b2 = float2(0.25 * aspect * cos(TAU * time / 30.0 + 4.0), 0.28 * sin(TAU * time / 24.0 + 3.0));
g = mix(g, k3.rgb, 0.45 * exp(-dot(p - b0, p - b0) / 0.08));
g = mix(g, k0.rgb, 0.45 * exp(-dot(p - b1, p - b1) / 0.06));
g = mix(g, k3.rgb, 0.35 * exp(-dot(p - b2, p - b2) / 0.05));
float ph = TAU * time / 30.0;
float n = value_noise(p * 1.8 + 1.5 * float2(cos(ph), sin(ph)));
float3 g = ramp4(clamp(0.5 + u + 0.6 * (n - 0.5), 0.0, 1.0), k0, k1, k2, k3);
float2 b0 = float2(0.35 * aspect * sin(TAU * time / 10.0), 0.25 * sin(TAU * time / 15.0 + 1.0));
float2 b1 = float2(0.30 * aspect * sin(TAU * time / 12.0 + 2.0), 0.22 * cos(TAU * time / 20.0));
float2 b2 = float2(0.25 * aspect * cos(TAU * time / 15.0 + 4.0), 0.28 * sin(TAU * time / 12.0 + 3.0));
float3 light = lighten(k3.rgb, 0.15);
g = mix(g, light, 0.7 * exp(-dot(p - b0, p - b0) / 0.176));
g = mix(g, lighten(k0.rgb, -0.15), 0.7 * exp(-dot(p - b1, p - b1) / 0.132));
g = mix(g, light, 0.6 * exp(-dot(p - b2, p - b2) / 0.11));
return g;
}
// Vagues : trois bandes sinus perpendiculaires à l'axe (12 s), ondulées (20 s).
// Vagues : trois bandes sinus perpendiculaires à l'axe (6 s), ondulées (10 s), crêtes éclairées.
float v = dot(gp - 0.5, float2(-dir.y, dir.x)) / denom;
float w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 20.0)) - time / 12.0));
return ramp4(clamp(0.5 + u + 0.07 * w, 0.0, 1.0), k0, k1, k2, k3);
float w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 10.0)) - time / 6.0));
return lighten(ramp4(clamp(0.5 + u + 0.18 * w, 0.0, 1.0), k0, k1, k2, k3), 0.07 * w);
}

// ============ Curseur MODÉLISÉ (mode 15) ============
Expand Down Expand Up @@ -2274,13 +2281,16 @@ fragment float4 ps_main(VSOut i [[stage_in]],
if (layer.mode > 4.5 && layer.mode < 5.5)
{
float2 dir = layer.fx.xy;
float slide = 0.0;
if (layer.fx.w > 0.5 && layer.fx.w < 1.5)
{
// Dérive : l'axe respire de ±15° (0.2617994 rad) en 20 s.
float da = 0.2617994 * sin(6.2831853 * layer.fx.z / 20.0);
// Dérive : l'axe balance de ±30° (0.5235988 rad) en 20 s, le dégradé glisse le long
// de lui de ±20 % en 15 s.
float da = 0.5235988 * sin(6.2831853 * layer.fx.z / 20.0);
float sa = sin(da);
float ca = cos(da);
dir = float2(dir.x * ca - dir.y * sa, dir.x * sa + dir.y * ca);
slide = 0.2 * sin(6.2831853 * layer.fx.z / 15.0);
}
float denom = max(abs(dir.x) + abs(dir.y), 1e-4);
// Paramétré sur le QUAD dès qu'il en a un (la bulle webcam), sinon sur la sortie. Pour le
Expand All @@ -2290,7 +2300,7 @@ fragment float4 ps_main(VSOut i [[stage_in]],
float2 gp = (layer.quad_px.x > 0.0 && layer.quad_px.y > 0.0)
? (i.local / layer.quad_px)
: i.pout;
float t = clamp(0.5 + dot(gp - 0.5, dir) / denom, 0.0, 1.0);
float t = clamp(0.5 + dot(gp - 0.5, dir) / denom + slide, 0.0, 1.0);
float3 g = ramp4(t, layer.color, layer.src_prev, layer.dst_prev, layer.src);
if (layer.fx.w > 1.5)
{
Expand Down
41 changes: 25 additions & 16 deletions crates/compositor/src/vk_shaders/layer.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -424,30 +424,36 @@ fn ramp4(t: f32, k0: vec4<f32>, k1: vec4<f32>, k2: vec4<f32>, k3: vec4<f32>) ->
return c;
}

// Eclaircit vers le blanc (k > 0) ou assombrit vers le noir (k < 0) d'une fraction |k|.
fn lighten(c: vec3<f32>, k: f32) -> vec3<f32> {
return select(c * (1.0 + k), c + (vec3<f32>(1.0) - c) * k, k > 0.0);
}

// Mouvements 2 (aurore) et 3 (vagues) du mode 5. Miroir ligne pour ligne de
// `gradient_motion` cote HLSL (commentaires complets la-bas).
fn gradient_motion(gp: vec2<f32>, dir: vec2<f32>, denom: f32, k0: vec4<f32>, k1: vec4<f32>,
k2: vec4<f32>, k3: vec4<f32>, time: f32, motion: f32, aspect: f32) -> vec3<f32> {
let TAU = 6.2831853;
let u = dot(gp - vec2<f32>(0.5), dir) / denom; // position le long de l'axe, -0.5..0.5
if motion < 2.5 {
// Aurore : rampe perturbee par un bruit lent, puis trois nappes gaussiennes.
// Aurore : rampe perturbee par un bruit, puis trois larges nappes gaussiennes.
let p = vec2<f32>((gp.x - 0.5) * aspect, gp.y - 0.5);
let ph = TAU * time / 120.0;
let n = value_noise(p * 2.5 + 1.5 * vec2<f32>(cos(ph), sin(ph)));
var g = ramp4(clamp(0.5 + u + 0.3 * (n - 0.5), 0.0, 1.0), k0, k1, k2, k3);
let b0 = vec2<f32>(0.35 * aspect * sin(TAU * time / 20.0), 0.25 * sin(TAU * time / 30.0 + 1.0));
let b1 = vec2<f32>(0.30 * aspect * sin(TAU * time / 24.0 + 2.0), 0.22 * cos(TAU * time / 40.0));
let b2 = vec2<f32>(0.25 * aspect * cos(TAU * time / 30.0 + 4.0), 0.28 * sin(TAU * time / 24.0 + 3.0));
g = mix(g, k3.rgb, 0.45 * exp(-dot(p - b0, p - b0) / 0.08));
g = mix(g, k0.rgb, 0.45 * exp(-dot(p - b1, p - b1) / 0.06));
g = mix(g, k3.rgb, 0.35 * exp(-dot(p - b2, p - b2) / 0.05));
let ph = TAU * time / 30.0;
let n = value_noise(p * 1.8 + 1.5 * vec2<f32>(cos(ph), sin(ph)));
var g = ramp4(clamp(0.5 + u + 0.6 * (n - 0.5), 0.0, 1.0), k0, k1, k2, k3);
let b0 = vec2<f32>(0.35 * aspect * sin(TAU * time / 10.0), 0.25 * sin(TAU * time / 15.0 + 1.0));
let b1 = vec2<f32>(0.30 * aspect * sin(TAU * time / 12.0 + 2.0), 0.22 * cos(TAU * time / 20.0));
let b2 = vec2<f32>(0.25 * aspect * cos(TAU * time / 15.0 + 4.0), 0.28 * sin(TAU * time / 12.0 + 3.0));
let light = lighten(k3.rgb, 0.15);
g = mix(g, light, 0.7 * exp(-dot(p - b0, p - b0) / 0.176));
g = mix(g, lighten(k0.rgb, -0.15), 0.7 * exp(-dot(p - b1, p - b1) / 0.132));
g = mix(g, light, 0.6 * exp(-dot(p - b2, p - b2) / 0.11));
return g;
}
// Vagues : trois bandes sinus perpendiculaires a l'axe (12 s), ondulees (20 s).
// Vagues : trois bandes sinus perpendiculaires a l'axe (6 s), ondulees (10 s), cretes eclairees.
let v = dot(gp - vec2<f32>(0.5), vec2<f32>(-dir.y, dir.x)) / denom;
let w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 20.0)) - time / 12.0));
return ramp4(clamp(0.5 + u + 0.07 * w, 0.0, 1.0), k0, k1, k2, k3);
let w = sin(TAU * (3.0 * u + 0.04 * sin(TAU * (1.5 * v + time / 10.0)) - time / 6.0));
return lighten(ramp4(clamp(0.5 + u + 0.18 * w, 0.0, 1.0), k0, k1, k2, k3), 0.07 * w);
}

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