Lofi Retro Camera Filter

A highly customizable VHS shader for Godot 4. Designed to give a retro camera feel without being too blurry. It features pixelation, chromatic aberration, scanlines, and a scrolling tracking glitch. The Opacity slider allows you to dial in the perfect amount of ‘retro’ for your scene.

Shader code
shader_type canvas_item;

// Main texture and parameters
uniform sampler2D SCREEN_TEXTURE : hint_screen_texture, filter_nearest;
uniform float pixel_size : hint_range(1.0, 10.0) = 3.0;
uniform float vhs_intensity : hint_range(0.0, 10.0) = 3.0;
uniform float noise_intensity : hint_range(0.0, 1.0) = 0.15;
uniform float opacity : hint_range(0.0, 1.0) = 0.5; 

// Quick & dirty random function
float random(vec2 uv) {
    return fract(sin(dot(uv, vec2(12.9898, 78.233))) * 43758.5453123);
}

void fragment() {
    // Keep a clean copy of the screen
    vec4 original_color = texture(SCREEN_TEXTURE, SCREEN_UV);
    
    // Convert to float to avoid the ivec2/float math crash in Godot 4
    vec2 screen_res = vec2(textureSize(SCREEN_TEXTURE, 0));
    
    // Pixel grid snapping
    vec2 grid_uv = round(SCREEN_UV * (screen_res / pixel_size)) / (screen_res / pixel_size);
    
    // Horizontal glitch - step time to make it chunky, not smooth
    float time_step = floor(TIME * 15.0); 
    float glitch = (random(vec2(time_step, floor(grid_uv.y * 30.0))) - 0.5) * 0.005 * vhs_intensity;
    
    // Tape tracking line - scrolls down the screen
    float tracking_pos = fract(TIME * 0.2); 
    float tracking_line = step(0.95, 1.0 - abs(grid_uv.y - tracking_pos));
    float tracking_glitch = tracking_line * (random(vec2(TIME)) - 0.5) * 0.05;
    
    // Apply all UV offsets at once
    vec2 uv = grid_uv;
    uv.x += glitch + tracking_glitch;

    // Split RGB channels for the color fringe
    float r = texture(SCREEN_TEXTURE, uv + vec2(0.002 * vhs_intensity, 0.0)).r;
    float g = texture(SCREEN_TEXTURE, uv).g;
    float b = texture(SCREEN_TEXTURE, uv - vec2(0.002 * vhs_intensity, 0.0)).b;
    vec3 vhs_color = vec3(r, g, b);

    // Hard scanlines - follows the pixel grid
    float scanline = mod(floor(SCREEN_UV.y * screen_res.y / pixel_size), 2.0) * 0.15;
    vhs_color -= scanline;

    // Static noise grain
    float noise = (random(uv + floor(TIME * 20.0)) - 0.5) * noise_intensity;
    vhs_color += noise;

    // Final mix - Lerp between clean and VHS look
    vec3 final_color = mix(original_color.rgb, vhs_color, opacity);

    COLOR = vec4(final_color, 1.0);
}
Live Preview
Tags
CRT, godot 4
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 ROT-7

Related shaders

guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments