跳到正文
cccskills
免费在 GitHub 公开

3d-ultra-realistic-water

Build open-ocean scenes in Three.js with rolling waves, reflections, sun glitter, foam, and boat wakes. A reusable module also provides wave-height queries for floating objects and techniques for keeping distant water stable as the camera moves.

中文简介正在准备中。当前显示英语简介。

原文查看原文说明

Build an ultra-realistic open ocean in Three.js with a deep-water Gerstner spectrum shaded per pixel from analytic derivatives, each wave faded at its own pixel footprint, plus Fresnel sky-cube and planar reflections, subsurface light through the crests, HDR sun glitter, Jacobian lace foam, a Kelvin wake, hull foam, buoyancy queries and aerial perspective into the sky's horizon haze. Use for seas, oceans, sunset or daylight water, sun glitter paths, ship or boat scenes, wakes, coastal flyovers and any 3D scene with a moving camera and a water horizon. Includes the approved water from Pirate Ship Sunset as a reusable module and demo.

实测评分 未实测

查看安装方法

适用场景

  • Create ocean scenes in daylight or at sunset
  • Add wakes and hull foam to ships and boats
  • Make floating objects follow the rendered waves
  • Render coastal flyovers with a distant water horizon

包含的文件(11)

  • SKILL.md10.6 KB
  • agents/openai.yaml328 B
  • assets/ocean.mjs29.6 KB
  • demo/build.mjs5.3 KB
  • demo/index.html802.4 KB
  • demo/preview.jpg219.3 KB
  • demo/PROMPT.md3.7 KB
  • demo/src/ui.css5.7 KB
  • demo/src/ui.html1.8 KB
  • demo/src/ui.js3.4 KB
  • references/ocean.md5.9 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

3D Ultra-Realistic Water

Use this for an open sea seen by a moving camera, with a horizon and a sun. Reach for something else in these cases:

  • Mirror-flat water seen from the waterline with no mesh: a full-screen analytic water plane is cheaper.
  • Ripples that follow the cursor on a UI surface: use shaders-cursor-ripples.
  • A small pool that reacts to touch: use a ping-pong height field.

Source: extracted from Pirate Ship Sunset, a single-file Three.js r169 scene in which a three-masted ship sails a rolling sea at sunset under an auto-cycling camera. The water had to stay convincing from 2 m above the waves out to a 12 km horizon.

The mechanism

Shade the sea per pixel from the analytic derivatives of a deep-water Gerstner sum, and fade each wave out once its wavelength falls below the pixel footprint. The per-pixel derivatives keep the glitter and foam sharp on a mesh whose triangles are 30 m wide at the horizon. The footprint fade stops the far sea from boiling into moiré.

Reuse the working effect

Copy assets/ocean.mjs. You pass in the project's own THREE, and the module does not import a second copy. Before you integrate it, read references/ocean.md, which covers the full constants, render order, buoyancy and post. The demo runs this same module inside the original scene. demo/build.mjs rebuilds it from the reference page.

import { createWaves, bakeOceanDetail, createOcean, createWake, createPlanarReflection } from './ocean.mjs';

const waves = createWaves({ direction: 1.22 });                 // wind heading, radians
const ocean = createOcean(THREE, {
  waves,
  detailTexture: bakeOceanDetail(THREE, renderer),              // baked once, 512²
  skyCube,                                                      // cube rendered from YOUR sky shader
  hazeGLSL: SKY_GLSL + 'vec3 oceanHaze(vec3 d){ return hazeColor(d); }',
  hazeUniforms: skyUniforms,                                    // may include uSunDir
  wake: createWake(THREE, renderer, { shipLength: 34 }),        // optional
  hull: { halfWidths, zMin: -18, zMax: 18, bowZ },              // optional, ship-local metres
});
scene.add(ocean.mesh);
const reflection = createPlanarReflection(THREE, renderer, { layer: 1 });
ocean.setReflection(reflection);

// every frame, in this order
waves.update(dt);                                  // CPU phases, before any height() query
ship.y = waves.height(ship.x, ship.z);             // buoyancy uses the same spectrum
ocean.update({ time, camera, ship: { x, z, fx, fz } });
wake.update(x, z, fx, fz, speed * dt);
reflection.render(scene, camera);                  // before the main pass
renderer.render(scene, camera);                    // into a HalfFloat target, then bloom + ACES

The stack, with the numbers that shipped

