Created
December 14, 2025 16:19
-
-
Save 0b5vr/a489c706b03e8f025c4b28cdd6970534 to your computer and use it in GitHub Desktop.
20251130_hex grid tunnel
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| precision highp float; | |
| #define lofi(i,j) (floor((i)/(j))*(j)) | |
| #define saturate(i) clamp(i,0.,1.) | |
| // 200 is recommended, use 400 if your machine is a beef | |
| const int ITER=400; | |
| const float FOG_START=30.0; | |
| const float FAR=40.0; | |
| const vec3 FOG_COLOR=vec3(1,2,3); | |
| const float PI=acos(-1.); | |
| const float TAU=2.*PI; | |
| const float SQRT3=sqrt(3.); | |
| uniform vec2 resolution; | |
| uniform float time; | |
| uniform int frame; | |
| uniform sampler2D backbuffer; | |
| out vec4 outColor; | |
| uvec3 seed; | |
| uvec3 hash3u(uvec3 s){ | |
| s=s*1145141919u+1919810u; | |
| s.x+=s.y*s.z; | |
| s.y+=s.z*s.x; | |
| s.z+=s.x*s.y; | |
| s^=s>>16; | |
| s.x+=s.y*s.z; | |
| s.y+=s.z*s.x; | |
| s.z+=s.x*s.y; | |
| return s; | |
| } | |
| vec3 hash3f(vec3 s){ | |
| uvec3 r=hash3u(floatBitsToUint(s)); | |
| return vec3(r)/float(0xffffffffu); | |
| } | |
| vec3 random3(){ | |
| seed=hash3u(seed); | |
| return vec3(seed)/float(0xffffffffu); | |
| } | |
| float random(){ | |
| return random3().x; | |
| } | |
| float easein(float x,float k){ | |
| x=saturate(x); | |
| return (k+1.)*pow(x,k)-k*pow(x,k+1.); | |
| } | |
| float easeout(float x,float k){ | |
| return 1.-easein(1.-x,k); | |
| } | |
| vec2 cis(float t){ | |
| return vec2(cos(t),sin(t)); | |
| } | |
| mat2 rotate2d(float t){ | |
| float c=cos(t),s=sin(t); | |
| return mat2(c,s,-s,c); | |
| } | |
| mat3 orthbas(vec3 z){ | |
| z=normalize(z); | |
| float isveryy=step(.999,abs(z.y)); | |
| vec3 up=vec3(0,1.-isveryy,isveryy); | |
| vec3 x=normalize(cross(up,z)); | |
| return mat3(x,cross(z,x),z); | |
| } | |
| vec2 dohex(vec2 p){ | |
| mat2 MAT_SKEW=mat2(1,1./SQRT3,0,2./SQRT3); | |
| mat2 INV_MAT_SKEW=mat2(1,-.5,0,SQRT3/2.); | |
| vec2 pt=p*MAT_SKEW; | |
| vec2 cell=floor(pt); | |
| cell.y-=mod(cell.y+cell.x+2.,3.)-1.; | |
| vec2 coord=pt-cell; | |
| float istop=step(coord.x,coord.y); | |
| cell+=vec2(1.-istop,istop); | |
| return cell*INV_MAT_SKEW; | |
| } | |
| vec4 gridt(vec3 ro,vec3 rd){ | |
| vec3 cell=vec3(dohex(ro.xy+rd.xy*1E-2),0); | |
| vec3 dice=hash3f(cell.xyy); | |
| float zoffset=dice.x; | |
| cell.z=lofi(ro.z+rd.z*1E-2-zoffset,1.)+.5+zoffset; | |
| mat4 dir=mat4( | |
| vec4(SQRT3*.5,.5,0,0), | |
| vec4(0,1,0,0), | |
| vec4(-SQRT3*.5,.5,0,0), | |
| vec4(0,0,1,0) | |
| ); | |
| vec4 rdd=vec4(rd,0)*dir; | |
| vec4 src=-(vec4(ro-cell,0)*dir)/rdd; | |
| vec4 dst=abs(vec2(SQRT3/2.,.5).xxxy/rdd); | |
| vec4 bv=src+dst; | |
| float b=min(min(bv.x,bv.y),min(bv.z,bv.w)); | |
| return vec4(cell,b); | |
| } | |
| vec4 isectmin(vec4 a,vec4 b){ | |
| return mix(a,b,step(b.w,a.w)); | |
| } | |
| vec4 isectbox(vec3 ro,vec3 rd,vec3 s){ | |
| vec3 src=-ro/rd; | |
| vec3 dst=abs(s/rd); | |
| vec3 fv=src-dst; | |
| vec3 bv=src+dst; | |
| float f=max(max(fv.x,fv.y),fv.z); | |
| float b=min(min(bv.x,bv.y),bv.z); | |
| vec3 n=-sign(rd)*step(f,fv); | |
| float isvalid=step(f,b)*step(0.,f); | |
| return vec4( | |
| n, | |
| mix(FAR,f,isvalid) | |
| ); | |
| } | |
| vec4 isecthex(vec3 ro,vec3 rd,float r,float l){ | |
| mat4 dir=mat4( | |
| vec4(SQRT3*.5,.5,0,0), | |
| vec4(0,1,0,0), | |
| vec4(-SQRT3*.5,.5,0,0), | |
| vec4(0,0,1,0) | |
| ); | |
| vec4 rdd=vec4(rd,0)*dir; | |
| vec4 src=-(vec4(ro,0)*dir)/rdd; | |
| vec4 dst=abs(vec2((SQRT3/2.)*r,l).xxxy/rdd); | |
| vec4 bv=src+dst; | |
| float b=min(min(bv.x,bv.y),min(bv.z,bv.w)); | |
| vec4 fv=src-dst; | |
| float f=max(max(fv.x,fv.y),max(fv.z,fv.w)); | |
| vec4 mask=step(f,fv); | |
| vec3 n=-(dir*mask).xyz*sign(dot(rdd,mask)); | |
| float isvalid=step(f,b)*step(0.,f); | |
| return vec4( | |
| n, | |
| mix(FAR,f,isvalid) | |
| ); | |
| } | |
| vec3 sampleggx(vec3 n,float aa){ | |
| vec3 xi=random3(); | |
| float costheta=sqrt((1.-xi.y)/(1.+(aa-1.)*xi.y)); | |
| float sintheta=sqrt(1.-costheta*costheta); | |
| return orthbas(n)*vec3( | |
| sintheta*cis(TAU*xi.x), | |
| costheta | |
| ); | |
| } | |
| void main() { | |
| seed=uvec3(gl_FragCoord.xy,frame); | |
| vec2 uv=gl_FragCoord.xy/resolution.xy; | |
| vec2 p=uv*2.-1.; | |
| p.x*=resolution.x/resolution.y; | |
| vec3 co; | |
| vec3 ro; | |
| vec3 rd; | |
| float rl; | |
| vec3 col=vec3(0); | |
| vec3 colrem=vec3(0); | |
| float samples=0.; | |
| for(int i=0;i<ITER;i++){ | |
| const float gridzmod=256.; | |
| float mytime; | |
| if(dot(colrem,vec3(1))<.4){ | |
| // reset | |
| float warptime=time/10.-0.8; // 3.8 for the sake of direction | |
| float warp1=easeout(6.*fract(warptime),6.); | |
| float warp0=easeout(6.*fract(warptime)-.1,6.); | |
| float warp=(warp1-warp0)/.1; | |
| warp=warp/(warp+1.); | |
| float prog=random(); | |
| colrem=mix( | |
| vec3(1), | |
| 4.*(.5-.5*cos(TAU*saturate(2.*prog-vec3(0,.5,1.)))), | |
| sqrt(warp) | |
| ); | |
| mytime=time-.0166*prog; | |
| float warpt=mix(warp0,warp1,prog)+floor(warptime); | |
| mytime+=3.*warpt; | |
| samples++; | |
| // init camera | |
| vec3 cx=vec3(1,0,0); | |
| vec3 cy=vec3(0,1,0); | |
| cx.yz*=rotate2d(1.2*warpt); | |
| cy.yz*=rotate2d(1.2*warpt); | |
| cx.zx*=rotate2d(-.11*mytime); | |
| cy.zx*=rotate2d(-.11*mytime); | |
| mat3 cb=mat3(cx,cy,cross(cx,cy)); | |
| co=cb*vec3(0,0,.6); | |
| co.z-=mod(4.*mytime,gridzmod); | |
| // init ray | |
| ro=co; | |
| vec2 pt=(p+2.*random3().xy/resolution.y)*rotate2d(.1*mytime+warpt); | |
| rd=cb*normalize(vec3(pt,-1.8+warp*(-.4-.2*length(p)*length(p)))); | |
| rl=0.; | |
| // dof | |
| { | |
| vec3 fp=ro+rd*5.; | |
| vec3 dice=random3(); | |
| vec2 disc=sqrt(dice.x)*cis(TAU*dice.y); | |
| ro+=0.01*vec3(disc,0)*cb; | |
| rd=normalize(fp-ro); | |
| } | |
| } | |
| // vars | |
| vec4 isect=vec4(FAR); | |
| float alpha=0.; | |
| float f0=.1; | |
| vec3 ecol=vec3(0); | |
| // scroll | |
| float scrdice=hash3f(dohex(ro.xy+rd.xy*1E-2).xyy).x; | |
| float scr=24.*smoothstep(-.2,.2,sin(.1*mytime+TAU*scrdice)); | |
| ro.z-=scr; | |
| // traverse | |
| vec4 grid=gridt(ro,rd); | |
| // roll dice | |
| vec3 dice=hash3f(grid.xyy); | |
| dice.z=floor(mod(grid.z,gridzmod)*dice.z*dice.z); | |
| dice=hash3f(dice); | |
| // isect emissive hexes | |
| if(length(grid.xy)>1.&&dice.z<.1){ | |
| float showz=grid.z-co.z+mix(0.,40.,dice.x); | |
| float size=easeout(showz/5.,5.)-easeout((showz-20.)/5.,3.); | |
| vec4 isect2=isecthex(ro-grid.xyz,rd,size*.7,size*.2); | |
| if(isect2.w<isect.w){ | |
| isect=isect2; | |
| alpha=.001; | |
| f0=.1; | |
| ecol=4.*(.1+saturate(cos(PI*(fract(dice.z*100.)-vec3(.16,.5,.83))))); | |
| } | |
| } | |
| // lines | |
| if(length(grid.xy)>1.){ | |
| float mt=PI/3.*floor(mod(dice.x*18.,6.)); | |
| mat2 m=rotate2d(mt); | |
| vec3 rpt=ro-grid.xyz; | |
| rpt.xy*=m; | |
| vec3 rdt=rd; | |
| rdt.xy*=m; | |
| vec4 isect2=isectmin( | |
| isecthex(rpt-vec3(.72,.0,.0),rdt,.01,.49), | |
| isecthex(rpt-vec3(-.72,.0,.0),rdt,.01,.49) | |
| ); | |
| isect2.xy*=rotate2d(-mt); | |
| if(isect2.w<isect.w){ | |
| isect=isect2; | |
| alpha=1.; | |
| f0=.0; | |
| ecol=vec3(1.8,1.9,2); | |
| } | |
| } | |
| // reflectors | |
| if(length(grid.xy)>1.&&dice.z<.54){ | |
| float mt=PI/3.*floor(dice.x*3.); | |
| mat2 m=rotate2d(mt); | |
| vec3 rpt=ro-grid.xyz; | |
| rpt.xy*=m; | |
| vec3 rdt=rd; | |
| rdt.xy*=m; | |
| vec4 isect2=isectmin( | |
| isectbox(rpt-vec3(0,0.8,0.),rdt,vec3(.5,0.02,.45)), | |
| isectbox(rpt-vec3(0,-0.8,0.),rdt,vec3(.5,0.02,.45)) | |
| ); | |
| bool isface=abs(isect2.y)>.5; | |
| isect2.xy*=rotate2d(-mt); | |
| if(isect2.w<isect.w){ | |
| isect=isect2; | |
| alpha=isface?exp(-3.-3.*dice.y):1.; | |
| f0=isface?.5:.04; | |
| ecol=vec3(0); | |
| } | |
| } | |
| // revert the scroll | |
| ro.z+=scr; | |
| // if the ray misses, continue | |
| if(isect.w==FAR){ | |
| ro+=grid.w*rd; | |
| rl+=grid.w; | |
| // if the ray goes way too far, grant fog instead | |
| if(rl>=FAR){ | |
| col+=colrem*FOG_COLOR; | |
| colrem*=0.; | |
| } | |
| continue; | |
| } | |
| // update ray | |
| ro+=isect.w*rd; | |
| rl+=isect.w; | |
| // do the shading | |
| vec3 n=isect.xyz; | |
| // fog | |
| float fog=smoothstep(FOG_START,FAR,rl); | |
| col+=colrem*fog*FOG_COLOR; | |
| colrem*=1.-fog; | |
| // ggx | |
| vec3 h=sampleggx(n,alpha*alpha); | |
| float dotvh=dot(-rd,h); | |
| rd=reflect(rd,h); | |
| // fresnel | |
| float f=mix(f0,1.,pow(saturate(1.-dotvh),5.)); | |
| // emissive | |
| col+=colrem*(1.-f)*ecol; | |
| // albedo | |
| colrem*=f*step(0.,dot(rd,n)); // fresnel, visibility | |
| } | |
| // post processing | |
| col/=samples; // sum -> average | |
| col=pow(col/(col+1.),vec3(.4545)); // reinhard, oetf | |
| // col*=1.-.2*length(p)*length(p); // vignette | |
| col=smoothstep(vec3(0.0,-0.1,-0.3),vec3(1.0,1.0,1.1),col); // color grading | |
| // temporal accumulation | |
| vec4 prev=texture(backbuffer,uv); | |
| outColor=mix(vec4(col,1),prev,.5*prev.w); | |
| // debug | |
| // outColor.xyz=cos(PI*(saturate(samples/10.)-vec3(.5,1.5,2.5)/3.)); // blue == enough samples, red == too few samples | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment