海洋模拟项目源码解析

静态效果

项目文件组成结构如下:

复制代码
├── LICENSE.md

├── makePG.py

├── OceanDemo-main.zip

├── package.json

├── README.md

├── src

│ ├── assets

│ │ ├── environment

│ │ │ └── qwantani_1k.hdr

│ │ └── ocean

│ │ ├── 00_noise0.exr

│ │ ├── babylonBuoy.glb

│ │ ├── buoy.glb

│ │ ├── fftInverseFFT.wgsl

│ │ ├── fftInverseFFT2.wgsl

│ │ ├── fftInverseFFT3.wgsl

│ │ ├── fftPrecompute.wgsl

│ │ ├── fisher_boat.glb

│ │ ├── foam1.jpg

│ │ ├── initialSpectrum.wgsl

│ │ ├── initialSpectrum2.wgsl

│ │ ├── ocean0.jpg

│ │ ├── ocean1.jpg

│ │ ├── ocean2.jpg

│ │ ├── ocean3.jpg

│ │ ├── timeDependentSpectrum.wgsl

│ │ └── wavesTexturesMerger.wgsl

│ ├── createScene.ts

│ ├── externalFileTypes.d.ts

│ ├── index.html

│ ├── index.ts

│ └── scenes

│ ├── buoyancy.ts

│ ├── fft.ts

│ ├── initialSpectrum.ts

│ ├── ocean.ts

│ ├── oceanGeometry.ts

│ ├── oceanGui.ts

│ ├── oceanMaterial.ts

│ ├── skyBox.ts

│ ├── tools

│ │ ├── computeHelper.ts

│ │ ├── exrSerializer.ts

│ │ ├── math.vector.float32.ts

│ │ ├── RTTDebug.ts

│ │ └── skyMaterialExt.ts

│ ├── wavesCascade.ts

│ ├── wavesGenerator.ts

│ └── wavesSettings.ts

├── tsconfig.json

└── webpack.config.js

第一步:入口与架构(搞清楚它是怎么跑起来的)

先看外壳,弄明白这些 TypeScript 文件是怎么组织到一起的。

  1. src/index.ts :这是整个应用的"点火装置"。你已经看过了,它负责初始化 WebGPUEngine

  2. src/createScene.ts :这是一个分发器。这个 Demo 显然支持不同的场景(比如纯海洋、浮力模拟等),它通过 URL 参数决定加载哪个模块。

  3. src/scenes/ocean.ts这是你最该仔细读的文件。 它把天空盒、船只模型、海洋几何体以及最重要的"波动生成器"串联在一起。

第二步:核心逻辑(学习物理是怎么变成画面的)

这是项目的灵魂,建议按以下逻辑链路去读:

  1. src/scenes/wavesSettings.ts:看这里。了解风速、海浪强度等参数是如何定义的。

  2. src/scenes/wavesGenerator.ts重头戏! 这里负责调度 GPU 计算。它会调用 computeHelper.ts 来运行那些 .wgsl(计算着色器)。

  3. src/scenes/oceanMaterial.ts :这里定义了你看到的海洋"长什么样"。它负责处理光照、折射、反射以及读取 GPU 计算出来的位移贴图

第三步:硬核算法(进阶深水区)

如果你想弄明白为什么这个海浪这么真实,就得看 src/assets/ocean/ 下的 .wgsl 文件。这是 Web 开发者最高阶的技能之一。

  • initialSpectrum.wgsl:生成初始的海浪频谱(通常基于 Phillips 频谱公式)。

  • fftInverseFFT.wgsl核心算法。利用快速傅里叶逆变换,把频率域的数据转回我们肉眼能看到的波动(高度场)。

  • wavesTexturesMerger.wgsl :把计算好的位移、法线、泡沫数据合并成最终的贴图。

第四步: 学习路径

阶段 文件 目标
1. 跑通流程 index.ts -> ocean.ts 搞清楚一个 WebGPU 场景从创建到渲染的生命周期。
2. 操控 UI oceanGui.ts 修改代码,看看调节风速(Wind Speed)时,哪些变量被传给了着色器。
3. 材质魔法 oceanMaterial.ts 尝试修改 oceanMaterial 里的颜色或反射率,理解着色器是如何渲染水面的。
4. 物理模拟 wavesGenerator.ts 理解 ComputeShader 是如何每一帧都在后台更新海浪形状的。

第四步:程序运行路径

从你双击 npm run dev 到屏幕上出现第一朵浪花,拆解出一条清晰的运行路径

