跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
JavaScript大前端算法

Three.js 中 GPGPU 水面模拟示例代码深度解析

深入解析 Three.js 官方 GPGPU 水面模拟示例。内容涵盖基础环境搭建、GPU 计算逻辑、水面渲染、鼠标交互及物体漂浮效果。通过拆解核心模块与着色器代码,提供从零开始的集成指南,包含关键代码片段与避坑建议,帮助开发者理解水面波动原理并快速应用到项目中。

性能调优发布于 2026/3/26更新于 2026/8/2257 浏览
Three.js 中 GPGPU 水面模拟示例代码深度解析

Three.js 中 GPGPU 水面模拟示例代码深度解析

对于刚接触 Three.js 的开发者来说,GPGPU(通用计算着色器)听起来可能很复杂,但其实这个水面模拟示例的核心逻辑可以拆解成简单易懂的模块!今天我们就从基础视角解析这个示例,还会教你如何快速把效果集成到自己的项目里。

一、先搞懂:这个示例到底在做什么?

简单来说,这个示例实现了:

  • 用 GPU 模拟真实的水面波动(比如丢石子的涟漪效果)
  • 鼠标点按/拖动时,水面会跟着产生波纹
  • 物体能'浮'在水面上,跟着水波晃动
  • 水面有反光、折射,看起来像真的水

你可以把它想象成:用代码做了一个'电子水池',还能伸手进去搅水玩~

二、核心模块拆解

1. 基础环境搭建:先搭好'舞台'

就像演戏要先搭舞台一样,Three.js 做 3D 效果也需要先准备基础环境:

// 创建相机(相当于你的眼睛)
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// 创建场景(放所有 3D 物体的'盒子')
scene = new THREE.Scene();
// 创建渲染器(负责把 3D 场景画到网页上)
renderer = new THREE.WebGLRenderer({ antialias: true });
// 把渲染结果放到网页里
document.body.appendChild(renderer.domElement);

这三行是 Three.js 的'标配',几乎所有项目都要写,记住就好!

2. GPGPU 水面计算:让水'动'起来

这里是核心,但我们不用懂复杂的 GPU 原理,只需要知道:

  • 用 GPUComputationRenderer 帮我们算水面波动
  • 用'高度图'(一张看不见的图片)记录每个位置的水面高度
  • 着色器(Shader)负责计算波纹怎么扩散
// 初始化 GPU 计算工具(128x128 是计算精度,数字越大效果越好但越卡)
gpuCompute = new GPUComputationRenderer(128, 128, renderer);
// 创建高度图(存水面高度的'秘密图片')
const heightmap = gpuCompute.createTexture();
// 添加计算任务:让着色器处理水面波动
heightmapVariable = gpuCompute.addVariable('heightmap', shaderCode, heightmap);

你可以把 GPU 计算理解成'找了个超级计算器专门算水面',比 CPU 算得快 10 倍!

3. 水面渲染:把计算结果画出来

有了水面高度数据,还要用'材质'把水画出来:

// 自定义水面材质(继承 Three.js 自带的材质)
class WaterMaterial extends THREE.MeshStandardMaterial {
  onBeforeCompile(shader) {
    // 关键:根据高度图让顶点'凸起',形成波浪
    shader.vertexShader = shader.vertexShader.replace('#include <begin_vertex>', `
      float height = texture2D(heightmap, uv).x;
      vec3 transformed = vec3(position.x, position.y + height, position.z);
    `);
  }
}
// 创建水面(用平面几何体 + 水面材质)
const waterGeometry = new THREE.PlaneGeometry(6, 6);
const waterMesh = new THREE.Mesh(waterGeometry, new WaterMaterial());
waterMesh.rotation.x = -Math.PI / 2; // 把平面旋转成水平(默认是垂直的)
scene.add(waterMesh);

PlaneGeometry 是'平面',旋转后就成了水面;材质负责让它看起来像水~

4. 鼠标交互:用鼠标'搅水'

想要鼠标点水时产生波纹,只需要两步:

  • 检测鼠标点在水面的哪个位置
  • 把位置传给 GPU 计算着色器
// 鼠标按下时记录位置
document.addEventListener('pointerdown', () => { mousedown = true; });
// 鼠标移动时计算波纹
function raycast() {
  if (mousedown) {
    // 用射线检测鼠标点到的水面位置
    raycaster.setFromCamera(mouseCoords, camera);
    const intersects = raycaster.intersectObject(waterMesh);
    if (intersects.length > 0) {
      // 把鼠标位置传给 GPU,让这里产生波纹
      heightmapVariable.material.uniforms.mousePos.value = intersects[0].point;
    }
  }
}

射线检测就像'从鼠标位置发射一道激光',看激光打到哪个物体上~

5. 物体漂浮:让物体跟着水动

物体能浮在水面,核心是:

  • 读取水面在物体位置的高度
  • 把物体的 Y 坐标(上下)设成水面高度
function updateDuck() {
  // 读取水面在鸭子位置的高度
  const waterHeight = getWaterHeight(duck.position.x, duck.position.z);
  // 让鸭子跟着水面高度走
  duck.position.y = waterHeight;
}

就像你站在蹦床上,蹦床凹下去你也会跟着下去~

三、快速集成指南

下面是精简版代码,直接复制到 HTML 文件就能运行:

<!DOCTYPE html>
<html>
<head>
  <title>我的水面效果</title>
  <style>body{margin: 0;}</style>
</head>
<body>
<script type="importmap">
{
  "imports": {
    "three": "https://unpkg.com/[email protected]/build/three.module.js",
    "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
  }
}
</script>
<script type="module">
import * as THREE from 'three';
import { GPUComputationRenderer } from 'three/addons/misc/GPUComputationRenderer.js';

