// @name Studio Chrome Spikeball
// @group Spike Geometry
// @param uCoreRadius "Core Radius" 0.72 0.35 1.15 0.01
// @param uSpikeLength "Spike Length" 0.58 0.08 1.35 0.01
// @param uSpikeRows "Spike Rows" 9.0 4.0 18.0 1.0
// @param uSpikeColumns "Spikes per Row" 18.0 6.0 36.0 1.0
// @param uSpikeSharpness "Spike Sharpness" 2.8 0.7 8.0 0.05
// @param uSpikeWidth "Spike Width" 0.78 0.35 1.6 0.01
// @param uRowStagger "Row Stagger" 0.75 0.0 1.0 0.01
// @param uPoleTaming "Pole Taming" 0.58 0.0 1.0 0.01
// @param uFacetDepth "Facet Depth" 0.035 0.0 0.16 0.001
// @param uInflation "Ball Inflation" 0.0 -0.25 0.35 0.01
// @group Motion
// @param uRotationX "Tilt" -0.18 -3.14 3.14 0.01
// @param uRotationY "Turn" 0.35 -3.14 3.14 0.01
// @param uSpinSpeed "Spin Speed" 0.16 -1.5 1.5 0.01
// @param uTumbleSpeed "Tumble Speed" 0.07 -1.0 1.0 0.01
// @param uSpikePulse "Spike Pulse" 0.08 0.0 0.8 0.01
// @param uPulseSpeed "Pulse Speed" 0.65 0.0 4.0 0.01
// @group Flashy Material
// @param uBaseHue "Plastic Hue" 0.965 0.0 1.0 0.005
// @param uSaturation "Plastic Saturation" 0.72 0.0 1.0 0.01
// @param uBrightness "Plastic Brightness" 0.36 0.02 1.5 0.01
// @param uMetalness "Metalness" 0.88 0.0 1.0 0.01
// @param uRoughness "Surface Roughness" 0.11 0.02 0.85 0.01
// @param uReflectivity "Environment Reflection" 1.28 0.0 2.5 0.01
// @param uFresnelPower "Fresnel Tightness" 4.4 0.5 9.0 0.1
// @param uClearcoat "Plastic Clearcoat" 0.74 0.0 2.0 0.01
// @param uClearcoatGloss "Clearcoat Gloss" 0.92 0.05 1.0 0.01
// @param uAnisotropy "Brushed Anisotropy" 0.18 0.0 1.0 0.01
// @param uIridescence "Edge Iridescence" 0.26 0.0 1.5 0.01
// @param uFilmThickness "Film Thickness" 0.43 0.0 2.0 0.01
// @param uFacetContrast "Facet Contrast" 0.32 0.0 1.5 0.01
// @group Environment Map
// @param uEnvHue "Environment Hue" 0.61 0.0 1.0 0.005
// @param uEnvSaturation "Environment Saturation" 0.56 0.0 1.0 0.01
// @param uEnvBrightness "Environment Brightness" 0.72 0.0 2.5 0.01
// @param uEnvRotation "Environment Rotation" -0.55 -3.14 3.14 0.01
// @param uEnvSpin "Environment Spin" 0.025 -0.5 0.5 0.005
// @param uHorizonGlow "Horizon Glow" 0.58 0.0 2.0 0.01
// @param uCardIntensity "Studio Card Intensity" 2.15 0.0 5.0 0.05
// @param uCardSharpness "Studio Card Sharpness" 32.0 2.0 96.0 1.0
// @param uCardRotation "Light Card Rotation" 0.32 -3.14 3.14 0.01
// @param uNeonRing "Neon Ring" 0.72 0.0 3.0 0.01
// @param uNeonHue "Neon Ring Hue" 0.91 0.0 1.0 0.005
// @group Lighting & Stage
// @param uKeyAngle "Key Light Angle" -0.72 -3.14 3.14 0.01
// @param uKeyHeight "Key Light Height" 0.68 0.05 1.6 0.01
// @param uKeyPower "Key Light Power" 1.35 0.0 4.0 0.01
// @param uRimPower "Rim Light Power" 1.42 0.0 4.0 0.01
// @param uFloorGloss "Floor Reflection" 0.56 0.0 1.0 0.01
// @param uFloorHue "Floor Hue" 0.65 0.0 1.0 0.005
// @param uShadowSoftness "Shadow Softness" 14.0 2.0 32.0 0.5
// @param uExposure "Exposure" 1.18 0.2 3.0 0.01
// @param uVignette "Vignette" 0.34 0.0 1.0 0.01
// @param uGrain "Film Grain" 0.008 0.0 0.08 0.001
// @group Camera & Audio
// @param uOffsetX "Screen X Position" 0.0 -1.25 1.25 0.01
// @param uOffsetY "Screen Y Position" 0.0 -0.75 0.75 0.01
// @param uCameraDistance "Camera Distance" 4.05 2.2 6.5 0.05
// @param uCameraHeight "Camera Height" 0.36 -1.5 2.2 0.01
// @param uCameraOrbit "Camera Orbit" 0.0 -3.14 3.14 0.01
// @param uCameraOrbitSpeed "Camera Orbit Speed" 0.035 -0.5 0.5 0.005
// @param uFocalLength "Focal Length" 1.58 1.0 3.0 0.01
// @param uRaySteps "Raymarch Steps" 84.0 40.0 108.0 1.0
// @param uAudioReact "Audio Reactivity" 0.65 0.0 2.0 0.01
// @param uBassSpikes "Bass Spike Expansion" 0.42 0.0 1.5 0.01
// @param uMidSpin "Mid Spin Boost" 0.32 0.0 1.5 0.01
// @param uTrebleFlash "Treble Highlight Flash" 0.55 0.0 2.0 0.01

