#version 460 precision mediump float; out vec4 o; const float PI = 3.14159265; const float TAU = (2. * PI); const float PHI = sqrt(5.) * 0.5 + 0.5; layout(location = 0) uniform float syncs[7]; layout(location = 8) uniform float fft_output[512]; // FFT_SIZE / 4 float u_time = syncs[0]; vec2 getUV() { const vec2 scale = vec2(0.00104166667, 0.00185185185); return gl_FragCoord.xy * scale - 1.0; } mat2 rot2D(float angle) { float s = sin(angle); float c = cos(angle); return mat2(c, -s, s, c); } float noise(in vec2 xy, in float seed) { return fract(tan(distance(xy * PHI, xy) * seed) * xy.x); } float getScaledFFT(int index, float scale, float offset) { // Clamp index to valid range index = clamp(index, 0, 511); // Get raw FFT value float raw = fft_output[index]; // Apply logarithmic scaling: log(1 + value * scale) + offset return log(1.0 + raw * scale) + offset; } ///////////////// // GEOMETRY // ///////////////// // Hexagonal prism, circumcircle variant float fHexagonCircumcircle(vec3 p, vec2 h) { vec3 q = abs(p); return max(q.y - h.y, max(q.x * sqrt(3.) * 0.5 + q.z * 0.5, q.z) - h.x); //this is mathematically equivalent to this line, but less efficient: //return max(q.y - h.y, max(dot(vec2(cos(PI/3), sin(PI/3)), q.zx), q.z) - h.x); } float sdHex(vec3 pos, float i, float angle) { float d1 = fHexagonCircumcircle(pos, vec2(0.86, i)); return d1; } float sdSphere(vec3 p, float r){ return length(p) -r; } ////////////// // SCENE // ////////////// // instructions -> opU( { float to union with } , vec2( {put shape here}, {put material here} ) ) vec2 opU( vec2 d1, vec2 d2 ) { return (d1.x 100.) { // Reduced max distance break; } if(res.x < 0.001 * t) { // Less precise hit detection hit = 1.; break; } } if (t > 100.) t = 0.; return vec3(t, mat, hit); } //////////////// // SHADING // //////////////// float softshadow(in vec3 ro, in vec3 rd, float mint, float maxt, float w) { float res = 1.0; float t = mint; for(int i = 0; i < 6; i++) { if(t > maxt) break; float h = mapScene(ro + t * rd).x; res = min(res, h / (w * t)); t += clamp(h, 0.1, 0.80); if(res < -1.0) break; } res = max(res, -1.0); return 0.25 * (1.0 + res) * (1.0 + res) * (2.0 - res); } vec3 calcNormal(vec3 pos) { vec2 e = vec2(.01, 0.); vec3 n = vec3(mapScene(pos + e.xyy).x - mapScene(pos - e.xyy).x, mapScene(pos + e.yxy).x - mapScene(pos - e.yxy).x, mapScene(pos + e.yyx).x - mapScene(pos - e.yyx).x); return normalize(n); } vec3 addPointLight(vec3 lightPos, vec3 lightColor, float intensity, vec3 worldPos, vec3 viewDir, vec3 normal, float roughness) { // Light vector from surface to light vec3 lightDir = lightPos - worldPos; float lightDistance = length(lightDir); lightDir = normalize(lightDir); // Attenuation (quadratic falloff) float attenuation = intensity / (1.0 + 0.09 * lightDistance + 0.032 * lightDistance * lightDistance); // Diffuse lighting (Lambert) float NdotL = max(dot(normal, lightDir), 0.0); vec3 diffuse = lightColor * NdotL * attenuation; // Specular lighting (Blinn-Phong) vec3 halfDir = normalize(lightDir + (-viewDir)); float NdotH = max(dot(normal, halfDir), 0.0); float shininess = mix(128.0, 8.0, roughness); // Convert roughness to shininess vec3 specular = lightColor * pow(NdotH, shininess) * attenuation; // Fresnel effect vec3 F0 = vec3(0.04); // Base reflectance for dielectrics vec3 fresnel = F0 + (1.0 - F0) * pow(clamp(1.0 - max(dot(halfDir, lightDir), 0.0), 0.0, 1.0), 5.0); // Soft shadows float shadow = softshadow(worldPos + normal * 0.01, lightDir, 0.02, lightDistance, 4.0); // Combine diffuse and specular with shadow return (diffuse + specular * fresnel) * shadow; } /*vec3 addPointLight(vec3 lightPos, vec3 lightColor, float intensity, vec3 worldPos, vec3 viewDir, vec3 normal) { vec3 lightDir = normalize(lightPos - worldPos); float lightDistance = length(lightPos - worldPos); // Attenuation float attenuation = intensity / (1.0 + 0.1 * lightDistance + 0.01 * lightDistance * lightDistance); // Diffuse float NdotL = max(dot(normal, lightDir), 0.0); // Specular (Blinn-Phong) vec3 halfDir = normalize(lightDir - viewDir); float NdotH = max(dot(normal, halfDir), 0.0); float specular = pow(NdotH, 32.0); // Shadow float shadow = softshadow(worldPos + normal * 0.01, lightDir, 0.01, lightDistance, 8.0); return lightColor * (NdotL + specular * 0.5) * attenuation * shadow; } */ vec3 shading(vec3 v, vec3 n, vec3 dir, float material) { float shininess = 0.01; vec3 outMaterial = vec3(0.0, 0.0, 0.0); if(material == 0.) { outMaterial = vec3(0.8314, 0.2941, 0.2941); shininess = 0.1; } else if(material == 1.) { outMaterial = vec3(0.6196, 0.6118, 0.6118); shininess = .7; } else if(material == 2.) { outMaterial = vec3(0.3255, 0.4784, 0.3255); shininess = .2; } else if(material == 3.) { outMaterial = vec3(0.2471, 0.3059, 0.6314); shininess = 1.0; } else if(material == 4.) { outMaterial = vec3(0.9961, 1.0, 0.9922); shininess = .1; } else if(material == 5.) { outMaterial = vec3(0.9961, 1.0, 0.9922); shininess = .3; } vec3 lights = vec3(0.); lights += addPointLight(vec3(0., 40.0, -10.), vec3(0.77, 0.26, 0.73), 3.0, v, dir, n, shininess); lights += addPointLight(vec3(0., 2.0, -5.0), vec3(0.08, 0.62, 0.75), 3.0, v, dir, n, shininess); //lights += addPointLight(vec3(0., 25.0, 0.0), vec3(0.5137, 0.1961, 0.7725), 3.0, v, dir, n, shininess); lights += addPointLight(vec3(0., 20.0, 15.), vec3(0.77, 0.26, 0.73), 3.0, v, dir, n, shininess); vec3 lightDir = vec3(0., 1., -3); //float sun_dif = clamp(dot(n, lightDir), 0., 1.); //float shadow = softshadow(v + n * 0.01, lightDir, .01, 30., 18.); //lights += vec3(0.6431, 0.7804, 0.8588) * sun_dif * shadow * occ; float ind = clamp(dot(n, normalize(lightDir * vec3(.0, -1.0, -2.0))), 0.0, 1.0); lights += vec3(0.08, 0.62, 0.75) * ind * 0.8; return outMaterial * max(vec3(0.), lights); } vec3 postProcess(vec3 col) { // float random = noise(gl_FragCoord.xy, 0.01+u_time); // float random2 = noise(gl_FragCoord.xy, .2+u_time); //col += 0.075*clamp(vec3(0.5*random, 0.5*random2, 0.5*random), 0.02, 1.); // dither // Normalized pixel coordinates (from 0 to 1) vec2 screenCoord = getUV(); // Vignette float radius = 0.8; float d = smoothstep(radius, radius - 0.4, length(screenCoord - vec2(0.5))); col = mix(col, col * d, 1.); // Contrast float contrast = .75; col = mix(col, smoothstep(0.0, 1.0, col), contrast); // Colour mapping col *= vec3(1.0, 1.0, 1.0); col = pow(col, vec3(0.4545)); // gamma // fade in at the beginning //col*=vec3(clamp((u_time-1.8)*0.5,0., 1.)); // fade out at the end // col*=vec3(clamp((120.-u_time)*.35, 0., 1.)); return col; } ////////////////// // RENDERING // ////////////////// vec3 getCameraRayDir(vec2 uv, vec3 camPos, vec3 camTarget, float fov) { vec3 f = normalize(camTarget-camPos), r = normalize(cross(vec3(0,1,0), f)), u = cross(f,r), c = f*fov, i = c + uv.x*r + uv.y*u, d = normalize(i); return d; } vec3 render(vec2 uv) { vec3 camPos = vec3(0.0, 20.0, -10.0); vec3 camTarget = vec3(0.0, -1.0, 10.0); // Adjust target as needed float fov = 1.0; vec3 rayDir = getCameraRayDir(uv, camPos, camTarget, fov); vec3 col = vec3(0.); // background color vec3 hitPos = vec3(0); vec3 t = castRay(camPos, rayDir, hitPos); if(t.x > 0.0) { vec3 nor = calcNormal(hitPos); col = shading(hitPos, nor, rayDir, t.y); } return col; } void main() { vec3 finalColor = render(getUV()); //finalColor = postProcess(finalColor); o = vec4(finalColor, 1.); }