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
struct VSOut {
  @builtin(position) pos: vec4f,
  @location(0) uv: vec2f,
};
struct CompositeUniforms {
  bloomStrength: f32,
  bloomRadius: f32,
  _pad0: vec2f,
  bloomFactors0: vec4f,
  bloomFactors1: vec4f,
};
@group(0) @binding(0) var<uniform> uniforms: CompositeUniforms;
@group(0) @binding(1) var blurTexture1: texture_2d<f32>;
@group(0) @binding(2) var blurTexture2: texture_2d<f32>;
@group(0) @binding(3) var blurTexture3: texture_2d<f32>;
@group(0) @binding(4) var blurTexture4: texture_2d<f32>;
@group(0) @binding(5) var blurTexture5: texture_2d<f32>;
@group(0) @binding(6) var linearSampler: sampler;
 
@vertex fn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {
  var p = array<vec2f, 3>(vec2f(-1.0, -3.0), vec2f(-1.0, 1.0), vec2f(3.0, 1.0));
  var out: VSOut;
  out.pos = vec4f(p[vi], 0.0, 1.0);
  out.uv = vec2f(p[vi].x * 0.5 + 0.5, 0.5 - p[vi].y * 0.5);
  return out;
}
 
fn factor(i: u32) -> f32 {
  let v = array<f32, 8>(
    uniforms.bloomFactors0.x, uniforms.bloomFactors0.y, uniforms.bloomFactors0.z, uniforms.bloomFactors0.w,
    uniforms.bloomFactors1.x, uniforms.bloomFactors1.y, uniforms.bloomFactors1.z, uniforms.bloomFactors1.w
  );
  return v[i];
}
 
fn lerpBloomFactor(f: f32) -> f32 {
  let mirrorFactor = 1.2 - f;
  return mix(f, mirrorFactor, uniforms.bloomRadius);
}
 
@fragment fn fs_main(in: VSOut) -> @location(0) vec4f {
  // UnrealBloomPass._getCompositeMaterial @ three 0.184.0. Tint colors are all white.
  let bloom = 3.0 * uniforms.bloomStrength * (
    lerpBloomFactor(factor(0u)) * textureSample(blurTexture1, linearSampler, in.uv).rgb +
    lerpBloomFactor(factor(1u)) * textureSample(blurTexture2, linearSampler, in.uv).rgb +
    lerpBloomFactor(factor(2u)) * textureSample(blurTexture3, linearSampler, in.uv).rgb +
    lerpBloomFactor(factor(3u)) * textureSample(blurTexture4, linearSampler, in.uv).rgb +
    lerpBloomFactor(factor(4u)) * textureSample(blurTexture5, linearSampler, in.uv).rgb
  );
  let bloomAlpha = max(bloom.r, max(bloom.g, bloom.b));
  return vec4f(bloom, bloomAlpha);
}