VHS Shader/Distortion

An implementation of https://www.shadertoy.com/view/Ms3XWH

 

Works best with minimal Shake, or else the scanlines disappear. Color distortion works best.

 

Works as Control Overlay with a ColorRect. Feel free to play with the values. 

 

Look at Test_CRT.tscn in the Github to see how it works.

Shader code
// adapted from:
// https://www.shadertoy.com/view/Ms3XWH

shader_type canvas_item;
uniform float shake: hint_range(0, 10) = 0.015;
uniform float noiseQuality: hint_range(0.1, 250,0.1) = 250;
uniform float noiseIntensity: hint_range(0, 0.05,0.001) = 0.001;
uniform float offsetIntensity: hint_range(0, 0.05,0.0001) = 0.05;
uniform float colorOffsetIntensity: hint_range(0.1, 1.5,0.001) = 0.5;
uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;

float rand(vec2 co)
{
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

float verticalBar(float pos, float uvY, float offset)
{
    float edge0 = (pos - shake);
    float edge1 = (pos + shake);

    float x = smoothstep(edge0, pos, uvY) * offset;
    x -= smoothstep(pos, edge1, uvY) * offset;
    return x;
}


void fragment() {
	vec2 iResolution = 1.0 / SCREEN_PIXEL_SIZE;
	vec2 uv = FRAGCOORD.xy / iResolution.xy;
    // why these values for i?
    for (float i = 0.0; i < 0.71; i += 0.1313)
    {
        float d = mod(TIME * i, 1.7);
        float o = sin(1.0 - tan(TIME * 0.24 * i));
    	o *= offsetIntensity;
        uv.x += verticalBar(d, uv.y, o);
    }
	float uvY = uv.y;
    uvY *= noiseQuality;
    uvY = float(int(uvY)) * (1.0 / noiseQuality);
    float noise = rand(vec2(TIME* 0.00001, uvY));
    uv.x += noise * noiseIntensity;
	
	vec2 offsetR = vec2(0.006 * sin(TIME), 0.0) * colorOffsetIntensity;
    vec2 offsetG = vec2(0.0073 * (cos(TIME * 0.97)), 0.0) * colorOffsetIntensity;
	
	float r = texture(screen_texture, uv + offsetR).r;
    float g = texture(screen_texture, uv + offsetG).g;
    float b = texture(screen_texture, uv).b;

	//vec4 tex = vec4(r, g, b, 1.0);
	vec4 tex=vec4(r, g, b,1.0);
    COLOR = tex;
}
Live Preview
Tags
canvas item, godot 4, Post processing, VHS
This shader is a port from an existing Shadertoy project. Shadertoy shaders are by default protected under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) license unless anything else has been stated by the author. For more info, see our License terms.

More from zuwiwano

Related shaders

Comments 4

  1. An error on the line:

    uniform sampler2D screen_texture : hint_screen_texture, repeat_disable, filter_nearest;
    

    "Expected valid type hint after ':'"

    1
      1. This should work for Godot 3:

        shader_type canvas_item;
        
        uniform float shake: hint_range(0, 10) = 0.015;
        
        uniform float noiseQuality: hint_range(0.1, 250,0.1) = 250;
        
        uniform float noiseIntensity: hint_range(0, 0.05,0.001) = 0.001;
        
        uniform float offsetIntensity: hint_range(0, 0.05,0.0001) = 0.05;
        
        uniform float colorOffsetIntensity: hint_range(0.1, 1.5,0.001) = 0.5;
        
        
        
        float rand(vec2 co)
        
        {
        
          return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
        
        }
        
        
        
        float verticalBar(float pos, float uvY, float offset)
        
        {
        
          float edge0 = (pos - shake);
        
          float edge1 = (pos + shake);
        
        
        
          float x = smoothstep(edge0, pos, uvY) * offset;
        
          x -= smoothstep(pos, edge1, uvY) * offset;
        
          return x;
        
        }
        
        
        
        
        void fragment() {
        
        	vec2 iResolution = 1.0 / SCREEN_PIXEL_SIZE;
        
        	vec2 uv = FRAGCOORD.xy / iResolution.xy;
        
          // why these values for i?
        
          for (float i = 0.0; i < 0.71; i += 0.1313)
        
          {
        
            float d = mod(TIME * i, 1.7);
        
            float o = sin(1.0 - tan(TIME * 0.24 * i));
        
          	o *= offsetIntensity;
        
            uv.x += verticalBar(d, uv.y, o);
        
          }
        
        	float uvY = uv.y;
        
          uvY *= noiseQuality;
        
          uvY = float(int(uvY)) * (1.0 / noiseQuality);
        
          float noise = rand(vec2(TIME* 0.00001, uvY));
        
          uv.x += noise * noiseIntensity;
        
        	
        
        	vec2 offsetR = vec2(0.006 * sin(TIME), 0.0) * colorOffsetIntensity;
        
          vec2 offsetG = vec2(0.0073 * (cos(TIME * 0.97)), 0.0) * colorOffsetIntensity;
        
        
        
        	float r = texture(SCREEN_TEXTURE, uv + offsetR).r;
        
          float g = texture(SCREEN_TEXTURE, uv + offsetG).g;
        
          float b = texture(SCREEN_TEXTURE, uv).b;
        
        
        
        	//vec4 tex = vec4(r, g, b, 1.0);
        
        	vec4 tex=vec4(r, g, b,1.0);
        
          COLOR = tex;
        
        }
        
        0
  2. Why does it become fully black when I zoom out much? When zoomed in it works as expected (using it as overlay to other CanvasItems ofc), but once I zoom out somewhat, it becomes an opaque black square.

    0