- 图形学
- 物理引擎
【免费下载链接】WebGL-Fluid-Simulation
Play with fluids in your browser (works even on mobile)
本指南围绕开源项目 WebGL-Fluid-Simulation 展开,它是一个纯浏览器端、基于 WebGL 的二维流体仿真引擎,项目定位是"在浏览器里玩流体(在手机上也能流畅运行)"。读完本文,你将掌握该项目的运行方式、Stable Fluids 类 GPU 求解管线(涡量、散度、压力 Jacobi 迭代、平流)、全部可调参数的含义与默认值、dat.GUI 交互面板的使用,以及移动端降级与截图导出等实战能力。
一、项目概览与快速上手
仓库根目录的 README.md 对该项目的定位非常简洁:"Play with fluids in your browser (works even on mobile)",并给出了在线体验入口、三份参考文献以及 MIT 许可证声明。整个项目是零依赖构建的静态网页应用,核心文件只有三个:
- index.html:页面骨架,包含
<canvas>、样式、移动端 App 推广弹层,并通过<script src="./script.js">引入主逻辑; - script.js:全部仿真代码(约 1600 行),包括 WebGL 上下文初始化、全部着色器、求解管线、输入交互与 GUI;
- dat.gui.min.js:dat.GUI 控件库,用于生成右侧的参数调节面板。
由于项目没有任何打包步骤,运行方式非常简单:直接用浏览器打开 index.html 即可(需要支持 WebGL1/WebGL2 的现代浏览器);若要部署到服务器,将仓库内所有文件作为静态资源原样托管即可。页面加载后会立即在 canvas 上注入若干随机 splat(色块),随后用户用鼠标拖拽或手指滑动即可向流体注入扰动。
从源码结构看,index.html 中的<canvas></canvas>是唯一渲染目标,script.js启动时的调用链为getWebGLContext→startGUI→updateKeywords→initFramebuffers→ 若干随机multipleSplats→update()主循环(script.js)。
二、核心实现原理:基于 GPU Gems 第 38 章的 Stable Fluids 求解
README 的 References 部分列出了本项目的三条算法脉络:
- GPU Gems 第 38 章("Fast Fluid Dynamics Simulation on the GPU")——这是本项目求解器的主干,即 Jos Stam 的Stable Fluids(稳定流体)方法在 GPU 上的实现;
- mharrys/fluids-2d与haxiomic/GPU-Fluid-Experiments——两个开源的二维/GPU 流体实验项目,为具体着色器写法与效果组织提供了参考。
围绕 GPU Gems 的思路,script.js中step(dt)函数(script.js)每帧按固定顺序执行以下 GPU 计算链:
| 阶段 | 对应着色器 | 作用 |
|---|---|---|
| 涡量计算 | curlShader | 由速度场计算旋度(vorticity),script.js |
| 涡量约束 | vorticityShader | 施加涡量增强力,弥补数值耗散、恢复湍流细节,强度由CURL控制,script.js |
| 散度计算 | divergenceShader | 计算速度场的散度,用于检测"不可压缩"约束的偏差,script.js |
| 压力初值清理 | clearShader | 以PRESSURE系数初始化压力场,script.js |
| 压力 Poisson 求解 | pressureShader | 用Jacobi 迭代解压力场,迭代次数由PRESSURE_ITERATIONS决定(默认 20 次),script.js |
| 压力梯度投影 | gradientSubtractShader | 从速度场减去压力梯度,使速度场变为无散度(不可压缩),script.js |
| 速度/染料平流 | advectionShader | 沿速度场反向追踪采样(semi-Lagrangian advection),并施加耗散衰减,script.js |
其中advectionShader值得单独说明:它默认依赖 GPU 的线性过滤,若设备不支持OES_texture_float_linear,着色器会自动编译进MANUAL_FILTERING宏,改用软件双线性插值(bilerp)完成采样(script.js),这也是项目兼容低端移动设备的关键设计之一。
三、参数配置详解:默认值与取值范围
整个仿真的可调状态集中在config对象中(script.js),理解这些参数即可控制效果与性能的平衡:
| 参数 | 默认值 | 说明 |
|---|---|---|
SIM_RESOLUTION | 128 | 物理(速度/压力)场分辨率,整数(32/64/128/256),越高物理细节越精细、越耗性能 |
DYE_RESOLUTION | 1024 | 染料(颜色)场分辨率,高/中/低/极低对应 1024/512/256/128 |
CAPTURE_RESOLUTION | 512 | 截图导出分辨率 |
DENSITY_DISSIPATION | 1 | 染料密度耗散(0~4),越大颜色消退越快 |
VELOCITY_DISSIPATION | 0.2 | 速度耗散(0~4),越大流体减速越快 |
PRESSURE | 0.8 | 压力场初值系数(0~1) |
PRESSURE_ITERATIONS | 20 | 压力 Jacobi 迭代次数,越大越接近精确解、越耗性能 |
CURL | 30 | 涡量增强强度(0~50,步进 1),增大后漩涡更"卷" |
SPLAT_RADIUS | 0.25 | 单次注入半径(0.01~1),实际使用时除以 100 再经宽高比修正 |
SPLAT_FORCE | 6000 | 指针拖动时施加的速度力倍数 |
SHADING | true | 是否启用伪光照(法线扰动明暗) |
COLORFUL | true | 是否随时间自动更换注入颜色 |
COLOR_UPDATE_SPEED | 10 | 颜色自动更换速度 |
PAUSED | false | 暂停仿真(按P键也可切换) |
BACK_COLOR | {r:0,g:0,b:0} | 背景色 |
TRANSPARENT | false | 透明背景模式(启用后在画布显示棋盘格) |
BLOOM | true | 辉光后处理开关 |
BLOOM_ITERATIONS | 8 | 辉光模糊的降采样级数 |
BLOOM_RESOLUTION | 256 | 辉光缓冲分辨率 |
BLOOM_INTENSITY | 0.8 | 辉光强度(GUI 可调 0.1~2.0) |
BLOOM_THRESHOLD | 0.6 | 辉光亮度阈值(GUI 可调 0~1) |
BLOOM_SOFT_KNEE | 0.7 | 阈值过渡的软膝(soft-knee)系数 |
SUNRAYS | true | 体积光(god rays)开关 |
SUNRAYS_RESOLUTION | 196 | 体积光缓冲分辨率 |
SUNRAYS_WEIGHT | 1.0 | 体积光强度(GUI 可调 0.3~1.0) |
从源码结构看,getResolution()(script.js)会根据画布宽高比把分辨率换算为{width, height}二元组,且保证长边对应分辨率数值;因此DYE_RESOLUTION=1024实际意味着"长边 1024"。
四、dat.GUI 面板与快捷键操作
startGUI()(script.js)使用 dat.GUI 构建了宽 300px 的右侧控制面板,包含以下分组:
- 质量(quality):
DYE_RESOLUTION四档(高 1024 / 中 512 / 低 256 / 极低 128),修改后立即重建帧缓冲; - 仿真分辨率(sim resolution):
SIM_RESOLUTION四档(32/64/128/256); - 基础滑块:density diffusion(
DENSITY_DISSIPATION)、velocity diffusion(VELOCITY_DISSIPATION)、pressure(PRESSURE)、vorticity(CURL)、splat radius(SPLAT_RADIUS); - 开关:shading(
SHADING)、colorful(COLORFUL)、paused(PAUSED); - Random splats 按钮:随机注入 5~24 个色块;
- Bloom 文件夹:enabled / intensity / threshold;
- Sunrays 文件夹:enabled / weight;
- Capture 文件夹:background color(取色器)、transparent 开关、take screenshot(截图导出);
- 外部链接:Github / Twitter / Discord / Check out mobile app。
快捷键方面(script.js):空格键触发一次随机多色注入,P键切换暂停/继续。
五、输入交互:鼠标与多点触控
项目同时支持鼠标与触摸输入,pointers数组维护了最多可同时存在的指针实例(script.js):
- 鼠标:
mousedown/mousemove/mouseup事件驱动单指针(script.js); - 触屏:
touchstart/touchmove/touchend支持多指同时注入,每个触点按identifier追踪(script.js)。
每个指针携带texcoordX/Y、prevTexcoordX/Y、deltaX/Y等状态(pointerPrototype,script.js),其中位移增量会被correctDeltaX/correctDeltaY按宽高比修正,再乘以SPLAT_FORCE后写入速度场(splatPointer,script.js)。颜色方面,generateColor()使用 HSV 随机色并整体乘以 0.15 保持暗色调(script.js),且COLORFUL开启时每COLOR_UPDATE_SPEED秒自动更换一次颜色(updateColors,script.js)。
六、渲染与后处理管线
render()(script.js)负责把染料场合成到屏幕,后处理按需启用:
- Shading(伪光照):
displayShaderSource中通过四邻域颜色梯度构造法线,再与固定光源做点积得到漫反射明暗(script.js); - Bloom(辉光):预滤波 → 逐级降采样模糊 → 反向累加 → 最终按
BLOOM_INTENSITY输出(applyBloom,script.js),模糊使用bloomBlurShader的四邻域均值; - Sunrays(体积光):
sunraysMaskShader依据亮度生成遮罩,再以sunraysShader沿径向做 16 次累积采样并施加Decay=0.95衰减(script.js),模拟光源散射; - 抖动消除色带:显示阶段叠加 LDR_LLL1_0.png 作为抖动纹理(dithering texture),在 Bloom 输出中混入 ±1/255 的噪声,平滑渐变区域的色带(script.js)。
这些效果通过Material.setKeywords机制按需编译着色器变体:updateKeywords()(script.js)把SHADING/BLOOM/SUNRAYS宏注入 display shader,并以宏组合哈希缓存 program,避免每帧重复编译。
七、截图导出:帧缓冲读取流程
Capture 面板的 "take screenshot" 按钮触发captureScreenshot()(script.js),完整流程为:
- 按
CAPTURE_RESOLUTION(默认 512)创建目标帧缓冲,执行一次离线render(target); framebufferToTexture用gl.readPixels(..., gl.FLOAT, ...)读回浮点 RGBA;normalizeTexture把浮点值 clamp 到 0~1 并转成Uint8Array(同时翻转 Y 轴);textureToCanvas写入 2D canvas,toDataURL()生成 PNG;downloadURI('fluid.png', datauri)触发浏览器下载(script.js)。
该功能对效果展示、壁纸制作与画面分享非常实用。
八、移动端适配与低端设备降级
"works even on mobile" 并非口号,源码中有三处显式的移动端/低端设备策略:
- 分辨率降级:
isMobile()(匹配Mobi|Android的 UA 正则,script.js)为真时,DYE_RESOLUTION从 1024 降到 512(script.js); - 能力探测降级:当设备不支持线性浮点过滤(
OES_texture_float_linear)时,关闭SHADING/BLOOM/SUNRAYS并把染料分辨率降到 512(script.js),同时平流着色器自动切换MANUAL_FILTERING软件插值; - 交互适配:移动端默认收起 GUI 面板(script.js);首次访问 20 秒后弹出 App 推广浮层(带
promo/promo-close关闭按钮,见 index.html 与 script.js),并提供 App Store / Google Play 徽标入口。
WebGL 上下文初始化方面,getWebGLContext()(script.js)优先尝试 WebGL2,失败则回退到 WebGL1 /experimental-webgl;纹理格式通过getSupportedFormat从R16F → RG16F → RGBA16F逐级探测,保证在不同 GPU 上都能找到可渲染的半浮点格式(script.js)。
九、参考文献与开源许可
README 明确标注了本项目的算法与灵感来源:
- GPU Gems 第 38 章:GPU 上的快速流体动力学仿真(Stable Fluids 方法),是求解管线的主干;
- mharrys/fluids-2d与haxiomic/GPU-Fluid-Experiments:二维流体与 GPU 流体实验项目,为着色器实现提供参考。
许可证方面,项目在 LICENSE 中以MIT License发布(Copyright (c) 2017 Pavel Dobryakov),允许自由使用、修改与再分发,只需保留版权声明。若要在自己的页面中集成或二次开发,直接以静态方式引入 index.html 与 script.js 即可,无需任何构建工具或第三方依赖。
- 图形学
- 物理引擎
【免费下载链接】WebGL-Fluid-Simulation
Play with fluids in your browser (works even on mobile)
相关推荐
终极指南:如何实现移动端丝滑WebGL流体模拟的Draw Call优化
终极指南:如何实现移动端丝滑WebGL流体模拟的Draw Call优化 WebGL Fluid Simulation是一款令人惊叹的开源项目,让用户能够在浏览器
图形学物理引擎Short项目测试策略全解析:从单元测试到端到端测试的完整方案
Short项目测试策略全解析:从单元测试到端到端测试的完整方案 想要构建一个稳定可靠的URL短链接服务吗?Short项目的完整测试策略为你提供了从单元测试到端到
Label Studio架构深度解析:从数据标注工具到AI数据基础设施的演进之路
Label Studio架构深度解析:从数据标注工具到AI数据基础设施的演进之路 在人工智能数据工程的演进历程中,我们见证了从简单标注工具到复杂数据基础设施的范
数据标注人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考