Doom Screen Melt Effect
A version of the Screen melt effect from Doom that plays as you start a level.
Values do need to be set by an outside script for the shader to work correctly.
This example code I used to achieve a similar effect to Doom. Creating a duplicate texture of the screen to overlay as menus swap, then melting away that duplicate texture with this shader.
extends ColorRect
var melting := false
var timer := 0.0 //Incremented by melt_speed then applied to the timer variable in the shader
@export var x_resolution := 100.0 //Value must be greater or equal to y_offsets.length() in the shader
@export var max_offset := 2.0
@export var melt_speed := 0.02
# Called when the node enters the scene tree for the first time.
func _ready():
hide()
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(_delta):
if melting:
timer += melt_speed
self.material.set_shader_parameter("timer", timer)
elif Input.is_action_just_pressed("Dev_Log"):
generate_offsets()
# Call this before transitioning, creates a copy of the screen texture so changes
# can be made underneath before melting to show the new screen.
func generate_offsets():
var offsets = []
for i in x_resolution:
offsets.append(randf_range(1.0, max_offset))
self.material.set_shader_parameter("y_offsets", offsets)
var img = get_viewport().get_texture().get_image()
var tex = ImageTexture.create_from_image(img)
self.material.set_shader_parameter("melt_tex", tex)
show()
# Call this after generate_offsets
func transition():
self.material.set_shader_parameter("melting", true)
melting = true
Shader code
shader_type canvas_item;
//Incremented by outside script to move effect
uniform float timer = 0.0;
//Array size determines how many vertical strips appear
uniform float[64] y_offsets;
//Texture to melt away, example above uses a duplicate screen texture as a transition
uniform sampler2D melt_tex;
//Controls whether the effect can play
uniform bool melting = false;
void fragment() {
vec2 tex_uv = SCREEN_UV;
if (melting)
{
float index = tex_uv.x * float(y_offsets.length());
tex_uv.y -= timer * y_offsets[int(index)];
if (tex_uv.y < 0.0 || tex_uv.y > 1.0) discard;
}
COLOR = texture(melt_tex, tex_uv);
}