precision highp float; uniform float time; uniform vec2 resolution; out vec4 fragColor; #define Rot(a) mat2(cos(a),-sin(a),sin(a),cos(a)) #define antialiasing(n) n/min(resolution.y,resolution.x) #define S(d) 1.-smoothstep(-1.2,1.2, (d)*resolution.y ) #define B(p,s) max(abs(p).x-s.x,abs(p).y-s.y) #define deg45 .707 #define R45(p) (( p + vec2(p.y,-p.x) ) *deg45) #define Tri(p,s) max(R45(p).x,max(R45(p).y,B(p,s))) #define PUV(p) vec2(log(length(p)),atan(p.y/p.x)) #define SkewX(a) mat2(1.0,tan(a),0.0,1.0) #define SkewY(a) mat2(1.0,0.0,tan(a),1.0) #define PI 3.14159 #define mapSizeX 17. #define mapSizeY 20. #define FONT_H 0.06 #define FONT_THICK 0.02 vec3 pathData[8] = vec3[]( vec3(mapSizeX*0.4, 0.0, -mapSizeY*0.6), vec3(-mapSizeX*0.3, 0.0, -mapSizeY*0.7), vec3(-mapSizeX*0.6, 0.0, -mapSizeY*0.3), vec3(-mapSizeX*0.15, 0.0, -mapSizeY*0.05), vec3(-mapSizeX*0.35, 0.0, mapSizeY*0.3), vec3(-mapSizeX*0.15, 0.0, mapSizeY*0.65), vec3(mapSizeX*0.25, 0.0, mapSizeY*0.75), vec3(mapSizeX*0.6, 0.0, mapSizeY*0.5) ); float Hash21(vec2 p) { p = fract(p*vec2(234.56,789.34)); p+=dot(p,p+34.56); return fract(p.x+p.y); } float noise (vec2 st) { vec2 i = floor(st); vec2 f = fract(st); float a = Hash21(i); float b = Hash21(i + vec2(1.0, 0.0)); float c = Hash21(i + vec2(0.0, 1.0)); float d = Hash21(i + vec2(1.0, 1.0)); vec2 u = f*f*(3.0-2.0*f); return mix(a, b, u.x) + (c - a)* u.y * (1.0 - u.x) + (d - b) * u.x * u.y; } float yawBetweenPoints(vec3 a, vec3 b) { vec2 d = (b - a).xz; return atan(d.x, d.y); } float pitchBetweenPoints(vec3 a, vec3 b) { vec3 d = b - a; float h = length(d.xz); return atan(d.y, h); } float lerpAngle(float a, float b, float t) { vec2 ca = vec2(sin(a), cos(a)); vec2 cb = vec2(sin(b), cos(b)); vec2 c = mix(ca, cb, t); if(length(c) < 1e-6) return a; c = normalize(c); return atan(c.x, c.y); } mat3 rotX3(float a) { float c = cos(a), s = sin(a); return mat3(1.0, 0.0, 0.0, 0.0, c, s, 0.0, -s, c); } mat3 rotY3(float a) { float c = cos(a), s = sin(a); return mat3(c, 0.0, -s, 0.0,1.0, 0.0, s, 0.0, c); } mat3 rotZ3(float a) { float c = cos(a); float s = sin(a); return mat3( c, -s, 0.0, s, c, 0.0, 0.0, 0.0, 1.0 ); } // thx, iq! https://iquilezles.org/articles/distfunctions2d/ float sdPolygon(vec2[4] v, in vec2 p ) { float d = dot(p-v[0],p-v[0]); float s = 1.0; for( int i=0, j=4-1; i<4; j=i, i++ ) { vec2 e = v[j] - v[i]; vec2 w = p - v[i]; vec2 b = w - e*clamp( dot(w,e)/dot(e,e), 0.0, 1.0 ); d = min( d, dot(b,b) ); bvec3 c = bvec3(p.y>=v[i].y,p.ye.y*w.x); if( all(c) || all(not(c)) ) s*=-1.0; } return s*sqrt(d); } // thx, iq! https://iquilezles.org/articles/distfunctions2d/ float sdHexagon( in vec2 p, in float r ) { const vec3 k = vec3(-0.866025404,0.5,0.577350269); p = abs(p); p -= 2.0*min(dot(k.xy,p),0.0)*k.xy; p -= vec2(clamp(p.x, -k.z*r, k.z*r), r); return length(p)*sign(p.y); } //thx Shane! getting the hex uv logic from the Shane's implementation here: https://www.shadertoy.com/view/Xljczw const vec2 s = vec2(1.7320508, 1); vec4 getHex(vec2 p){ vec4 hC = floor(vec4(p, p - vec2(1, .5))/s.xyxy) + .5; vec4 h = vec4(p - hC.xy*s, p - (hC.zw + .5)*s); return dot(h.xy, h.xy)=0.3 && n<0.6){ d2 = pattern2(hgr.xy,n); } else { d = abs(sdHexagon(hgr.xy, 0.35))-0.05; if(!isFinal){ float d3 = c25(hgr.xy*0.7); d = min(d,d3); } else { float d3 = animNumbers(hgr.xy*0.7,n*5.); d = min(d,d3); } } d = min(d,d2); d = max(-(abs(sdHexagon(hgr.xy, 0.46))-0.05),d); col = mix(col,vec3(0.5),S(d)); return col; } vec2 sdRepeatNBetween(vec2 p, vec2 a, vec2 b, float r, int N) { vec2 dir = b - a; float len2 = dot(dir, dir); float k = dot(p - a, dir) / len2; float idx = floor(k * float(N - 1) + 0.5); idx = clamp(idx, 0.0, float(N - 1)); vec2 center = a + dir * (idx / float(N - 1)); return center; } float obstacles(vec2 p, vec2 a, vec2 b){ float r = 0.25; float num = distance(a,b) / (2.0 * r); vec2 c = sdRepeatNBetween(p,a,b,r,int(ceil(num))); float d = obstacle(p,c,r); return d; } float centerObjects(vec2 p, vec2 a, vec2 b){ float r = 1.5; float num = distance(a,b) / (2.0 * r); vec2 c = sdRepeatNBetween(p,a,b,r,int(ceil(num))); vec2 dir = b - a; float angle = atan(dir.y, dir.x)+radians(90.); float d = arrow(0.5*(p-c)*Rot(-angle)); float m = length(p-a)-r; d = max(-m,d); m = length(p-b)-r; d = max(-m,d); return d; } float sideRepeatLines(vec2 p, float dist){ vec2 prevP = p; p.y-=time*1.2; p.y = mod(p.y,1.2)-0.6; p*= SkewY(radians(-45.)); float d = B(p,vec2(0.1,0.4)); return max((abs(prevP.y)-dist),d); } float sideLines(vec2 p, float dist, float w){ p.x = abs(p.x)-(w*0.25); float d = sideRepeatLines(p,dist*0.3); return d; } vec2 calcNormal(vec2 prev, vec2 current, vec2 next) { vec2 dir = vec2(0.); if (prev != current) dir += current - prev; if (next != current) dir += next - current; return normalize(vec2(-dir.y, dir.x)); } vec3 courseMap(vec2 p, vec3 bgCol, vec3 col, float morph) { float d = B(p, vec2(mapSizeX, mapSizeY)); col = mix(col, bgCol, S(d)); d = max(d,drawHexBg(p*0.5)); col = mix(col, vec3(1.), 1.-smoothstep(-0.01,0.01,d)); float lsize = 1.7; float lsize2 = 2.2; const int pathCount = pathData.length(); vec2 normals[pathCount]; for (int i = 0; i < pathCount; i++) { vec2 prev = pathData[(i + pathCount - 1) % pathCount].xz; vec2 current = pathData[i].xz; vec2 next = pathData[(i + 1) % pathCount].xz; normals[i] = calcNormal(prev, current, next); } float d2 = 10.; float d3 = 10.; float d4 = 10.; float d5 = 10.; for (int i = 0; i < pathCount; i++) { vec2 a = pathData[i].xz; vec2 b = pathData[(i + 1) % pathCount].xz; vec2 nA = normals[i]; vec2 nB = normals[(i + 1) % pathCount]; float ld = sdPolygon( vec2[4](a + nA*lsize2, b + nB*lsize2, b - nB*lsize2,a - nA*lsize2),p); d2 = min(ld,d2); float od = obstacles(p, a + nA*lsize2, b + nB*lsize2); d3 = min(od,d3); od = obstacles(p, a - nA*lsize2, b - nB*lsize2); d3 = min(od,d3); od = centerObjects(p, a, b); d4 = min(od,d4); vec2 dir = b - a; float angle = atan(dir.y, dir.x)+radians(90.); vec2 midP = p-((a+b)*0.5); float w = distance(a + nA*lsize2,a - nA*lsize2); od = sideLines(midP*Rot(-angle),distance(a,b),w); d5 = min(od,d5); } col = mix(col, vec3(0.05), mix(0.,S(abs(d2)-0.5),morph)); col = mix(col, vec3(0.45), mix(0.,S(d2),morph)); // fix the small gaps. col = mix(col, vec3(0.3)+noise(p*20.*Rot(radians(45.)))*0.05, mix(0.,S(d2),morph)); vec2 dir = pathData[1].xz - pathData[0].xz; float angle = atan(dir.y, dir.x)+radians(90.); col = startLine((p-mix(pathData[0].xz,pathData[1].xz,0.12))*Rot(-angle),col,morph); col = mix(col, vec3(0.7), mix(0.,S(d3),morph)); col = mix(col, vec3(0.7), mix(0.,S(d4),morph)); col = mix(col, vec3(0.6), mix(0.,S(d5),morph)); return col; } vec3 courseMapForUI(vec2 p, vec3 bgCol, vec3 col, vec3 camPos) { float lsize = 1.2; const int pathCount = pathData.length(); float d = 10.; for (int i = 0; i < pathCount; i++) { vec2 a = pathData[i].xz; vec2 b = pathData[(i + 1) % pathCount].xz; float ld = lineTo(p, a, b) - lsize; d = min(d,ld); } col = mix(col, vec3(1.), S(d)); col = mix(col, vec3(0.2), S(abs(d)-0.2)); d = length(p - camPos.xz) - 0.8; col = mix(col, vec3(0.3), S(d)); return col; } vec3 carDoor(vec2 p, vec3 col){ vec2 prevP = p; float a = radians(45.); float d = B(p,vec2(0.013,0.15)); p.x = abs(p.x); p.y = abs(p.y)-0.12; d = max(dot(p,vec2(cos(a),sin(a))),d); col = mix(col, vec3(0.),S(d)); p = prevP; d = B(p,vec2(0.005,0.1)); col = mix(col, vec3(0.9),S(d)); return col; } vec3 carEngineFire(vec2 p, vec3 col){ p.x*=4.; p.y*=-0.9; p.x+=noise(p*Rot(radians(time*100.))*60.)*0.08; float d = Tri(p,vec2(0.1)); col = mix(col, vec3(0.5),1.-smoothstep(0.,0.05,d)); return col; } vec3 carMain(vec2 p, vec3 col, float leftVal, float rightVal){ vec2 prevP = p; float a = radians(45.); float d = B(p-vec2(0.,0.05),vec2(0.08,0.1)); p.x = abs(p.x); p.y-=0.19; d = max(dot(p,vec2(cos(a),sin(a))),d); p = prevP; float d2 = B(p,vec2(0.17,0.06)); d = min(d,d2); a = radians(-45.); p.y+=0.05; d2 = B(p,vec2(0.07,0.06)); p.x = abs(p.x); p.y+=0.11; d2 = max(dot(p,vec2(cos(a),sin(a))),d2); d = min(d,d2); p = prevP; p.x = abs(p.x)-0.12; a = radians(-45.); p.y+=0.035; d2 = B(p,vec2(0.05,0.06)); p.x = abs(p.x); p.y+=0.08; d2 = max(dot(p,vec2(cos(a),sin(a))),d2); d = min(d,d2); p = prevP; p.x = abs(p.x)-0.12; a = radians(45.); p.y-=0.03; d2 = B(p,vec2(0.035,0.06)); p.x = abs(p.x); p.y-=0.075; d2 = max(dot(p,vec2(cos(a),sin(a))),d2); d = min(d,d2); col = mix(col, vec3(0.),S(d)); p = prevP; p.y-=0.01; a = radians(45.); d = B(p,vec2(0.05,0.035)); p.x = abs(p.x)-0.06; p.y = abs(p.y); d = max(dot(p,vec2(cos(a),sin(a))),d); p = prevP; p.y+=0.05; p.y = abs(p.y)-0.008; d2 = B(p,vec2(0.05,0.005)); d = min(d,d2); p = prevP; p.y+=0.082; a = radians(-45.); d2 = B(p,vec2(0.06,0.012)); p.x = abs(p.x)-0.062; d2 = max(dot(p,vec2(cos(a),sin(a))),d2); p = prevP; p.y+=0.07; a = radians(-45.); float mask = B(p,vec2(0.05,0.012)); p.x = abs(p.x)-0.055; mask = max(dot(p,vec2(cos(a),sin(a))),mask); d2 = max(-mask,d2); d = min(d,d2); p = prevP; p.y-=0.095; a = radians(45.); d2 = B(p,vec2(0.06,0.04)); p.x = abs(p.x)-0.07; d2 = max(dot(p,vec2(cos(a),sin(a))),d2); p = prevP; p.y-=0.06; a = radians(45.); mask = B(p,vec2(0.045,0.016)); p.x = abs(p.x)-0.045; mask = max(dot(p,vec2(cos(a),sin(a))),mask); d2 = max(-mask,d2); d = min(d,d2); p = prevP; p.x = abs(p.x)-0.12; p.y+=0.035; a = radians(45.); d2 = B(p,vec2(0.032,0.045)); p.x = abs(p.x)-0.055; p.y = abs(p.y); d2 = max(dot(p,vec2(cos(a),sin(a))),d2); d = min(d,d2); p = prevP; p.x = abs(p.x)-0.12; p.y-=0.07; d2 = B(p,vec2(0.025,0.011)); p.x = abs(p.x)-0.02; d2 = max(dot(p,vec2(cos(a),sin(a))),d2); d = min(d,d2); p = prevP; p.x = abs(p.x)-0.12; p.y-=0.027; a = radians(45.); d2 = B(p,vec2(0.05,0.02)); p.x = abs(p.x)-0.04; d2 = max(dot(p,vec2(cos(a),sin(a))),d2); p = prevP; p.x = abs(p.x)-0.12; p.y-=0.008; a = radians(45.); mask = B(p,vec2(0.04,0.016)); p.x = abs(p.x)-0.03; mask = max(dot(p,vec2(cos(a),sin(a))),mask); d2 = max(-mask,d2); d = min(d,d2); col = mix(col, vec3(0.9),S(d)); p = prevP; float targetY = 0.03; // left door col = carDoor(p-vec2(-0.18,leftVal*targetY),col); // right door col = carDoor(p-vec2(0.18,rightVal*targetY),col); p.x = abs(p.x)-0.13; p.y+=0.2; col = carEngineFire(p,col); return col; } vec3 car(vec2 p, vec3 col, float leftVal, float rightVal){ vec2 prevP = p; p.x*= 0.5; p.y*= 2.5; p.y+=0.25; float d = length(p)-0.1; col = mix(col, col*vec3(0.7),S(d)); p = prevP; p.y+=sin(time*5.)*0.01; float targetSknewX = 10.; p*=SkewX(radians(leftVal*targetSknewX)); p*=SkewX(radians(rightVal*-targetSknewX)); col = carMain(p-vec2(0.0,0.03),col,leftVal,rightVal); return col; } vec3 carEnergy(vec2 p, vec3 col){ vec2 prevP = p; p.x*= 0.5; p.y*= 2.5; p.y+=time*2.; p.y = mod(p.y,0.22)-0.11; float d = abs(length(p)-0.1)-0.015; p = prevP; float mask = B(p-vec2(0.0,0.1),vec2(0.25,0.4)); vec2 cp = p-vec2(0.0,-0.35); cp.y*=2.; float d2 = length(cp)-0.25; mask = min(mask,d2); p = prevP; d = max(mask,d); col = mix(col, col*vec3(2.),S(d)); return col; } float carEffectParts(vec2 p){ vec2 prevP = p; p.x*=3.; float d = length(p)-0.4; return d; } vec3 carEffect(vec2 p, vec2 p2, vec3 col){ p.y+=time*2.; vec2 prevP = p; p*=6.; vec2 id = floor(p); vec2 gr = fract(p)-0.5; float n = Hash21(id); float d = carEffectParts(gr); if(n<0.5)d = 10.; d = max((length(p2)-0.22),d); col = mix(col,col*vec3(2.),S(d)); return col; } float cubicInOut(float t) { return t < 0.5 ? 4.0 * t * t * t : 0.5 * pow(2.0 * t - 2.0, 3.0) + 1.0; } float getTime(float t, float duration){ return clamp(t,0.0,duration)/duration; } vec3 effectOverlay1(vec2 p, vec2 p2, vec3 col){ vec2 id = floor(p); vec2 gr = fract(p)-0.5; float n = Hash21(id); float d = (n<0.5) ? 10. :B(gr,vec2(0.5*n,0.05*n)); vec3 ecol = col+(vec3(mix(0.0,1.,gr.x+0.5))* length(p2)-0.1); col = mix(col,ecol,S(d)); return col; } vec3 effectOverlay2(vec2 p, vec2 p2, vec3 col){ vec2 id = floor(p); vec2 gr = fract(p)-0.5; float n = Hash21(id); gr *= n; float d = (n<0.5) ? 10. : length(gr)-0.1; vec3 ecol = col+(vec3(1.0)* length(p2)-0.2); col = mix(col,ecol,S(d)); return col; } vec3 windEffect(vec2 p, vec3 col, float alpha, float s){ vec2 prevP = p; p*=s; p.y+=time*5.; vec2 id = floor(p); vec2 gr = fract(p)-0.5; float n = Hash21(id); gr *= n*2.; gr.x*=2.; float d = (n<0.5) ? 10. : arrow(gr); p = prevP; d = max(B(p, vec2(mapSizeX, mapSizeY)),d); col = mix(col,col+vec3(alpha),S(d)); return col; } vec3 ui(vec2 p, vec3 col){ vec2 prevP = p; float x = 0.58; float d = times(p-vec2(x,0.2)); col = mix(col,vec3(1.),S(d)); d = B(p-vec2(x,0.275),vec2(0.2,0.005)); col = mix(col,vec3(1.),S(d)); d = speedMeter(p-vec2(x+0.11,0.33)); col = mix(col,vec3(1.),S(d)); d = power(p-vec2(x,0.41)); col = mix(col,vec3(1.),S(d)); return col; } vec3 drawRaceScene(vec2 p, vec3 col){ float speed = 0.55; float totalSegs = float(pathData.length()); float firstSceneTotal = 1.5; float endSceneTotal = 3.; float totalLaps = 2.0; float raceTotal = totalSegs*totalLaps; float sceneTotal = firstSceneTotal+raceTotal+endSceneTotal; float t = time*speed; float scene = mod(t,sceneTotal); int raceRound = 1; int effect = 0; float initialUiX = 0.6; bool isFinal = false; if(scene=firstSceneTotal+totalSegs && scene=firstSceneTotal+raceTotal && scene < endAnimStart){ initialRot = 0.0; initialHeight = 0.0; initialUiX = 0.0; initialMapX = 0.0; time = getTime(time-(firstSceneTotal+raceTotal),duration); float anim = time; initialCarY = (anim*0.25); carScale = 1.+(anim*0.5); courseMorph = 1.; } else if(scene >= endAnimStart && scene < sceneTotal){ time = getTime(time-endAnimStart,duration*0.7); float anim = cubicInOut(time); easeValue = anim*90.; initialHeight = -(anim*8.); initialRot = radians(easeValue); initialCarY = 0.25-(anim*0.75); initialUiX= (anim*0.6); initialMapX = -(anim*40.); carScale = 1.5-(anim*0.5); courseMorph = 1.-anim; } if(scene>=firstSceneTotal+0.1 && scene=firstSceneTotal+raceTotal){ seg = 0; segT = 0.0; } // display race round if(scene=firstSceneTotal-0.25 && scene<=firstSceneTotal+0.3){ raceRound = 1; } else if(scene>=firstSceneTotal+totalSegs-0.25 && scene<=firstSceneTotal+totalSegs+0.4){ raceRound = 2; } else if(scene>=firstSceneTotal+raceTotal && scene=firstSceneTotal+totalSegs){ isFinal = true; } // effect if(scene>=firstSceneTotal+(totalSegs-1.0) && scene < firstSceneTotal+(totalSegs-0.25)){ effect = 1; } else if(scene>=firstSceneTotal+(raceTotal-1.0) && scene < firstSceneTotal+(raceTotal-0.25)){ effect = 2; } int segs = int(totalSegs); int i0 = seg; int i1 = (seg + 1) % segs; int i2 = (seg + 2) % segs; vec3 A = pathData[i0]; vec3 B = pathData[i1]; vec3 C = pathData[i2]; vec3 camPos = mix(A, B, segT); float yawAB = yawBetweenPoints(A, B); float yawBC = yawBetweenPoints(B, C); float blendT = smoothstep(0.7, 1.0, segT); float yaw = lerpAngle(yawAB, yawBC, blendT); float pitchAB = pitchBetweenPoints(A, B); float pitchBC = pitchBetweenPoints(B, C); float pitch = lerpAngle(pitchAB, pitchBC, blendT); mat3 camRotation = rotY3(yaw) * rotX3((pitch+radians(10.))+initialRot); vec3 ray = normalize(vec3(p, 1.0)); ray = camRotation * ray; float z = ray.z; vec2 proj = ray.xy / z; float d = 10.; float groundHeight = -0.5+initialHeight; float t = (groundHeight - camPos.y) / ray.y; vec3 hit = camPos + ray * t; // render background vec3 ray2 = ray; vec3 r = normalize(ray); r.x=abs(r.x); float newX = atan(r.x, r.z) / (2.0*PI) + 0.5; float newY = asin(clamp(r.y, -1.0, 1.0)) / PI + 0.5; vec2 bgp = vec2(newX,newY); col = bg(vec2(newX,newY),col,isFinal); // render course map if(t > 0.0) { col = courseMap(hit.xz,vec3(0.15),col,courseMorph); if(scene>=firstSceneTotal-0.25 && scene=firstSceneTotal-0.25 && scene 0.001 ? 1.0 : 0.0; float turnBlend = smoothstep(0.5, 1.0, segT); leftVal *= turnBlend; rightVal *= turnBlend; col = car((p- vec2(0.,-0.3+initialCarY))*carScale,col,leftVal,rightVal); if(scene>=firstSceneTotal+totalSegs+1. && scene=firstSceneTotal+totalSegs+1.7 && scene