#define PI 3.14159265359

mat2 sbRot(float a){float s=sin(a),c=cos(a);return mat2(c,-s,s,c);}
float sbHash(vec2 p){p=fract(p*vec2(123.34,456.21));p+=dot(p,p+45.32);return fract(p.x*p.y);}
vec3 sbHsv(vec3 c){vec3 p=abs(fract(c.xxx+vec3(0.0,.6666667,.3333333))*6.0-3.0);return c.z*mix(vec3(1.0),clamp(p-1.0,0.0,1.0),c.y);}

vec3 sbObjectPoint(vec3 p){
    float spin=iTime*(uSpinSpeed+iMid*uAudioReact*uMidSpin*.12)+uRotationY;
    p.xz=sbRot(spin)*p.xz;
    p.yz=sbRot(uRotationX+sin(iTime*uTumbleSpeed)*.28)*p.yz;
    p.xy=sbRot(iTime*uTumbleSpeed*.42)*p.xy;
    return p;
}

float sbSpikeField(vec3 n){
    float rows=max(4.0,floor(uSpikeRows+.5));
    float cols=max(6.0,floor(uSpikeColumns+.5));
    float latitude=asin(clamp(n.y,-1.0,1.0))/PI+.5;
    float rowCoord=latitude*rows;
    float row=floor(rowCoord);
    float stagger=mod(row,2.0)*.5*uRowStagger;
    float colCoord=atan(n.z,n.x)/(2.0*PI)*cols+stagger;
    float dx=abs(fract(colCoord+.5)-.5)*2.0;
    float dy=abs(fract(rowCoord+.5)-.5)*2.0;
    float pole=mix(1.0,max(.24,sqrt(max(1.0-n.y*n.y,.0))),uPoleTaming);
    vec2 cell=vec2(dx*max(pole,.28),dy)/max(uSpikeWidth,.05);
    float cone=clamp(1.0-length(cell),0.0,1.0);
    float facets=1.0-uFacetDepth*(abs(dx-dy)+abs(dx+dy-1.0));
    return pow(cone,max(uSpikeSharpness,.25))*max(facets,.7);
}

