#version 330 in vec3 world_position; uniform float time; uniform vec3 camera_position; uniform vec3 sun_direction; uniform vec3 moon_direction; uniform vec3 sun_colour; uniform vec3 moon_colour; uniform vec3 ambient_colour; uniform vec3 horizon_colour; uniform vec3 zenith_colour; uniform float sun_intensity; uniform float moon_intensity; uniform float ambient_intensity; out vec4 finalColor; float hash12(vec2 position) { vec3 hash_value = fract(position.xyx * 0.1031); hash_value += dot(hash_value, hash_value.yzx + vec3(33.33)); return fract((hash_value.x + hash_value.y) * hash_value.z); } float noise2(vec2 position) { vec2 cell = floor(position); vec2 fraction = fract(position); vec2 curve = fraction * fraction * (vec2(3.0) - 2.0 * fraction); float a = hash12(cell); float b = hash12(cell + vec2(1.0, 0.0)); float c = hash12(cell + vec2(0.0, 1.0)); float d = hash12(cell + vec2(1.0, 1.0)); return mix( mix(a, b, curve.x), mix(c, d, curve.x), curve.y ); } float fbm(vec2 position) { vec2 sample_position = position; float value = 0.0; value += noise2(sample_position) * 0.500; sample_position *= 2.02; value += noise2(sample_position) * 0.250; sample_position *= 2.03; value += noise2(sample_position) * 0.125; sample_position *= 2.01; value += noise2(sample_position) * 0.0625; return value; } float wave_height(vec2 position) { vec2 domain = position * 0.008; vec2 warp = vec2( fbm(domain * 0.8 + vec2(time * 0.030, 0.0)), fbm(domain * 0.8 + vec2(7.3, time * 0.024)) ); domain += (warp - vec2(0.5)) * 1.8; float wave_a = sin(dot(domain, normalize(vec2(1.0, 0.28))) * 7.0 + time * 0.45); float wave_b = sin(dot(domain, normalize(vec2(-0.42, 1.0))) * 11.0 + time * 0.72); float wave_c = sin(dot(domain, normalize(vec2(0.75, 0.63))) * 15.0 - time * 0.95); float wave_d = sin(dot(domain, normalize(vec2(-0.88, 0.18))) * 5.0 + time * 0.28); return wave_a * 0.50 + wave_b * 0.25 + wave_c * 0.15 + wave_d * 0.10; } vec3 wave_normal(vec2 position) { const float offset = 2.0; float height = wave_height(position); float height_x = wave_height(position + vec2(offset, 0.0)); float height_z = wave_height(position + vec2(0.0, offset)); return normalize(vec3( height - height_x, 0.55, height - height_z )); } float light_glint(vec3 normal, vec3 view_direction, vec3 light_direction, float power) { vec3 half_direction = normalize( light_direction + view_direction ); return pow(max(dot(normal, half_direction), 0.0), power); } void main() { vec2 position = world_position.xz; vec3 normal = wave_normal(position); vec3 view_direction = normalize( camera_position - world_position ); /* Large-scale irregular turquoise/blue variation. */ float patch_a = fbm( position * 0.0025 + vec2(time * 0.010, 3.1) ); float patch_b = fbm( position * 0.0013 + vec2(-2.7, time * 0.006) ); float water_patch = clamp( patch_a * 0.65 + patch_b * 0.35, 0.0, 1.0 ); vec3 deep_blue = vec3(0.02, 0.42, 0.70); vec3 turquoise = vec3(0.03, 0.73, 0.74); vec3 aqua = vec3(0.16, 0.78, 0.88); vec3 base_colour = mix( deep_blue, turquoise, water_patch ); base_colour = mix( base_colour, aqua, smoothstep(0.72, 1.0, water_patch) ); /* * Actually light the base water using the current environment. * During the day this stays bright; at night it becomes properly * dark and takes on the ambient sky colour. */ vec3 ambient_light = vec3(0.10) + ambient_colour * ambient_intensity * 1.8; vec3 colour = base_colour * ambient_light; /* * Fake reflected sky. The reflected wave normal determines * whether we see more horizon or zenith colour. */ float facing = max(dot(normal, view_direction), 0.0); float fresnel = 1.0 - facing; fresnel *= fresnel; fresnel *= fresnel; vec3 reflected_direction = reflect( -view_direction, normal ); float sky_height = smoothstep( -0.12, 0.70, reflected_direction.y ); vec3 reflected_sky = mix( horizon_colour, zenith_colour, sky_height ); colour = mix( colour, reflected_sky, 0.18 + fresnel * 0.45 ); /* Sun and moon glints. */ float sun_glint = light_glint( normal, view_direction, normalize(sun_direction), 24.0 ); float moon_glint = light_glint( normal, view_direction, normalize(moon_direction), 20.0 ); colour += sun_colour * sun_glint * sun_intensity * 0.30; colour += moon_colour * moon_glint * moon_intensity * 0.18; /* Irregular whitecaps. */ float cap_a = fbm( position * 0.020 + vec2(time * 0.080, -time * 0.050) ); float cap_b = fbm( position * 0.045 + vec2(-4.0, time * 0.110) ); float cap_field = cap_a * 0.55 + cap_b * 0.45; float crest = wave_height( position * 1.35 + vec2(17.0, -9.0) ) * 0.5 + 0.5; float steepness = clamp( 1.0 - normal.y, 0.0, 1.0 ); float whitecap = smoothstep(0.72, 0.86, cap_field) * smoothstep(0.58, 0.80, crest) * smoothstep(0.02, 0.10, steepness); /* * Foam also receives environment lighting rather than remaining * glowing white at night. */ vec3 foam_colour = vec3(0.92, 0.98, 0.98); foam_colour *= vec3(0.18) + ambient_colour * ambient_intensity * 1.5; foam_colour += sun_colour * sun_intensity * 0.25; colour = mix( colour, foam_colour, whitecap * 0.90 ); finalColor = vec4(0.03, 0.73, 0.74, 1.0); }