CRT screen

Barrel curve, scanlines and chromatic split over a plain 2D scene. The whole look is one fullscreen pass.

The editor above is running this project. Change a line and it reloads.

Godot classes used

ColorRectCanvasLayerPolygon2D

The code

scripts/main.gd
extends Node

var bars: Array[Polygon2D] = []
var elapsed: float = 0.0


func _ready() -> void:
	var view: Vector2 = get_viewport().get_visible_rect().size

	var background: ColorRect = ColorRect.new()
	background.color = Color('#101820')
	add_child(background)
	background.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

	for i in 8:
		var bar: Polygon2D = Polygon2D.new()
		bar.polygon = PackedVector2Array([Vector2.ZERO, Vector2(90, 0), Vector2(90, 260), Vector2(0, 260)])
		bar.color = Color.from_hsv(float(i) / 8.0, 0.55, 0.95)
		bar.position = Vector2(view.x * 0.5 - 400 + i * 100, view.y * 0.5 - 130)
		add_child(bar)
		bars.append(bar)

	var title: Label = Label.new()
	title.text = 'CRT POST PROCESSING'
	title.add_theme_font_size_override('font_size', 42)
	title.position = Vector2(view.x * 0.5 - 250, view.y * 0.2)
	add_child(title)

	# the ColorRect sits on a CanvasLayer above everything and reads what was already drawn
	var layer: CanvasLayer = CanvasLayer.new()
	layer.layer = 10
	add_child(layer)

	var material: ShaderMaterial = ShaderMaterial.new()
	material.shader = Get.shader('crt')

	var screen: ColorRect = ColorRect.new()
	screen.material = material
	layer.add_child(screen)
	screen.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)

	print('the scene below is plain 2D, the whole look is one fullscreen shader')


func _process(delta: float) -> void:
	elapsed += delta

	for i in bars.size():
		bars[i].scale.y = 0.4 + 0.6 * abs(sin(elapsed * 1.5 + i * 0.6))
shaders/crt.gdshader
shader_type canvas_item;

uniform sampler2D screen_texture: hint_screen_texture, filter_linear;
uniform float curvature = 0.18;
uniform float scanline_strength = 0.35;
uniform float scanline_count = 320.0;
uniform float chromatic = 0.0018;

void fragment() {
	vec2 centered = SCREEN_UV * 2.0 - 1.0;
	centered *= 1.0 + curvature * dot(centered, centered) * 0.35;

	vec2 uv = centered * 0.5 + 0.5;

	if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {
		COLOR = vec4(0.0, 0.0, 0.0, 1.0);
	} else {
		float r = texture(screen_texture, uv + vec2(chromatic, 0.0)).r;
		float g = texture(screen_texture, uv).g;
		float b = texture(screen_texture, uv - vec2(chromatic, 0.0)).b;

		float line = 1.0 - scanline_strength * step(0.5, fract(uv.y * scanline_count));
		float edge = 1.0 - 0.6 * dot(centered, centered) * 0.25;

		COLOR = vec4(vec3(r, g, b) * line * edge, 1.0);
	}
}

More Shaders examples