GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
src/vk/shader2d.wgsl (59.1K)
1 // The toolkit's 2D pipeline shader — the union of the two wgpu-era dialects:
2 // the engine shader's wavy-blob effect (clip_circle.x == -999 sentinel) and the
3 // designer shader's window-corner rounding + circle clip + blur-behind branch
4 // (negative alpha samples the backdrop). Clients that don't use a feature pay
5 // nothing: radius 0 disables corner rounding, the backdrop is renderer-managed,
6 // and plain quads take the final `return in.color` path.
7
8 @group(0) @binding(0) var t_backdrop: texture_2d<f32>;
9 @group(0) @binding(1) var s_backdrop: sampler;
10
11 struct WindowInfo {
12 window_size: vec2<f32>,
13 corner_radius: f32,
14 // Corner-shape exponent shared with the plates and the rounded-rect clip:
15 // circular arc at 2, superellipse squircle above.
16 corner_shape: f32,
17 // Custom bevel/carve profile (cce_ui::layout::set_bevel_profile_keys):
18 // x nonzero enables it, y = live sample count in `profile`.
19 profile_meta: vec4f,
20 // Slope samples of the profile's height curve h(v) (v 0 = plateau, 1 =
21 // carve floor / boss crest), sample i at v = (i + 0.5) / count, packed 4
22 // per vec4. carve_slope reads these in place of its analytic smoothstep.
23 profile: array<vec4f, 8>,
24 // Custom EDGE profile for the plate perimeter roll
25 // (cce_ui::layout::set_roll_profile_keys) — same encoding, read by
26 // roll_slope in place of the analytic superellipse quadrant. The curve is
27 // the roll's descent progress: 0 at the face join, 1 at the silhouette.
28 roll_meta: vec4f,
29 roll_profile: array<vec4f, 8>,
30 // Pinned relief heights in physical px (cce_ui::layout::bevel_height /
31 // roll_height): x = a carve's drop, y = the plate roll's rise. 0 = follow
32 // the wall width — RECESS_DEPTH × width for a carve, a quarter-round of
33 // radius width for the roll. Divided by the batch's own wall width
34 // (p_light.w) they become the slope scale, so a pinned 0.5 mm drop is
35 // the same geometry whatever wall it is cut with.
36 relief_meta: vec4f,
37 }
38
39 @group(0) @binding(2) var<uniform> window_info: WindowInfo;
40
41 // One carve (recess) belonging to an SDF-lit plate: a rounded box subtracted
42 // from the plate's material. rect = center + half-extents, radii per-corner
43 // (both physical px; a wall the carve shares with the plate's edge is encoded
44 // by extending the box past the plate on that side). params = [transition
45 // width px, depth px, 0, 0].
46 struct PlateFeature {
47 rect: vec4f,
48 radii: vec4f,
49 params: vec4f,
50 }
51 // Double-buffered by frame-in-flight: slot k's 64 entries belong to frame
52 // index k. The plate's push constants carry the absolute offset.
53 struct PlateFeatures {
54 items: array<PlateFeature, 128>,
55 }
56 @group(0) @binding(3) var<uniform> plate_features: PlateFeatures;
57
58 // Signed distance to the window's rounded silhouette at pos: positive outside
59 // the corner arcs (and past the window bounds), large-negative elsewhere so the
60 // straight edges keep their exact hard cut at the buffer boundary. The corner
61 // family follows window_info.corner_shape — circular arc at 2, superellipse
62 // squircle above, with the Lp branch's first-order |∇| correction so a feather
63 // built on this distance keeps ~uniform width around the arc (the same
64 // construction as rr_sdf_grad and the tessellated plate corners).
65 fn window_corner_distance(pos: vec2<f32>) -> f32 {
66 let w = window_info.window_size.x;
67 let h = window_info.window_size.y;
68 let r = window_info.corner_radius;
69
70 if (pos.x < 0.0 || pos.x > w || pos.y < 0.0 || pos.y > h) {
71 return 1e5;
72 }
73 if (r <= 0.0) {
74 return -1e5;
75 }
76 let q = abs(pos - vec2f(w * 0.5, h * 0.5)) - vec2f(w * 0.5 - r, h * 0.5 - r);
77 if (q.x > 0.0 && q.y > 0.0) {
78 let shape = window_info.corner_shape;
79 if (shape > 2.001) {
80 let lp = max(pow(pow(q.x, shape) + pow(q.y, shape), 1.0 / shape), 1e-4);
81 let g = vec2f(pow(q.x / lp, shape - 1.0), pow(q.y / lp, shape - 1.0));
82 return (lp - r) / max(length(g), 1e-4);
83 }
84 return length(q) - r;
85 }
86 return -1e5;
87 }
88
89 // Per-batch push constants (112 bytes). The first two vec4s are the rounded-rect
90 // clip: rect0 = [cx, cy, bx, by] (center + SDF half-extents), rect1 = [corner
91 // radius, enabled flag, plate mode, corner shape]. When plate mode is
92 // nonzero the batch is an SDF-lit plate (1 = raised plate, 2 = recess overlay)
93 // and the p_* block describes it. The corner shape exponent selects circular
94 // (2) vs superellipse (> 2) corners for BOTH the clip SDF and the plate —
95 // see rr_sdf_grad; it is set whenever either consumer is live. Physical
96 // pixels, like clip_position.
97 struct RRectClip {
98 rect0: vec4f,
99 rect1: vec4f,
100 // Plate SDF box: center + half-extents. May extend past the drawn cover
101 // quad — that is how a recess suppresses a wall (the edge lies outside the
102 // covered pixels, so its shading never lands).
103 p_rect: vec4f,
104 // Per-corner radii [tl, tr, br, bl].
105 p_radii: vec4f,
106 // xyz = unit vector toward the light (screen space, +z out of the screen),
107 // w = bevel roll width in px.
108 p_light: vec4f,
109 // [shading strength, specular strength, shininess, curvature/AO strength].
110 p_mat: vec4f,
111 // Mode 1 (raised plate): xy = [offset, count] into plate_features — the
112 // carves CSG'd out of this plate's material; z = the plate's FROST
113 // recipe, compression and refraction as 12-bit fixed point in one float
114 // (hi·FROST_PACK_BASE + lo, each over FROST_PACK_MAX — see
115 // material::Frost::pack); w = the blur kernel's sigma in physical px,
116 // 0 = a clear plate (one clean sample). Unread on an opaque plate.
117 // Mode 2 (free recess overlay): the host-plate box (center + half-extents)
118 // the carve fades out against — a wall flush with the host's edge dies
119 // across the host's perimeter roll; far-away sides sit at ±1e5 (no fade).
120 p_host: vec4f,
121 // RGB multiplies the lit roll's specular color — neutral white normally,
122 // a highlight color on a marked (focused) plate. w unused.
123 p_spec_tint: vec4f,
124 }
125 var<push_constant> rrect_clip: RRectClip;
126
127 // Plate modes, as carried in rect1.z (see PlatePush::mode). Compared by EQUALITY
128 // on a rounded int, never by range: the ranges these replaced were ordered, and
129 // the order was load-bearing without saying so — mode 8's branch had to precede
130 // the `> 5.5` fillet branch or the fillet arm would have swallowed it, taken 4
131 // off, and drawn every groove as a ridge. Equality makes a new mode inert
132 // wherever it is added rather than silently captured by a neighbour.
133 // Frost recipe packing — mirrored by `scene::material::Frost`, checked by
134 // its tests against this text.
135 const FROST_PACK_MAX: f32 = 4095.0;
136 const FROST_PACK_BASE: f32 = 4096.0;
137 // The kernel stride a frosted surface with NO recipe uses — the droplet
138 // (its push block is full) and a raw negative-alpha vertex from outside the
139 // display list: the panel's default kernel (Frost::DEFAULT_RADIUS at scale
140 // 2), which is exactly the fixed 5.5 px stride every frosted plate had
141 // before recipes were per plate.
142 const LEGACY_STRIDE: f32 = 5.5;
143
144 const MODE_NONE: i32 = 0; // not a plate batch
145 const MODE_PLATE: i32 = 1; // raised lit plate: fill + rolled perimeter + CSG carves
146 const MODE_RECESS: i32 = 2; // free carve, interior one step DOWN
147 const MODE_BOSS: i32 = 3; // free carve, interior one step UP
148 const MODE_RIDGE: i32 = 4; // raised rim straddling the boundary
149 const MODE_SPHERE: i32 = 5; // hemisphere-lit disc
150 const MODE_FILLET_DOWN: i32 = 6; // concave inside-corner wall, recessed
151 const MODE_FILLET_UP: i32 = 7; // concave inside-corner wall, raised
152 const MODE_GROOVE: i32 = 8; // slab carve about an arbitrary line
153 const MODE_TROUGH: i32 = 9; // sunken valley straddling the boundary
154 const MODE_DROPLET: i32 = 10; // hanging water droplet clinging to the box top
155 const MODE_ROLL: i32 = 11; // fill-less rolled perimeter, composited as an overlay
156 const MODE_DROPLET_SCRIM: i32 = 12; // flat feathered fill of the droplet silhouette
157 const MODE_LATTICE: i32 = 13; // periodic well field: nearest-cell carve, one evaluation
158 const MODE_UNION: i32 = 14; // union of feature boxes carved/raised as one wall
159 const MODE_GROUT: i32 = 15; // flat colour outside a periodic field of rounded cells
160 // Fillet modes rejoin the shared free-carve path as their flat equivalents.
161 const FILLET_TO_STEP: i32 = 4; // 6 -> RECESS, 7 -> BOSS
162
163 const TAU: f32 = 6.28318530718;
164 // Ambient floor of the plate lighting model: the fraction of illumination that
165 // arrives from everywhere rather than from the directional light. Keeps shadow
166 // walls readable instead of crushing to black.
167 const PLATE_AMBIENT: f32 = 0.55;
168 // Amplitude of the bright crest line hugging a raised plate's silhouette — the
169 // ambient-catching convex rim that makes glass read as glass.
170 const PLATE_CREST: f32 = 0.25;
171 // The far-edge shade line's strength relative to the glint (roll_shade_line):
172 // 1 is the exact mirror; 0.5 keeps dark faces from bottoming out at black.
173 const PLATE_SHADE_LINE: f32 = 0.5;
174 // Recess depth as a fraction of the roll width (a recess is visually shallower
175 // than a raised plate's full quarter-round).
176 const RECESS_DEPTH: f32 = 0.6;
177
178 // Signed distance and gradient of the plate's rounded box at p, as
179 // (grad.x, grad.y, distance). Analytic — no dpdx/dpdy — so the clip discards
180 // above the plate branch cannot poison derivative quads, and corners need no
181 // special casing: the gradient swings continuously around each arc.
182 //
183 // rect1.w is the corner shape exponent: 2 = circular arcs; > 2 swaps them for
184 // superellipse (Lp-norm) corners — Apple-style continuous curvature, where
185 // curvature ramps smoothly to zero at the edge join instead of jumping from
186 // 1/r, so the lit roll's highlight sweeps a corner without a G2 kink. The Lp
187 // gradient is not unit length, so both the direction and the distance carry a
188 // first-order |∇| correction — exact on the boundary, and well within a shade
189 // step over the roll's few-px band.
190 fn rr_sdf_grad(p: vec2f, prect: vec4f, pradii: vec4f) -> vec3f {
191 let c = p - prect.xy;
192 let side = select(pradii.xw, pradii.yz, c.x > 0.0);
193 let r = select(side.x, side.y, c.y > 0.0);
194 let q = abs(c) - prect.zw + vec2f(r, r);
195 let s = vec2f(select(-1.0, 1.0, c.x >= 0.0), select(-1.0, 1.0, c.y >= 0.0));
196 if (q.x > 0.0 && q.y > 0.0) {
197 let shape = rrect_clip.rect1.w;
198 if (shape > 2.001) {
199 let lp = max(pow(pow(q.x, shape) + pow(q.y, shape), 1.0 / shape), 1e-4);
200 let g = vec2f(pow(q.x / lp, shape - 1.0), pow(q.y / lp, shape - 1.0));
201 let gm = max(length(g), 1e-4);
202 // First-order |∇|-corrected distance: exact on the boundary and on
203 // the axis/diagonal rays, but mid-arc it runs up to ~4% of the
204 // depth low, so the roll band's contours drift off the true
205 // parallels of the arc as the roll widens.
206 let d0 = (lp - r) / gm;
207 let dir = g / gm;
208 // One re-evaluation at the projected near-boundary point tightens
209 // the band to true parallels (error /5 to /10 over the lit part of
210 // the roll). Trusted only near the boundary: past the roll the
211 // projection approaches the Lp field's degenerate center and
212 // diverges, so the step is clamped and the result blends back to
213 // the plain first-order value — beyond 1.5 rolls the field is
214 // bit-identical to the pre-refinement one (flat fill; only
215 // crest/AO tails read it there).
216 let roll = max(rrect_clip.p_light.w, 2.0);
217 let step = clamp(d0, -roll, roll);
218 let q1 = max(q - step * dir, vec2f(1e-4));
219 let lp1 = max(pow(pow(q1.x, shape) + pow(q1.y, shape), 1.0 / shape), 1e-4);
220 let g1 = vec2f(pow(q1.x / lp1, shape - 1.0), pow(q1.y / lp1, shape - 1.0));
221 let gm1 = max(length(g1), 1e-4);
222 let d1 = step + (lp1 - r) / gm1;
223 let w = smoothstep(roll, roll * 1.5 + 2.0, abs(d0));
224 let nrm = normalize(mix(g1 / gm1, dir, w));
225 return vec3f(s * nrm, mix(d1, d0, w));
226 }
227 let len = max(length(q), 1e-4);
228 return vec3f(s * q / len, len - r);
229 }
230 if (q.x > q.y) {
231 return vec3f(s.x, 0.0, q.x - r);
232 }
233 return vec3f(0.0, s.y, q.y - r);
234 }
235
236 // Specular of a roll at tilt `slope` whose outward horizontal facing is along
237 // `g`: the profile alignment (how close the roll's tilt is to the half-vector's
238 // tilt) powered by shininess, times a gentle azimuthal falloff, minus the flat
239 // face's baseline so the face contributes zero. Deliberately DECOUPLED rather
240 // than Blinn-Phong's pow(dot(n, hv), s): coupled, a straight edge's normal can
241 // never fully reach the half-vector (it tilts in one plane only) while a corner
242 // diagonal's can, so the power function crushes edge lines relative to corner
243 // glints and the meeting fattens into a blob that ignores the corner arc.
244 // Decoupled, the band keeps constant inset, width, and peak intensity as it
245 // sweeps a corner — the highlight follows the silhouette.
246 // `sv` is the surface's slope vector — the horizontal part of the unnormalized
247 // normal (-∇height, 1): its magnitude is the tilt, its direction the facing.
248 fn roll_spec(sv: vec2f) -> f32 {
249 return roll_lobe(sv, rrect_clip.p_light.xyz);
250 }
251
252 // The glint's dark counterpart: the SAME decoupled lobe — same inset, width
253 // and peak — on the edges facing AWAY from the light, which is roll_spec
254 // evaluated under the light's azimuth mirrored. Subtracted in colour units
255 // exactly as the glint is added, scaled by PLATE_SHADE_LINE. It exists
256 // because the diffuse fall-off alone cannot answer the glint: with the
257 // ambient floor and the truncated roll the far edge bottoms out near 0.8 of
258 // the face, and only in its last pixel, while the glint adds ~0.17 of white
259 // over a band — so a raised plate read lit on one side and merely unlit on
260 // the other, never shadowed.
261 fn roll_shade_line(sv: vec2f) -> f32 {
262 let l = rrect_clip.p_light.xyz;
263 return roll_lobe(sv, vec3f(-l.xy, l.z)) * PLATE_SHADE_LINE;
264 }
265
266 fn roll_lobe(sv: vec2f, light: vec3f) -> f32 {
267 let m = length(sv);
268 if (m < 1e-5) {
269 return 0.0;
270 }
271 let hv = normalize(light + vec3f(0.0, 0.0, 1.0));
272 let shininess = rrect_clip.p_mat.z;
273 let facing = sv / m;
274 let cos_t = inverseSqrt(1.0 + m * m);
275 let sin_t = m * cos_t;
276 let hxy = length(hv.xy);
277 let prof = cos_t * hv.z + sin_t * hxy; // cos(tilt - half-vector tilt)
278 let az = clamp(dot(facing, hv.xy) / max(hxy, 1e-4), 0.0, 1.0);
279 return rrect_clip.p_mat.y * max(pow(prof, shininess) - pow(hv.z, shininess), 0.0) * az * az;
280 }
281
282 // roll_spec with the azimuth mask dropped: every edge shades as if it faced
283 // the light, so the glint the light-facing edges normally get sweeps the
284 // WHOLE silhouette at the same inset, width, and peak (the decoupled profile
285 // keeps those constant through corners by construction). The focused-plate
286 // treatment: the familiar specular line, accent-tinted, on all four sides.
287 fn roll_spec_wrap(sv: vec2f) -> f32 {
288 let m = length(sv);
289 if (m < 1e-5) {
290 return 0.0;
291 }
292 let hv = normalize(rrect_clip.p_light.xyz + vec3f(0.0, 0.0, 1.0));
293 let shininess = rrect_clip.p_mat.z;
294 let cos_t = inverseSqrt(1.0 + m * m);
295 let sin_t = m * cos_t;
296 let hxy = length(hv.xy);
297 let prof = cos_t * hv.z + sin_t * hxy;
298 return rrect_clip.p_mat.y * max(pow(prof, shininess) - pow(hv.z, shininess), 0.0);
299 }
300
301 // How squarely a rim faces the light's azimuth, 0..1 — the weight on the
302 // plate crest. The crest was a flat +PLATE_CREST on every side, and on the
303 // far (down-light) edges that out-measured the roll's own diffuse fall-off at
304 // every point of the profile: a raised plate had a bright rim toward the
305 // light and NO dark rim away from it. Weighted this way the near edges keep
306 // their crest and the far edges keep only their diffuse shading, so the
307 // silhouette reads lit on one side and shadowed on the other, like the
308 // glint's counterpart. Light from straight overhead has no near or far side
309 // and keeps the crest everywhere.
310 fn crest_weight(facing: vec2f) -> f32 {
311 let lxy = rrect_clip.p_light.xy;
312 let m = length(lxy);
313 if (m < 1e-4) {
314 return 1.0;
315 }
316 return max(dot(facing, lxy) / m, 0.0);
317 }
318
319 // Slope of the raised roll's height profile at f (0 at the face join, 1 at the
320 // silhouette). Circular (shape 2): a quarter-round h = sqrt(1 - f²) — tangent-
321 // continuous with the face but with a curvature JUMP at the join (1/t → 0), the
322 // profile-space twin of a circular plan corner. shape > 2 swaps in the matching
323 // superellipse quadrant h = (1 - f^n)^(1/n): its curvature ramps to zero at the
324 // join, so the roll's shading fades into the face instead of ending on a line.
325 // The slope has the closed form (f/h)^(n-1), which IS the circular formula at
326 // n = 2 — the same one-exponent generalization as the plan corners.
327 // The descent is truncated at ROLL_CUT of the quadrant: the roll shades as if
328 // the slab's rim were cut off partway down, so the profile ends on a bounded
329 // slope instead of plunging vertical at the silhouette (the full quadrant put
330 // nearly all of its drop in the outer third of the roll, reading as a hard
331 // dropoff line at the very edge).
332 const ROLL_CUT: f32 = 0.8;
333
334 fn roll_slope(f: f32) -> f32 {
335 let t = max(rrect_clip.p_light.w, 0.001);
336 let rr = select(1.0, window_info.relief_meta.y / t, window_info.relief_meta.y > 0.0);
337 return roll_slope_unit(f) * rr;
338 }
339
340 // The unit-rise roll (a quarter-round of radius t, or the custom edge LUT);
341 // roll_slope scales it by the pinned rise.
342 fn roll_slope_unit(f: f32) -> f32 {
343 // Custom edge profile: sample the uploaded ramp LUT. Face pixels saturate
344 // at f = 0 (the roll band's interior end), so taper the slope to zero
345 // there or every face pixel would inherit the curve's start slope; the
346 // silhouette end keeps whatever slope the curve was drawn ending on.
347 if (window_info.roll_meta.x > 0.5) {
348 let n = window_info.roll_meta.y;
349 let fcl = clamp(f, 0.0, 1.0);
350 let x = clamp(fcl * n - 0.5, 0.0, n - 1.0);
351 let i0 = u32(floor(x));
352 let i1 = min(i0 + 1u, u32(n) - 1u);
353 let fr = x - floor(x);
354 let s0 = window_info.roll_profile[i0 >> 2u][i0 & 3u];
355 let s1 = window_info.roll_profile[i1 >> 2u][i1 & 3u];
356 let win = clamp(fcl * n * 0.667, 0.0, 1.0);
357 return mix(s0, s1, fr) * win;
358 }
359 let shape = rrect_clip.rect1.w;
360 let fc = f * ROLL_CUT;
361 if (shape > 2.001) {
362 let h = pow(max(1.0 - pow(fc, shape), 1e-4), 1.0 / shape);
363 return pow(fc / h, shape - 1.0);
364 }
365 return fc / sqrt(max(1.0 - fc * fc, 1e-4));
366 }
367
368 // Slope of a carve's transition profile (0 on the surrounding plateau → 1 on
369 // the carve floor) at v in [0, 1] across the wall. With a custom profile
370 // installed (window_info.profile_meta.x), the slope comes from the uploaded
371 // ramp LUT — it may go negative (non-monotonic curves: rims, ogees) and its
372 // integral is the curve's net rise, not necessarily 1. Otherwise the analytic
373 // default: smoothstep normally, smootherstep (zero SECOND derivative at both
374 // plateaus) under a continuous-curvature corner_shape — the step's analog of
375 // the superellipse roll.
376 fn carve_slope(v: f32) -> f32 {
377 if (window_info.profile_meta.x > 0.5) {
378 let n = window_info.profile_meta.y;
379 let vc = clamp(v, 0.0, 1.0);
380 // Samples sit at v = (i + 0.5) / n; lerp between the two neighbors.
381 let x = clamp(vc * n - 0.5, 0.0, n - 1.0);
382 let i0 = u32(floor(x));
383 let i1 = min(i0 + 1u, u32(n) - 1u);
384 let fr = x - floor(x);
385 let s0 = window_info.profile[i0 >> 2u][i0 & 3u];
386 let s1 = window_info.profile[i1 >> 2u][i1 & 3u];
387 // The curve describes ONLY the wall band; the surfaces on either side
388 // are flat by definition, so taper to exactly zero at both ends
389 // (~1.5 samples). Without this every face pixel (v saturates at 1
390 // inside a feature) inherits the endpoint slope, and the SDF
391 // gradient's nearest-edge regions facet the face into triangles.
392 let win = clamp(min(vc, 1.0 - vc) * n * 0.667, 0.0, 1.0);
393 return mix(s0, s1, fr) * win;
394 }
395 if (rrect_clip.rect1.w > 2.001) {
396 let w = v * (1.0 - v);
397 return 30.0 * w * w;
398 }
399 return 6.0 * v * (1.0 - v);
400 }
401
402 // Per-pixel lighting of a plate. The plate is one composite height field:
403 // the host's rolled-edge surface minus every carve's profile, with the carve
404 // depth measured RELATIVE to the local surface (a deboss/etch, not a flat
405 // milling plane — a flat tool would swallow the perimeter roll wherever a
406 // band overlaps it, deleting the plate's own edge shading there). Heights
407 // subtract, so slope vectors ADD: the pixel's normal comes from the summed
408 // analytic slopes of every feature over it, and the junction where a carve's
409 // wall crosses the plate's perimeter roll is the smooth composite of both
410 // tilts, ending in the rim notch a real groove leaves. One lighting
411 // evaluation per pixel — features never blend in color space. Shading is
412 // expressed relative to the flat face (shade ratio 1.0, specular delta 0.0)
413 // so the face keeps exactly the app's chosen color.
414 fn plate_shade(frag: vec2f, vcol: vec4f) -> vec4f {
415 let l = rrect_clip.p_light.xyz;
416 let strength = rrect_clip.p_mat.x;
417 let flat_shade = PLATE_AMBIENT + (1.0 - PLATE_AMBIENT) * l.z;
418 // The mode arrives as a float only because the push block is all f32.
419 let mode = i32(round(rrect_clip.rect1.z));
420
421 // MODE_SPHERE: a sphere-lit disc (the slider thumb). p_rect.xy is the center,
422 // p_rect.z the radius, physical px. The disc is shaded as a hemisphere
423 // under the same light/material as the plates — ambient floor, diffuse off
424 // the sphere normal, the decoupled roll specular (its glint lands where
425 // the surface tilt meets the half-vector, ~a third of the way out toward
426 // the light) — and, like a plate face, the shade is expressed relative to
427 // the flat face so the color at the lit center is exactly the app's.
428 // MODE_GROUT: the vertex colour, flat, everywhere OUTSIDE a periodic
429 // field of identical rounded cells — the grid lines of a graph whose
430 // cells are whatever lies beneath showing through, corners included.
431 // The same fold as MODE_LATTICE (p_rect = one cell's centre and
432 // half-extents, p_host.xy = the period, p_radii = the corner radius),
433 // but no lighting: coverage is the cell SDF's outside, 1px anti-aliased,
434 // so the cells' superellipse corners are exact and the whole grid is one
435 // draw. Flat strips could never paint the notch a rounded cell leaves at
436 // each crossing.
437 if (mode == MODE_GROUT) {
438 let per = max(rrect_clip.p_host.xy, vec2f(1e-3));
439 var gc = frag - rrect_clip.p_rect.xy;
440 gc = gc - per * round(gc / per);
441 let lg = rr_sdf_grad(gc, vec4f(0.0, 0.0, rrect_clip.p_rect.zw), rrect_clip.p_radii);
442 let cov = clamp(lg.z + 0.5, 0.0, 1.0);
443 if (cov <= 0.0) {
444 discard;
445 }
446 return vec4f(vcol.rgb, vcol.a * cov);
447 }
448
449 if (mode == MODE_SPHERE) {
450 let c = frag - rrect_clip.p_rect.xy;
451 let r = max(rrect_clip.p_rect.z, 0.001);
452 let dist = length(c);
453 let aa = clamp(r - dist + 0.5, 0.0, 1.0); // 1px silhouette anti-aliasing
454 if (aa <= 0.0) {
455 discard;
456 }
457 let h = sqrt(max(r * r - dist * dist, 1e-3));
458 let n = vec3f(c / r, h / r); // unit on the sphere's surface
459 let diff = PLATE_AMBIENT + (1.0 - PLATE_AMBIENT) * max(dot(n, l), 0.0);
460 let shade = 1.0 + (diff / flat_shade - 1.0) * strength;
461 let spec = roll_spec(c / h);
462 return vec4f(vcol.rgb * shade + rrect_clip.p_spec_tint.rgb * (spec * strength), vcol.a * aa);
463 }
464
465 // MODE_DROPLET: a hanging water droplet clinging to the box's TOP edge.
466 // Field reinterpretation (the push block cannot grow):
467 // p_rect = the droplet box, center + half-extents (like a plate);
468 // p_radii = [sag, belly radius, belly half-width, blend k] px;
469 // p_host = [sheet bottom-corner radius px, edge clarity 0-1, dome
470 // amplitude, attach (top-corner) radius px];
471 // p_spec_tint = [core density, contact-shadow reach px, contact-shadow
472 // strength, bottom-bow edge rise px] — a droplet's glint is always
473 // white, so the tint RGB slots are free;
474 // p_mat.w = fresnel rim crest amplitude (droplets carve nothing, so the
475 // AO slot is free); p_light.w = shaded band width px.
476 // The silhouette is the smooth union of a film SHEET attached to the top
477 // edge (square top corners — the attach line; bottom lifted by sag) and a
478 // BELLY capsule resting on the box bottom: the polynomial smin forms the
479 // waist/neck a real drop's surface tension pulls in. Shading reuses the
480 // plate vocabulary — roll_slope tilt over the band, ambient/diffuse,
481 // decoupled roll specular — plus two water terms: a fresnel rim crest
482 // (f³, like PLATE_CREST but tunable) and a thin-edge clarity falloff on
483 // the tint alpha, so the (compositor- or resolve_blur-) frosted backdrop
484 // shows through clearer at the rim.
485 if (mode == MODE_DROPLET || mode == MODE_DROPLET_SCRIM) {
486 let c = rrect_clip.p_rect.xy;
487 let hx = rrect_clip.p_rect.z;
488 let hy = rrect_clip.p_rect.w;
489 let sag = rrect_clip.p_radii.x;
490 let br = rrect_clip.p_radii.y;
491 let bw = rrect_clip.p_radii.z;
492 let k = max(rrect_clip.p_radii.w, 1.0);
493 let sr = rrect_clip.p_host.x;
494 let clarity = rrect_clip.p_host.y;
495 let dome = rrect_clip.p_host.z;
496 let ar = rrect_clip.p_host.w;
497
498 // Sheet: bottom lifted by sag; top corners carry the attach radius —
499 // the meniscus taper that curves the sides into the attach line (0 =
500 // the square-shouldered clinging-pool look).
501 let a_rect = vec4f(c.x, c.y - sag * 0.5, hx, hy - sag * 0.5);
502 let ga = rr_sdf_grad(frag, a_rect, vec4f(ar, ar, sr, sr));
503 var d = ga.z;
504 var g = ga.xy;
505 // Belly (radius > 0 only): a horizontal capsule resting on the box
506 // bottom, joined by polynomial smooth union — one drop, smooth neck.
507 // The gradient is the same weighted mix as the distance, renormalized.
508 if (br > 0.5) {
509 let b_rect = vec4f(c.x, c.y + hy - br, bw, br);
510 let gb = rr_sdf_grad(frag, b_rect, vec4f(br));
511 let hm = clamp(0.5 + 0.5 * (gb.z - ga.z) / k, 0.0, 1.0);
512 d = mix(gb.z, ga.z, hm) - k * hm * (1.0 - hm);
513 g = mix(gb.xy, ga.xy, hm);
514 }
515 // Bottom bow (p_spec_tint.w = edge rise, px): smooth-INTERSECT the
516 // drop with a disc whose lowest point touches the drop's bottom
517 // center — the bottom becomes one continuous circular arc, rising by
518 // the given amount at x = ±hx. The radius follows from that fixed
519 // rise (R = hx²/2·rise), so wide drops flatten toward the middle on
520 // their own. smax = -smin(-a,-b): same polynomial blend, sign flipped.
521 let bow = rrect_clip.p_spec_tint.w;
522 if (bow > 0.25) {
523 let bigr = hx * hx / (2.0 * bow);
524 let cc = vec2f(c.x, c.y + hy - bigr);
525 let pc = frag - cc;
526 let dl = max(length(pc), 1e-3);
527 let dc = dl - bigr;
528 let gc = pc / dl;
529 let hm2 = clamp(0.5 + 0.5 * (d - dc) / k, 0.0, 1.0);
530 d = mix(dc, d, hm2) + k * hm2 * (1.0 - hm2);
531 g = mix(gc, g, hm2);
532 }
533 g = normalize(g);
534
535 let din = -d;
536 let aa2 = clamp(din + 0.5, 0.0, 1.0);
537 // MODE_DROPLET_SCRIM: the same silhouette, filled flat and feathered
538 // inward — a vignette shaped exactly like the drop it sits in, for a
539 // caller that needs a legible ground under text without a second lit
540 // body. It shares this mode's SDF rather than approximating the shape
541 // with a rounded rect, which is the whole point: the two can never
542 // disagree about where the drop's edge is. p_light.w carries the
543 // feather (px) instead of the shading band, which is only read below.
544 if (mode == MODE_DROPLET_SCRIM) {
545 if (aa2 <= 0.0) {
546 discard;
547 }
548 let fth = max(rrect_clip.p_light.w, 0.001);
549 let sa2 = vcol.a * clamp(din / fth, 0.0, 1.0) * aa2;
550 if (sa2 <= 0.004) {
551 discard;
552 }
553 return vec4f(vcol.rgb, sa2);
554 }
555 if (aa2 <= 0.0) {
556 // Outside the silhouette: the contact shadow — a soft dark
557 // falloff cast below the drop's lower arc (weighted by the
558 // outward gradient's downward component, so the attach line and
559 // sides stay clean). The cover quad overhangs the box by the
560 // reach to give these fragments pixels to land on.
561 let sh_reach = rrect_clip.p_spec_tint.y;
562 let sh_amp = rrect_clip.p_spec_tint.z;
563 if (sh_reach < 0.5 || sh_amp <= 0.0) {
564 discard;
565 }
566 let down_sh = clamp(g.y, 0.0, 1.0);
567 let sfall = 1.0 - clamp(d / sh_reach, 0.0, 1.0);
568 let sa = sh_amp * sfall * sfall * down_sh;
569 if (sa <= 0.004) {
570 discard;
571 }
572 return vec4f(0.0, 0.0, 0.0, sa);
573 }
574 var base = vcol;
575 if (vcol.a < 0.0) {
576 // No recipe: every p_host slot is the drop's geometry. The
577 // kernel default, no compression (what a drop always drew).
578 base = resolve_blur(frag, vcol, vec2f(0.0), 0.0, 0.0, LEGACY_STRIDE);
579 }
580 let t2 = max(rrect_clip.p_light.w, 0.001);
581 let u = clamp(din / t2, 0.0, 1.0);
582 let f = 1.0 - u;
583 // Continuous dome: the drop is a spherical-cap height field over the
584 // silhouette — h = sqrt(2u - u²), vertical at the rim, flattening
585 // toward the interior — so the normal varies over the ENTIRE body
586 // and the diffuse rolls from lit shoulder to shaded belly instead of
587 // reading as a flat face inside a shaded band (the old roll_slope
588 // treatment, which only tilted the skirt).
589 let hdome = sqrt(max(2.0 * u - u * u, 1e-4));
590 let sv = g * ((1.0 - u) / hdome * dome);
591 let n = normalize(vec3f(sv, 1.0));
592 let diff = PLATE_AMBIENT + (1.0 - PLATE_AMBIENT) * max(dot(n, l), 0.0);
593 // Rim crest weighted toward the BOTTOM edge (g.y > 0, y-down): a
594 // hanging drop concentrates transmitted light into a caustic along
595 // its lower arc, while the attach line stays quiet.
596 let down = clamp(g.y, 0.0, 1.0);
597 let extra = rrect_clip.p_mat.w * f * f * f * (0.3 + 1.2 * down * down);
598 let shade = 1.0 + (diff / flat_shade - 1.0 + extra) * strength;
599 let spec = roll_spec(sv);
600 // Thin edges are clearer water; the deep interior densifies by the
601 // core term (thickest water in the middle — the text's field).
602 let body = mix(clarity, 1.0 + rrect_clip.p_spec_tint.x, u);
603 return vec4f(
604 base.rgb * shade + vec3f(spec * strength),
605 min(abs(base.a) * body, 1.0) * aa2,
606 );
607 }
608
609 let gd = rr_sdf_grad(frag, rrect_clip.p_rect, rrect_clip.p_radii);
610 let d = -gd.z; // positive inside the plate, in px
611 let t = max(rrect_clip.p_light.w, 0.001);
612
613 if (mode == MODE_PLATE) {
614 let aa = clamp(d + 0.5, 0.0, 1.0); // 1px silhouette anti-aliasing
615 if (aa <= 0.0) {
616 discard;
617 }
618 let u = clamp(d / t, 0.0, 1.0);
619 let f = 1.0 - u;
620 // Host roll slope vector: vertical at the silhouette, flat where the
621 // roll meets the face — then every carve's slope adds to it, and its
622 // shoulder/fillet ambient term joins the roll's crest.
623 var sv = gd.xy * roll_slope(f);
624 // The plate's OWN roll, kept apart from the carves added below: a
625 // focused plate's accent ring traces this alone (see the tinted
626 // branch), so the wells carved into it never wear the ring too.
627 let sv_rim = sv;
628
629 // Rim refraction, resolved BEFORE the shading below because the
630 // backdrop it bends is `base`.
631 //
632 // The roll is a real surface with a real tilt — `sv_rim` IS that tilt
633 // (the horizontal part of the unnormalized normal), already computed
634 // for the specular. Displacing the backdrop sample along it is what a
635 // curved edge does to what you see through it: the view compresses
636 // toward the silhouette and the plate stops being a rectangle of haze
637 // and starts being a slab with a thickness.
638 //
639 // Scaled by the roll width `t`, so a 12px bevel bends more than a 2px
640 // one and the effect tracks the plate's own geometry rather than
641 // drifting off it at another radius. The clarity ramp is f*f — the
642 // clear window belongs to the outer third of the roll, and the face
643 // must reach zero exactly or the whole plate unfrosts.
644 // The plate's own recipe, from its push block (see RRectClip.p_host).
645 let fz = rrect_clip.p_host.z;
646 let fhi = floor(fz / FROST_PACK_BASE);
647 let k_plate = clamp(fhi / FROST_PACK_MAX, 0.0, 1.0);
648 let refr = clamp((fz - fhi * FROST_PACK_BASE) / FROST_PACK_MAX, 0.0, 1.0);
649 let stride = rrect_clip.p_host.w * 0.5;
650 var base = vcol;
651 if (vcol.a < 0.0) {
652 base = resolve_blur(frag, vcol, sv_rim * (refr * t), refr * f * f, k_plate, stride);
653 }
654 var extra = PLATE_CREST * f * f * f * crest_weight(gd.xy);
655 let f_off = u32(rrect_clip.p_host.x);
656 let f_cnt = u32(rrect_clip.p_host.y);
657 for (var i = 0u; i < f_cnt; i = i + 1u) {
658 let feat = plate_features.items[f_off + i];
659 let fg = rr_sdf_grad(frag, feat.rect, feat.radii);
660 let ft = max(feat.params.x, 0.001);
661 let v = clamp(-fg.z / ft + 0.5, 0.0, 1.0);
662 if (v <= 0.0) {
663 continue;
664 }
665 // params.y (depth) is signed: positive carves down, negative
666 // raises a boss. The slope vector follows automatically; the
667 // shoulder/fillet ambient term flips with it (a boss's convex
668 // shoulder is at the top of its wall, not the bottom).
669 sv += -(feat.params.y / ft) * carve_slope(v) * fg.xy;
670 extra += rrect_clip.p_mat.w * sin(v * TAU) * sign(feat.params.y);
671 }
672 let n = normalize(vec3f(sv, 1.0));
673 let diff = PLATE_AMBIENT + (1.0 - PLATE_AMBIENT) * max(dot(n, l), 0.0);
674 let shade = 1.0 + (diff / flat_shade - 1.0 + extra) * strength;
675 // p_spec_tint.w = 1 marks an accent-tinted plate (the focused-pane
676 // treatment): the specular line WRAPS — the exact glint the
677 // light-facing edges always carry runs the whole silhouette in the
678 // accent color, same inset, width, and peak. Nothing else about the
679 // plate's shading changes (accent-wash variants were tried and read
680 // as painted frames). Neutral plates (w = 0) keep the directional
681 // glint, byte-identical.
682 let tw = rrect_clip.p_spec_tint.w;
683 if (tw > 0.0) {
684 // The wrap runs on the plate's own roll ONLY (sv_rim), never on
685 // the carves: with the full slope every well carved into a
686 // focused plate — each parameter control on the designer's
687 // parameter pane — drew its own accent ring, reading as if every
688 // control were focused alongside the pane.
689 let spec = roll_spec_wrap(sv_rim);
690 // A FILL-LESS tinted plate is a pure focus ring (the network
691 // cursor): the wrapped glint alone, on the plate's own roll — so
692 // the line traces the same superellipse silhouette, radius
693 // family, and inset as every node and pane, which a
694 // boundary-straddling carve band cannot (outward offsets of an
695 // Lp corner round off).
696 if (abs(base.a) < 0.004) {
697 return vec4f(rrect_clip.p_spec_tint.rgb, spec * strength * aa);
698 }
699 // The carves keep the neutral directional glint an unfocused
700 // plate gives them (white, as p_spec_tint.rgb is for w = 0);
701 // sv_rim is zero on the face, so this is exactly their term.
702 let carve_spec = roll_spec(sv - sv_rim);
703 return vec4f(
704 base.rgb * shade + rrect_clip.p_spec_tint.rgb * (spec * strength) + vec3f(carve_spec * strength),
705 abs(base.a) * aa,
706 );
707 }
708 let spec = roll_spec(sv);
709 let dark = roll_shade_line(sv);
710 return vec4f(base.rgb * shade + rrect_clip.p_spec_tint.rgb * (spec * strength) - vec3f(dark * strength), abs(base.a) * aa);
711 }
712
713 if (mode == MODE_ROLL) {
714 // Fill-less rolled perimeter: MODE_PLATE's roll — same profile, crest
715 // and specular, spanning the full width INSIDE the silhouette — for a
716 // window whose face is not a plate fill (the designer's full-bleed 3D
717 // canvas). With no fill to shade into, it composites like the free
718 // carves: darkening is a black multiply, brightening a translucent
719 // white screen, over whatever is beneath. No CSG features: an overlay
720 // owns no surface, so carves never group into it (the tessellator
721 // never opens it as a host).
722 let aa = clamp(d + 0.5, 0.0, 1.0);
723 if (aa <= 0.0) {
724 discard;
725 }
726 let u = clamp(d / t, 0.0, 1.0);
727 let f = 1.0 - u;
728 let sv = gd.xy * roll_slope(f);
729 let extra = PLATE_CREST * f * f * f * crest_weight(gd.xy);
730 let n = normalize(vec3f(sv, 1.0));
731 let diff = PLATE_AMBIENT + (1.0 - PLATE_AMBIENT) * max(dot(n, l), 0.0);
732 let spec = roll_spec(sv);
733 let dark = roll_shade_line(sv);
734 let v = (diff / flat_shade - 1.0 + extra + spec - dark) * strength * aa;
735 if (v >= 0.0) {
736 return vec4f(1.0, 1.0, 1.0, min(v, 1.0));
737 }
738 return vec4f(0.0, 0.0, 0.0, min(-v, 1.0));
739 }
740
741 // Free-floating recess, boss, or ridge (one not grouped into a host plate —
742 // e.g. in a widget's own paint): an overlay over whatever is painted
743 // beneath — no fill, no silhouette. Junction behavior here is the heuristic
744 // host-box fade; grouped features get the exact CSG above.
745 // MODE_RECESS = interior one step DOWN, MODE_BOSS = interior one step UP —
746 // the same wall with the height sign flipped. MODE_RIDGE is a
747 // raised bump straddling the boundary, both sides at the base level — ONE
748 // profile evaluation, so its crest carries a single specular/shoulder term
749 // instead of a boss+recess double-stack. MODE_TROUGH is that bump inverted
750 // (a valley), for the same reason: it replaced the recess-ring+boss stack
751 // `inset_plate` used to emit for every flush control in the DE.
752 // All profiles straddle the boundary (span [-t/2, t/2]). Darkening is exact
753 // multiplicative shading (black at alpha 1 - shade); brightening is a
754 // translucent white screen.
755 //
756 // Concave fillet (MODE_FILLET_DOWN / MODE_FILLET_UP): the wall follows a
757 // quarter ARC whose centre sits out in the pocket — the inside-corner
758 // rounding the box SDF cannot express. p_rect.xy = centre, .z = radius;
759 // p_radii.x = the wedge's start angle (quarter span, HARD-cut at the
760 // tangent lines — the straight walls continue the profile exactly there).
761 // Distance/gradient swap to radial; everything downstream is the shared
762 // free-carve path via `eff` (minus FILLET_TO_STEP: 6→RECESS, 7→BOSS).
763 var eff = mode;
764 var fd = d;
765 var fgd = gd.xy;
766 var wedge = 1.0;
767 // MODE_GROOVE: a SLAB carve — the band of half-width p_rect.z about the
768 // line through p_rect.xy with unit normal p_radii.xy. Distance is |signed
769 // distance to that line| minus the half-width, so ONE profile evaluation
770 // yields both walls (the gradient flips sign across the centre line, tilting
771 // them apart) and the groove costs a single specular term. The box SDF is
772 // axis-aligned by construction; this is how a mark runs at an angle.
773 // Rejoins the shared free-carve path as a recess (eff = 2).
774 if (mode == MODE_GROOVE) {
775 let nrm = rrect_clip.p_radii.xy;
776 let c = frag - rrect_clip.p_rect.xy;
777 let s = dot(c, nrm);
778 fd = abs(s) - rrect_clip.p_rect.z;
779 fgd = nrm * select(-1.0, 1.0, s >= 0.0);
780 eff = MODE_RECESS;
781 } else if (mode == MODE_LATTICE) {
782 // MODE_LATTICE: a periodic field of identical rounded wells. Fold
783 // the pixel into the period about one cell's centre (p_rect.xy;
784 // period in p_host.xy) and take the box distance to THAT cell —
785 // identical axis-aligned boxes centred in their period cells, so
786 // the folded cell is always the nearest one and this is the exact
787 // union distance of every well. One profile evaluation, so the
788 // rails between cells and the diagonals at each crossing are true
789 // mitres instead of stacked per-cell overlays. The wall runs from
790 // the cell edge OUTWARD: floor at the edge, plateau one run out.
791 //
792 // The wall's outer edge is NOT the offset curve (every point one run
793 // from the cell): that contour rounds each corner at radius + run,
794 // and with a run of half a rail the crossings read as big sweeping
795 // arcs while the cells themselves keep tight corners. A moulding
796 // does not offset its corners, it mitres them — so the outer edge is
797 // the cell box grown by the run with SHARP corners, and the wall is
798 // the fraction of the way across the band between the two contours
799 // (u = 1 at the cell edge, 0 at the outer contour). On the straight
800 // rails that is exactly distance / run; around a corner the band
801 // widens along the diagonal and four walls meet on the mitre lines.
802 // Sharp, not the cell's radius, so that where the run is half the
803 // rail the four outer boxes meet at a point and the crest lines run
804 // continuously through the crossing as hips — a rounded outer corner
805 // left a flat lozenge on top of every crossing. Lit by the cell's
806 // gradient. The -t/2 recentres the shared path's boundary-straddling
807 // band on [edge, edge + t].
808 let per = max(rrect_clip.p_host.xy, vec2f(1e-3));
809 var c = frag - rrect_clip.p_rect.xy;
810 c = c - per * round(c / per);
811 let lg = rr_sdf_grad(c, vec4f(0.0, 0.0, rrect_clip.p_rect.zw), rrect_clip.p_radii);
812 let lo = rr_sdf_grad(c, vec4f(0.0, 0.0, rrect_clip.p_rect.zw + vec2f(t)), vec4f(0.0));
813 let band = max(lg.z - lo.z, 1e-3);
814 let frac = clamp(lg.z / band, 0.0, 1.0);
815 fd = (0.5 - frac) * t;
816 fgd = lg.xy;
817 eff = MODE_RECESS;
818 } else if (mode == MODE_UNION) {
819 // MODE_UNION: the boxes in the feature run p_host.xy = [offset,
820 // count] are one shape. Each box's wall is MITRED like the lattice's:
821 // the band runs between the box shrunk by t/2 and the box grown by
822 // t/2, both at the box's own corner radius, and the pixel's position
823 // is its fraction across that band (an offset band would round the
824 // outer corners at radius + t/2). The union takes the box the pixel
825 // is deepest in — max over the run of the band coordinate, with that
826 // box's gradient — so a box's wall vanishes inside another and the
827 // outline is evaluated once. p_radii.x = 1 raises the union (boss)
828 // instead of carving it.
829 let u_off = u32(rrect_clip.p_host.x);
830 let u_cnt = u32(rrect_clip.p_host.y);
831 let hw = 0.5 * t;
832 var best = -1e9;
833 var bgrad = vec2f(0.0, -1.0);
834 for (var i = 0u; i < u_cnt; i = i + 1u) {
835 let feat = plate_features.items[u_off + i];
836 let inner = vec4f(feat.rect.xy, max(feat.rect.zw - vec2f(hw), vec2f(0.5)));
837 let outer = vec4f(feat.rect.xy, feat.rect.zw + vec2f(hw));
838 let gi = rr_sdf_grad(frag, inner, feat.radii);
839 let go = rr_sdf_grad(frag, outer, feat.radii);
840 let band = max(gi.z - go.z, 1e-3);
841 let fdi = (0.5 - clamp(gi.z / band, 0.0, 1.0)) * t;
842 if (fdi > best) {
843 best = fdi;
844 bgrad = gi.xy;
845 }
846 }
847 fd = best;
848 fgd = bgrad;
849 eff = select(MODE_RECESS, MODE_BOSS, rrect_clip.p_radii.x > 0.5);
850 } else if (mode == MODE_FILLET_DOWN || mode == MODE_FILLET_UP) {
851 eff = mode - FILLET_TO_STEP;
852 let c = frag - rrect_clip.p_rect.xy;
853 let dist = max(length(c), 1e-4);
854 fd = dist - rrect_clip.p_rect.z;
855 fgd = -c / dist;
856 let a0 = rrect_clip.p_radii.x;
857 let ang = atan2(c.y, c.x);
858 let rel = ang - a0 - floor((ang - a0) / TAU) * TAU;
859 wedge = select(0.0, 1.0, rel <= 1.5707964);
860 }
861 let u = clamp(fd / t + 0.5, 0.0, 1.0);
862 // Drop over run: the pinned height against THIS carve's wall, else the
863 // analytic ratio (the tessellator's CSG features apply the same rule).
864 let cd = select(RECESS_DEPTH, window_info.relief_meta.x / t, window_info.relief_meta.x > 0.0);
865 var slope = 0.0;
866 var curv = 0.0;
867 if (eff == MODE_RIDGE || eff == MODE_TROUGH) {
868 // Ridge bump: the carve profile mirrored about the boundary (rising
869 // outer half, falling inner half), amplitude halved so the wall tilt
870 // matches a step's despite the doubled profile rate. MODE_TROUGH is the
871 // same profile inverted — falling outer half, rising inner half — the
872 // valley a flush inset control leaves. Sharing this branch is the point:
873 // both get ONE evaluation, so neither can drift into the two-pass
874 // double-shading the stacked form had.
875 let w = clamp(select(2.0 * u, 2.0 - 2.0 * u, u > 0.5), 0.0, 1.0);
876 let up = select(-1.0, 1.0, eff == MODE_RIDGE);
877 let rising = select(-1.0, 1.0, u <= 0.5) * up;
878 slope = rising * 0.5 * cd * 2.0 * carve_slope(w);
879 // Each half-wall is a boss wall: concave fillet at its base, convex
880 // shoulder toward the crest — and ZERO at the plateaus and crest, so
881 // flat ground composites to exactly nothing (a constant term here
882 // tints the whole cover quad). A trough's curvature flips with it: the
883 // convex shoulders sit at the plateau lips, the concave fillet at the
884 // floor.
885 curv = -up * rrect_clip.p_mat.w * sin(w * TAU);
886 } else {
887 let dir = select(-1.0, 1.0, eff == MODE_BOSS);
888 // The profile slope is carve_slope's family: smoothstep-derived
889 // normally, smootherstep (zero second derivative at the plateaus)
890 // under a continuous-curvature corner_shape — shading eases in and out
891 // instead of starting on a line.
892 slope = dir * cd * carve_slope(u);
893 // Curvature: the convex shoulder catches ambient light, the concave
894 // fillet self-occludes — on the outer half for a recess, inner for a
895 // boss.
896 curv = -dir * rrect_clip.p_mat.w * sin(u * TAU);
897 }
898 let sv = fgd * slope;
899 let n = normalize(vec3f(sv, 1.0));
900 let diff = PLATE_AMBIENT + (1.0 - PLATE_AMBIENT) * max(dot(n, l), 0.0);
901 let spec = roll_spec(sv);
902 // Fade the carve out across the host plate's perimeter roll (see p_host).
903 let hb = rrect_clip.p_host;
904 let host_d = min(hb.z - abs(frag.x - hb.x), hb.w - abs(frag.y - hb.y));
905 let att = clamp(host_d / t, 0.0, 1.0) * wedge;
906 var v = (diff / flat_shade - 1.0 + curv + spec) * strength * att;
907 // p_spec_tint.w = 1 marks a tinted carve — the FOCUS treatment. It
908 // renders as the wrapped specular line alone (roll_spec_wrap: the glint
909 // the light-facing edges normally carry, swept around the whole
910 // outline), matching the focused plates' accent glint exactly; the
911 // relief's diffuse/curvature terms drop so a standalone focus ring reads
912 // as the line, not a lit step. Plates leave w at 0.
913 let tw = rrect_clip.p_spec_tint.w;
914 if (tw > 0.0) {
915 // The PLATE's monotonic roll profile, not the carve wall's: a wall's
916 // slope is a bell (rises then falls), so its tilt crosses the glint
917 // angle twice and drew two concentric lines. With roll_slope the ring
918 // is exactly a plate silhouette's glint — one line, same position.
919 let fr = clamp(1.0 - u, 0.0, 1.0);
920 // ...and ENDS at that silhouette (the rect outset by t/2), 1px
921 // anti-aliased like a plate's own. Past it `u` saturates at 0 and
922 // roll_slope(1) is the profile's steepest point, so every pixel of
923 // the cover quad outside the ring drew the full glint — a flat
924 // tinted block, square-cornered (the quad's own shape), around the
925 // rounded ring.
926 let sil = clamp(fd + 0.5 * t + 0.5, 0.0, 1.0);
927 v = roll_spec_wrap(fgd * roll_slope(fr)) * strength * att * sil;
928 }
929 if (v >= 0.0) {
930 // Highlight: the white screen mixes toward the tint color, slightly
931 // boosted so the accent reads at the rim's low alphas.
932 let hl = mix(vec3f(1.0), rrect_clip.p_spec_tint.rgb, tw);
933 return vec4f(hl, min(v * (1.0 + 0.5 * tw), 1.0));
934 }
935 // Shadow: the complementary counter-tint (warm against a cool accent),
936 // kept dark (~22%) so it still reads as shadow with a hue cast, not a
937 // second glow — the painter's warm-light/cool-shadow trick. Untinted
938 // carves stay black.
939 let sh = (vec3f(1.0) - rrect_clip.p_spec_tint.rgb) * 0.22 * tw;
940 return vec4f(sh, min(-v * (1.0 + 0.5 * tw), 1.0));
941 }
942
943 struct VertexOutput {
944 @builtin(position) clip_position: vec4f,
945 @location(0) color: vec4f,
946 @location(1) ndc_position: vec2f,
947 @location(2) clip_circle: vec3f,
948 }
949
950 @vertex
951 fn vs_main(
952 @location(0) position: vec2f,
953 @location(1) color: vec4f,
954 @location(2) clip_circle: vec3f,
955 ) -> VertexOutput {
956 var out: VertexOutput;
957 out.clip_position = vec4f(position, 0.0, 1.0);
958 out.color = color;
959 out.ndc_position = position;
960 out.clip_circle = clip_circle;
961 return out;
962 }
963
964 @fragment
965 fn fs_main(in: VertexOutput) -> @location(0) vec4f {
966 // Wavy-blob effect (engine shader.wgsl): the -999 sentinel renders a
967 // rippled, fading disc in NDC space.
968 if (in.clip_circle.x == -999.0) {
969 let y = length(vec2f(in.ndc_position.x, in.ndc_position.y));
970 let x = atan2(in.ndc_position.y, in.ndc_position.x);
971
972 // Wavy boundary radius with 7 lobes
973 let R_theta = 0.60 + 0.06 * sin(7.0 * x);
974
975 // Radial density: 1.0 at center, fading out to 0.0 at R_theta
976 let density = 1.0 - smoothstep(R_theta - 0.25, R_theta, y);
977
978 // Sine wave effect driven by the x value (distance around the circle)
979 let sin_effect = sin(7.0 * x);
980
981 // Normalized radius from 0.0 (center) to 1.0 (boundary)
982 let r_normalized = clamp(y / R_theta, 0.0, 1.0);
983
984 let gray = in.color.xyz;
985
986 // Scale the ripple amplitude by the normalized radius to fade it out at the center
987 let alpha = clamp(density * (1.0 - r_normalized * 0.25 * (1.0 - sin_effect)), 0.0, 1.0);
988
989 if (y > R_theta + 0.02) {
990 discard;
991 }
992
993 let final_alpha = alpha * (1.0 - smoothstep(R_theta - 0.02, R_theta + 0.02, y)) * in.color.w;
994 return vec4f(gray, final_alpha);
995 }
996
997 // Window-corner coverage: ~1px feather along the squircle silhouette in
998 // place of the old hard circular discard, so the window edge, the 3D scene
999 // fill, and the plates' tessellated corners all sit on the same curve.
1000 var clip_cov = 1.0 - smoothstep(-0.5, 0.5, window_corner_distance(in.clip_position.xy));
1001 if (clip_cov <= 0.0) {
1002 discard;
1003 }
1004 // Circular clip: ~1px feather folded into the coverage (mirroring the
1005 // rounded-rect clip below) — a clipped edge doubles as the silhouette AA
1006 // for circle prims drawn as cover quads.
1007 if (in.clip_circle.z > 0.0) {
1008 let dx = in.clip_position.x - in.clip_circle.x;
1009 let dy = in.clip_position.y - in.clip_circle.y;
1010 let dist = sqrt(dx * dx + dy * dy);
1011 clip_cov *= 1.0 - smoothstep(in.clip_circle.z - 0.5, in.clip_circle.z + 0.5, dist);
1012 if (clip_cov <= 0.0) {
1013 discard;
1014 }
1015 }
1016 // Rounded-rect clip (per-batch): the round-cornered box through rr_sdf_grad,
1017 // so the clipped silhouette follows the same corner_shape family (rect1.w:
1018 // circular arc at 2, superellipse squircle above) as the tessellated plate
1019 // corners around it, with a ~1px feather folded into the fragment alpha in
1020 // place of the old hard discard — a clipped edge and a drawn plate corner
1021 // share both curve and AA. Fully-outside fragments still discard.
1022 if (rrect_clip.rect1.y > 0.5) {
1023 let r = rrect_clip.rect1.x;
1024 let prect = vec4f(rrect_clip.rect0.xy, rrect_clip.rect0.zw + vec2f(r, r));
1025 let d = rr_sdf_grad(in.clip_position.xy, prect, vec4f(r)).z;
1026 clip_cov *= 1.0 - smoothstep(-0.5, 0.5, d);
1027 if (clip_cov <= 0.0) {
1028 discard;
1029 }
1030 }
1031
1032 // SDF-lit plate batch (mode in the push constants; see plate_shade).
1033 if (i32(round(rrect_clip.rect1.z)) != MODE_NONE) {
1034 let c = plate_shade(in.clip_position.xy, in.color);
1035 return vec4f(c.rgb, c.a * clip_cov);
1036 }
1037
1038 // A raw negative-alpha vertex with no plate block: geometry pushed from
1039 // outside the display list (a legacy host's own quads). No recipe to
1040 // read, so the kernel default and no compression. Everything the
1041 // display list frosts is a plate batch and never lands here.
1042 if (in.color.a < 0.0) {
1043 let c = resolve_blur(in.clip_position.xy, in.color, vec2f(0.0), 0.0, 0.0, LEGACY_STRIDE);
1044 return vec4f(c.rgb, c.a * clip_cov);
1045 }
1046
1047 return vec4f(in.color.rgb, in.color.a * clip_cov);
1048 }
1049
1050 // Blur-behind resolve for a negative-alpha plate color: frosted glass — the
1051 // FULLY blurred backdrop is the base (no clean-backdrop passthrough; mixing
1052 // the clean sample back in at plate opacity left translucent plates barely
1053 // blurred), tinted by the plate color at |alpha| opacity.
1054 //
1055 // `k_in` is the plate's luminance compression and `stride` its kernel's tap
1056 // spacing in physical px (sigma = 2 taps); both come from the plate's own
1057 // push block (MODE_PLATE), or are the no-recipe defaults (droplet, raw
1058 // vertices). A stride of 0 is a CLEAR plate: one clean sample, tinted.
1059 fn resolve_blur(pos: vec2f, color: vec4f, refract: vec2f, clarity: f32, k_in: f32, stride: f32) -> vec4f {
1060 let tex_size = vec2f(textureDimensions(t_backdrop));
1061
1062 var backdrop_color = vec4f(0.0);
1063 if (stride <= 0.0) {
1064 backdrop_color = textureSample(t_backdrop, s_backdrop, (pos + refract) / tex_size);
1065 } else {
1066 var blurred = vec4f(0.0);
1067 var total_weight = 0.0;
1068 // 7x7 Gaussian kernel at `stride` px (sigma two taps, reach ±3
1069 // taps); the linear sampler between taps papers over the stride.
1070 // The panel default is 5.5 px; a 2.5 px stride was technically a
1071 // blur but read as plain translucency — fine detail beneath a
1072 // frosted menu stayed legible, which is not what frosted glass does.
1073 for (var x = -3.0; x <= 3.0; x += 1.0) {
1074 for (var y = -3.0; y <= 3.0; y += 1.0) {
1075 let offset = vec2f(x, y) * stride;
1076 let sample_uv = (pos + offset) / tex_size;
1077 let weight = exp(-(x*x + y*y) / (2.0 * 2.0 * 2.0));
1078 blurred += textureSample(t_backdrop, s_backdrop, sample_uv) * weight;
1079 total_weight += weight;
1080 }
1081 }
1082 backdrop_color = blurred / total_weight;
1083 }
1084
1085 // The rim's clear window onto the backdrop.
1086 //
1087 // Refraction has to sample something with STRUCTURE or it is invisible:
1088 // displacing a field that has already been blurred to sigma ~11px moves
1089 // smooth values around and reads as nothing at all. So the rim takes a
1090 // CLEAN sample, displaced by the roll's tilt, and cross-fades to the
1091 // frosted body — which is also what a real slab does, its thin edge
1092 // scattering over a shorter path than its thick middle (the droplet
1093 // branch already trades on that: "thin edges are clearer water").
1094 //
1095 // One extra tap, not three: per-channel dispersion inside a band this
1096 // narrow is invisible once the body blur is 49 taps, and paying for it
1097 // would triple the most expensive path in this shader to be erased.
1098 if (clarity > 0.001) {
1099 let clean = textureSample(t_backdrop, s_backdrop, (pos + refract) / tex_size);
1100 backdrop_color = mix(backdrop_color, clean, clamp(clarity, 0.0, 1.0));
1101 }
1102
1103 let opacity = -color.a;
1104
1105 // Luminance-range compression, the plate's legibility control.
1106 //
1107 // The blur above destroys the backdrop's spatial DETAIL and preserves its
1108 // mean LUMINANCE — and text contrast is a mean-luminance property, so on
1109 // its own the mix below hands the backdrop's brightness straight through
1110 // at (1 - opacity). At the designer dialog's 0.25 that is 75% of whatever
1111 // is behind it: over the dark viewport a row label runs ~12:1, over
1112 // something bright ~1.2:1, which is not a contrast ratio so much as its
1113 // absence. No amount of extra blur moves either number.
1114 //
1115 // So remap the backdrop's luminance toward the plate's own key, keeping
1116 // its chromaticity. This is not "darken" and not opacity: it is
1117 // SYMMETRIC, pulling a bright backdrop down and a dark one UP, so what it
1118 // removes is the plate's swing through the ink's luminance rather than
1119 // the view through it. Hue, chroma and movement all still read.
1120 // Compression is a LEGIBILITY control and the rim carries no text, so the
1121 // clear window opened there is exempt in proportion to how clear it is.
1122 // Tone-mapping it would pull the refracted view back toward the plate's
1123 // own key — the exact contrast the rim exists to show — and the effect
1124 // measured nearly invisible with the two fighting.
1125 let k = clamp(k_in, 0.0, 1.0) * (1.0 - clamp(clarity, 0.0, 1.0));
1126 let W = vec3f(0.2126, 0.7152, 0.0722);
1127 let bl = dot(backdrop_color.rgb, W);
1128 let key = dot(color.rgb, W);
1129 // `target` is a WGSL reserved word.
1130 let keyed = mix(bl, key, k);
1131 // Scaling by keyed/bl holds chromaticity exactly. The hazard is the
1132 // RATIO: lifting a near-black backdrop toward a bright key multiplies
1133 // its 8-bit chroma by tens — banding, then channels clipping past 1 —
1134 // so the guard cross-fades to the neutral key luminance as the lift
1135 // grows, over ratio 2..8. Keyed to the ratio rather than to the
1136 // backdrop's luminance (the pre-2026-09-20 form: any backdrop under 5%
1137 // linear went neutral whenever k was non-zero at all), it is continuous
1138 // in k: at k = 0 the ratio is 1 and nothing happens, at a hair above 0
1139 // nearly nothing, and a pull DOWN toward a dark key (ratio < 1) never
1140 // touches the hue — the measured case: a navy viewport under a
1141 // #101018 tint kept going grey at k = 0.01. The select keeps k = 0 an
1142 // exact identity (the ratio's 1e-4 floor would otherwise darken true
1143 // black by a hair).
1144 let ratio = keyed / max(bl, 1e-4);
1145 let scaled = backdrop_color.rgb * ratio;
1146 let guard = smoothstep(2.0, 8.0, ratio);
1147 let guarded = mix(scaled, vec3f(keyed), guard);
1148 let compressed = select(guarded, backdrop_color.rgb, k <= 0.0);
1149
1150 return vec4f(mix(compressed, color.rgb, opacity), 1.0);
1151 }