NatureGL Waterv1.1.0

Reference

Quality and performance

Four tiers, from integrated laptop GPUs to desktop cards. The top two switch the waves to a GPU FFT. Every setting can be overridden on its own, at creation or at runtime.

js
const water = await WaterSystem.create({ renderer, scene, camera, quality: 'medium' });
water.setQualityLevel('ultra');
water.setQualityLevel('high', { msaa: 0, ssrSteps: 32 });   // tier + overrides
water.qualityName;   // 'high'
water.quality;       // the effective settings
ultra
low
lowultra
The demo's default view on sunset, low against ultra. Low uses 24 Gerstner waves at 75 % resolution with no bloom, foam field or reflections; ultra uses the FFT with three 256² cascades.

#Every field

Fieldlowmediumhighultra
waveSourcegerstnergerstnerfftfftWhere the waves come from
fftSize128128128256FFT resolution per cascade (power of two)
fftCascades22231–3 cascades: 512 m, 67.3 m rotated 35°, 10.7 m rotated 78°
waves24324056Gerstner components. On FFT tiers only used if you override waveSource
gridAngular × gridRadial160 × 140208 × 170256 × 200320 × 240Polar grid round the camera
renderScale0.750.911Offscreen resolution relative to the drawing buffer
msaa0044World-pass MSAA samples
bloomLevels0456Bloom mips (0 = off)
foamResolution02565121024Persistent foam field texels (0 = instant foam only)
foamExtent256256320400Foam field size round the camera, m
ssrSteps002032Screen-space reflection steps (0 = sky only)
detailLayers1222Micro-normal layers
godRaySteps061016Underwater light-shaft samples (0 = off)
bathymetryResolution192256384512Default seabed bake size

setQualityLevel always recompiles the ocean material. The spectrum, grid, FFT and foam field are rebuilt only when their settings change. A bathymetry baked without an explicit resolution is re-baked at the new tier's size.

#Measured frame rates

Apple M5, headless Chromium with ANGLE/Metal, 1280×720, the full demo scene, uncapped (--disable-gpu-vsync --disable-frame-rate-limit), GPU otherwise idle, measured back to back with the original page:

fps
The original single-file page~200
medium (Gerstner 32)~220
high (FFT 128² × 2, MSAA 4×, SSR, bloom, foam field)~140 (120 with vsync)
ultra (FFT 256² × 3)~107

Run npm test to print the fps line on your own machine. Run one smoke test at a time; parallel GPU runs skew the numbers.

#Where the time goes

js
water.profiling = true;
setInterval(() => console.table(water.gpuTimings), 2000);   // ms per pass, smoothed
  • World-pass MSAA is the biggest single cost on high, about 20 %. setQualityLevel('high', { msaa: 0 }) trades it for aliased silhouettes.
  • Resolution. renderScale scales every offscreen target. The demo also lowers its pixel ratio in 0.25 steps (down to 0.75) when the frame time stays above about 34 ms.
  • Reflections. ssrSteps: 0 falls back to sky-only reflections.
  • Waves. fftCascades: 2 on ultra, or waveSource: 'gerstner' on high, cuts the FFT cost.
  • Foam. A smaller foamResolution keeps trails and whitecaps with coarser lace. 0 turns the field off.