Applications

This is hard! It involves raymarching repeating gothic architecture (instancing towers across an infinite grid with gothic silhouettes and w…

This is hard! It involves raymarching repeating gothic architecture (instancing towers across an infinite grid with gothic silhouettes and windows), a displaced ocean surface with believable wave moti

DGX agentx-post
applicationsethan-mollick--x

This is hard! It involves raymarching repeating gothic architecture (instancing towers across an infinite grid with gothic silhouettes and windows), a displaced ocean surface with believable wave motion, and stormy atmospheric lighting/fog to tie it together... and doing this all with no textures or external assets, just math. Here is the full code: /* D R O W N E D C A T H E D R A L C I T Y · mk2 — (classic) / precision highp float; uniform vec2 resolution; uniform vec2 mouse; uniform float time; #define CITY_STEPS 90 #define WATER_STEPS 60 #define REFL_STEPS 20 #define VOL_STEPS 18 #define FAR 200.0 #define CELL 8.0 #define CREST_K 2.2 // wave crest sharpness (1 = round sine, higher = choppier) #define WIN_LIT 0.46 // fraction of windows lit (lower = more lit) #define BEAM_BRIGHT 0.07 // lighthouse beam intensity #define BEAM_SPEED 0.50 // beam rotation speed const vec3 SUN = vec3(-0.548, 0.685, -0.480); / ---------- hash / noise ---------- / float hash21(vec2 p){ p=fract(pvec2(123.34,345.45)); p+=dot(p,p+34.345); return fract(p.xp.y); } float hash11(float n){ return fract(sin(n)43758.5453123); } float noise(vec2 p){ vec2 i=floor(p), f=fract(p); f=ff(3.0-2.0f); return mix(mix(hash21(i),hash21(i+vec2(1,0)),f.x), mix(hash21(i+vec2(0,1)),hash21(i+vec2(1,1)),f.x),f.y); } float fbm(vec2 p){ float v=0.0,a=0.5; mat2 m=mat2(1.6,1.2,-1.2,1.6); for(int i=0;i<4;i++){ v+=anoise(p); p=mp; a=0.5; } return v; } /* ---------- ocean ---------- / float crest(float s){ float u=0.5+0.5s; u=pow(u,CREST_K); return u2.0-1.0; } float oceanLow(vec2 p){ float t=time, h=0.0; h += 0.95crest(sin(dot(p,vec2( 0.16, 0.11))+t1.10)); h += 0.70crest(sin(dot(p,vec2(-0.12, 0.21))-t0.90)); h += 0.50crest(sin(dot(p,vec2( 0.09,-0.17))+t0.75)); h += 0.30crest(sin(dot(p,vec2( 0.23, 0.06))+t1.35)); return h; } float oceanHigh(vec2 p){ float h=oceanLow(p); h += (fbm(p0.35+vec2(0.0,time0.18))-0.5)0.75; h += (fbm(p0.90-vec2(time0.16,0.0))-0.5)0.30; h += (fbm(p2.10+vec2(time0.25,0.0))-0.5)0.12; return h; } / ---------- sdf ---------- / float sdBox(vec3 p, vec3 b){ vec3 q=abs(p)-b; return length(max(q,0.0))+min(max(q.x,max(q.y,q.z)),0.0); } float sdCappedCone(vec3 p, float h, float r1, float r2){ vec2 q=vec2(length(p.xz),p.y); vec2 k1=vec2(r2,h), k2=vec2(r2-r1,2.0h); vec2 ca=vec2(q.x-min(q.x,(q.y<0.0)?r1:r2),abs(q.y)-h); vec2 cb=q-k1+k2clamp(dot(k1-q,k2)/dot(k2,k2),0.0,1.0); float s=(cb.x<0.0&&ca.y<0.0)?-1.0:1.0; return ssqrt(min(dot(ca,ca),dot(cb,cb))); } float towerSDF(vec3 p){ vec2 cid=floor(p.xz/CELL); vec2 local=mod(p.xz,CELL)-0.5CELL; vec3 q=vec3(local.x,p.y,local.y); float r0=hash21(cid), r1=hash21(cid+5.7); float halfW=0.85+r10.75; float botY=-9.0, topY, spireH; if(r0<0.18){ topY=-5.0; spireH=0.0; } else { topY=1.0+r015.0; spireH=2.0+r15.5; } float cy=0.5(botY+topY), halfH=0.5*(topY-botY); float s1=sdBox(q-vec3(0.0,cy,0.0),vec3(halfW, halfH,halfW0.55)); float s2=sdBox(q-vec3(0.0,cy,0.0),vec3(halfW0.55, halfH,halfW)); float d=min(s1,s2); if(spireH>0.0){ d=min(d,sdCappedCone(q-vec3(0.0,topY+spireH0.5,0.0),spireH0.5,halfW1.0,0.04)); float pin=1.0+r11.6; // 4 corner pinnacles (abs-mirrored) vec3 qc=vec3(abs(q.x)-halfW0.72, q.y-(topY+pin0.5), abs(q.z)-halfW0.72); d=min(d,sdCappedCone(qc,pin0.5,0.16,0.02)); } return d; } vec3 calcNormal(vec3 p){ vec2 e=vec2(0.01,0.0); return normalize(vec3(towerSDF(p+e.xyy)-towerSDF(p-e.xyy), towerSDF(p+e.yxy)-towerSDF(p-e.yxy), towerSDF(p+e.yyx)-towerSDF(p-e.yyx))); } vec3 waterNormal(vec3 p){ float e=0.10, h=oceanHigh(p.xz); return normalize(vec3(h-oceanHigh(p.xz+vec2(e,0.0)), e, h-oceanHigh(p.xz+vec2(0.0,e)))); } /* ---------- gothic window: pointed (equilateral) arch + tracery + glow ---------- / vec3 windowField(vec2 f){ // f in [-0.5,0.5]; returns (pane, glow, glassArea) float halfW=0.30, bottom=-0.42, sh=0.02, A=0.38, e=0.012; float c=(AA-halfWhalfW)/(2.0halfW), R=halfW+c; float rx=smoothstep(halfW,halfW-e,abs(f.x)); float ry=smoothstep(bottom-e,bottom,f.y)smoothstep(sh,sh-e,f.y); float rect=rxry; float dl=(f.x+c)(f.x+c)+(f.y-sh)(f.y-sh); float dr=(f.x-c)(f.x-c)+(f.y-sh)(f.y-sh); float arch=smoothstep(RR,RR-0.02,max(dl,dr))smoothstep(sh-e,sh,f.y); float glassA=clamp(max(rect,arch),0.0,1.0); float mull=smoothstep(0.018,0.030,abs(f.x)); // mullion float tran=smoothstep(0.016,0.028,abs(f.y-(sh-0.18))); // transom float pane=glassAmin(mull,tran); float gr=length(vec2(f.x/0.34,f.y/0.42)); return vec3(pane, exp(-grgr1.3), glassA); } /* ---------- storm: full-scene flash + forked bolt event ---------- / vec3 stormStrike(){ // (flashIntensity, boltX, boltLife) float t=time, flash=0.0, bx=0.0, blife=0.0; for(int i=0;i<3;i++){ float fi=float(i), sp=0.31+fi0.11, ph=tsp+fi7.0, seg=floor(ph); if(hash11(seg+fi23.1)>0.80){ float fr=fract(ph); float env=exp(-fr12.0)+0.5exp(-abs(fr-0.08)20.0)+0.25exp(-abs(fr-0.16)18.0); env=0.6+0.4hash11(seg1.7+fi); flash+=env; if(env>blife){ blife=env; bx=(hash11(seg3.3+fi5.0)2.0-1.0)0.9; } } } return vec3(clamp(flash,0.0,1.5),bx,clamp(blife,0.0,1.0)); } / ---------- sky ---------- / vec3 skyColor(vec3 rd, vec3 strike){ float fl=strike.x, y=clamp(rd.y,-0.3,1.0); vec3 col=mix(vec3(0.090,0.100,0.130),vec3(0.020,0.030,0.050),smoothstep(0.0,0.65,y)); vec2 sp=rd.xz/(abs(rd.y)+0.20); float c1=fbm(sp0.6+vec2(time0.015,time0.006)); float c2=fbm(sp1.4-vec2(time0.010,0.0)); float clouds=smoothstep(0.45,1.05,c10.7+c20.5); col=mix(col,mix(vec3(0.045,0.055,0.075),vec3(0.120,0.130,0.160),c2),clouds*(1.0-smoothstep(0.0,0.55,y))); float md=pow(clamp(dot(rd,normalize(vec3(0.30,0.55,0.60))),0.0,1.0),60.0); col+=vec3(0.20,0.22,0.28)md(1.0-clouds0.8); col+=flmix(vec3(0.35,0.42,0.65),vec3(0.65,0.70,0.95),clouds) (0.30+0.70clouds)(0.40+0.60(1.0-smoothstep(0.0,0.70,y))); col+=vec3(0.090,0.100,0.130)pow(1.0-clamp(abs(rd.y)/0.40,0.0,1.0),3.0)0.40; return col; } / ---------- stone ---------- / vec3 stoneAlbedo(vec3 p, vec3 n){ vec3 base=vec3(0.115,0.125,0.140); base=mix(vec3(0.88,0.92,1.0),vec3(1.08,1.0,0.88),hash21(floor(p.xz/CELL)+2.3)); vec2 suv=(abs(n.x)>abs(n.z))?vec2(p.z,p.y):vec2(p.x,p.y); base=1.0-0.45*(1.0-smoothstep(0.0,0.16,abs(fract(suv.x2.2+0.5)-0.5))); // colonettes base=1.0-0.28*(1.0-smoothstep(0.0,0.10,abs(fract(p.y0.45)-0.5))); // string courses base=0.72+0.5fbm(p.xz0.6+p.y0.25); // grime base=0.85+0.3noise(suv6.0); // fine grain return base; } vec3 shadeStone(vec3 p, vec3 rd, vec3 strike){ float flash=strike.x; vec3 n=calcNormal(p), alb=stoneAlbedo(p,n); float rel=p.y-oceanLow(p.xz); float wet=smoothstep(1.6,-0.3,rel); float algae=smoothstep(2.4,-0.6,rel)smoothstep(-1.8,0.4,rel)(0.5+0.5fbm(p.xz1.6+p.y0.5)); alb=mix(alb,vec3(0.05,0.11,0.06),clamp(algae0.7,0.0,1.0)); alb*=mix(1.0,0.55,wet); float dif=clamp(dot(n,SUN),0.0,1.0), sky=clamp(0.5+0.5n.y,0.0,1.0); vec3 col=alb0.16 + albskyvec3(0.16,0.20,0.28) + albdifvec3(0.5,0.5,0.55); float fres=pow(1.0-clamp(dot(n,-rd),0.0,1.0),4.0); col+=fresvec3(0.12,0.15,0.20)(0.5+0.5wet); float sp=pow(clamp(dot(n,normalize(SUN-rd)),0.0,1.0),mix(30.0,120.0,wet)); col+=spmix(0.15,0.9,wet)vec3(0.55,0.62,0.72); col+=flashalbvec3(0.6,0.7,1.0)(0.3+0.7dif+0.5fres); // windows float faceMask=smoothstep(0.55,0.22,abs(n.y)); vec2 suv=(abs(n.x)>abs(n.z))?vec2(p.z,p.y):vec2(p.x,p.y); vec2 sc=suv/vec2(1.15,1.55), cell=floor(sc), f=fract(sc)-0.5; vec2 axisSeed=(abs(n.x)>abs(n.z))?vec2(11.0,3.0):vec2(2.0,17.0); vec2 cidp=floor(p.xz/CELL); vec3 win=windowField(f); float lsel=hash21(cell+cidp3.7+axisSeed); float lit=step(WIN_LIT,lsel); vec3 wcol=vec3(1.0,0.60,0.26); float v1=hash21(cell+cidp7.1+axisSeed); if(v1>0.90) wcol=vec3(0.45,0.80,1.0); else if(v1>0.82) wcol=vec3(0.5,1.0,0.62); float flick=0.78+0.18sin(time(2.5+lsel5.0)+lsel60.0)+0.06sin(time19.0+lsel200.0); float gate=faceMasksmoothstep(-1.0,1.6,p.y)(1.0-wet0.7); col=mix(col,col0.30,win.zgate*(1.0-lit)); col+=win.zgate(1.0-lit)vec3(0.02,0.03,0.05); float e=gatelitflick; col+=win.xewcol2.4 + win.yewcol0.65; return col; } / ---------- marchers ---------- / float marchCity(vec3 ro, vec3 rd){ float t=0.05; for(int i=0;i<CITY_STEPS;i++){ float d=towerSDF(ro+rdt); if(d<0.002t) return t; t+=clamp(d,0.02,2.0); if(t>FAR) break; } return -1.0; } float marchCityRefl(vec3 ro, vec3 rd){ float t=0.1; for(int i=0;i<REFL_STEPS;i++){ float d=towerSDF(ro+rdt); if(d<0.01t) return t; t+=clamp(d,0.05,3.0); if(t>80.0) break; } return -1.0; } float marchWater(vec3 ro, vec3 rd){ float t=0.05,lastT=t; for(int i=0;i<WATER_STEPS;i++){ vec3 p=ro+rdt; float h=oceanLow(p.xz); if(p.y-h<0.0){ float a=lastT,b=t; for(int j=0;j<6;j++){ float m=0.5*(a+b); vec3 pm=ro+rdm; if(pm.y-oceanLow(pm.xz)<0.0) b=m; else a=m; } return 0.5(a+b); } lastT=t; t+=max(0.12,(p.y-h)0.5); if(t>FAR) break; } return -1.0; } / ---------- water ---------- / vec3 shadeWater(vec3 p, vec3 rd, vec3 strike){ float flash=strike.x; vec3 n=waterNormal(p), rfl=reflect(rd,n); rfl.y=max(rfl.y,0.02); vec3 rcol; float tr=marchCityRefl(p+n0.05,rfl); if(tr>0.0) rcol=mix(shadeStone(p+rfltr,rfl,strike),skyColor(rfl,strike),1.0-exp(-tr0.06)); else rcol=skyColor(rfl,strike); float crestH=oceanHigh(p.xz); vec3 tcol=vec3(0.012,0.030,0.040)+vec3(0.02,0.10,0.10)smoothstep(0.4,1.6,crestH); // murk + crest glow float fres=0.02+0.98pow(1.0-clamp(dot(n,-rd),0.0,1.0),5.0); vec3 col=mix(tcol,rcol,fres); float fn=fbm(p.xz1.3+time0.25), fn2=fbm(p.xz3.0-time0.4); float crestFoam=smoothstep(1.2,2.2,crestH)smoothstep(0.35,0.85,fn); float streak=smoothstep(0.6,0.95,fn2)smoothstep(0.8,1.6,crestH)0.5; float baseFoam=(1.0-smoothstep(0.0,1.0,towerSDF(vec3(p.x,0.0,p.z))))(0.5+0.5fn)0.9; float foam=clamp(max(max(crestFoam,streak),baseFoam),0.0,1.0)(0.6+0.4fn2); col=mix(col,vec3(0.78,0.82,0.85),foam); float spe=pow(clamp(dot(n,normalize(SUN-rd)),0.0,1.0),220.0); col+=spe*(0.4+3.0flash)vec3(0.8,0.85,1.0); col+=flashfresvec3(0.3,0.4,0.6); return col; } /* ---------- volumetrics: sweeping beacon + rain + lightning haze ---------- / vec3 volumetrics(vec3 ro, vec3 rd, float tmax, vec3 strike){ float vmax=min(tmax,90.0), dt=vmax/float(VOL_STEPS); float t=dt(0.5+0.5hash21(gl_FragCoord.xy)); float ang=timeBEAM_SPEED; vec3 bdir=normalize(vec3(cos(ang),-0.16,sin(ang))); vec3 bpos=vec3(7.0,23.0,ro.z+34.0), acc=vec3(0.0); for(int i=0;i<VOL_STEPS;i++){ if(t>vmax) break; vec3 q=ro+rdt; float rfog=0.010+0.030exp(-max(q.y+2.0,0.0)0.10); vec3 toq=q-bpos; float along=dot(toq,bdir); if(along>0.5){ vec3 perp=toq-alongbdir; float beam=exp(-dot(perp,perp)/(0.6+alongalong0.012))exp(-along0.018); beam*=0.5+0.9fbm(q.xz0.6+q.y0.4+vec2(0.0,time1.6)); beam*=smoothstep(-2.0,16.0,q.y); acc+=beamvec3(1.0,0.92,0.72)BEAM_BRIGHTdt; } acc+=strike.xrfogvec3(0.45,0.55,0.80)0.5dt; acc+=rfogvec3(0.05,0.06,0.08)0.3dt; t+=dt; } return acc; } /* ---------- rain / bolt / tonemap ---------- / float rainLayer(vec2 uv, float sc, float sp){ uv=sc; uv.x+=uv.y0.25; uv.y+=timesp; uv.x+=floor(uv.y); vec2 f=fract(uv); return (1.0-smoothstep(0.0,0.06,abs(f.x-0.5)))smoothstep(0.0,0.2,f.y)(1.0-smoothstep(0.55,1.0,f.y))step(0.72,hash21(floor(uv))); } float boltGlow(vec2 uv, vec3 strike){ float life=strike.z; if(life<0.02) return 0.0; float bx=strike.y, seedA=bx7.0+3.1; float jit=(fbm(vec2(uv.y3.5+seedA,seedA))-0.5)0.6 +(fbm(vec2(uv.y11.0+seedA2.0,seedA0.7))-0.5)0.22 +(fbm(vec2(uv.y26.0+seedA,seedA1.3))-0.5)0.08; float xp=bx+jit; float g=(0.010/(abs(uv.x-xp)+0.010))smoothstep(-0.9,0.6,uv.y); if(uv.y<0.1){ float jit2=(fbm(vec2(uv.y9.0+seedA3.0,seedA4.0))-0.5)0.5; g+=(0.006/(abs(uv.x-(xp+(0.1-uv.y)1.2+jit2))+0.006))0.6; // fork } return glife1.4; } vec3 aces(vec3 x){ return clamp((x(2.51x+0.03))/(x*(2.43x+0.59)+0.14),0.0,1.0); } void main(){ vec2 uv=(gl_FragCoord.xy2.0-resolution.xy)/resolution.y; vec3 strike=stormStrike(); float ct=time; vec3 ro=vec3(0.7sin(ct0.25),4.6,ct3.0); ro.y+=oceanLow(ro.xz)0.30+sin(ct0.8)0.18; float pitch=-0.9+0.6sin(ct0.12); vec3 ta=ro+vec3(0.6sin(ct0.16),pitch,4.0); vec3 f=normalize(ta-ro), r=normalize(cross(vec3(0,1,0),f)), u=cross(f,r); float roll=sin(ct0.45)0.05+sin(ct0.21)0.025; vec3 rr=rcos(roll)+usin(roll), uu=ucos(roll)-rsin(roll); vec3 rd=normalize(uv.xrr+uv.yuu+1.25f); float tC=marchCity(ro,rd); float tW=(rd.y<0.0)?marchWater(ro,rd):-1.0; vec3 col; float dist; if(tC>0.0 && (tW<0.0||tC<tW)){ col=shadeStone(ro+rdtC,rd,strike); dist=tC; } else if(tW>0.0) { col=shadeWater(ro+rdtW,rd,strike); dist=tW; } else { col=skyColor(rd,strike); dist=FAR; } col=mix(col,skyColor(rd,strike),1.0-exp(-dist0.016)); // fog col+=volumetrics(ro,rd,dist,strike); // beam + rain + haze col+=(rainLayer(uv,14.0,16.0)0.8+rainLayer(uv1.7,22.0,22.0)0.5)0.03vec3(0.8,0.85,0.95); col+=boltGlow(uv,strike)vec3(0.75,0.82,1.0); // visible bolt col=1.04; col=mix(col,colvec3(0.9,1.0,1.04),0.3); // cool the shadows col*=1.0-0.18*dot(uv,uv); // vignette col=aces(col); col=pow(col,vec3(0.95)); gl_FragColor=vec4(col,1.0); }

Source: Ethan Mollick (X) | 2026-05-28

Loading related sources…