vec2 sbMap(vec3 worldP){
    vec3 p=sbObjectPoint(worldP-vec3(0.0,.18,0.0));
    float r=length(p),field=sbSpikeField(p/max(r,.0001));
    float pulse=1.0+uSpikePulse*sin(iTime*uPulseSpeed+field*PI);
    pulse+=iBass*uAudioReact*uBassSpikes*.22;
    float shell=uCoreRadius+uInflation+uSpikeLength*field*pulse;
    float ball=(r-shell)*.58;
    float floorD=worldP.y+1.16;
    return ball<floorD?vec2(ball,1.0):vec2(floorD,2.0);
}

vec3 sbNormal(vec3 p){vec2 e=vec2(.0018,0.0);return normalize(vec3(sbMap(p+e.xyy).x-sbMap(p-e.xyy).x,sbMap(p+e.yxy).x-sbMap(p-e.yxy).x,sbMap(p+e.yyx).x-sbMap(p-e.yyx).x));}

vec2 sbMarch(vec3 ro,vec3 rd){
    float d=0.0,id=-1.0,steps=max(40.0,floor(uRaySteps+.5));
    for(int i=0;i<108;i++){if(float(i)>=steps)break;vec2 h=sbMap(ro+rd*d);if(abs(h.x)<.0014){id=h.y;break;}d+=max(h.x*.72,.001);if(d>10.0)break;}
    return vec2(d,id);
}

float sbShadow(vec3 ro,vec3 rd){float shade=1.0,t=.02;for(int i=0;i<26;i++){float h=sbMap(ro+rd*t).x;shade=min(shade,uShadowSoftness*h/t);t+=clamp(h,.012,.18);if(h<.001||t>5.0)break;}return clamp(shade,0.0,1.0);}
float sbAO(vec3 p,vec3 n){float occ=0.0,sca=1.0;for(int i=1;i<5;i++){float h=.035*float(i);occ+=(h-sbMap(p+n*h).x)*sca;sca*=.58;}return clamp(1.0-occ*2.7,0.0,1.0);}

vec3 sbEnvironment(vec3 d){
    d.xz=sbRot(uEnvRotation+iTime*uEnvSpin)*d.xz;
    vec3 zenith=sbHsv(vec3(fract(uEnvHue+.07),uEnvSaturation,.16*uEnvBrightness));
    vec3 horizon=sbHsv(vec3(uEnvHue,uEnvSaturation*.72,.68*uEnvBrightness));
    vec3 env=mix(zenith,horizon,pow(1.0-abs(d.y),2.2)*uHorizonGlow);
    env+=vec3(.035,.025,.045)*uEnvBrightness;
    vec3 cardDir=normalize(vec3(cos(uCardRotation),.16,sin(uCardRotation)));
    float cardA=pow(max(dot(d,cardDir),0.0),uCardSharpness);
    float cardB=pow(max(dot(d,normalize(vec3(-cardDir.x,.55,-cardDir.z))),0.0),uCardSharpness*.55);
    float strip=pow(max(1.0-abs(d.x*.86+d.y*.28),0.0),uCardSharpness*1.5)*smoothstep(-.3,.65,d.z);
    env+=(vec3(1.0,.91,.78)*cardA+vec3(.56,.72,1.0)*cardB*.68+vec3(1.0)*strip*.25)*uCardIntensity;
    float ring=exp(-pow((abs(d.y)-.31)*18.0,2.0))*smoothstep(-.4,.8,d.z);
    env+=sbHsv(vec3(uNeonHue,.78,1.0))*ring*uNeonRing;
    return env;
}

vec3 sbFilm(float ndv){
    float phase=(1.0-ndv)*uFilmThickness*17.0;
    return .56+.44*cos(phase+vec3(0.0,2.1,4.2));
}