// 1. 基础环境
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
camera.position.set(0, 2, 4);

// 2. 初始化 GPU 计算
const gpuCompute = new GPUComputationRenderer(128, 128, renderer);
const heightmap = gpuCompute.createTexture();
// 填充初始高度(随便填点噪声让水面有初始波动)
const data = heightmap.image.data;
for (let i = 0; i < data.length; i += 4) {
  data[i] = Math.random() * 0.1; // 初始高度
}

// 3. 水面计算着色器(核心:波纹扩散公式)
const heightmapShader = `
  uniform vec2 mousePos;
  uniform float mouseSize;
  void main() {
    vec2 uv = gl_FragCoord.xy / resolution.xy;
    vec4 prev = texture2D(heightmap, uv);
    // 简单波纹公式(不用懂,复制就行)
    float dist = length(uv * 6 - mousePos);
    float wave = dist < mouseSize ? 0.1 : 0.0;
    gl_FragColor = vec4(prev.x * 0.95 + wave, 0, 0, 1);
  }
`;
const heightmapVariable = gpuCompute.addVariable('heightmap', heightmapShader, heightmap);
gpuCompute.init();

// 4. 创建水面
class WaterMaterial extends THREE.MeshStandardMaterial {
  constructor() {
    super({ color: 0x9bd2ec, metalness: 0.9, roughness: 0 });
  }
  onBeforeCompile(shader) {
    shader.uniforms.heightmap = { value: null };
    shader.vertexShader = shader.vertexShader.replace('#include <begin_vertex>', `
      float h = texture2D(heightmap, uv).x;
      vec3 transformed = vec3(position.x, position.y + h, position.z);
    `);
  }
}
const waterGeo = new THREE.PlaneGeometry(6, 6, 128, 128);
const waterMat = new WaterMaterial();
const waterMesh = new THREE.Mesh(waterGeo, waterMat);
waterMesh.rotation.x = -Math.PI / 2;
scene.add(waterMesh);

// 5. 鼠标交互
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
let mousedown = false;
window.addEventListener('pointerdown', () => mousedown = true);
window.addEventListener('pointerup', () => mousedown = false);
window.addEventListener('pointermove', (e) => {
  mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
});

// 6. 动画循环
function animate() {
  requestAnimationFrame(animate);
  // 计算水面
  gpuCompute.compute();
  waterMat.heightmap = gpuCompute.getCurrentRenderTarget(heightmapVariable).texture;
  // 鼠标交互
  if (mousedown) {
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObject(waterMesh);
    if (intersects.length > 0) {
      const p = intersects[0].point;
      heightmapVariable.material.uniforms.mousePos.value = p;
      heightmapVariable.material.uniforms.mouseSize.value = 0.5;
    }
  }
  renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>

四、注意事项

  1. 运行方式:必须用本地服务器(比如 VSCode 的 Live Server 插件),直接双击 HTML 文件会报错!
  2. 性能问题:如果卡,把 128 改成 64(计算精度降低,效果稍差但更流畅)
  3. 水面颜色:改 0x9bd2ec 就能换颜色(比如 0x00ffff 是浅蓝色)
  4. 波纹大小:改 mouseSize.value = 0.5 里的 0.5,数字越大波纹范围越广

五、总结

其实 Three.js 的 GPGPU 水面效果并没有想象中难,核心就是:

  • 用 GPU 算水面高度 → 用材质画水面 → 加交互让水'活'起来

哪怕你是零基础,只要复制上面的代码,改改参数(比如水面大小、颜色、波纹强度),就能快速做出自己的水面效果!后续可以再慢慢深入学习着色器和物理公式。

目录

  1. Three.js 中 GPGPU 水面模拟示例代码深度解析
  2. 一、先搞懂:这个示例到底在做什么?
  3. 二、核心模块拆解
  4. 1. 基础环境搭建:先搭好“舞台”
  5. 2. GPGPU 水面计算:让水“动”起来
  6. 3. 水面渲染:把计算结果画出来
  7. 4. 鼠标交互:用鼠标“搅水”
  8. 5. 物体漂浮:让物体跟着水动
  9. 三、快速集成指南
  10. 四、注意事项
  11. 五、总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 2026 年 3 月 11 日全球 AI 前沿动态:智能体生态与模型突破
  • C++ AIGC 推理延迟优化关键技巧与实战
  • AI Agent 架构:基础组成模块深度解析
  • KingbaseES 数据库智能 SQL 防护机制详解:原理、模式与性能实测
  • 新手如何从零开始学习漏洞挖掘
  • LeetCode 42 接雨水算法详解:暴力、DP、双指针与单调栈四种解法
  • AIGC 技术在元宇宙与虚拟世界中的应用实践
  • 学术写作 AIGC 检测与降重功能解析
  • 飞书机器人集成 HunyuanOCR 自动识别群聊图片
  • 微调 LLM 构建任务型对话 Agent 方案与实践
  • FPGA 工程最常见的 10 个玄学 BUG 与排查思路(实战踩坑总结)
  • Python 高阶函数:map() 原理、实战与常见陷阱
  • 基于本地知识库的私有 GPT 助手定制教程
  • OpenClaw 在 Ubuntu 20.04 系统上的部署步骤
  • OpenClaw 搭建有性格私人 AI 助手完整配置指南
  • Python 基础篇:内置类型与方法详解
  • 大模型、AI 大模型与 GPT 模型解析:定义、优势及应用
  • 2025 年 12 月 GESP C++ 二级真题解析
  • 基于C++的DPU医疗数据处理初探
  • Python 爬虫接单指南:技能要求、法律边界与实战建议

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online