Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
scenefx/render/fx_renderer/shaders/droplet.frag (4.7K)
1 // Droplet backdrop refraction: the region behind a status-bar water drop,
2 // re-rendered through the drop's lens.
3 //
4 // The silhouette is the same shape the cce-ui client draws — a superellipse-
5 // rounded box (attach radius on the top corners, sheet radius on the bottom)
6 // smooth-intersected with a huge disc whose lowest point touches the box
7 // bottom (the continuous bottom arc) — so the refracting region and the
8 // drawn drop can never disagree. Inside it, the backdrop texture (scenefx's
9 // unblurred optimized-blur snapshot of everything beneath the bar layer) is
10 // sampled with two warps:
11 //
12 // - a rim-weighted offset along the SDF gradient: the image bends around
13 // the drop edge the way glass pulls the background with it;
14 // - a faint inverted, minified sample about the drop center: the upside-
15 // down image of the scene a real hanging drop shows in its belly.
16 //
17 // The client's translucent tint, gleam and text composite on top of this.
18
19 #ifdef GL_FRAGMENT_PRECISION_HIGH
20 precision highp float;
21 #else
22 precision mediump float;
23 #endif
24
25 varying vec4 v_color;
26 varying vec2 v_texcoord;
27
28 uniform sampler2D tex;
29 uniform vec2 tex_size;
30 uniform vec2 position; // box origin (same space corner shaders use)
31 uniform vec2 size; // box size, px
32 uniform float attach_r; // top-corner radius px
33 uniform float sheet_r; // bottom-corner radius px
34 uniform float bow_rise; // bottom-arc edge rise px (0 = flat bottom run)
35 uniform float blend_k; // smooth-intersection blend px
36 uniform float curve; // corner-shape exponent (2 = circular)
37 uniform float band_px; // dome skirt width px (the refraction falloff scale)
38 uniform float refr; // rim refraction strength px (sign flips direction)
39 uniform float ghost; // inverted-lens ghost strength 0-1
40
41 // Signed distance to the drop silhouette at box-local p (y DOWN from the box
42 // top). The rounded-box part is the same construction as the client's
43 // rr_sdf_grad: quadrant-selected corner radius, Lp corners with a first-order
44 // |grad| correction above exponent 2.
45 float drop_dist(vec2 p) {
46 vec2 half_ext = size * 0.5;
47 vec2 c = p - half_ext;
48 float r = (c.y > 0.0) ? sheet_r : attach_r;
49 vec2 q = abs(c) - half_ext + vec2(r, r);
50 float d;
51 if (q.x > 0.0 && q.y > 0.0) {
52 if (curve > 2.001 && r > 0.0) {
53 vec2 u = q / r;
54 float lp = max(pow(pow(u.x, curve) + pow(u.y, curve), 1.0 / curve), 1e-4);
55 vec2 g = vec2(pow(u.x / lp, curve - 1.0), pow(u.y / lp, curve - 1.0));
56 d = r * (lp - 1.0) / max(length(g), 1e-4);
57 } else {
58 d = length(q) - r;
59 }
60 } else {
61 d = max(q.x, q.y) - r;
62 }
63 if (bow_rise > 0.25) {
64 // Smooth-intersect with the bottom-arc disc (smax = -smin(-a,-b)).
65 float hx = half_ext.x;
66 float R = hx * hx / (2.0 * bow_rise);
67 vec2 cc = vec2(hx, size.y - R);
68 float dc = length(p - cc) - R;
69 float k = max(blend_k, 1.0);
70 float hm = clamp(0.5 + 0.5 * (d - dc) / k, 0.0, 1.0);
71 d = mix(dc, d, hm) + k * hm * (1.0 - hm);
72 }
73 return d;
74 }
75
76 void main() {
77 // Box-local coords with y down. NOTE: deliberately NOT the corner
78 // shaders' extra y flip — that transform is unverifiable from the
79 // symmetric shapes that use it (equal radii look the same flipped), and
80 // with this drop's asymmetric silhouette it rendered the attach taper at
81 // the BOTTOM (live bug: black flipped boxes behind the modules).
82 // gl_FragCoord minus the box position is already top-down box-local
83 // here.
84 vec2 rel = gl_FragCoord.xy - position;
85
86 float d = drop_dist(rel);
87 float aa = clamp(-d + 0.5, 0.0, 1.0);
88 if (aa <= 0.0) {
89 discard;
90 }
91
92 // SDF gradient by central differences, in the same y-down local space.
93 vec2 grad = normalize(vec2(
94 drop_dist(rel + vec2(1.0, 0.0)) - drop_dist(rel - vec2(1.0, 0.0)),
95 drop_dist(rel + vec2(0.0, 1.0)) - drop_dist(rel - vec2(0.0, 1.0))
96 ) + vec2(1e-6, 0.0));
97
98 // Rim-weighted lens profile: 1 at the silhouette, 0 deep inside.
99 float t = clamp(-d / max(band_px, 1.0), 0.0, 1.0);
100 float lens = (1.0 - t) * (1.0 - t);
101
102 // Refraction: offset the sample along the gradient. gl_FragCoord shares
103 // the box-local frame's orientation (see `rel` above), so the offset
104 // applies directly. Positive refr samples outward — the background bends
105 // around the drop edge.
106 vec2 off = grad * (refr * lens);
107 vec2 uv = (gl_FragCoord.xy + off) / tex_size;
108 vec4 col = texture2D(tex, clamp(uv, vec2(0.0), vec2(1.0)));
109
110 // Inverted lens ghost: the belly shows a faint upside-down, minified
111 // image of the scene about the drop center.
112 if (ghost > 0.001) {
113 vec2 center_fc = position + size * 0.5;
114 vec2 ghost_uv = (center_fc + (center_fc - gl_FragCoord.xy) * 0.35) / tex_size;
115 vec4 gcol = texture2D(tex, clamp(ghost_uv, vec2(0.0), vec2(1.0)));
116 col.rgb = mix(col.rgb, gcol.rgb, ghost * t * t);
117 }
118
119 gl_FragColor = vec4(col.rgb, 1.0) * aa;
120 }