(1)初始化阶段:点火与引擎挂载

一切的起点在 src/index.ts

  • 浏览器握手 :程序首先通过 WebGPUEngine.IsSupportedAsync 检查你的浏览器(Canary 版本)是否支持 WebGPU。

  • 引擎启动 :创建一个 WebGPUEngine 实例,并配置 deviceDescriptor。这里非常关键,它申请了 timestamp-queryindirect-first-instance 等高级特性,这是跑计算着色器(Compute Shader)的前提。

  • 模块加载 :通过 getSceneModuleWithName 动态加载 src/scenes/ocean.ts(默认场景)。

(2)场景构建阶段:资源与逻辑组装

当程序进入 src/scenes/ocean.tscreateScene 函数时,它开始疯狂"搭积木":

A. 环境配置
  • 调用 skyBox.ts 生成天空盒。

  • 加载 src/assets/environment/qwantani_1k.hdr。这不只是背景,它为海洋提供了 IBL (基于图像的光照),决定了海水的环境反射。

B. 核心组件实例化(最关键的路径)

程序在这里初始化了两个重量级对象:

  1. WavesGenerator (src/scenes/wavesGenerator.ts):这是"大脑",负责所有物理计算。

  2. OceanMaterial (src/scenes/oceanMaterial.ts):这是"皮肤",负责最终的视觉呈现。

(3)渲染循环阶段:数据的"生死时速"

一旦执行 engine.runRenderLoop,每一帧(约 16ms)都会经历以下闭环:

步骤一:频谱生成(Initial & Time-Dependent)
  • 文件路径initialSpectrum.ts -> initialSpectrum.wgsl

  • 逻辑:GPU 根据风速、海浪强度(Settings),在频率域生成初始的海浪分布。它不是生成形状,而是生成"波的能量密度"。

步骤二:逆傅里叶变换(IFFT)
  • 文件路径wavesGenerator.ts -> fftInverseFFT.wgsl

  • 逻辑 :这是项目最硬核的地方。GPU 将频率域的数据通过 IFFT 转换回空间域。

  • 产出 :生成几张关键的 RTT (渲染目标贴图)

    • Displacement Map:告诉顶点网格往哪里跳(高度和水平位移)。

    • Normal Map:决定光的折射和反射角度。

    • Foam Map:计算波浪破碎处的泡沫。

步骤三:材质着色(Fragment Shading)
  • 文件路径oceanMaterial.ts

  • 逻辑

    1. 顶点偏移 :读取 Displacement Map,把平整的 oceanGeometry 顶起来,变成起伏的海浪。

    2. 像素着色 :结合天空盒反射、HDR 环境光,以及 mix-blend-mode: screen 等逻辑,计算出深蓝到浅绿的渐变。

(4) 交互阶段:反馈与浮力

  • buoyancy.ts :如果你在场景里放了那只鸭子或小船(.glb 模型),程序会实时采样海浪的高度数据。

  • 逻辑 :它读取当前时刻坐标下的海浪高度,通过 buoyancy.ts 计算浮力,实时修正模型的 positionrotation,让船看起来真的漂在水上。

相关推荐
用户2986985301432 分钟前
React 中 HTML 内容转 Word 文档的实现方案
javascript·react.js·html
mONESY37 分钟前
我用 Next.js 16 + Supabase 从零做了个「背单词」H5 应用
javascript
boooooooom40 分钟前
手把手做一个图 RAG 烹饪问答系统:Neo4j + Milvus + LLM 的工程实践
前端·javascript·后端
染指11104 小时前
103.RAG-LLamaIndex后端rag问答-聊天接口
前端·javascript·vue.js·人工智能
单线程_0112 小时前
从案例分析 Vue3 Tokenizer+Parser 源码三
前端·javascript·vue.js
小磊哥er13 小时前
深入解构Claude Code - 第 8 篇 · 数据放哪、钱怎么算
javascript·ai编程
kyriewen15 小时前
我装了30多个Skill,给AI安排了8个岗位
前端·javascript·ai编程
默_笙17 小时前
🔥 让 AI 帮我写完整个 Next.js 博客:框架就是 AI 的"上下文 buff"
前端·javascript
喵本喵叁肆20 小时前
06-M6-部门过滤与综合研判-从问答机到研判助手
前端·javascript·jquery
এ慕ོ冬℘゜21 小时前
JavaScript学习心得:从只会语法,到真正会写业务逻辑
开发语言·javascript·ecmascript