#version 120 const int nLightCount = 2; //Amount of population considered 'half full' const float basePopulation = 7.0; const float distortDist = 4.0 / 2048.0; const float distortScale = 128.0; const float mergeNoise = 2.0; const float mergeScale = 2.0; const float mergeA = 5.0, mergeB = 7.0; const float extremeness = 1.8; const float baseSpec = 0.5; uniform sampler2D surfaces[3], cities, masks, diffNoise; uniform float population, time[2]; varying vec3 normal, binormal, tangent; varying vec3 npos, vertPaint, origo; varying vec2 uv; vec3 light[nLightCount]; float dist[nLightCount]; vec2 nightLights(vec3 incidentLight, float devLevel) { float brightness = clamp(max(incidentLight.r, max(incidentLight.g, incidentLight.b)) * 0.9, 0.0, 1.0); brightness /= 0.4; float popDensity = population / (population + basePopulation); float city = texture2D(cities, uv).r; float buildup = clamp((popDensity - (1.0 - devLevel)) * 2.0, 0.0, 1.0); float lightFactor = sqrt(max(popDensity, 0.4)); return vec2(lightFactor * max(1.0 - brightness, 0.0) * buildup, max(buildup - 0.5, 0.0) * 2.0) * city; } float hash2D(vec2 n) { return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453); } float hash3D(float n ) { return fract(sin(n)*753.5453123); } float noise2D(vec2 n) { const vec2 d = vec2(0.0, 1.0); vec2 b = floor(n), f = smoothstep(vec2(0.0), vec2(1.0), fract(n)); return mix(mix(hash2D(b), hash2D(b + d.yx), f.x), mix(hash2D(b + d.xy), hash2D(b + d.yy), f.x), f.y); } float noise3D(vec3 x ) { vec3 p = floor(x); vec3 f = fract(x); f = f*f*(3.0-2.0*f); float n = p.x + p.y*157.0 + 113.0*p.z; return mix(mix(mix( hash3D(n+ 0.0), hash3D(n+ 1.0),f.x), mix( hash3D(n+157.0), hash3D(n+158.0),f.x),f.y), mix(mix( hash3D(n+113.0), hash3D(n+114.0),f.x), mix( hash3D(n+270.0), hash3D(n+271.0),f.x),f.y),f.z); } float fbm3D(vec3 n) { float total = 0.0, amplitude = 1.0; for (int i = 0; i < 5; i++) { total += noise3D(n) * amplitude; n += n; amplitude *= 0.5; } return total; } float fbm2D(vec2 n) { float total = 0.0, amplitude = 1.0; for (int i = 0; i < 5; i++) { total += noise2D(n) * amplitude; n += n; amplitude *= 0.5; } return total; } float extremify(float x, float treme) { if(x < 0.5) x = pow(x, treme); else x = 1.0 - pow(1.0 - x, treme); return x; } void main() { vec3 color = gl_FrontMaterial.diffuse.rgb; vec2 mask = texture2D(masks, uv).rg; float distort = mask.r; vec2 lookup = uv; lookup.x *= 36.0; mask = texture2D(masks, lookup).rg; vec2 noise = texture2D(diffNoise, uv * distortScale).rg; lookup += distort * (noise - vec2(0.5)) * distortDist * vec2(1.0,0.5); vec3 surfA = texture2D(surfaces[0], lookup).rgb; vec3 surfB = texture2D(surfaces[1], lookup).rgb; vec3 surfC = texture2D(surfaces[2], lookup).rgb; float surf = lookup.x + (texture2D(diffNoise, lookup * mergeScale).b * mergeNoise - mergeNoise/2.0); surf = mod(surf, mergeA) / mergeA; surf = extremify(abs(surf - 0.5) * 2.0, extremeness); float surf2 = lookup.x + (texture2D(diffNoise, lookup * mergeScale).b * mergeNoise - mergeNoise/2.0); surf2 = mod(surf2, mergeB) / mergeB; surf2 = extremify(abs(surf2 - 0.5) * 2.0, extremeness); vec3 diffuseSamp = mix(surfA, surfB, surf); diffuseSamp = mix(diffuseSamp, surfC, surf2); float gloss = 0.5; float shininess = gl_FrontMaterial.shininess * (gloss + baseSpec); vec3 n = normalize(normal); vec3 v = normalize(npos); if(nLightCount > 0) { const int i = 0; light[i] = gl_LightSource[i].position.xyz + npos; dist[i] = length(light[i]); light[i] = light[i] / dist[i]; } if(nLightCount > 1) { const int i = 1; light[i] = gl_LightSource[i].position.xyz + npos; dist[i] = length(light[i]); light[i] = light[i] / dist[i]; } vec3 ambient = gl_LightModel.ambient.rgb * gl_FrontMaterial.ambient.rgb; vec3 diffuse = vec3(0.0); vec3 specular = vec3(0.0); float shadow = abs(mod(uv.x + time[0] + 0.0, 1.0/8.0) * 8.0 - 0.5) * 2.0; shadow = smoothstep(0.7,0.8, shadow); if(nLightCount > 0) { const int i = 0; float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i])); float intensity = max(0.0, dot(n, light[i])) * falloff * (1.0 - shadow * 0.9); diffuse += gl_LightSource[i].diffuse.rgb * intensity; vec3 r = -reflect(light[i], n); specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity); } if(nLightCount > 1) { const int i = 1; float falloff = 1.0 / (1.0 + (gl_LightSource[i].quadraticAttenuation * dist[i] * dist[i])); float intensity = max(0.0, dot(n, light[i])) * falloff * (1.0 - shadow * 0.9); diffuse += gl_LightSource[i].diffuse.rgb * intensity; vec3 r = -reflect(light[i], n); specular += gl_LightSource[i].specular.rgb * (pow(max(0.0, dot(r, v)), shininess) * intensity); } specular *= gl_FrontMaterial.specular.rgb * (0.5 + gloss - 0.5); vec2 cityState = nightLights(diffuse, mask.g); /* float turbulence = noise2D(vec2(uv.x * 100.0 -time * 15.0, uv.y * 6.0)); color = ((diffuse + ambient) * color * mix(diffuseSamp.rgb, vec3(0.5), cityState.y)) + specular + cityState.x * vec3(1.0,0.9,0.55); float clouds = max(0.0, fbm2D(vec2(uv.x * 125.0 + time * 17.0, uv.y * 5.0 + time * 1.0) + turbulence) * (fbm2D(vec2(uv.x*400 - time * 16.0, uv.y * 18.0 - time * 6.0)) * 0.5 + 0.5) - 0.75); clouds *= min(1.0, uv.x * (1.0 - uv.x) * 25.0); vec3 atmosphere = pow((1.0 - dot(v, n)), 4.0) * vec3(0.5,0.75,1.0) * vertPaint.r * 2.0; shadow = (1.0 - shadow * 0.8); gl_FragColor.rgb = mix(atmosphere + clouds, mix(color, color * 0.25, clouds), vertPaint.r);// * gl_LightSource[0].diffuse.rgb; gl_FragColor.a = mix(min(1.0, clouds + atmosphere.r), 1.0, vertPaint.r); */ float clearSky = clamp(abs(mod(uv.x * 5.0 + 0.5, 1.0) * 2.0 - 1.0) * 3.0 - 0.1, 0.0, 1.0); float turbulence = noise2D(vec2(uv.x * 300.0 -time[1] * 50.0, uv.y * 25.0)) * (1.0 - clearSky * 0.8); color = ((diffuse + ambient) * color * mix(diffuseSamp.rgb, vec3(0.5), cityState.y)) + specular + cityState.x * vec3(1.0,0.9,0.55); float clouds = min(1.0, (1.0 - max(0.0, fbm2D(vec2(uv.x * 125.0 + time[1] * 25, uv.y * 5.0 + time[1] * 5.0) + turbulence))) * 2.0);// * (fbm2D(vec2(uv.x*400 - time[1] * 16.0, uv.y * 18.0 - time[1] * 6.0)) * 0.5 + 0.5) - 0.75); // calculate a mask that makes clear skies over deserts and hides the UV seam that generates bad noise. clouds *= clearSky; //<0.5,0.7,1.0> <1.0,0.5,0.1> vec3 atmosphere = pow((1.0 - dot(v, n)), 2.0) * (mix(vec3(0.5,0.7,1.0), vec3(1.0,0.5,0.1), shadow) * (1.0 - pow(shadow, 2.0))) * (1.0 - vertPaint.r) * 2.0; shadow = (1.0 - shadow * 0.75); gl_FragColor.rgb = mix(mix(atmosphere, gl_LightSource[0].diffuse.rgb, clouds) * shadow, mix(color, color * 0.75, clouds), vertPaint.r); gl_FragColor.a = mix(min(1.0, clouds + atmosphere.r * shadow), 1.0, vertPaint.r); }