out vec4 outcolor; in vec2 frag_texture_coord; uniform sampler2D crt_texture; uniform sampler2D bloom_texture; uniform float bloom_strength; uniform vec2 bloom_size; uniform vec2 src_image_size; uniform vec2 viewport_size; void main() { vec3 crt_color = texture(crt_texture, frag_texture_coord).rgb; vec3 bloom_color = texture(bloom_texture, frag_texture_coord).rgb; // Calculate warp boundary mask to clip bloom outside CRT area vec2 ipos = frag_texture_coord * viewport_size; // Convert to normalized device coordinates [-1, 1] vec2 pos = ipos * (2.0 / viewport_size) - vec2(1.0); // Apply barrel distortion vec2 warp = vec2(1.0 / 24.0, 1.0 / 16.0); pos *= vec2(1.0 + (pos.y * pos.y) * warp.x, 1.0 + (pos.x * pos.x) * warp.y); // Convert back to UV vec2 uv = (pos + vec2(1.0)) * 0.5; // Rounded corner cutoff (CRT bezel effect) - only clips the corners float corner_radius = 0.05; // Radius of corner rounding (0.0 = sharp corners, 0.2 = very rounded) vec2 edge_distance = abs(pos) - vec2(1.0 - corner_radius); float dist = length(max(edge_distance, 0.0)); // Antialiased edge using smoothstep (creates soft 1-2 pixel transition) float edge_softness = 0.003; // Controls antialiasing width (smaller = sharper) float mask = smoothstep(corner_radius + edge_softness, corner_radius - edge_softness, dist); // Also check bounds mask *= (uv.x >= 0.0 && uv.x <= 1.0 && uv.y >= 0.0 && uv.y <= 1.0) ? 1.0 : 0.0; outcolor = vec4((crt_color + bloom_color * bloom_strength) * mask, 1.0); }