diff options
| author | tech kaczynski <tk@techkaczynski.dev> | 2026-09-22 22:38:57 +1000 |
|---|---|---|
| committer | tech kaczynski <tk@techkaczynski.dev> | 2026-09-22 22:38:57 +1000 |
| commit | 08cb8d6a585dd552b69a488d9db1be123f01467a (patch) | |
| tree | 77d50ce30bbc102b08486929ba654b5bef70af43 /res/sdr/ocean.fs | |
initial commit.
Diffstat (limited to 'res/sdr/ocean.fs')
| -rw-r--r-- | res/sdr/ocean.fs | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/res/sdr/ocean.fs b/res/sdr/ocean.fs new file mode 100644 index 0000000..53b81f5 --- /dev/null +++ b/res/sdr/ocean.fs @@ -0,0 +1,264 @@ +#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); +} |