layerwhat it doesconstants
Spectrum8 Gerstner waves, deep-water dispersion ω = √(g·k)L 78 → 3.1 m (×0.63 per step), A 0.8 → 0.02 m, headings 0, +23, −17, +38, −31, +12, −49, +64° off the wind, Q 0.17 → 0.03 (Σ 0.77)
Gridexponential rings centred under the camerar = 11.92·(e^(0.033·i) − 1), 210 rings × 400 segments = 84,001 verts, 167,600 tris, reaches ~12.2 km; spacing 0.033·(r + 12)
Footprint fadeper wave, in both shadersfade = 1 − smoothstep(0.18·L, 0.5·L, footprint); vertex footprint 1.2 × ring spacing, pixel footprint 2 × (fwidth(x) + fwidth(z))
Normalper pixel, normalize(cross(dP/dz, dP/dx))Jacobian J = dPdx.x·dPdz.z − dPdx.z·dPdz.x drives crest foam
Micro detailbaked 512² tile: 48 sines with integer wave vectors, amplitude ∝ |k|^−1.35two octaves at 19 m and 7.3 m (the second rotated 90°), strength 0.4·e^(−d/500) + 0.08
ReflectionSchlick with water F0 = 0.02; 256² sky cube re-rendered every 0.25 s; half-res planar pass for the ship and islandsplanar UV nudged by N.xz · (0.06, 0.03) · clamp(30/d, 0.2, 1)
Bodydeep colour plus sunlit subsurface through the wave crestsdeep (0.006, 0.03, 0.048), scatter (0.035, 0.3, 0.27) linear; scatter × toward-sun² × crest height × (0.35 + 4·(1 − N.y))
GlitterHDR specular, left for the bloom to catchpow(R·L, 90)·1.2 + pow(R·L, 12)·0.06 + pow(R·L, 1400)·40, sun colour (4.2, 2.3, 1.1); bloom threshold 1.6, knee 0.8
Foamthresholds a Worley lattice by a mask, so the foam stays lacycrest smoothstep(0.6, 0.28, J); foam = smoothstep(1 − m, 1.2 − m, pattern·0.88 + m·0.22); gone by 1600 m
Wake512² half-float field over 380 m, trailing the shipKelvin arm offset 1.2 + 0.35·a (atan 0.35 ≈ 19.3°), arm decay 115 m, stern wash 170 m, calm slick 260 m
Hazefogs into the sky's own horizon colourfog = (1 − e^(−d·2.8e-4))^1.6

Rules, each with the failure it prevents

All of these were A/B tested in the demo scene with the sim frozen, so only the one change differed between frames.

  • Fade every wave at its own footprint, in both the vertex and fragment shaders. Without the fade, the 3–8 m waves are sampled below their wavelength. The far sea turns into dense grain right up to a hard horizon, and the sun's sheen on the far water disappears. Between consecutive frames, the flicker in the far band went from 2.3 to 5.8 grey levels on average, with the 95th percentile rising from 5 to 23. In motion, that reads as boiling.
  • Compute the normal in the fragment shader from the analytic derivatives. If you shade from the interpolated vertex normal, detail disappears past the near field. The far sea turns into long smooth streaks that follow the mesh rings, and the glitter smears instead of breaking into sparkles.
  • Keep ΣQ below 1. Steepness scales the sideways displacement. Past a sum of about 1, the Jacobian goes negative: the surface folds through itself, and the foam mask saturates into flat white slabs on the crests. setSeaState() lets amplitude keep rising but caps steepness at ΣQ = 0.95.
  • Clamp the reflection vector above the horizon: R.y = abs(R.y) + 0.002. Some wave backs reflect downward. Without the clamp, those faces sample the flat lower half of the sky cube and read as pale grey smears with no glitter. At a grazing shot, 3–4% of the frame changed by as much as 154 levels.
  • Fog into the same haze function the sky draws. One flat fog colour matches the sky only on the sun side. Everywhere else it leaves a hard line where the water meets the sky.
  • Scroll detail and foam textures with the floating-origin offset, using the same divisor as the sampling scale. The reference sampled foam at /9 and /3.3 but offset it by /11 and /4.1. At every 1200 m rebase, the foam pattern jumped by about 24 tiles. The module fixes this.
  • Wrap wave phases to [0, 2π) on the CPU in float64, including the floating-origin term. If the shader receives raw time, ω·t passes 16,000 rad for the 3 m wave within an hour. That makes the float32 sin() argument the part that degrades first. This rule is precision reasoning and was not observed in the demo.
  • Query height with the same spectrum you render. Gerstner waves move points sideways by up to 4.5 m here, so height(x, z) inverts the horizontal displacement with 4 fixed-point steps before it reads y. Reading disp(x, z).y directly was off by 0.15 m on average and by up to 1.17 m on steep crests, across 20,000 samples. After 4 steps, the worst horizontal miss is 0.18 m.

Colour and pipeline

  • Every uniform colour is linear HDR. Render the scene into a HalfFloat target, and tone-map once in the composite: ACES, then sRGB. The reference sets renderer.toneMapping = NoToneMapping and applies ACES itself, at exposure 0.9 and bloom 0.35.
  • The sky cube is rendered from the page's own sky shader, with the tiny sun disc left out. The analytic specular term draws the sun's path on the water.
  • The planar reflection renders only a layer holding the objects that stand in the water. It clips at y > −0.05, and it reuses the frame's shadow map (shadowMap.autoUpdate = false).

