{
  "license": "CC0-1.0",
  "shader": {
    "id": "r3cadihe",
    "title": "Sandwich Supernova",
    "license": "CC0-1.0",
    "provenance_type": "witnessed_generation",
    "made_with": "glslop",
    "status": "active",
    "compile_ok": true,
    "render_hash": "187c7a73fc7e3c6a",
    "tags": [
      "raymarch",
      "3d",
      "food",
      "sandwich",
      "sdf",
      "dof",
      "multipass"
    ],
    "parents": [],
    "author": "zazi5inxhmgi",
    "author_name": "ufffd",
    "author_url": "/u/ufffd",
    "created_at": "2026-09-18T21:11:58.329Z",
    "poster_url": "/posters/r3cadihe.avif",
    "stats": {
      "slops": 1,
      "views": 98,
      "forks": 0
    },
    "self": "/api/v1/shaders/r3cadihe",
    "isf": "/*{\n  \"DESCRIPTION\": \"Sandwich Supernova - a raymarched, spinning sandwich (pick a recipe and a bread, or let it cycle) while its own ingredients burst out of a tunnel behind it, one ingredient per seeded wedge of the burst. Depth of field in a second pass.\",\n  \"CREDIT\": \"Claude Fable 5.1\",\n  \"CATEGORIES\": [\"generator\", \"3d\", \"food\"],\n  \"INPUTS\": [\n    { \"NAME\": \"sandwich\", \"TYPE\": \"long\", \"DEFAULT\": 0, \"VALUES\": [0,1,2,3,4,5,6,7,8],\n      \"LABELS\": [\"Auto cycle\",\"BLT\",\"Club\",\"Cheeseburger\",\"Ham & Swiss on rye\",\"Veggie\",\"Breakfast bun\",\"PB&J\",\"Chef's random (seed)\"] },\n    { \"NAME\": \"bread\", \"TYPE\": \"long\", \"DEFAULT\": 0, \"VALUES\": [0,1,2,3,4,5],\n      \"LABELS\": [\"Recipe default\",\"White\",\"Whole wheat\",\"Marble rye\",\"Sesame bun\",\"Toasted white\"] },\n    { \"NAME\": \"seed\", \"TYPE\": \"float\", \"DEFAULT\": 3.0, \"MIN\": 0.0, \"MAX\": 100.0 },\n    { \"NAME\": \"spin\", \"TYPE\": \"float\", \"DEFAULT\": 0.45, \"MIN\": -2.0, \"MAX\": 2.0 },\n    { \"NAME\": \"burstSpeed\", \"TYPE\": \"float\", \"DEFAULT\": 1.0, \"MIN\": 0.0, \"MAX\": 3.0 },\n    { \"NAME\": \"explode\", \"TYPE\": \"float\", \"DEFAULT\": 0.0, \"MIN\": 0.0, \"MAX\": 1.0 },\n    { \"NAME\": \"backdrop\", \"TYPE\": \"color\", \"DEFAULT\": [0.03, 0.40, 0.52, 1.0] }\n  ],\n  \"PASSES\": [ { \"TARGET\": \"tab\", \"FLOAT\": true, \"FILTER\": \"NEAREST\" }, { \"TARGET\": \"scene\", \"FLOAT\": true }, { } ]\n}*/\n\n// Everything edible here is ONE generic \"slab\" SDF (footprint x thickness, with folds, rings,\n// holes, waves, droop and a dome) driven by a parameter block per ingredient; all the character\n// lives in the once-per-pixel material function. That keeps the unrolled march loops small.\n//\n// The sandwich is a y-stack, so the map only evaluates the layer whose y-interval contains p and\n// clamps the step to the ray's exit from that interval. The burst does the same with (ring, z) cells\n// of a polar tunnel: the camera sits on the tunnel axis, so a ray never changes angular sector.\n\n#define TAU 6.2831853\n#define CAMZ 7.0\n#define FOCAL 1.65\n#define R0 1.9\n#define RW 2.2\n#define SZ 5.6\n#define ZFAR 52.0\n#define PERIOD 7.5\n\n// ---- per-frame scene description (built once per pixel in setup) ----\nfloat gNW;                                               // number of burst wedges (unique ingredients)\nvec4 gSelA, gSelB;                                       // layer thresholds as read back by the scene pass\nmat4 gCM; vec4 gCE, gCF; vec3 gQ; float gSc;             // parameter block + local point of the last map call\nfloat gYlo, gYhi;                                        // current y extent of the (possibly mid-cascade) stack\nfloat gFront, gFd; vec3 gWA, gWB;                        // morph front along the tunnel; (count, rotation, flip) of the old / new wedge sets\nmat3 gSR; vec3 gPos; float gPop;                         // sandwich rotation / position / pop scale\nvec2 gSw; float gZ; float gSeedB; float gWRot; float gWFlip;\n\n// ---- marching state + captures written by the last slab() call ----\nvec3 gRd; float gClamp; float gDs;\nvec3 gLp; float gD2, gSide, gYs, gMat, gSd;\nmat3 gR;\n\nfloat h11(float n){ return fract(sin(n*12.9898)*43758.5453); }\nfloat h21(vec2 p){ p=fract(p*vec2(123.34,456.21)); p+=dot(p,p+45.32); return fract(p.x*p.y); }\nfloat h31(vec3 p){ p=fract(p*vec3(.1031,.1030,.0973)); p+=dot(p,p.yzx+33.33); return fract((p.x+p.y)*p.z); }\nfloat vnoise(vec3 x){\n  vec3 i=floor(x), f=fract(x); f=f*f*(3.-2.*f);\n  return mix(mix(mix(h31(i),h31(i+vec3(1,0,0)),f.x),mix(h31(i+vec3(0,1,0)),h31(i+vec3(1,1,0)),f.x),f.y),\n             mix(mix(h31(i+vec3(0,0,1)),h31(i+vec3(1,0,1)),f.x),mix(h31(i+vec3(0,1,1)),h31(i+vec3(1,1,1)),f.x),f.y),f.z);\n}\nfloat fbm(vec3 p){ return .5*vnoise(p)+.3*vnoise(p*2.03+7.)+.2*vnoise(p*4.1+13.); }\n\n// M = [A: hx,hz,h,roundness | B: waveAmp,waveFreq,edgeGrow,dome(+up/-down) | C: foldX,foldX2,foldZ,intervalBelow | D: yCentre or scale, intervalAbove, material, seed]\n// E = ringSpacing, holeFreq, droop, cornerRadius      F = cos, sin, outlineWobble, domeRadius\nfloat slab(vec3 p, mat4 M, vec4 E, vec4 F){\n  vec4 A=M[0], B=M[1], C=M[2], D=M[3];\n  float py=p.y;\n  p.y*= B.w<0. ? -1. : 1.;\n  float hd=abs(B.w);\n  p.xz=vec2(F.x*p.x-F.y*p.z, F.y*p.x+F.x*p.z);\n  p.xz-=(vec2(fract(D.w*7.31),fract(D.w*13.7))-.5)*.07;\n  if(C.x>0.) p.x=abs(p.x)-C.x;\n  if(C.y>0.) p.x=abs(p.x)-C.y;\n  if(C.z>0.) p.z=abs(p.z)-C.z;\n  float r=length(p.xz);\n  vec2 q=abs(p.xz)-A.xy+E.w;\n  float dB=length(max(q,0.))+min(max(q.x,q.y),0.)-E.w;\n  float dE=(length(p.xz/A.xy)-1.)*min(A.x,A.y);\n  float d2=mix(dB,dE,A.w);\n  float ang=atan(p.z,p.x);\n  d2+=F.z*(sin(ang*5.+D.w*40.)*.6+sin(ang*9.-D.w*23.)*.4);\n  float eg=smoothstep(-.55,0.,d2/min(A.x,A.y));\n  if(E.x>0.) d2=max(d2,abs(mod(r,E.x)-.5*E.x)-.3*E.x);\n  if(E.y>0.){\n    vec2 g=p.xz*E.y+D.w*7.; vec2 ci=floor(g);\n    d2=max(d2,(.1+.3*h21(ci+D.w)-length(g-ci-.5))/E.y);\n  }\n  float w=B.x*sin(B.y*p.z+D.w*30.+1.6*sin(B.y*.55*p.x+D.w*17.))*mix(1.,eg,B.z)+.45*B.x*eg*B.z*sin(B.y*2.3*p.x-D.w*50.+2.*p.z)-E.z*eg*eg;\n  float k=clamp(1.-r*r/(F.w*F.w),0.,1.);\n  float y2=p.y-w-hd*(k-1.)*.5;\n  float th=A.z+hd*k*.5;\n  float er=min(A.z*.9,.05);\n  vec2 w2=vec2(d2,abs(y2)-th)+er;\n  float d=min(max(w2.x,w2.y),0.)+length(max(w2,0.))-er;\n  d=max(d,max(py-D.y,-py-C.w)+.006);\n  gLp=vec3(p.x,y2,p.z); gD2=d2; gSide=smoothstep(-.012,.012,w2.x-w2.y); gYs=y2; gMat=D.z; gSd=D.w;\n  return d;\n}\n\nvoid fetch(float x){\n  gCM=mat4(IMG_PIXEL(tab,vec2(x,.5)),IMG_PIXEL(tab,vec2(x,1.5)),IMG_PIXEL(tab,vec2(x,2.5)),IMG_PIXEL(tab,vec2(x,3.5)));\n  gCE=IMG_PIXEL(tab,vec2(x,4.5)); gCF=IMG_PIXEL(tab,vec2(x,5.5));\n}\n\nfloat mapS(vec3 p){\n  fetch(.5+dot(step(gSelA,vec4(p.y)),vec4(1.))+dot(step(gSelB,vec4(p.y)),vec4(1.)));\n  vec4 D=gCM[3];\n  gQ=p-vec3(0.,D.x,0.);\n  float d=slab(gQ,gCM,gCE,gCF)*.75;\n  gDs=d;\n  if(gClamp>.5){\n    float tb=1e3;\n    if(gRd.y>1e-4) tb=(D.x+D.y-p.y)/gRd.y;\n    else if(gRd.y<-1e-4) tb=(D.x-gCM[2].w-p.y)/gRd.y;\n    if(tb<0.) tb=1e3;\n    d=min(d,tb+.004);\n  }\n  return d;\n}\n\nfloat mapB(vec3 p){\n  p.xy=vec2(gSw.x*p.x-gSw.y*p.y, gSw.y*p.x+gSw.x*p.y);\n  float r=length(p.xy);\n  float j=clamp(floor((r-R0)/RW),0.,2.);\n  float rc=R0+(j+.5)*RW;\n  float ns=6.+4.*j;\n  float a=atan(p.y,p.x)/TAU+.5;\n  float k=min(floor(a*ns),ns-1.);\n  float hl=h21(vec2(k,j)+gSeedB);\n  float zz=p.z-gZ*(.75+.5*hl)+hl*SZ*9.;\n  float m=floor(zz/SZ);\n  float lz=zz-(m+.5)*SZ;\n  float hi=h21(vec2(k+17.*j,m)+gSeedB*1.7);\n  float ac=(k+.5)/ns;\n  // pieces the front has passed already belong to the next sandwich; they shrink to nothing as it crosses them\n  float fd=(m+.5)*SZ+gZ*(.75+.5*hl)-hl*SZ*9.-gFront;\n  vec3 wv= fd<0. ? gWB : gWA;\n  float wa=fract((wv.z>.5?1.-ac:ac)+wv.y);\n  float wi=min(floor(wa*wv.x),wv.x-1.);\n  fetch((fd<0. ? 20.5 : 10.5)+wi);\n  gFd=fd;\n  float th=(ac-.5)*TAU;\n  vec2 er=vec2(cos(th),sin(th));\n  vec3 q=vec3(p.xy-rc*er-vec2(-er.y,er.x)*(hi-.5)*1.3-er*(fract(hi*9.7)-.5)*.4, lz);   // scatter within the cell so lanes don't read as poles\n  float a1=hi*TAU+gZ*.11*(.5+hl)+TIME*(.3+.5*hi), a2=hl*TAU+TIME*(.25+.4*fract(hi*5.3));\n  float c1=cos(a1),s1=sin(a1),c2=cos(a2),s2=sin(a2);\n  gR=mat3(1.,0.,0., 0.,c1,s1, 0.,-s1,c1)*mat3(c2,0.,-s2, 0.,1.,0., s2,0.,c2);\n  gSc=gCM[3].x*(.85+.3*fract(hi*3.1))*max(smoothstep(.2,3.5,abs(fd)),.03);\n  gQ=gR*q/gSc;\n  float d=slab(gQ,gCM,gCE,gCF)*gSc*.75;\n  gDs=d;\n  if(gClamp>.5){\n    float tz=(lz+.5*SZ)/max(-gRd.z,1e-3);\n    float rr=length(gRd.xy);\n    float tr=(R0+(j+1.)*RW-r)/max(rr,1e-4);\n    if(r<R0) tr=(R0-r)/max(rr,1e-4);\n    d=min(d,min(tz,tr)+.02);\n  }\n  return d;\n}\n\n// ---- ingredient table ----\n// 1 lettuce 2 tomato 3 bacon 4 cheddar 5 swiss 6 ham 7 red onion 8 pickle 9 patty 10 fried egg 11 bread(bt)\n// 12 mayo 13 mustard 14 ketchup 15 peanut butter 16 grape jelly\nvoid ingr(float ty, float bt, float pos, float sd, out mat4 M, out vec4 E, out vec4 F){\n  vec4 A=vec4(1.,1.,0.,0.), B=vec4(0.,1.,0.,0.), C=vec4(0.);\n  E=vec4(0.,0.,0.,.05);\n  float ow=0., dr=1., rotA=TAU, rot0=0., mt=ty;\n  vec4 bA=vec4(1.,1.04,.12,0.); float bcr=.22;\n  if(bt<3.5&&bt>2.5){ bA=vec4(1.22,.86,.11,.7); bcr=.3; }\n  else if(bt>3.5&&bt<4.5){ bA=vec4(1.1,1.1,.09,1.); }\n  if(ty<.5){ }\n  else if(ty<1.5){ A=vec4(1.27,1.2,.02,1.); B=vec4(.085,6.2,.8,0.); ow=.1; }\n  else if(ty<2.5){ A=vec4(.47,.47,.06,1.); C=vec4(.47,0.,.47,0.); ow=.008; }\n  else if(ty<3.5){ A=vec4(.2,1.13,.026,0.); B=vec4(.06,8.,0.,0.); C=vec4(.5,.25,0.,0.); E.w=.07; ow=.014; rotA=.25; }\n  else if(ty<4.5){ A=vec4(.9,.9,.022,0.); B=vec4(.012,3.,0.,0.); E=vec4(0.,0.,.05,.04); rotA=.3; rot0=.785; }\n  else if(ty<5.5){ A=vec4(.9,.9,.024,0.); B=vec4(.012,3.,0.,0.); E=vec4(0.,2.4,.05,.04); rotA=.3; rot0=.785; }\n  else if(ty<6.5){ A=vec4(1.12,1.06,.03,1.); B=vec4(.04,4.2,.7,0.); ow=.05; }\n  else if(ty<7.5){ A=vec4(.97,.97,.035,1.); E.x=.2; }\n  else if(ty<8.5){ A=vec4(.2,.2,.03,1.); B=vec4(.012,42.,0.,0.); C=vec4(.5,.25,.3,0.); }\n  else if(ty<9.5){ A=vec4(1.06,1.06,.12,1.); ow=.03; }\n  else if(ty<10.5){ A=vec4(.96,.9,.02,1.); B=vec4(.02,5.,.6,.11); ow=.1; dr=.3; }\n  else if(ty<11.5){\n    mt=20.+bt; rotA=.12; A=bA; E.w=bcr; ow=.012;\n    if(bt>3.5&&bt<4.5){ dr=1.1; B.w= pos>.5 ? .44 : (pos<-.5 ? -.14 : 0.); }\n  }\n  else {\n    float thick= ty>14.5 ? .034 : .016;                       // peanut butter and jelly go on thick\n    A=vec4(bA.xy*(ty>14.5 ? .97 : .9),thick,max(bA.w,.45)); B=vec4(.012,5.,0.,0.); E=vec4(0.,0.,.025,.3); ow=.07; rotA=.2;\n  }\n  float ra=(h11(sd*3.7+1.)-.5)*rotA+rot0;\n  M=mat4(A,B,C,vec4(0.,0.,mt,sd));\n  F=vec4(cos(ra),sin(ra),ow,dr);\n}\n\n// half height of a layer's y-interval (thickness + dome + room for its waves)\nfloat Hof(float ty, float bt, float pos){\n  float h=.132;\n  if(ty<.5) h=0.;\n  else if(ty<1.5) h=.108; else if(ty<2.5) h=.072; else if(ty<3.5) h=.087; else if(ty<4.5) h=.07;\n  else if(ty<5.5) h=.072; else if(ty<6.5) h=.078; else if(ty<7.5) h=.047; else if(ty<8.5) h=.053;\n  else if(ty<9.5) h=.132; else if(ty<10.5) h=.105;\n  else if(ty>14.5) h=.058;\n  else if(ty>11.5) h=.04;\n  else if(bt>2.5&&bt<3.5) h=.122;\n  else if(bt>3.5&&bt<4.5) h=.102+(pos>.5 ? .22 : (pos<-.5 ? .07 : 0.));\n  return h;\n}\n\n// pass 0: the whole scene description as a tiny lookup table. Each texel works out only the one block it holds.\n//   x 0..8 sandwich layers | x 10..17 burst wedges of the outgoing recipe | x 20..27 of the incoming one | x 30 scalars\nvoid tablePass(){\n  vec2 fc=floor(gl_FragCoord.xy);\n  if(fc.x>30.5||fc.y>5.5){ gl_FragColor=vec4(0.); return; }\n  float cyc=floor(TIME/PERIOD), tc=TIME-cyc*PERIOD;\n  bool nextWave= tc>PERIOD-.8;\n  float front=-ZFAR-6.+(nextWave ? tc-(PERIOD-.8) : tc+.8)*42.;\n  bool isA=(fc.x>9.5&&fc.x<18.5)||(fc.x>29.5&&fc.y>2.5&&fc.y<3.5);\n  bool isB=(fc.x>19.5&&fc.x<28.5)||(fc.x>29.5&&fc.y>3.5);\n  float c=cyc;\n  if(isA) c= nextWave ? cyc : cyc-1.;\n  if(isB) c= nextWave ? cyc+1. : cyc;\n  float sel=float(sandwich), sseed=seed, anim=0.;\n  if(sandwich==0){ anim=1.; sel=1.+mod(c,9.); if(sel>7.5){ sel=8.; sseed=seed+c*1.37; } }\n  else front=1e3;\n\n  vec4 P=vec4(12.,1.,2.,3.), Q=vec4(11.,0.,0.,0.);\n  float bt=5.;\n  if(sel<1.5){ }\n  else if(sel<2.5){ P=vec4(12.,6.,2.,11.); Q=vec4(1.,3.,11.,0.); bt=1.; }\n  else if(sel<3.5){ P=vec4(1.,9.,4.,2.); Q=vec4(7.,14.,13.,11.); bt=4.; }\n  else if(sel<4.5){ P=vec4(13.,6.,5.,8.); Q=vec4(1.,11.,0.,0.); bt=3.; }\n  else if(sel<5.5){ P=vec4(12.,1.,2.,7.); Q=vec4(8.,5.,11.,0.); bt=2.; }\n  else if(sel<6.5){ P=vec4(14.,4.,3.,10.); bt=4.; }\n  else if(sel<7.5){ P=vec4(15.,16.,11.,0.); Q=vec4(0.); bt=1.; }\n  else {\n    float nf=2.+floor(h11(sseed+.3)*4.99);\n    vec4 sA=vec4(1.,2.,3.,4.), sB=vec4(5.,6.,7.,8.);\n    vec4 hA=fract(sin((sA*5.1+sseed*1.7)*12.9898)*43758.5453), hB=fract(sin((sB*5.1+sseed*1.7)*12.9898)*43758.5453);\n    vec4 kA=fract(hA*17.3), kB=fract(hB*17.3);\n    vec4 rA=mix(1.+floor(hA*9.99),12.+floor(fract(hA*7.7)*2.99),step(.78,kA));\n    vec4 rB=mix(1.+floor(hB*9.99),12.+floor(fract(hB*7.7)*2.99),step(.78,kB));\n    // slot s holds a filling while s<=nf, the top bread at s==nf+1, nothing above\n    P=mix(rA,vec4(11.),step(nf+.5,sA))*step(sA,vec4(nf+1.5));\n    Q=mix(rB,vec4(11.),step(nf+.5,sB))*step(sB,vec4(nf+1.5));\n    bt=1.+floor(h11(sseed*2.3+9.)*4.99);\n  }\n  if(bread>0) bt=float(bread);\n  float rseed=sseed+sel*11.3+bt*3.1;\n  float T[9];\n  T[0]=11.; T[1]=P.x; T[2]=P.y; T[3]=P.z; T[4]=P.w; T[5]=Q.x; T[6]=Q.y; T[7]=Q.z; T[8]=Q.w;\n  float nl=0.;\n  for(int i=0;i<9;i++) if(T[i]>.5) nl+=1.;\n\n  // walk the stack once: interval of every layer (rest height + cascade offset), and which\n  // ingredient is the w-th unique one. Layers drop in bottom-first and lift off top-first; an\n  // offset can never be less than the layer below's, so intervals stay ordered and a bounce shoves upward.\n  float Sel[9];\n  float seen=0., yc=0., yr=0., offP=0., nw=0.;\n  float ty=0., tpos=0., tyc=0., tUp=0., tLo=0., tsd=.5;\n  float wq=fc.x-(fc.x>19.5 ? 20. : 10.);\n  float eg=explode*.2;\n  for(int i=0;i<9;i++){\n    float fi=float(i);\n    float pos= i==0 ? -1. : (fi>nl-1.5 ? 1. : 0.);\n    float has=step(.5,T[i]);\n    float H=Hof(T[i],bt,pos)+eg*has;\n    float tl=tc-fi*.11;\n    float u=clamp(tl/.62,0.,1.);\n    float off=7.5*(1.-u)*(1.-u)+.1*abs(sin(max(tl-.62,0.)*9.))*exp(-max(tl-.62,0.)*5.);\n    float v=max(tc-(PERIOD-1.1)-(nl-1.-fi)*.07,0.)/.55;\n    off=max((off+9.*v*v)*anim,offP)*has+offP*(1.-has);\n    float s=off-offP; offP=off;\n    Sel[i]= has>.5 ? yc : 1e4;\n    float bit=exp2(T[i]);\n    bool dup= has<.5 || mod(floor(seen/bit+.001),2.)>.5;\n    seen+= dup ? 0. : bit;\n    bool mine= fc.x<9.5 ? abs(fc.x-fi)<.5 : (!dup && abs(wq-nw)<.5);\n    if(mine){ ty=T[i]; tpos=pos; tyc=yc+s+H; tUp=H; tLo=H+s; tsd=h11(rseed+fi*1.9)+.01; }\n    if(!dup) nw+=1.;\n    yc+=s+2.*H; yr+=2.*H;\n  }\n  float yh=.5*yr;\n  mat4 M; vec4 E, F;\n  ingr(ty,bt,tpos,tsd,M,E,F);\n  if(fc.x<9.5) M=mat4(M[0],M[1],vec4(M[2].xyz,tLo),vec4(tyc-yh,tUp,M[3].z,tsd));\n  else {\n    // a wedge of the burst: the same ingredient as one loose piece, normalised in size (sauces fly as dollops)\n    vec4 A=M[0], B=M[1];\n    if(ty>10.5) B.w= (bt>3.5&&bt<4.5) ? .44 : 0.;\n    float sc=clamp(.74/max(A.x,A.y),.74,2.6);\n    if(ty>11.5){ A=vec4(.5,.46,.05,1.); B=vec4(.03,4.,0.,.34); F=vec4(1.,0.,.1,.52); E=vec4(0.,0.,0.,.05); sc=1.05; }\n    M=mat4(A,B,vec4(0.,0.,0.,10.),vec4(sc,10.,M[3].z,tsd));\n    F=vec4(1.,0.,F.z,F.w);\n  }\n  vec4 o=F;\n  if(fc.y<.5) o=M[0]; else if(fc.y<1.5) o=M[1]; else if(fc.y<2.5) o=M[2]; else if(fc.y<3.5) o=M[3];\n  else if(fc.y<4.5) o=E;\n  if(fc.x>29.5){\n    if(fc.y<.5) o=vec4(Sel[1],Sel[2],Sel[3],Sel[4])-yh;\n    else if(fc.y<1.5) o=vec4(Sel[5],Sel[6],Sel[7],Sel[8])-yh;\n    else if(fc.y<2.5) o=vec4(-yh,yc-yh,front,0.);\n    else o=vec4(max(nw,1.),h11(rseed+4.4),h11(rseed+8.8),0.);\n  }\n  gl_FragColor=o;\n}\n\nvoid motion(){\n  float sa=TIME*spin+.6, ta=.36+.07*sin(TIME*.7);\n  if(sandwich==0){\n    // a twirl as it assembles, a wind-up as it lifts off\n    float tc=mod(TIME,PERIOD);\n    float u=1.-clamp(tc/1.7,0.,1.), v=max(tc-(PERIOD-1.1),0.);\n    sa+=5.*u*u*u+3.*v*v;\n  }\n  float cs=cos(sa), ss=sin(sa), ct=cos(ta), st=sin(ta);\n  gSR=mat3(1.,0.,0., 0.,ct,st, 0.,-st,ct)*mat3(cs,0.,-ss, 0.,1.,0., ss,0.,cs);\n  gPos=vec3(0.,.06*sin(TIME*1.1)-.05,0.);\n\n  float sw=TIME*.05;\n  gSw=vec2(cos(sw),sin(sw));\n  gZ=TIME*burstSpeed*5.5;\n}\n\n// ---- materials: albedo / roughness / translucency / bump, from the slab-local captures ----\nvoid material(float id, vec3 lp, float d2, float side, float ys, float sd,\n              out vec3 alb, out float rough, out float sss, out float bAmp, out float bFreq){\n  float r=length(lp.xz), ang=atan(lp.z,lp.x);\n  float n1=fbm(lp*5.+sd*10.), n2=vnoise(lp*23.+sd*5.);\n  alb=vec3(.8); rough=.6; sss=0.; bAmp=.3; bFreq=20.;\n  if(id<1.5){\n    // pale rib down the middle, chevron veins off it, dark frilly rim, blotchy chlorophyll\n    float edge=smoothstep(-.85,-.03,d2);\n    float rib=smoothstep(.10,.0,abs(lp.z))*smoothstep(1.1,.2,abs(lp.x));\n    float vv=abs(fract(lp.x*2.6-abs(lp.z)*1.7+.25*n1)-.5);\n    float veins=smoothstep(.07,.0,vv)*smoothstep(1.15,.35,r)*.55;\n    alb=mix(vec3(.34,.52,.10),vec3(.07,.24,.03),edge);\n    alb=mix(alb,vec3(.13,.36,.05),smoothstep(.35,.7,n1)*.6);\n    alb=mix(alb,vec3(.74,.82,.46),max(rib*.9,veins));\n    alb=mix(alb,vec3(.62,.74,.30),smoothstep(.55,.1,r)*.5);\n    alb*=.8+.35*n2;\n    rough=.62-.12*rib; sss=.85; bAmp=1.8-1.2*rib; bFreq=6.5;\n  } else if(id<2.5){\n    float lobe=abs(sin(ang*2.+sd*9.+.4*sin(ang*3.)));\n    float ch=smoothstep(.15,.5,lobe)*smoothstep(.35,.30,r)*smoothstep(.08,.15,r);\n    alb=mix(vec3(.70,.05,.03),vec3(.86,.13,.06),n1);\n    alb=mix(alb,vec3(.80,.22,.05),ch*.8);\n    vec2 g=vec2(ang*2.2,r*9.)+n2*.3;\n    float sdl=length((fract(g)-.5)*vec2(1.,.55));\n    alb=mix(alb,vec3(.93,.72,.33),smoothstep(.17,.10,sdl)*ch*.85);\n    alb=mix(alb,vec3(.90,.45,.30),smoothstep(.09,.03,r)*.7);\n    alb=mix(alb,vec3(.60,.03,.02),side);\n    rough=mix(.18,.28,side); sss=.5; bAmp=.12+.5*ch; bFreq=26.;\n  } else if(id<3.5){\n    float f=vnoise(vec3(lp.x*13.+sd*31.,lp.z*.9+sin(lp.x*9.)*.2,sd*3.));\n    float fat=smoothstep(.56,.66,f);\n    vec3 meat=mix(vec3(.46,.09,.05),vec3(.24,.05,.025),smoothstep(.3,.75,n1));      // rendered dark where it crisped\n    vec3 fatc=mix(vec3(.93,.70,.42),vec3(.78,.45,.20),smoothstep(.35,.8,n2));       // golden, not white\n    alb=mix(meat,fatc,fat);\n    alb=mix(alb,meat*1.3,smoothstep(.75,.9,vnoise(vec3(lp.x*40.,lp.z*3.,sd)))*fat*.6); // lean threads in the fat\n    alb=mix(alb,vec3(.13,.04,.015),smoothstep(-.07,0.,d2)*.75);\n    rough=mix(.3,.22,fat); sss=.2+.35*fat; bAmp=1.1; bFreq=19.;\n  } else if(id<4.5){\n    alb=vec3(1.,.50,.04)*(.9+.15*n1); rough=.33; sss=.7; bAmp=.06;\n  } else if(id<5.5){\n    alb=vec3(1.,.83,.40)*(.9+.15*n1); rough=.33; sss=.75; bAmp=.06;\n  } else if(id<6.5){\n    alb=mix(vec3(.90,.47,.48),vec3(.98,.72,.70),smoothstep(.3,.7,n1));\n    alb=mix(alb,vec3(.70,.30,.27),smoothstep(-.05,-.005,d2)*.8);\n    rough=.36; sss=.45; bAmp=.25; bFreq=11.;\n  } else if(id<7.5){\n    float rl=mod(r,.2)/.2;\n    alb=mix(vec3(.97,.93,.96),vec3(.52,.08,.36),smoothstep(.55,.78,rl));\n    alb=mix(alb,vec3(.80,.62,.80),.3*smoothstep(.45,.2,rl));\n    rough=.25; sss=.75; bAmp=.1;\n  } else if(id<8.5){\n    alb=mix(vec3(.66,.72,.26),vec3(.22,.34,.06),smoothstep(-.045,-.012,d2));\n    float sr=smoothstep(.05,.08,r)*smoothstep(.14,.11,r);\n    alb=mix(alb,vec3(.86,.88,.55),sr*smoothstep(.2,.7,abs(sin(ang*4.)))*.8);\n    alb=mix(alb,vec3(.20,.32,.05),side);\n    rough=.2; sss=.6; bAmp=.15;\n  } else if(id<9.5){\n    alb=mix(vec3(.17,.08,.04),vec3(.40,.21,.10),n1);\n    alb*=.5+.8*n2;\n    rough=.5; bAmp=1.4; bFreq=17.;\n  } else if(id<10.5){\n    float yk=smoothstep(.31,.27,r);\n    alb=mix(vec3(.97,.96,.91),vec3(1.,.55,.02),yk);\n    alb=mix(alb,vec3(.72,.45,.16),(1.-yk)*smoothstep(-.16,0.,d2)*(.3+.7*n1));\n    rough=mix(.33,.1,yk); sss=.35; bAmp=.2*(1.-yk); bFreq=9.;\n  } else if(id<16.5){\n    float sw=sin(lp.x*7.+3.*n1+1.5*sin(lp.z*6.));                  // knife marks\n    if(id<12.5){ alb=vec3(.97,.94,.82); rough=.4; sss=.6; }\n    else if(id<13.5){ alb=mix(vec3(.90,.60,.03),vec3(.42,.25,.04),smoothstep(.74,.82,n2)*.7); rough=.33; sss=.3; }\n    else if(id<14.5){ alb=vec3(.56,.025,.012); rough=.15; sss=.35; }\n    else if(id<15.5){ alb=vec3(.60,.36,.13); rough=.42; sss=.2; }\n    else { alb=vec3(.17,.012,.13); rough=.07; sss=.7; }\n    alb*=.88+.12*sw; bAmp=1.; bFreq=5.;\n  } else {\n    float bt=id-20.;\n    vec3 crumb=vec3(.95,.87,.69), crust=vec3(.62,.34,.11);\n    float toast=0., pk=1.;\n    if(bt<2.5&&bt>1.5){ crumb=vec3(.70,.49,.27); crust=vec3(.38,.20,.08); }\n    else if(bt<3.5&&bt>2.5){\n      float swl=sin(r*8.+ang*2.+n1*5.+sd*20.);\n      crumb=mix(vec3(.82,.66,.44),vec3(.44,.26,.13),smoothstep(-.2,.2,swl)); crust=vec3(.27,.145,.07); pk=.55;\n    }\n    else if(bt<4.5&&bt>3.5){ crumb=vec3(.97,.89,.68); crust=vec3(.78,.42,.10); }\n    else if(bt>4.5) toast=1.;\n    // open crumb: big irregular cells, medium pores, fine pits - each a little darker and warmer inside\n    float p1=smoothstep(.60,.78,vnoise(lp*9.+sd*9.));\n    float p2=smoothstep(.56,.78,vnoise(lp*21.+4.));\n    float p3=smoothstep(.55,.8,vnoise(lp*47.));\n    float pores=(p1*.5+p2*.35+p3*.22)*pk;\n    crumb=mix(crumb,crumb*vec3(.55,.42,.30),min(pores,1.));\n    if(bt>1.5&&bt<2.5) crumb=mix(crumb,vec3(.30,.18,.08),smoothstep(.72,.8,vnoise(lp*46.+3.))*.8);\n    // toast browns the middle of the face and leaves a pale ring inside the crust\n    float tz=toast*smoothstep(.28,.62,n1*.55+.6*smoothstep(-.06,-.4,d2)-.25*p1);\n    crumb=mix(crumb,crumb*vec3(.74,.45,.20),tz);\n    float bun= (bt>3.5&&bt<4.5) ? 1. : 0.;\n    float cm=max(side,smoothstep(-.075,-.03,d2));\n    cm=mix(cm,max(cm,step(0.,ys)),bun);\n    crust*=.75+.5*n1;\n    crust=mix(crust,crust*.6,smoothstep(.4,.9,abs(lp.y)/.12)*(1.-bun)*side);   // bake darkens toward the slice's face edges\n    // sesame\n    vec2 g=lp.xz*6.5+sd*3.; vec2 ci=floor(g);\n    float hs=h21(ci+sd*13.); float sa2=hs*TAU;\n    vec2 sq=g-ci-.5-(vec2(hs,fract(hs*7.7))-.5)*.5;\n    sq=vec2(cos(sa2)*sq.x-sin(sa2)*sq.y, sin(sa2)*sq.x+cos(sa2)*sq.y);\n    float ses=smoothstep(.17,.12,length(sq*vec2(1.,2.1)))*step(.3,hs)*bun*step(0.,ys)*smoothstep(1.05,.85,r);\n    alb=mix(crumb,crust,cm);\n    alb=mix(alb,vec3(.98,.90,.70),ses);\n    rough=mix(.92,mix(.55,.3,bun),cm); sss=.2;\n    bAmp=mix(1.9,.4,cm)+ses*1.5; bFreq=mix(21.,9.,cm);\n  }\n}\n\nvec3 bgcol(vec2 uv){\n  float rho=length(uv), ang=atan(uv.y,uv.x);\n  vec3 bd=backdrop.rgb;\n  float rays=smoothstep(-.1,.1,sin(ang*12.+TIME*.6));\n  vec3 c=mix(bd*.20,bd*.62+.01,rays);\n  c*=1.25-.95*smoothstep(.05,1.,rho);\n  vec3 glow=mix(vec3(1.,.90,.70),bd+.5,.3);\n  float pulse=exp(-pow((gFront+ZFAR-4.)*.05,2.));\n  c+=glow*(1.0*exp(-rho*4.2)+1.6*exp(-rho*11.))*(1.+.8*pulse)+glow*pulse*.22*exp(-rho*1.6);\n  return c;\n}\n\nvec3 envc(vec3 r, vec3 L){\n  vec3 e=mix(vec3(.10,.085,.07),backdrop.rgb*.55+.18,smoothstep(-.4,.7,r.y));\n  e+=smoothstep(.80,.97,dot(r,L))*vec3(1.,.93,.82)*3.5;\n  e+=pow(max(-r.z,0.),3.)*vec3(1.,.95,.85)*1.3;\n  return e;\n}\n\nvec3 lightIt(vec3 pw, vec3 n, vec3 rd, vec3 alb, float rough, float sss, float sh, float ao){\n  vec3 L1=normalize(vec3(-.55,.78,.6)), k1=vec3(1.,.91,.78)*2.7;\n  vec3 L2=normalize(vec3(0.,1.5,-40.)-pw), k2=mix(vec3(1.,.95,.85),backdrop.rgb+.45,.4)*1.6;\n  float ndl=max(dot(n,L1),0.), ndb=max(dot(n,L2),0.), ndv=max(dot(n,-rd),0.);\n  vec3 amb=mix(vec3(.30,.22,.16),backdrop.rgb*.22+vec3(.50,.47,.43),n.y*.5+.5)*.6;\n  vec3 c=alb*(k1*ndl*sh+amb*ao+k2*ndb*.55*(.4+.6*ao));\n  // thin-sheet translucency: light arriving on the far side leaks through, tinted by the flesh\n  vec3 tint=mix(alb*alb*1.5,alb,.45);\n  c+=sss*tint*(k1*max(-dot(n,L1),0.)*.4*(.3+.7*sh)+k2*max(-dot(n,L2),0.)*.55+k1*.05*(1.-ndl))*(.5+.5*ao);\n  float a=rough*rough;\n  float ex=min(2./(a*a)-2.,1500.);\n  vec3 h1=normalize(L1-rd), h2=normalize(L2-rd);\n  float fr=.04+.96*pow(1.-ndv,5.);\n  c+=k1*sh*ndl*(ex+2.)/8.*pow(max(dot(n,h1),0.),ex)*(.04+.3*pow(1.-max(dot(h1,-rd),0.),5.));\n  c+=k2*ndb*(ex+2.)/8.*pow(max(dot(n,h2),0.),ex)*.05*ao;\n  c+=envc(reflect(rd,n),L1)*fr*(1.-rough)*(1.-rough)*ao;\n  c+=k2*pow(1.-ndv,3.)*.22*ao*(.3+.7*smoothstep(-.2,.6,-n.z));\n  return c;\n}\n\n// perturb a normal (given in texture space) by the gradient of a small noise field\nvec3 bump(vec3 n, vec3 tp, float amp, float fq){\n  vec2 e=vec2(.35/fq,0.);\n  float b0=vnoise(tp*fq)+.5*vnoise(tp*fq*2.3+5.);\n  vec3 g=vec3(vnoise((tp+e.xyy)*fq)+.5*vnoise((tp+e.xyy)*fq*2.3+5.),\n              vnoise((tp+e.yxy)*fq)+.5*vnoise((tp+e.yxy)*fq*2.3+5.),\n              vnoise((tp+e.yyx)*fq)+.5*vnoise((tp+e.yyx)*fq*2.3+5.))-b0;\n  g/=e.x*fq;\n  g-=n*dot(g,n);\n  return normalize(n-g*amp*.09);\n}\n\nvoid scenePass(){\n  motion();\n  gSelA=IMG_PIXEL(tab,vec2(30.5,.5)); gSelB=IMG_PIXEL(tab,vec2(30.5,1.5));\n  vec4 g2=IMG_PIXEL(tab,vec2(30.5,2.5));\n  gYlo=g2.x; gYhi=g2.y; gFront=g2.z;\n  gWA=IMG_PIXEL(tab,vec2(30.5,3.5)).xyz; gWB=IMG_PIXEL(tab,vec2(30.5,4.5)).xyz;\n  gSeedB=floor(h11(seed+.7)*90.); gPop=1.;\n  vec2 uv=(gl_FragCoord.xy-.5*RENDERSIZE.xy)/RENDERSIZE.y;\n  vec3 ro=vec3(0.,0.,CAMZ), rd=normalize(vec3(uv,-FOCAL));\n  vec3 bg=bgcol(uv);\n  vec3 col=bg; float coc=.011;\n\n  // hit record, shaded once below whichever march produced it\n  float kind=0.;                       // 0 nothing, 1 sandwich, 2 burst\n  vec3 q0=vec3(0.), pw=vec3(0.), pl=vec3(0.);\n  mat4 M0=mat4(0.); vec4 E0=vec4(0.), F0=vec4(0.); mat3 R0m=mat3(1.);\n  float sh=1., ao=1., fog=1., fd0=1e3;\n\n  // ---------- the sandwich ----------\n  if(gPop>.02){\n    vec3 rol=((ro-gPos)*gSR)/gPop, rdl=rd*gSR;\n    rdl+=vec3(1e-5)*step(abs(rdl),vec3(1e-5));\n    vec3 bh=vec3(1.6,.5*(gYhi-gYlo)+.02,1.6), bc=vec3(0.,.5*(gYhi+gYlo),0.);\n    vec3 ta=(bc-bh-rol)/rdl, tb=(bc+bh-rol)/rdl;\n    vec3 tmn=min(ta,tb), tmx=max(ta,tb);\n    float tN=max(max(tmn.x,tmn.y),tmn.z), tF=min(min(tmx.x,tmx.y),tmx.z);\n    if(tN<tF && tF>0.){\n      float t=max(tN,0.); float hit=-1.;\n      gRd=rdl; gClamp=1.;\n      for(int i=0;i<56;i++){\n        float d=mapS(rol+rdl*t);\n        if(gDs<.0015){ hit=t; break; }\n        t+=d; if(t>tF) break;\n      }\n      if(hit>0.){\n        kind=1.;\n        pl=rol+rdl*hit; q0=gQ; M0=gCM; E0=gCE; F0=gCF;\n        pw=gSR*(pl*gPop)+gPos;\n      }\n    }\n  }\n\n  // ---------- the burst ----------\n  float rr=length(rd.xy);\n  if(kind<.5 && rr>.02 && gPop>.02){\n    float t=max(R0/rr,.6), tE=min((R0+3.*RW)/rr,(CAMZ+ZFAR)/(-rd.z));\n    float hit=-1.;\n    gRd=rd; gClamp=1.;\n    for(int i=0;i<48;i++){\n      float d=mapB(ro+rd*t);\n      if(gDs<.0015*t){ hit=t; break; }\n      t+=d; if(t>tE) break;\n    }\n    if(hit>0.){\n      kind=2.;\n      pw=ro+rd*hit; q0=gQ; M0=gCM; E0=gCE; F0=gCF; R0m=gR; fd0=gFd;\n      fog=exp(-.042*max(-pw.z,0.))*smoothstep(-ZFAR,-ZFAR+16.,pw.z);\n      coc=min(.02,abs(1./CAMZ-1./hit)*.21);\n    }\n  }\n\n  if(kind>.5){\n    // normal in slab space, straight from the slab with the parameter block the march left behind\n    vec2 e=vec2(1.,-1.)*.0022;\n    vec3 n=normalize(e.xyy*slab(q0+e.xyy,M0,E0,F0)+e.yyx*slab(q0+e.yyx,M0,E0,F0)+e.yxy*slab(q0+e.yxy,M0,E0,F0)+e.xxx*slab(q0+e.xxx,M0,E0,F0));\n    slab(q0,M0,E0,F0);\n    vec3 alb; float rough,sss,bA,bF;\n    material(gMat,gLp,gD2,gSide,gYs,gSd,alb,rough,sss,bA,bF);\n    ao=.72+.28*smoothstep(-.3,0.,gD2);\n    n=bump(n,q0,bA,bF);\n    if(kind<1.5){\n      gClamp=0.;\n      float oc=(.04-mapS(pl+n*.04)/.75)*8.+(.13-mapS(pl+n*.13)/.75)*3.5;\n      ao=clamp(1.-oc*.6,0.,1.);\n      // soft self-shadow, marched in sandwich space\n      vec3 Ll=normalize(vec3(-.55,.78,.6))*gSR;\n      float ts=.02;\n      vec3 po=pl+n*.012;\n      gRd=Ll; gClamp=1.;\n      for(int i=0;i<36;i++){\n        float d=mapS(po+Ll*ts);\n        sh=min(sh,10.*gDs/.75/ts+.22*ts*ts);      // far-off layers (mid-cascade) cast only a faint, wide shadow\n        ts+=max(d,.012);\n        if(sh<.02||ts>2.6) break;\n      }\n      sh=clamp(sh,0.,1.); sh=sh*sh*(3.-2.*sh);\n      n=gSR*n; coc=0.;\n    } else {\n      n=n*R0m;\n      n=vec3(gSw.x*n.x+gSw.y*n.y, -gSw.y*n.x+gSw.x*n.y, n.z);\n    }\n    vec3 lit=lightIt(pw,n,rd,alb,rough,sss,sh,ao);\n    lit+=vec3(1.,.8,.5)*2.5*exp(-abs(fd0)*.55);          // pieces glow white-hot as the morph front crosses them\n    col=mix(bg,lit,fog);\n  }\n  gl_FragColor=vec4(col,coc);\n}\n\nvoid postPass(){\n  vec2 uv=gl_FragCoord.xy/RENDERSIZE.xy;\n  vec4 c0=IMG_NORM_PIXEL(scene,uv);\n  float rad=max(c0.a,.75/RENDERSIZE.y);\n  vec3 acc=c0.rgb; float ws=1.;\n  float j=fract(52.9829189*fract(dot(gl_FragCoord.xy,vec2(.06711056,.00583715))))*TAU;\n  vec2 asp=vec2(RENDERSIZE.y/RENDERSIZE.x,1.);\n  for(int i=0;i<44;i++){\n    float fi=float(i)+.5;\n    float rr=sqrt(fi/44.)*rad;\n    float a=fi*2.39996+j;\n    vec4 s=IMG_NORM_PIXEL(scene,uv+rr*vec2(cos(a),sin(a))*asp);\n    float w=clamp((max(s.a,.75/RENDERSIZE.y)-rr*.7)/(rr*.3+1e-5),0.,1.);\n    w*=1.+.25*dot(s.rgb,vec3(.33));              // brighter samples bloom a little in the bokeh\n    acc+=s.rgb*w; ws+=w;\n  }\n  vec3 col=acc/ws;\n  vec2 q=uv-.5;\n  col*=1.-.55*dot(q,q)*1.6;\n  col*=.95;\n  col=(col*(2.51*col+.03))/(col*(2.43*col+.59)+.14);\n  col=pow(clamp(col,0.,1.),vec3(1./2.2));\n  col=mix(vec3(dot(col,vec3(.299,.587,.114))),col,1.12);\n  col=mix(col,col*col*(3.-2.*col),.35);\n  col+=(h21(gl_FragCoord.xy+fract(TIME)*91.)-.5)*.012;\n  gl_FragColor=vec4(col,1.);\n}\n\nvoid main(){\n  if(PASSINDEX==0) tablePass(); else if(PASSINDEX==1) scenePass(); else postPass();\n}\n",
    "generator": {
      "model": "claude-fable-5-1",
      "provider": "anthropic",
      "prompt": null,
      "params": null
    }
  }
}