Opus 4.8 Generates Complex Neo-Gothic Ocean Shader in One Shot
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(p*vec2(123.34,345.45)); p+=dot(p,p+34.345); return fract(p.x*p.y); } float hash11(float n){ return fract(sin(n)*43758.5453123); } float noise(vec2 p){ vec2 i=floor(p), f=fract(p); f=f*f*(3.0-2.0*f); 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+=a*noise(p); p=m*p; a*=0.5; } return v; }
/* ---------- ocean ---------- */ float crest(float s){ float u=0.5+0.5*s; u=pow(u,CREST_K); return u*2.0-1.0; } float oceanLow(vec2 p){ float t=time, h=0.0; h += 0.95*crest(sin(dot(p,vec2( 0.16, 0.11))+t*1.10)); h += 0.70*crest(sin(dot(p,vec2(-0.12, 0.21))-t*0.90)); h += 0.50*crest(sin(dot(p,vec2( 0.09,-0.17))+t*0.75)); h += 0.30*crest(sin(dot(p,vec2( 0.23, 0.06))+t*1.35)); return h; } float oceanHigh(vec2 p){ float h=oceanLow(p); h += (fbm(p*0.35+vec2(0.0,time*0.18))-0.5)*0.75; h += (fbm(p*0.90-vec2(time*0.16,0.0))-0.5)*0.30; h += (fbm(p*2.10+vec2(time*0.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.0*h); vec2 ca=vec2(q.x-min(q.x,(q.y<0.0)?r1:r2),abs(q.y)-h); vec2 cb=q-k1+k2*clamp(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 s*sqrt(min(dot(ca,ca),dot(cb,cb))); } float towerSDF(vec3 p){ vec2 cid=floor(p.xz/CELL); vec2 local=mod(p.xz,CELL)-0.5*CELL; vec3 q=vec3(local.x,p.y,local.y); float r0=hash21(cid), r1=hash21(cid+5.7); float halfW=0.85+r1*0.75; float botY=-9.0, topY, spireH; if(r0<0.18){ topY=-5.0; spireH=0.0; } else { topY=1.0+r0*15.0; spireH=2.0+r1*5.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,halfW*0.55)); float s2=sdBox(q-vec3(0.0,cy,0.0),vec3(halfW*0.55, halfH,halfW)); float d=min(s1,s2); if(spireH>0.0){ d=min(d,sdCappedCone(q-vec3(0.0,topY+spireH*0.5,0.0),spireH*0.5,halfW*1.0,0.04)); float pin=1.0+r1*1.6; // 4 corner pinnacles (abs-mirrored) vec3 qc=vec3(abs(q.x)-halfW*0.72, q.y-(topY+pin*0.5), abs(q.z)-halfW*0.72); d=min(d,sdCappedCone(qc,pin*0.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=(A*A-halfW*halfW)/(2.0*halfW), 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=rx*ry; 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(R*R,R*R-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=glassA*min(mull,tran); float gr=length(vec2(f.x/0.34,f.y/0.42)); return vec3(pane, exp(-gr*gr*1.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+fi*0.11, ph=t*sp+fi*7.0, seg=floor(ph); if(hash11(seg+fi*23.1)>0.80){ float fr=fract(ph); float env=exp(-fr*12.0)+0.5*exp(-abs(fr-0.08)*20.0)+0.25*exp(-abs(fr-0.16)*18.0); env*=0.6+0.4*hash11(seg*1.7+fi); flash+=env; if(env>blife){ blife=env; bx=(hash11(seg*3.3+fi*5.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(sp*0.6+vec2(time*0.015,time*0.006)); float c2=fbm(sp*1.4-vec2(time*0.010,0.0)); float clouds=smoothstep(0.45,1.05,c1*0.7+c2*0.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-clouds*0.8); col+=fl*mix(vec3(0.35,0.42,0.65),vec3(0.65,0.70,0.95),clouds) *(0.30+0.70*clouds)*(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.x*2.2+0.5)-0.5))); // colonettes base*=1.0-0.28*(1.0-smoothstep(0.0,0.10,abs(fract(p.y*0.45)-0.5))); // string courses base*=0.72+0.5*fbm(p.xz*0.6+p.y*0.25); // grime base*=0.85+0.3*noise(suv*6.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.5*fbm(p.xz*1.6+p.y*0.5)); alb=mix(alb,vec3(0.05,0.11,0.06),clamp(algae*0.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.5*n.y,0.0,1.0); vec3 col=alb*0.16 + alb*sky*vec3(0.16,0.20,0.28) + alb*dif*vec3(0.5,0.5,0.55); float fres=pow(1.0-clamp(dot(n,-rd),0.0,1.0),4.0); col+=fres*vec3(0.12,0.15,0.20)*(0.5+0.5*wet); float sp=pow(clamp(dot(n,normalize(SUN-rd)),0.0,1.0),mix(30.0,120.0,wet)); col+=sp*mix(0.15,0.9,wet)*vec3(0.55,0.62,0.72); col+=flash*alb*vec3(0.6,0.7,1.0)*(0.3+0.7*dif+0.5*fres); // 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+cidp*3.7+axisSeed); float lit=step(WIN_LIT,lsel); vec3 wcol=vec3(1.0,0.60,0.26); float v1=hash21(cell+cidp*7.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.18*sin(time*(2.5+lsel*5.0)+lsel*60.0)+0.06*sin(time*19.0+lsel*200.0); float gate=faceMask*smoothstep(-1.0,1.6,p.y)*(1.0-wet*0.7); col=mix(col,col*0.30,win.z*gate*(1.0-lit)); col+=win.z*gate*(1.0-lit)*vec3(0.02,0.03,0.05); float e=gate*lit*flick; col+=win.x*e*wcol*2.4 + win.y*e*wcol*0.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+rd*t); if(d<0.002*t) 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+rd*t); if(d<0.01*t) 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+rd*t; 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+rd*m; 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+n*0.05,rfl); if(tr>0.0) rcol=mix(shadeStone(p+rfl*tr,rfl,strike),skyColor(rfl,strike),1.0-exp(-tr*0.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.98*pow(1.0-clamp(dot(n,-rd),0.0,1.0),5.0); vec3 col=mix(tcol,rcol,fres); float fn=fbm(p.xz*1.3+time*0.25), fn2=fbm(p.xz*3.0-time*0.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.5*fn)*0.9; float foam=clamp(max(max(crestFoam,streak),baseFoam),0.0,1.0)*(0.6+0.4*fn2); 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.0*flash)*vec3(0.8,0.85,1.0); col+=flash*fres*vec3(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.5*hash21(gl_FragCoord.xy)); float ang=time*BEAM_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+rd*t; float rfog=0.010+0.030*exp(-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-along*bdir; float beam=exp(-dot(perp,perp)/(0.6+along*along*0.012))*exp(-along*0.018); beam*=0.5+0.9*fbm(q.xz*0.6+q.y*0.4+vec2(0.0,time*1.6)); beam*=smoothstep(-2.0,16.0,q.y); acc+=beam*vec3(1.0,0.92,0.72)*BEAM_BRIGHT*dt; } acc+=strike.x*rfog*vec3(0.45,0.55,0.80)*0.5*dt; acc+=rfog*vec3(0.05,0.06,0.08)*0.3*dt; t+=dt; } return acc; }
/* ---------- rain / bolt / tonemap ---------- */ float rainLayer(vec2 uv, float sc, float sp){ uv*=sc; uv.x+=uv.y*0.25; uv.y+=time*sp; 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=bx*7.0+3.1; float jit=(fbm(vec2(uv.y*3.5+seedA,seedA))-0.5)*0.6 +(fbm(vec2(uv.y*11.0+seedA*2.0,seedA*0.7))-0.5)*0.22 +(fbm(vec2(uv.y*26.0+seedA,seedA*1.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.y*9.0+seedA*3.0,seedA*4.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 g*life*1.4; } vec3 aces(vec3 x){ return clamp((x*(2.51*x+0.03))/(x*(2.43*x+0.59)+0.14),0.0,1.0); }
void main(){ vec2 uv=(gl_FragCoord.xy*2.0-resolution.xy)/resolution.y; vec3 strike=stormStrike(); float ct=time;
vec3 ro=vec3(0.7*sin(ct*0.25),4.6,ct*3.0); ro.y+=oceanLow(ro.xz)*0.30+sin(ct*0.8)*0.18; float pitch=-0.9+0.6*sin(ct*0.12); vec3 ta=ro+vec3(0.6*sin(ct*0.16),pitch,4.0); vec3 f=normalize(ta-ro), r=normalize(cross(vec3(0,1,0),f)), u=cross(f,r); float roll=sin(ct*0.45)*0.05+sin(ct*0.21)*0.025; vec3 rr=r*cos(roll)+u*sin(roll), uu=u*cos(roll)-r*sin(roll); vec3 rd=normalize(uv.x*rr+uv.y*uu+1.25*f);
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+rd*tC,rd,strike); dist=tC; } else if(tW>0.0) { col=shadeWater(ro+rd*tW,rd,strike); dist=tW; } else { col=skyColor(rd,strike); dist=FAR; }
col=mix(col,skyColor(rd,strike),1.0-exp(-dist*0.016)); // fog col+=volumetrics(ro,rd,dist,strike); // beam + rain + haze col+=(rainLayer(uv,14.0,16.0)*0.8+rainLayer(uv*1.7,22.0,22.0)*0.5)*0.03*vec3(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,col*vec3(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); }
I had early access to Opus 4.8. Was impressed by it. Here is Opus 4.8's one shot of "create a visually interesting shader that can run in twigl, make it like an infinite city of neo-gothic towers partially drowned in a stormy ocean with large waves" (this is all done with math)