#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; } float noise(in vec2 xy, in float seed) { return fract(tan(distance(xy * PHI, xy) * seed) * xy.x); } // 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 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; } // Modify your mapScene function vec2 mapScene(in vec3 p) { float mat = 0.; float d = 1e9; float a = 0.; vec2 rippleCenter = vec2(7.,7.); float rippleSpeed = 4.0; float rippleFreq = 1.0; float rippleDecay = 0.25; // Hexagonal grid float hexGap = 0.2; for(float j = 0.; j < 16.; j++) { vec3 po = p; po += vec3((1.6 + hexGap) * 8, -5., -(1.88 + hexGap) * 10); po += vec3(0, 0., (1.88 + hexGap) * j); for(float i = 0.; i < 16.; i++) { if(mod(i, 2.) == 0.) { po -= vec3(1.6 + hexGap, 0., 1.); } else { po += vec3(-(1.6 + hexGap), 0., 1.); } // Add individual hexagon ripples based on distance from center int hexDist = int(length(vec2(i, j) - rippleCenter.xy)); //float wave = sin(hexDist * rippleFreq - u_time * rippleSpeed) * exp(-hexDist * rippleDecay); // Apply ripple to hexagon size and position // float hexSize = fft_output[int(i+1)*int(j+1)]*5.0; // sin(1.5*u_time)+ wave //float hexSize = fft_output[hexDist] * 5.0; float hexSize = getScaledFFT(hexDist, 15.0, 0.0) * 2.0; // Adjusted multiplier a = sdHex(po, 1. + hexSize, 0.); d = min(d, a); if(d == a) { mat = 1.; } } } return vec2(d, mat); } vec3 castRay(vec3 ro, vec3 rd, inout vec3 pos) { float t = 0.; float mat = 0.; float hit = 0.; // Reduced from 40 to 24 steps for(int i = 0; i < 30; i++) { pos = ro + rd * t; vec2 res = mapScene(pos); // Increase step size multiplier for faster marching t += res.x; mat = res.y; if(t > 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); } 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; } */ float getAmbientOcc(vec3 p, vec3 n) { float occ = 0.; float weight = 1.; for(int i = 0; i < 8; i++) { float len = 0.01 + 0.02 * float(i * i); float dist = mapScene(p + n * len).x; occ += (len - dist) * weight; weight *= 0.85; } return 1.0 - clamp(0.6 * occ, 0., 1.); } 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(-10., 10.0, 0.), vec3(0.77, 0.26, 0.73), 3.0, v, dir, n, shininess); lights += addPointLight(vec3(0., 10.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); 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; } vec3 getCameraRay(vec2 uv, vec3 camPos, vec3 camTarget, float fov) { // Calculate camera's orthonormal basis vec3 camForward = normalize(camTarget - camPos); vec3 camRight = normalize(cross(vec3(0.0, 1.0, 0.0), camForward)); vec3 camUp = normalize(cross(camForward, camRight)); vec3 rayDir = normalize(uv.x * camRight + uv.y * camUp + camForward * fov); return rayDir; } // Camera positioning function vec3 getCameraPosition(float time, int cameraMode) { vec3 camPos; camPos = vec3(0.0, 30.0, -10.0); if(cameraMode == 1) { // Orbiting camera vec3 camTarget = vec3(0.0, 0.0, -20.0); float orbitRadius = 20.0; float orbitSpeed = 0.2; float orbitHeight = 10.0; float angle = time * orbitSpeed; camPos = camTarget + vec3(cos(angle) * orbitRadius, orbitHeight + sin(time * 0.8) * 2.0, sin(angle) * orbitRadius); } else if(cameraMode == 2) { // Smooth camera movement float t = time * 0.06; camPos = vec3(sin(t) * 15.0, 30.0 + cos(t * 0.5) * 5.0, cos(t) * 15.0); } else if(cameraMode == 3) { // First person style movement float walkSpeed = 2.0; camPos = vec3(sin(time * walkSpeed) * 0.1, 8.0 + sin(time * walkSpeed * 2.0) * 0.05, time * 0.5); } return camPos; } // Main camera function that combines everything vec3 setupCamera(vec2 uv, float time, int positionMode) { vec3 camPos = getCameraPosition(time, positionMode); vec3 camTarget = vec3(0.0, -1.0, 10.0); // Adjust target as needed float fov = 1.; return getCameraRay(uv, camPos, camTarget, fov); } // Simplified version of your render function using the new camera system vec3 render(vec2 uv) { // Choose camera modes: // Position: 0=static, 1=orbit, 2=smooth, 3=walk // Ray: 0=standard, 1=zoom, 2=dof int positionMode = 2; // Static vec3 rayDir = setupCamera(uv, u_time, positionMode); vec3 camPos = getCameraPosition(u_time, positionMode); vec3 col = vec3(0.102, 0.2431, 0.3412); 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.); }