git.lucas.co / cce-compositor
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 }