diff options
Diffstat (limited to 'shaders/ui.frag.glsl')
| -rw-r--r-- | shaders/ui.frag.glsl | 94 |
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); } |