Cost

The ocean mesh is 167,600 triangles. Its fragment shader runs the 8-wave loop once per pixel, plus 6 texture taps, 1 cube tap and 1 planar tap. The planar pass re-renders the reflection layer at half resolution. Under heavy machine load, headless Chrome ran the whole Pirate Ship Sunset scene in 10–13 ms per frame at 1440×900. Hiding the ocean or skipping the reflection pass each changed that by less than the run-to-run noise (about ±2 ms), so neither is the bottleneck. The reference's lever is adaptive resolution: DPR is capped at 1.6, and a resolution scale of 0.6–1.0 steps down when the 60-frame average passes 26 ms and back up below 14.5 ms.

Lifecycle and accessibility

  • Clamp dt to 1/20 s. The reference also rebases the world every 1200 m, shifting the ship, wake, particles, camera and waves.offX/offZ together.
  • Under reduced motion, stop simulation time and keep rendering until the intro fade finishes, then hold the frame. Re-render only when a control changes. The demo's __OD.hold() implements this.
  • Pause with the tab: setAnimationLoop stops when the page is hidden, and the dt clamp absorbs the gap when it resumes.

Verify

  • With the camera 2 m above the water facing the sun, the far sea resolves into haze and sheen, not grain. Toggling the footprint fade off makes the far band flicker between frames.
  • Per-vertex normals visibly lose the far micro detail. If they don't, the normal is still being taken from the vertices.
  • Raise the sea state to 1.6: the crests stay lacy, with no flat white slabs.
  • At a grazing view, no pale grey smears appear on the wave backs.
  • Anti-sun horizon: the water blends into the sky with no line.
  • Floating objects ride the crests (height() inverted), and the wake arms open at about 19°.
  • Reduced motion holds a composed still, controls still re-render, and the console is clean at 1440×900 and 390×844.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Build woven fabric in Three.js with visible threads, realistic sheen, seams, and thick hems. The skill covers folds that preserve texture proportions, light passing through thin cloth, and variations in dye and wear.

中文简介正在准备中。当前显示英语简介。

  • Creating backlit curtains
  • Differentiating linen, silk, and wool
  • Adding hems and stitches for close-up views
MengTo/Skills69792026年10月18日 更新

Build falling leaves in Three.js and similar 3D scenes, with visible front, edge, and back views as they tumble and drift. Coordinate wind, scene depth, camera-aware recycling, and performance checks for a convincing autumn effect.

中文简介正在准备中。当前显示英语简介。

  • Adding falling autumn leaves to a 3D scene
  • Coordinating leaf tumble, sideways drift, and wind
  • Managing leaves around buildings and camera movement
MengTo/Skills69792026年10月18日 更新

Add coordinated seasonal transitions to an existing 3D scene. Blend foliage, lighting, sky, ground, snow, and particles from one shared state while preserving camera controls and interactions, so the same place remains recognizable throughout spring, summer, fall, and winter.

中文简介正在准备中。当前显示英语简介。

  • Add a four-season selector to an outdoor 3D scene
  • Show seasonal changes in an architectural walkthrough
  • Coordinate foliage, sunlight, sky, and ground appearance
MengTo/Skills69792026年10月18日 更新

Create or integrate detailed 3D models with smooth outlines and believable edges for close-up views. Prepare lower-detail versions for distant viewing, prioritize loading, and measure rendering costs in the actual scene.

中文简介正在准备中。当前显示英语简介。

  • Creating a detailed 3D model for close-up views
  • Replacing visibly faceted curves with smoother geometry
  • Preparing detailed architectural assets for rendering
MengTo/Skills69792026年10月18日 更新

Prepare detailed 3D materials that remain clear in close-up views. The skill guides texture resolution, physically based material maps, surface mapping, filtering, and progressive loading while checking download size and GPU memory use.

中文简介正在准备中。当前显示英语简介。

  • Prepare wood, stone, and fabric materials for close-up views
  • Fix texture seams, grain direction, and normal map orientation
  • Preserve floor and foliage detail at oblique or distant views
MengTo/Skills69792026年10月18日 更新

Create realistic metal surfaces in Three.js by combining structured reflections, studio lighting, scratches, fingerprints, and beveled edges. Includes polished, brushed, and aged finishes for objects such as tools, rings, and metal product parts.

中文简介正在准备中。当前显示英语简介。

  • Fixing metal that looks like gray plastic
  • Creating polished or brushed metal finishes
  • Adding patina and worn edges
MengTo/Skills69792026年10月18日 更新

查看 MengTo 的全部技能

报告此技能的问题