CRT Display Shader (Pixel Mask, Scanlines & Glow) [Godot 4.4.1]

This shader emulates the look of a classic CRT display by combining several visual elements often found in retro hardware, like Subpixel Masking simulates RGB phosphor arrangements, with adjustable subpixel strength, Scanlines add a horizontal intensity variation to mimic the interlaced output of old CRTs, Glow Effect gives a subtle light bloom based on view direction, adding realism and depth, Pixel Grid Emulation is achieved through a resolution-based texture resampling mechanism and UV Transform Controls allow texture translation, scaling, and rotation in degrees, because sometimes, UVs on meshes aren’t on-point.

HOW TO USE:

Assign this shader to any spatial material and provide a texture (e.g. a rendered screen, UI image, or sprite atlas). Adjust the resolution and pixel size to fit your visual needs.

Why do things complicated, guys…

Shader code
shader_type spatial;
/**
CC0 (Public domain),
i mean, idc, really, let's be happy together

**/
/** CRT‑Main-Paramters **/
uniform vec2 resolution = vec2(320.0, 240.0);
uniform float pixel_size = 1.5;
uniform float scanline_strength = 0.2;
uniform float subpixel_strength = 0.6;
uniform float glow_intensity = 0.3;

/** Textur‑Parameters **/
uniform sampler2D albedo_texture : filter_nearest, source_color;
uniform vec2 tex_offset = vec2(0.0);
uniform vec2 tex_scale  = vec2(1.0);
uniform float tex_rotation_degrees = 0.0;

/** Rotation into Radians (internal conversion/translation) **/
vec2 rotate_uv(vec2 uv, float angle, vec2 pivot){
    float cos_a = cos(angle), sin_a = sin(angle);
    mat2 rot = mat2(vec2(cos_a, sin_a), vec2(-sin_a, cos_a));
    uv -= pivot; uv = rot * uv; uv += pivot;
    return uv;
}

void fragment() {
    vec2 uv = UV;
    vec2 pivot = vec2(0.5);

    float rad = radians(tex_rotation_degrees);
    uv = (uv - pivot) / tex_scale + pivot;
    uv = rotate_uv(uv, rad, pivot);
    uv += tex_offset;

    vec2 frag = uv * resolution;
    float scan = sin(frag.y * PI) * 0.5 + 0.5;
    scan = mix(1.0, scan, scanline_strength);
    float idx = mod(frag.x / pixel_size, 3.0);
    vec3 mask = vec3(
        float(idx < 1.0),
        float(idx >= 1.0 && idx < 2.0),
        float(idx >= 2.0)
    );
    mask = mix(vec3(1.0), mask, subpixel_strength);
    
    vec2 sample_uv = floor(uv * (resolution * 3.0)) / (resolution * 3.0);

    vec3 col = texture(albedo_texture, sample_uv).rgb;

    float vd = dot(NORMAL, -VIEW);
    float glow = pow(vd, 4.0) * glow_intensity;

    ALBEDO = col * scan * (1.0 - subpixel_strength);
    EMISSION = (col * mask * scan) + glow;
}
Live Preview
Tags
cctv, CRT, display, LCD, monitor, pixel, rbg, retro, tv, video, video games
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from paitorocxon

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments