Skip to content

Instantly share code, notes, and snippets.

@mrange

mrange/atari.glsl

Created Mar 3, 2021
Embed
What would you like to do?
atari tribute
// License CC0: Tribute to my old Atari
// That's where it started for me
#define TIME iTime
#define RESOLUTION iResolution
#define PI 3.141592654
#define TAU (2.0*PI)
#define L2(x) dot(x, x)
#define ROT(a) mat2(cos(a), sin(a), -sin(a), cos(a))
#define TTIME (TAU*TIME)
#define PSIN(x) (0.5+0.5*sin(x))
float hash(vec2 co) {
return fract(sin(dot(co, vec2(12.9898,58.233))) * 13758.5453);
}
vec2 hextile(inout vec2 p) {
// See Art of Code: Hexagonal Tiling Explained!
// https://www.youtube.com/watch?v=VmrIDyYiJBA
const vec2 sz = vec2(1.0, sqrt(3.0));
const vec2 hsz = 0.5*sz;
vec2 p1 = mod(p, sz)-hsz;
vec2 p2 = mod(p - hsz, sz)-hsz;
vec2 p3 = mix(p2, p1, vec2(dot(p1, p1) < dot(p2, p2)));
vec2 n = ((p3 - p + hsz)/sz);
p = p3;
// Rounding to make hextile 0,0 well behaved
return round(n*2.0)/2.0;
}
float tanh_approx(float x) {
// return tanh(x);
float x2 = x*x;
return clamp(x*(27.0 + x2)/(27.0+9.0*x2), -1.0, 1.0);
}
// IQ's polynomial min
float pmin(float a, float b, float k) {
float h = clamp(0.5+0.5*(b-a)/k, 0.0, 1.0);
return mix(b, a, h) - k*h*(1.0-h);
}
float pmax(float a, float b, float k) {
return -pmin(-a, -b, k);
}
float pabs(float a, float k) {
return pmax(a, -a, k);
}
// IQ's box
float box(vec2 p, vec2 b) {
vec2 d = abs(p)-b;
return length(max(d,0.0)) + min(max(d.x,d.y),0.0);
}
float circle(vec2 p, float r) {
return length(p) - r;
}
// IQ's isosceles triangle
float isosceles(vec2 p, vec2 q) {
p.x = abs(p.x);
vec2 a = p - q*clamp( dot(p,q)/dot(q,q), 0.0, 1.0 );
vec2 b = p - q*vec2( clamp( p.x/q.x, 0.0, 1.0 ), 1.0 );
float s = -sign( q.y );
vec2 d = min( vec2( dot(a,a), s*(p.x*q.y-p.y*q.x) ),
vec2( dot(b,b), s*(p.y-q.y) ));
return -sqrt(d.x)*sign(d.y);
}
// IQ's horseshoe
float horseshoe(vec2 p, vec2 c, float r, vec2 w) {
p.x = abs(p.x);
float l = length(p);
p = mat2(-c.x, c.y,
c.y, c.x)*p;
p = vec2((p.y>0.0)?p.x:l*sign(-c.x),
(p.x>0.0)?p.y:l );
p = vec2(p.x,abs(p.y-r))-w;
return length(max(p,0.0)) + min(0.0,max(p.x,p.y));
}
// IQ's segment
float segment(vec2 p, vec2 a, vec2 b) {
vec2 pa = p-a, ba = b-a;
float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
return length( pa - ba*h );
}
// IQ's segment
float parabola(vec2 pos, float k) {
pos.x = abs(pos.x);
float ik = 1.0/k;
float p = ik*(pos.y - 0.5*ik)/3.0;
float q = 0.25*ik*ik*pos.x;
float h = q*q - p*p*p;
float r = sqrt(abs(h));
float x = (h>0.0) ?
pow(q+r,1.0/3.0) - pow(abs(q-r),1.0/3.0)*sign(r-q) :
2.0*cos(atan(r,q)/3.0)*sqrt(p);
return length(pos-vec2(x,k*x*x)) * sign(pos.x-x);
}
float atari(vec2 p) {
p.x = abs(p.x);
float db = box(p, vec2(0.36, 0.32));
float dp0 = -parabola(p-vec2(0.4, -0.235), 4.0);
float dy0 = p.x-0.115;
float d0 = mix(dp0, dy0, smoothstep(-0.25, 0.125, p.y)); // Very hacky
float dp1 = -parabola(p-vec2(0.4, -0.32), 3.0);
float dy1 = p.x-0.07;
float d1 = mix(dp1, dy1, smoothstep(-0.39, 0.085, p.y)); // Very hacky
float d2 = p.x-0.035;
const float sm = 0.025;
float d = 1E6;
d = min(d, max(d0, -d1));;
d = pmin(d, d2, sm);
d = pmax(d, db, sm);
return d;
}
float atari_a(inout vec2 p, vec2 off) {
p -= vec2(0.275, 0.0);
float d0 = isosceles(p*vec2(1.0, -1.0)-vec2(0.0, -0.225), vec2(0.20, 0.65))-0.1;
float d1 = isosceles(p*vec2(1.0, -1.0)-vec2(0.0, -0.18), vec2(0.13, 0.55))-0.005;
float d2 = box(p-vec2(0.0, -0.135), vec2(0.15, 0.06));
float d3 = p.y+0.325;
float d = d0;
d = max(d, -d1);
d = pmin(d, d2, 0.0125);
d = pmax(d, -d3, 0.0125);
p -= vec2(0.275, 0.0) + off;
return d;
}
float atari_i(inout vec2 p, vec2 off) {
p -= vec2(0.07, 0.0);
float d0 = box(p, vec2(0.07, 0.325)-0.0125)-0.0125;
float d = d0;
p -= vec2(0.07, 0.0) + off;
return d;
}
float atari_r(inout vec2 p, vec2 off) {
p -= vec2(0.22, 0.0);
float d0 = p.y+0.325;
float d1 = circle(p - vec2(-0.12, 0.225), 0.1);
const float a = PI/2.0;
const vec2 c = vec2(cos(a), sin(a));
vec2 hp = p;
hp -= vec2(0.0, 0.14);
hp.xy = -hp.yx;
float d2 = horseshoe(hp, c, 0.125, 0.2175*vec2(0.12,0.275));
float d3 = segment(p-vec2(-0.015, 0.005), vec2(0.0), vec2(0.22, -0.4))-0.07;
float d5 = p.y - 0.205;
float d6 = box(p - vec2(-0.155, -0.075), vec2(0.065, 0.30));
float d7 = box(p - vec2(-0.055, 0.225), vec2(0.06, 0.1));
float d = d1;
d = min(d, d7);
d = max(d, -d5);
d = min(d, d2);
d = min(d, d6);
d = min(d, d3);
d = pmax(d, -d0, 0.0125);
p -= vec2(0.25, 0.0)+off;
return d;
}
float atari_t(inout vec2 p, vec2 off) {
p -= vec2(0.195, 0.0);
float d0 = box(p - vec2(0.0, 0.265), vec2(0.195, 0.06)-0.0125)-0.0125;
float d1 = box(p - vec2(0.0, -0.03), vec2(0.07, 0.295)-0.0125)-0.0125;
float d = d0;
d = pmin(d, d1, 0.0125);
p -= vec2(0.195, 0.0) + off;
return d;
}
float atari_text(vec2 p) {
p -= vec2(-1.0, 0.0);
float d = 1E6;
d = min(d, atari_a(p, vec2(-0.075, 0.0)));
d = min(d, atari_t(p, vec2(-0.055, 0.0)));
d = min(d, atari_a(p, vec2(0.02, 0.0)));
d = min(d, atari_r(p, vec2(0.01, 0.0)));
d = min(d, atari_i(p, vec2(0.0, 0.0)));
return d;
}
float height_(vec2 p) {
p *= 0.2;
vec2 p0 = p;
vec2 n0 = hextile(p0);
p0 *= ROT(TAU*hash(n0));
const float ss = 0.95;
float d0 = atari(p0/ss)*ss;
float d = d0;
return 0.25*tanh_approx(smoothstep(0.0125, -0.0125, d)*exp(2.0*-d));
// return 0.25*smoothstep(0.0125, -0.0125, d);
}
float height(vec2 p) {
const mat2 rot1 = ROT(1.23);
float tm = 123.0+TTIME/320.0;
p += 5.0*vec2(cos(tm), sin(tm*sqrt(0.5)));
const float aa = -0.45;
const mat2 pp = (1.0/aa)*rot1;
float h = 0.0;
float a = 1.0;
float d = 0.0;
for (int i = 0; i < 6; ++i) {
h += a*height_(p);
d += a;
a *= aa;
p *= pp;
}
const float hf = -0.125;
return hf*(h/d)+hf;
}
vec3 normal(vec2 p) {
vec2 v;
vec2 w;
vec2 e = vec2(4.0/RESOLUTION.y, 0);
vec3 n;
n.x = height(p + e.xy) - height(p - e.xy);
n.y = 2.0*e.x;
n.z = height(p + e.yx) - height(p - e.yx);
return normalize(n);
}
vec3 color(vec2 p) {
vec2 ppp = p;
const float s = 1.0;
const vec3 lp1 = vec3(1.0, 1.25, 1.0)*vec3(s, 1.0, s);
const vec3 lp2 = vec3(-1.0, 1.25, 1.0)*vec3(s, 1.0, s);
float aa = 2.0/RESOLUTION.y;
float h = height(p);
vec3 n = normal(p);
vec3 ro = vec3(0.0, -10.0, 0.0);
vec3 pp = vec3(p.x, 0.0, p.y);
vec3 po = vec3(p.x, h, p.y);
vec3 rd = normalize(ro - po);
vec3 ld1 = normalize(lp1 - po);
vec3 ld2 = normalize(lp2 - po);
float diff1 = max(dot(n, ld1), 0.0);
float diff2 = max(dot(n, ld2), 0.0);
vec3 rn = n;
vec3 ref = reflect(rd, rn);
float ref1 = max(dot(ref, ld1), 0.0);
float ref2 = max(dot(ref, ld2), 0.0);
vec3 lcol1 = vec3(1.25, 1.35, 2.0);
vec3 lcol2 = vec3(2.0, 1.55, 1.25);
vec3 lpow1 = 0.15*lcol1/L2(ld1);
vec3 lpow2 = 0.25*lcol2/L2(ld2);
vec3 dm = vec3(1.0)*tanh_approx(-h*10.0+0.125);
vec3 col = vec3(0.0);
col += dm*pow(diff1, 4.0)*lpow1;
col += dm*pow(diff2, 4.0)*lpow2;
vec3 rm = vec3(1.0)*mix(0.25, 1.0, tanh_approx(-h*1000.0));
col += rm*pow(ref1, 20.0)*lcol1;
col += rm*pow(ref2, 20.0)*lcol2;
const float zp = 1.35;
float di = atari_text(ppp/zp)*zp;
float dio = di;
dio = abs(dio-0.015) - 0.0075;
di = min(di, dio);
col += -lcol2*0.125*(exp(-5.0*max(di, 0.0)));
col = mix(col, vec3(1.0), smoothstep(-aa, aa, -di));
di = abs(di-0.025);
di = abs(di-0.0125);
col += 0.7*lcol2*(exp(-20.0*max(di+0., 0.0)))*pow(PSIN(-0.8+p.x-p.y-TTIME/16.0), 10.0);
return col;
}
// Post processing I found somewhere on shadertoy years ago
vec3 postProcess(vec3 col, vec2 q) {
col = clamp(col, 0.0, 1.0);
col = pow(col, 1.0/vec3(2.2));
col = col*0.6+0.4*col*col*(3.0-2.0*col);
col = mix(col, vec3(dot(col, vec3(0.33))), -0.4);
col *=0.5+0.5*pow(19.0*q.x*q.y*(1.0-q.x)*(1.0-q.y),0.7);
return col;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
vec2 q = fragCoord/RESOLUTION.xy;
vec2 p = -1. + 2. * q;
p.x *= RESOLUTION.x/RESOLUTION.y;
vec3 col = color(p);
col = clamp(col, 0.0, 1.0);
col *= smoothstep(0.0, 4.0, TIME);
col = postProcess(col, q);
fragColor = vec4(col, 1.0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment