summaryrefslogtreecommitdiff
path: root/shaders/crt.frag.glsl
blob: 1c9b15a87a644c5f2b6b74bee74e6ba774b54502 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#version 450

// The Crt builtin's post-process, one pass, fully static (no animation, no
// glitches). Screen point cc shows the scene at uv' = cc*(1 + BARREL*r2),
// and src/crt.zig maps mouse input through that same transform — keep them
// equal or clicks drift near the edges.

layout(set = 2, binding = 0) uniform sampler2D u_scene;

layout(location = 0) out vec4 o_col;

const float BARREL = 0.012; // barrel curvature; MUST match src/crt.zig barrel
const float VIGNETTE = 0.07; // corner darkening: *= 1 - VIGNETTE*r2^2
const vec2 PHOSPHOR = vec2(2.0, 2.0); // uv quantize cell in scene px (~1/7 of a glyph cell)
const float SCAN = 0.15; // scanline depth, one raised-cosine band per PHOSPHOR.y rows
const float CHROMA = 0.001; // R/B radial sample offset, grows toward the edges
const float BLOOM = 0.4; // bright-pass neighbor glow added back
const float BLOOM_MIN = 0.5; // channel level where the bright pass starts
const float LIFT = 1.08; // output brightness lift (offsets scan+vignette losses)

void main() {
    vec2 size = vec2(textureSize(u_scene, 0));
    vec2 cc = gl_FragCoord.xy / size * 2.0 - 1.0;
    float r2 = dot(cc, cc);
    vec2 uv = cc * (1.0 + BARREL * r2) * 0.5 + 0.5;
    vec3 col = vec3(0.0);
    if (all(greaterThanEqual(uv, vec2(0.0))) && all(lessThanEqual(uv, vec2(1.0)))) {
        vec2 q = (floor(uv * size / PHOSPHOR) + 0.5) * PHOSPHOR / size;
        vec2 ca = cc * (CHROMA * r2);
        col = vec3(texture(u_scene, q + ca).r, texture(u_scene, q).g, texture(u_scene, q - ca).b);
        vec2 px = PHOSPHOR / size;
        vec3 nb = texture(u_scene, q + vec2(px.x, 0.0)).rgb + texture(u_scene, q - vec2(px.x, 0.0)).rgb +
            texture(u_scene, q + vec2(0.0, px.y)).rgb + texture(u_scene, q - vec2(0.0, px.y)).rgb;
        col += max(nb * 0.25 - BLOOM_MIN, 0.0) * BLOOM;
    }
    col *= 1.0 - VIGNETTE * r2 * r2;
    col *= 1.0 - SCAN * (0.5 + 0.5 * cos(gl_FragCoord.y * 6.2831853 / PHOSPHOR.y));
    o_col = vec4(col * LIFT, 1.0);
}