diff options
Diffstat (limited to 'shaders/image.frag.glsl')
| -rw-r--r-- | shaders/image.frag.glsl | 91 |
1 files changed, 90 insertions, 1 deletions
diff --git a/shaders/image.frag.glsl b/shaders/image.frag.glsl index 927d5f65..747ceef7 100644 --- a/shaders/image.frag.glsl +++ b/shaders/image.frag.glsl @@ -3,8 +3,97 @@ layout(set = 2, binding = 0) uniform sampler2D u_image; layout(location = 0) in vec2 v_uv; +layout(location = 1) in vec2 v_panel_uv; +layout(location = 2) 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; +} + +// The same seven procedural ASCII marks as ui.frag.glsl and the macOS Metal +// panel kernel. Native attachments are content in the panel, so they should +// not switch to a smaller two-symbol animation when the glyph grid over them +// is using `. : + * # % @`. +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() { - o_col = texture(u_image, v_uv); + vec4 source = texture(u_image, v_uv); + uint effect = v_effect.x & 0x7fffffffu; + bool oldLayer = (v_effect.x & 0x80000000u) != 0u; + float progress = clamp(uintBitsToFloat(v_effect.y), 0.0, 1.0); + uvec2 dimensions = max( + uvec2(v_effect.w & 0xffffu, v_effect.w >> 16), + uvec2(1u)); + uvec2 cellCoord = min( + uvec2(floor(max(v_panel_uv, vec2(0.0)) * vec2(dimensions))), + dimensions - 1u); + float noise = cellNoise(v_effect.z, cellCoord.x, cellCoord.y); + if (effect == 3u) { + bool reveal = progress >= 1.0 || (progress > 0.0 && noise < progress); + if (oldLayer == reveal) discard; + o_col = source; + return; + } + if (effect == 4u) { + float diagonal = float((cellCoord.x + cellCoord.y * 2u) % 17u) / 17.0; + float threshold = noise * 0.72 + diagonal * 0.28; + bool reveal = progress >= 1.0 || (progress > 0.0 && progress >= threshold); + bool wave = !reveal && progress > 0.0 && progress + 0.22 >= threshold; + if (oldLayer) { + if (wave || reveal) discard; + o_col = source; + return; + } + if (reveal) { + o_col = source; + return; + } + if (!wave) discard; + vec2 cellUV = fract(v_panel_uv * vec2(dimensions)); + int glyph = min(int(floor(noise * 7.0)), 6); + float mark = asciiGlyph(cellUV, glyph); + float luma = dot(source.rgb, vec3(0.2126, 0.7152, 0.0722)); + vec3 paper = source.rgb * 0.10; + // Preserve source colour when it already separates from the dimmed + // paper. Solid black pixels have no such separation, so derive a + // readable mark from their own luminance instead of letting the ASCII + // phase disappear on dark images/PDF regions. + vec3 contrastInk = luma > 0.5 ? vec3(0.0) : vec3(1.0); + float sourceContrast = smoothstep(0.025, 0.14, length(source.rgb - paper)); + vec3 ink = mix(contrastInk, source.rgb, sourceContrast); + vec3 dimInk = mix(paper, ink, 0.62); + o_col = vec4(mix(paper, dimInk, mark), source.a); + return; + } + o_col = source; } |
