summaryrefslogtreecommitdiff
path: root/shaders/ui.frag.glsl
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-16 15:49:12 -0300
committerGabriel Schneider <[email protected]>2026-08-18 23:44:42 -0300
commit1551e409c31992437cb2fa864f576d45c8433801 (patch)
treee2fae8451f87b735a1360c7c2e383fdc40165789 /shaders/ui.frag.glsl
parentbe2a9957708cbf0c478ca861c4a1f0f227bbfe10 (diff)
downloadpardes-1551e409c31992437cb2fa864f576d45c8433801.tar.gz
pardes-1551e409c31992437cb2fa864f576d45c8433801.zip
big slow change: prebuilt shaders (SPIR-V/Metal), core gui reflow, docs, web + snapshot refresh
Diffstat (limited to 'shaders/ui.frag.glsl')
-rw-r--r--shaders/ui.frag.glsl94
1 files changed, 92 insertions, 2 deletions
diff --git a/shaders/ui.frag.glsl b/shaders/ui.frag.glsl
index d00a0601..2113f6ae 100644
--- a/shaders/ui.frag.glsl
+++ b/shaders/ui.frag.glsl
@@ -8,13 +8,103 @@ layout(set = 2, binding = 0) uniform sampler2D u_atlas;
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec3 v_fg;
layout(location = 2) in vec3 v_bg;
+layout(location = 3) in vec2 v_cell_uv;
+layout(location = 4) flat in uvec4 v_effect;
layout(location = 0) out vec4 o_col;
+float cellNoise(uint serial, uint col, uint row) {
+ uint x = serial ^ (col * 0x9e3779b9u) ^ (row * 0x85ebca6bu);
+ x ^= x >> 16;
+ x *= 0x7feb352du;
+ x ^= x >> 15;
+ x *= 0x846ca68bu;
+ x ^= x >> 16;
+ return float(x & 0xffffu) / 65535.0;
+}
+
+float asciiGlyph(vec2 uv, int glyph) {
+ vec2 p = uv * 2.0 - 1.0;
+ float thin = 0.12;
+ float dotShape = 1.0 - smoothstep(0.10, 0.20, length(p - vec2(0.0, 0.45)));
+ float barH = 1.0 - smoothstep(thin, thin + 0.08, abs(p.y));
+ float barV = 1.0 - smoothstep(thin, thin + 0.08, abs(p.x));
+ if (glyph == 0) return dotShape;
+ if (glyph == 1) return max(dotShape, 1.0 - smoothstep(0.10, 0.20, length(p + vec2(0.0, 0.45))));
+ if (glyph == 2) return max(barH, barV);
+ if (glyph == 3) {
+ float d0 = 1.0 - smoothstep(thin, thin + 0.08, abs(p.x - p.y));
+ float d1 = 1.0 - smoothstep(thin, thin + 0.08, abs(p.x + p.y));
+ return max(max(d0, d1), max(barH, barV));
+ }
+ if (glyph == 4) {
+ float rails = max(
+ 1.0 - smoothstep(thin, thin + 0.08, abs(abs(p.x) - 0.34)),
+ 1.0 - smoothstep(thin, thin + 0.08, abs(abs(p.y) - 0.34))
+ );
+ return rails;
+ }
+ float ring = 1.0 - smoothstep(thin, thin + 0.08, abs(length(p * vec2(0.8, 1.0)) - 0.48));
+ return max(ring, glyph == 6 ? dotShape : 0.0);
+}
+
void main() {
float a = texture(u_atlas, v_uv).r;
+ bool oldLayer = (v_effect.x & 0x80000000u) != 0u;
+ uint effect = v_effect.x & 0x7fffffffu;
+ float progress = clamp(uintBitsToFloat(v_effect.y), 0.0, 1.0);
+ uint cellCol = v_effect.w & 0xffffu;
+ uint cellRow = v_effect.w >> 16;
+ float noise = cellNoise(v_effect.z, cellCol, cellRow);
+
+ vec3 cell = mix(v_bg, v_fg, a);
+
+ // 3 = data dissolve. Changed cells retain the frozen old cell until their
+ // stable threshold, then atomically become the new cell. Exact endpoints
+ // mirror panel_animation.dissolveRevealed, including the zero hash.
+ if (effect == 3u) {
+ bool reveal = progress >= 1.0 || (progress > 0.0 && noise < progress);
+ if (oldLayer ? reveal : !reveal) discard;
+ o_col = vec4(cell, 1.0);
+ return;
+ }
+
+ // 4 = ASCII materialization. A diagonal/noise threshold matches the TTY
+ // compositor's idea: cells briefly become punctuation, then their real
+ // atlas glyph arrives. The procedural marks keep the GUI implementation
+ // entirely shader-side.
+ if (effect == 4u) {
+ if (progress <= 0.0) {
+ if (!oldLayer) discard;
+ o_col = vec4(cell, 1.0);
+ return;
+ }
+ if (progress >= 1.0) {
+ if (oldLayer) discard;
+ o_col = vec4(cell, 1.0);
+ return;
+ }
+ float diagonal = float((cellCol + cellRow * 2u) % 17u) / 17.0;
+ float threshold = noise * 0.72 + diagonal * 0.28;
+ if (progress >= threshold) {
+ if (oldLayer) discard;
+ o_col = vec4(cell, 1.0);
+ return;
+ }
+ if (progress + 0.22 >= threshold) {
+ if (oldLayer) discard;
+ int glyph = int(floor(noise * 7.0));
+ float ink = asciiGlyph(v_cell_uv, min(glyph, 6));
+ vec3 dimInk = mix(v_bg, v_fg, 0.62);
+ o_col = vec4(mix(v_bg, dimInk, ink), 1.0);
+ return;
+ }
+ if (!oldLayer) discard;
+ o_col = vec4(cell, 1.0);
+ return;
+ }
+
// FreeType's hinted grayscale bitmap is already the intended coverage.
// Preserve it exactly; weight-changing gamma hacks blur small stems.
- vec3 col = mix(v_bg, v_fg, a);
- o_col = vec4(col, 1.0);
+ o_col = vec4(cell, 1.0);
}