void mainImage(out vec4 fragColor,in vec2 fragCoord){
    vec2 uv=(fragCoord-.5*iResolution.xy)/iResolution.y;
    float cameraAngle=uCameraOrbit+iTime*uCameraOrbitSpeed;
    vec3 ro=vec3(sin(cameraAngle)*uCameraDistance,uCameraHeight,cos(cameraAngle)*uCameraDistance);
    vec3 target=vec3(0.0,.08,0.0),fw=normalize(target-ro),rt=normalize(cross(fw,vec3(0.0,1.0,0.0))),up=cross(rt,fw);
    vec3 rd=normalize(fw*uFocalLength+rt*(uv.x-uOffsetX)+up*(uv.y-uOffsetY));
    vec3 col=sbEnvironment(rd)*.16;
    vec2 hit=sbMarch(ro,rd);
    if(hit.y>0.0){
        vec3 p=ro+rd*hit.x,n=sbNormal(p),v=-rd;
        float ndv=max(dot(n,v),0.0),fres=pow(1.0-ndv,uFresnelPower);
        float la=uKeyAngle;vec3 l=normalize(vec3(cos(la),uKeyHeight,sin(la)));
        float shadow=sbShadow(p+n*.008,l),ao=sbAO(p,n),ndl=max(dot(n,l),0.0);
        vec3 refl=reflect(-v,n);
        vec3 sharpEnv=sbEnvironment(refl);
        vec3 broadEnv=sbEnvironment(normalize(mix(refl,n,.38+uRoughness*.35)));
        vec3 env=mix(sharpEnv,broadEnv,uRoughness*uRoughness);
        if(hit.y<1.5){
            vec3 base=sbHsv(vec3(uBaseHue,uSaturation,uBrightness));
            float spikeMask=sbSpikeField(normalize(sbObjectPoint(p-vec3(0.0,.18,0.0))));
            base*=mix(1.0-uFacetContrast*.28,1.0+uFacetContrast*.28,spikeMask);
            vec3 dielectric=base*(.12+.58*ndl*uKeyPower*shadow)+env*uReflectivity*(.18+.82*fres);
            vec3 metal=env*mix(vec3(.74),base*2.4,.38)*(uReflectivity*(.42+.58*fres));
            col=mix(dielectric,metal,uMetalness)*ao;
            vec3 h=normalize(l+v);
            float gloss=mix(10.0,210.0,1.0-uRoughness);
            float spec=pow(max(dot(n,h),0.0),gloss)*(1.0-uRoughness*.72);
            float tangentSpec=pow(max(dot(normalize(n+vec3(uAnisotropy*.18,0.0,0.0)),h),0.0),gloss*.45);
            float coatExp=mix(18.0,420.0,uClearcoatGloss);
            float coat=pow(max(dot(n,h),0.0),coatExp)*uClearcoat;
            col+=vec3(1.0,.94,.86)*(spec*uKeyPower+coat*2.0+tangentSpec*uAnisotropy*.24)*shadow;
            col+=sbFilm(ndv)*fres*uIridescence*(.28+env);
            col+=vec3(1.0,.86,.72)*pow(max(dot(n,-l),0.0),2.0)*uRimPower*.16;
            col+=vec3(1.0)*iTreble*uAudioReact*uTrebleFlash*(spec+coat)*.5;
        }else{
            vec3 floorBase=sbHsv(vec3(uFloorHue,.34,.055));
            float grid=pow(.5+.5*cos(p.x*3.2)*cos(p.z*3.2),12.0)*.045;
            vec3 floorEnv=sbEnvironment(reflect(-v,n));
            float reflectedBall=exp(-length(p.xz)*1.28)*uFloorGloss;
            col=floorBase*(.24+.58*ndl*shadow)+floorEnv*uFloorGloss*.13+grid;
            col+=sbHsv(vec3(uBaseHue,uSaturation,.8))*reflectedBall*.16;
        }
    }
    col*=uExposure;
    col*=1.0-uVignette*smoothstep(.18,1.05,length(uv));
    col=col/(1.0+col*.62);
    col+=(sbHash(fragCoord+fract(iTime)*731.7)-.5)*uGrain;
    col=pow(max(col,0.0),vec3(.78,.81,.85));
    fragColor=vec4(col,1.0);
}
