GPU-accelerated UI toolkit (Vulkan)
git clone https://git.lucas.co/cce-ui.git
droplet: core density (DropletSpec::core)
The body opacity now ramps from clarity at the rim up to 1 + core (×
the color's own alpha, clamped opaque) at the deep interior — the
water reads thickest in the middle, which is where a module's text
sits, so glyphs get a calmer field without losing the watery rim.
Rides p_spec_tint.x: a droplet's glint is always white, so the tint
RGB slots were free and now carry droplet params. Default 0.35.
Co-Authored-By: Claude Fable 5 <[email protected]>
src/backend/window_runner.rs | 4 +++-
src/scene/paint.rs | 8 ++++++++
src/vk/renderer.rs | 6 ++++--
src/vk/shader2d.wgsl | 12 +++++++-----
4 files changed, 22 insertions(+), 8 deletions(-)
diff --git a/src/backend/window_runner.rs b/src/backend/window_runner.rs
index 17fed56..2a33655 100644
--- a/src/backend/window_runner.rs
+++ b/src/backend/window_runner.rs
@@ -2024,7 +2024,9 @@ pub fn tessellate_display_list(
// DE material's (a drop is wetter than the DE's plates).
material: [plate_mat[0], spec.gleam, spec.shine, spec.rim],
host: [sr * scale, spec.clarity.clamp(0.0, 1.0), spec.dome, ar * scale],
- specular_tint: [1.0, 1.0, 1.0, bow * scale],
+ // Droplet glints are always white, so the tint RGB slots
+ // carry droplet params instead: x = core density.
+ specular_tint: [spec.core.clamp(0.0, 2.0), 1.0, 1.0, bow * scale],
mode: 10.0,
shape: spec.curve.clamp(2.0, 6.0),
});
diff --git a/src/scene/paint.rs b/src/scene/paint.rs
index ad2a554..4b662af 100644
--- a/src/scene/paint.rs
+++ b/src/scene/paint.rs
@@ -113,6 +113,13 @@ pub struct DropletSpec {
/// instead of two arcs meeting, and the contact eases out of the flat
/// top like a meniscus. Clamped to [2, 6].
pub curve: f32,
+ /// Extra tint density at the drop's deep interior: the body opacity ramps
+ /// from `clarity` at the rim up to `1 + core` (× the color's own alpha,
+ /// clamped to opaque) inside — the water reads thickest in the middle,
+ /// which is also where a module's text sits, so glyphs get a calmer
+ /// field without giving up the watery rim. 0 = the original flat
+ /// interior falloff.
+ pub core: f32,
}
impl Default for DropletSpec {
@@ -138,6 +145,7 @@ impl Default for DropletSpec {
rim: 0.5,
bow: 0.12,
curve: 2.6,
+ core: 0.35,
}
}
}
diff --git a/src/vk/renderer.rs b/src/vk/renderer.rs
index 6b480e5..8a61db2 100644
--- a/src/vk/renderer.rs
+++ b/src/vk/renderer.rs
@@ -97,8 +97,10 @@ pub struct PlatePush {
/// (slab carve about a line: `rect` = [cx, cy, half-width, _], `radii.xy` =
/// the line's unit normal, `host` = the surface it is engraved into),
/// 9.0 = trough, 10.0 = droplet (`radii` = [sag, belly r, belly half-w,
- /// blend k] px, `host` = [sheet corner r px, clarity, dome amplitude, _],
- /// `material.w` = fresnel rim — see shader2d's MODE_DROPLET).
+ /// blend k] px, `host` = [sheet corner r px, clarity, dome amplitude,
+ /// attach r px], `material.w` = fresnel rim, `specular_tint` = [core
+ /// density, _, _, bottom-bow rise px] — droplet glints are always white,
+ /// so the tint RGB is repurposed; see shader2d's MODE_DROPLET).
pub mode: f32,
/// Corner shape exponent: 2.0 = circular arcs, > 2 = superellipse
/// (continuous-curvature) corners — see shader2d's `plate_sdf_grad`.
diff --git a/src/vk/shader2d.wgsl b/src/vk/shader2d.wgsl
index 7da9948..d016ff5 100644
--- a/src/vk/shader2d.wgsl
+++ b/src/vk/shader2d.wgsl
@@ -354,7 +354,8 @@ fn plate_shade(frag: vec2f, vcol: vec4f) -> vec4f {
// p_radii = [sag, belly radius, belly half-width, blend k] px;
// p_host = [sheet bottom-corner radius px, edge clarity 0-1, dome
// amplitude, attach (top-corner) radius px];
- // p_spec_tint.w = bottom-bow edge rise px (0 = flat bottom run);
+ // p_spec_tint = [core density, _, _, bottom-bow edge rise px] — a
+ // droplet's glint is always white, so the tint RGB slots are free;
// p_mat.w = fresnel rim crest amplitude (droplets carve nothing, so the
// AO slot is free); p_light.w = shaded band width px.
// The silhouette is the smooth union of a film SHEET attached to the top
@@ -434,11 +435,12 @@ fn plate_shade(frag: vec2f, vcol: vec4f) -> vec4f {
let extra = rrect_clip.p_mat.w * f * f * f;
let shade = 1.0 + (diff / flat_shade - 1.0 + extra) * strength;
let spec = roll_spec(sv);
- // Thin edges are clearer water: the tint opacity falls toward the rim.
- let body = mix(clarity, 1.0, u);
+ // Thin edges are clearer water; the deep interior densifies by the
+ // core term (thickest water in the middle — the text's field).
+ let body = mix(clarity, 1.0 + rrect_clip.p_spec_tint.x, u);
return vec4f(
- base.rgb * shade + rrect_clip.p_spec_tint.rgb * (spec * strength),
- abs(base.a) * body * aa2,
+ base.rgb * shade + vec3f(spec * strength),
+ min(abs(base.a) * body, 1.0) * aa2,
);
}