Particles ocean

A deep-water surface driven by a real inverse FFT. A Phillips spectrum evolves in frequency space, Stockham passes produce a displacement field, and half a million particles ride the waves through an HDR bloom chain.

Open fullscreen
import { fullscreenPosition, wrapLoad } from "./ocean-common.wgsl";
 
struct NormalFoamUniforms {
  resolution: f32,
  worldSize: f32,
  displacementScale: f32,
  choppiness: f32,
  foamThreshold: f32,
};
@group(0) @binding(0) var<uniform> u: NormalFoamUniforms;
@group(0) @binding(1) var u_displacement: texture_2d<f32>;
 
struct VSOut { @builtin(position) pos: vec4f };
 
@vertex fn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {
  var out: VSOut;
  out.pos = fullscreenPosition(vi);
  return out;
}
 
@fragment fn fs_main(in: VSOut) -> @location(0) vec4f {
  let N = i32(u.resolution);
  let coord = vec2i(in.pos.xy - vec2f(0.5));
  let dx = u.worldSize / u.resolution;
 
  let c = wrapLoad(u_displacement, coord, N).xyz * u.displacementScale;
  let r = wrapLoad(u_displacement, coord + vec2i(1, 0), N).xyz * u.displacementScale;
  let l = wrapLoad(u_displacement, coord - vec2i(1, 0), N).xyz * u.displacementScale;
  let t = wrapLoad(u_displacement, coord + vec2i(0, 1), N).xyz * u.displacementScale;
  let b = wrapLoad(u_displacement, coord - vec2i(0, 1), N).xyz * u.displacementScale;
 
  let dhdx = (r.y - l.y) / (2.0 * dx);
  let dhdz = (t.y - b.y) / (2.0 * dx);
  let normal = normalize(vec3f(-dhdx, 1.0, -dhdz));
 
  let dDxdx = (r.x - l.x) / (2.0 * dx);
  let dDzdz = (t.z - b.z) / (2.0 * dx);
  let dDxdz = (t.x - b.x) / (2.0 * dx);
  let dDzdx = (r.z - l.z) / (2.0 * dx);
  let J = (1.0 + dDxdx) * (1.0 + dDzdz) - dDxdz * dDzdx;
 
  let foam = 1.0 - smoothstep(u.foamThreshold, u.foamThreshold + 0.8, J);
  return vec4f(normal, foam);
}