Heart Slasher
https://www.shadertoy.com/view/l3G3R1
Shader code
shader_type canvas_item;
#define iTime TIME
#define iResolution 1.0/SCREEN_PIXEL_SIZE
uniform float ARROWS : hint_range(-1.0, 100.0, 1) = 5.0;
uniform float VANISH_TIME = 1.5;
uniform float animation_speed : hint_range(0.0, 10.0, 0.1) = 2.0;
uniform float shake_amplitude : hint_range(0.0, 5.0, 0.1) = 0.7;
uniform vec3 heart_color = vec3(1, 0, 0);
uniform vec3 halo_color = vec3(1.000, 0.922, 0.078);
uniform vec3 bg_color = vec3(0.5, 0.0, 0.0);
uniform bool remove_bg = false;
const float HPI = PI * 0.5;
float dot2(vec2 x) {
return dot(x, x);
}
float hash11(float p) {
p = fract(p * .1031);
p *= p + 33.33;
p *= p + p;
return fract(p);
}
float noise(float p) {
float fl = floor(p);
float fc = fract(p);
return mix(hash11(fl), hash11(fl + 1.0), fc);
}
float hash12(vec2 p) {
vec3 p3 = fract(p.xyx * .1031);
p3 += dot(p3, p3.yzx + 33.33);
return fract((p3.x + p3.y) * p3.z);
}
float noise2d(vec2 p) {
vec2 ip = floor(p);
vec2 u = fract(p);
u = u * u * (3.0 - 2.0 * u);
float res = mix(
mix(hash12(ip), hash12(ip + vec2(1, 0)), u.x),
mix(hash12(ip + vec2(0, 1)), hash12(ip + vec2(1)), u.x), u.y);
return res * res;
}
float fbm(float p, int octaves) {
float s = 0.0, m = 0.0, a = 1.0;
for(int i = 0; i < octaves; i++) {
s += a * noise(p);
m += a;
a *= 0.6;
p *= 1.8;
}
return s / m;
}
float fbm2d(vec2 p, int octaves) {
float s = 0.0, m = 0.0, a = 1.0;
for(int i = 0; i < octaves; i++) {
s += a * noise2d(p);
m += a;
a *= 0.6;
p *= 1.8;
}
return s / m;
}
float heart(vec2 p) {
p.y += 0.5;
p.x = abs(p.x);
if (p.y + p.x > 1.0)
return sqrt(dot2(p - vec2(0.25,0.75))) - sqrt(2.0) / 4.0;
return sqrt(min(dot2(p - vec2(0.00,1.00)),
dot2(p - 0.5 * max(p.x + p.y, 0.0)))) * sign(p.x-p.y);
}
float shake(float t) {
t = clamp(t, 0.0, 1.0);
return (fbm(pow(t, 0.3) * 8.0, 3) * (1.0 + cos(t * HPI)) - sin(t * HPI)) * (1.0 - t) * pow(t, 0.7);
}
float line(vec2 p, vec2 p1, vec2 p2) {
vec2 center = (p1 + p2) * 0.5;
float len = length(p2 - p1);
vec2 dir = (p2 - p1) / len;
vec2 rel_p = p - center;
return dot(rel_p, vec2(dir.y, -dir.x));
}
float segment(vec2 p, vec2 p1, vec2 p2) {
vec2 center = (p1 + p2) * 0.5;
float len = length(p2 - p1);
vec2 dir = (p2 - p1) / len;
vec2 rel_p = p - center;
float dist1 = abs(dot(rel_p, vec2(dir.y, -dir.x)));
float dist2 = abs(dot(rel_p, dir)) - 0.5 * len;
return max(dist1, dist2);
}
float arrow(vec2 uv) {
const float outline = 0.01;
const float line_width = 1.8;
float height = 0.2 * max(0.5, sqrt(uv.x));
const float head_size = 0.2;
const float size = 0.02;
uv.x -= line_width * 0.45 + head_size * 0.5;
float a1 = line(uv, -head_size * vec2(1, -height), vec2(0));
float a2 = line(uv, -head_size * vec2(1, -height), -vec2(3.0 * head_size / 4.0, 0));
float a3 = line(uv, -head_size * vec2(1, height), vec2(0));
float a4 = line(uv, -head_size * vec2(1, height), -vec2(3.0 * head_size / 4.0, 0));
float head = max(max(-a1, a3), -max(-a2, a4));
uv.y = abs(uv.y);
vec2 n = vec2(0,(fbm(uv.x * 5.0 + iTime * 0.5, 3) * 2.0 - 1.0) * 0.03);
float tail = segment(uv - n, vec2(-line_width*0.9, 0), vec2(-line_width - head_size * 0.9, head_size * 0.5));
tail = min(tail, segment(uv - n, vec2(-line_width*0.85 - head_size , 0), vec2(-line_width - head_size * 1.4, head_size * 0.4)));
tail = min(tail, segment(uv - n, vec2(-line_width*0.93 - head_size , 0), vec2(-line_width - head_size * 1.7, head_size * 0.3)));
float stick = segment(uv, - vec2((head_size * 1.5 + line_width), 0), vec2(-0.2, 0));
stick = min(stick, tail);
return min(abs(head - size) - outline, mix(stick - outline * smoothstep(-1.9, -1.2, uv.x), max(outline, head - outline), step(head, size)));
}
float shoot_arrow(vec2 uv, float t, float id) {
t = clamp(t - id, 0.0, 1.0);
float f = length(fwidth(uv));
vec2 auv = uv;
float a = noise(-id * 5.0 - 0.05);
auv = cos(a * TAU) * auv + sin(a * TAU) * vec2(-auv.y, auv.x);
auv.x -= (smoothstep(0.0, 0.8, t * 2.0) - 1.0) * 7.0;
return arrow(auv);
}
void fragment()
{
float mr = min(iResolution.x, iResolution.y);
vec2 uv = (FRAGCOORD.xy * 2.0 - iResolution.xy) / mr * 1.5;
uv.y = -uv.y;
float f = length(fwidth(uv));
float t = mod(iTime * animation_speed, ARROWS + VANISH_TIME);
float ft = floor(t);
float a = noise(-ft * 5.0 - 0.05);
vec2 dir = vec2(cos(-a * TAU), sin(-a * TAU));
float ts = fract(min(t, ARROWS));
float s = shake((ts * 2.0 - 0.65) * 2.5) * shake_amplitude;
uv -= dir * s;
float d = heart(uv);
float m = smoothstep(0.0 + f, 0.0 - f, d);
vec3 col = vec3(m) * heart_color;
col += sqrt(max(0.0, -d * 4.0)) - m;
col = abs(col);
float arrw = shoot_arrow(uv, t, 0.0);
for (float i = 1.0; i < ARROWS; ++i)
arrw = min(arrw, shoot_arrow(uv, t, i));
arrw += fbm2d(uv * 4.0, 4) * smoothstep(ARROWS, ARROWS + VANISH_TIME, t) * 0.5;
col = mix(vec3(smoothstep(f, -f, arrw)), col, m);
float flash = (1.0 - smoothstep(0.0, 1.0, ts)) * smoothstep(0.5, 0.9, ts * 2.0) * (1.0 + abs(s) * 6.0) * 0.7;
float g = (1.0 - m) * exp(-sqrt(max(0.0, d)) * 3.0);
col += g * sin(g * halo_color + bg_color);
col.rb += (col.g + col.b) * vec2(1.0 / g, g + flash) - vec2(col.r, col.r);
COLOR = vec4(col,1.0);
if(remove_bg == true){
COLOR.a = COLOR.r;
}
}