xxxxxxxxxx
//WIN 98
//twitter.com/mattywillo_
//made for twitter.com/sableRaph's Weekly Creative Coding Challenge
//the topic was "Windows 98 and other classic operating systems"
//join the birbs nest discord to participate and vote on future topics
//https://discord.gg/S8c7qcjw2b
//quick rendition of the classic pipes screensaver with some silly reflections and highlights
//i have made 0 attempts to optimize or speed this up, runs terribly slow
//this isn't actually raymarching and thus is technically something different for me
//i did not look at any videos of the old screensaver and just winged it so it's not at all faithful
let t = 0;
let rat = 1.0;
let canvas,sh;
let pts = [];
let step_min = .05;
let step_max = .2;
const AA = 1; //set to 2 for AA
const quality = 1.0; //lower for better performance
//increase this for more pipes and to mess with your pc lmao
//has a hard upper limit based on your browsers maximum frag shader uniforms
//probably should pack them into a texture to get around this instead but it's midnight so i won't be doing that
//¯\_(ツ)_/¯
const seg_count = 200;
//this function does a random walk for pipe and sets it as a uniform for the shader
//you might notice that this only generates a single path, the two other pipes are just the same path flipped around because of uniform limits and laziness
function gen() {
pts = [Array(seg_count-1)].reduce(a=>[a,(l=>{
let r = [l];
while(r.every((x,i)=>x==l[i]) || r.some(x=>x>.75||x<-.75)) {r = [l]; r[Math.random()*3|0] += (Math.random()*(step_max-step_min)+step_min)*(Math.random()>.5?1:-1)}
return r;
})(a.slice(-1)[0])],[[0,1,2].map(x=>Math.random()*.5-.25)]).flat();
sh.setUniform('pts', pts);
}
//noprotect
let shSrc = [
`attribute vec3 aPosition;
varying vec2 uv;
void main() {
gl_Position = vec4(aPosition*2.-1.,1.);
uv = vec2(aPosition.x, 1.-aPosition.y);
}
`,
`precision highp float;
#define PI `+Math.PI+`
#define MAXD 10.
#define SEGS `+seg_count+`
#define AA `+AA+`
varying vec2 uv;
uniform vec2 res;
uniform float t;
uniform vec3 pts[SEGS];
struct hit {
float d;
vec3 n;
vec3 c;
};
//iq's ray/capsule intersect function
float capsule(vec3 ro, vec3 rd, vec3 pa, vec3 pb, float r ) {
vec3 ba = pb - pa;
vec3 oa = ro - pa;
float baba = dot(ba,ba);
float bard = dot(ba,rd);
float baoa = dot(ba,oa);
float rdoa = dot(rd,oa);
float oaoa = dot(oa,oa);
float a = baba-bard*bard;
float b = baba*rdoa-baoa*bard;
float c = baba*oaoa-baoa*baoa-r*r*baba;
float h = b*b-a*c;
if(h>=0.) {
float t = (-b-sqrt(h))/a;
float y = baoa + t*bard;
if(y>0.0 && y<baba) return t;
vec3 oc = (y<=0.0) ? oa : ro - pb;
b = dot(rd,oc);
c = dot(oc,oc) - r*r;
h = b*b - c;
if(h>0.0) return -b - sqrt(h);
}
return MAXD;
}
//iq's capsule normal function
vec3 capNormal(vec3 pos, vec3 a, vec3 b, float r ) {
vec3 ba = b - a;
vec3 pa = pos - a;
float h = clamp(dot(pa,ba)/dot(ba,ba),0.0,1.0);
return (pa - h*ba)/r;
}
mat3 cam(vec3 ro, vec3 c_t, float r) {
vec3 cw =normalize(c_t-ro);
vec3 cp = vec3(sin(r), cos(r),0);
vec3 cu = normalize(cross(cw,cp));
vec3 cv = normalize(cross(cu,cw));
return mat3(cu,cv,cw);
}
hit trace(vec3 ro, vec3 rd) {
float d = MAXD;
vec3 n = vec3(0);
vec3 c = vec3(0);
for(int i = 0; i < SEGS-1; i++) {
float s = float(i)/float(SEGS-1);
if(t<s) break;
float cd = capsule(ro,rd,pts[i],pts[i+1],.01);
if(cd<d) {
d = cd;
n = capNormal(ro+rd*d,pts[i],pts[i+1],.01);
c = vec3(219,180,44)/255.;
}
cd = capsule(ro,rd,pts[i].yzx,pts[i+1].yzx,.01);
if(cd<d) {
d = cd;
n = capNormal(ro+rd*d,pts[i].yzx,pts[i+1].yzx,.01);
c = vec3(206,212,218)/255.;
}
cd = capsule(ro+vec3(.1),rd,pts[i].zyx,pts[i+1].zyx,.01);
if(cd<d) {
d = cd;
n = capNormal(ro+vec3(.1)+rd*d,pts[i].zyx,pts[i+1].zyx,.01);
c = vec3(222,171,144)/255.;
}
}
return hit(d,n,c);
}
void main() {
vec2 p = uv*2.-1.;
vec3 ld = normalize(vec3(1,-1,.1));
vec3 col = vec3(0);
for(float i = 0.; i<float(AA); i++)
for(float j = 0.; j<float(AA); j++) {
//uncomment this to make the camera move
// vec3 ro = vec3(sin(t*PI*2.),0,cos(t*PI*2.))+vec3(i,j,0)*.001;
vec3 ro = vec3(0,0,1)+vec3(i,j,0)*.001;
vec3 rd = cam(ro, vec3(0),0.)*normalize(vec3(p,1.));
hit h = trace(ro,rd);
vec3 c = vec3(1);
if(h.d<MAXD) {
//lighting. idk bro i'm just throwing math at the wall and seeing what sticks i should have gone to bed 45 minutes ago
c = h.c*(clamp(.5+.25*dot(h.n,vec3(0,1,0)),0.,1.))*(.5+.5*h.n.z)+clamp(dot(h.n,ld),.0,1.);
hit r = trace(ro+rd*(h.d-1e-4),reflect(h.n,-rd));
if(r.d < MAXD) c = c*.75+(r.c*(clamp(.5+.25*dot(r.n,vec3(0,1,0)),0.,1.))*(.5+.5*r.n.z)+clamp(dot(r.n,ld),.0,1.))*.75;
}
col += pow(c,vec3(2.));
}
col/=float(AA)*float(AA);
gl_FragColor = vec4(col,1);
}
`]
function draw() {
if(((t/10)|0) != (((t+deltaTime/1000)/10)|0)) {gen()}
t+=deltaTime/1000.;
scale(w*.5,h*.5);
background(0);
sh.setUniform('t', (t/10)%1.);
rect(0,0,0);
}
function setup() {
pixelDensity(quality);
w = Math.min(windowWidth, windowHeight*rat);
h = w/rat;
colorMode(RGB, 1, 1, 1, 1);
setAttributes('antialias', true);
canvas = createCanvas(w,h, WEBGL);
sh = new p5.Shader(undefined, shSrc);
shader(sh);
gen();
sh.setUniform('res', [1/w,1/h]);
}
function windowResized() {
w = Math.min(windowWidth, windowHeight*rat);
h = w/rat;
resizeCanvas(w, h);
}