当前位置: 首页 > news >正文

Three.js 波涛海浪教程

波涛海浪 ·move Sea· ▶ 在线运行案例

  • 案例合集:三维可视化功能案例(threehub.cn)
  • 开源仓库github地址:https://github.com/z2586300277/three-cesium-examples
  • 400个案例代码:网盘链接

你将学到什么

  • ShaderMaterial 自定义着色器实现核心视觉效果
  • OrbitControls 相机轨道交互
  • 水面反射/镜像材质
  • 场景雾效增强纵深
  • requestAnimationFrame渲染循环与resize自适应

效果说明

本案例演示波涛海浪效果:基于 WebGL 实现「波涛海浪」可视化效果,附完整可运行源码;核心用到 ShaderMaterial、OrbitControls、水面反射/镜像材质。建议先打开文首在线案例查看动态画面,再对照下方源码逐步理解。

核心概念

  • Scene / Camera / WebGLRenderer构成最小渲染闭环;大场景可开logarithmicDepthBuffer缓解 Z-fighting。
  • ShaderMaterial通过uniforms+ 自定义 GLSL 控制逐像素/逐点效果;透明粒子常配合depthTest: false
  • OrbitControls提供轨道旋转/缩放;开启enableDamping后需在 animate 中controls.update()

实现步骤

  • 搭建 Scene、PerspectiveCamera、WebGLRenderer,挂载 canvas 并处理resize
  • 定义 uniforms / onBeforeCompile 或 ShaderMaterial,编写 GLSL 与材质参数
  • 创建 OrbitControls(及 Raycaster 等交互控件,若源码包含)
  • requestAnimationFrame循环中更新状态并 render(Cesium 为viewer.render或自动渲染)
  • 代码要点

    import * as THREE from "three";

    import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js"; import * as dat from "dat.gui";

    // refer https://codepen.io/aderaaij/pen/XWpMONO const vertexShader =#include

    uniform float uTime;

    uniform float uBigWavesElevation; uniform vec2 uBigWavesFrequency; uniform float uBigWaveSpeed;

    uniform float uSmallWavesElevation; uniform float uSmallWavesFrequency; uniform float uSmallWavesSpeed; uniform float uSmallWavesIterations;

    varying float vElevation;

    // Classic Perlin 3D Noise // by Stefan Gustavson // vec4 permute(vec4 x){return mod(((x34.0)+1.0)x, 289.0);} vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;} vec3 fade(vec3 t) {return ttt(t(t*6.0-15.0)+10.0);}

    float cnoise(vec3 P){ vec3 Pi0 = floor(P); // Integer part for indexing vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1 Pi0 = mod(Pi0, 289.0); Pi1 = mod(Pi1, 289.0); vec3 Pf0 = fract(P); // Fractional part for interpolation vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); vec4 iy = vec4(Pi0.yy, Pi1.yy); vec4 iz0 = Pi0.zzzz; vec4 iz1 = Pi1.zzzz;

    vec4 ixy = permute(permute(ix) + iy); vec4 ixy0 = permute(ixy + iz0); vec4 ixy1 = permute(ixy + iz1);

    vec4 gx0 = ixy0 / 7.0; vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5; gx0 = fract(gx0); vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); vec4 sz0 = step(gz0, vec4(0.0)); gx0 -= sz0 * (step(0.0, gx0) - 0.5); gy0 -= sz0 * (step(0.0, gy0) - 0.5);

    vec4 gx1 = ixy1 / 7.0; vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5; gx1 = fract(gx1); vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); vec4 sz1 = step(gz1, vec4(0.0)); gx1 -= sz1 * (step(0.0, gx1) - 0.5); gy1 -= sz1 * (step(0.0, gy1) - 0.5);

    vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);

    vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); g000 *= norm0.x; g010 *= norm0.y; g100 *= norm0.z; g110 *= norm0.w; vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); g001 *= norm1.x; g011 *= norm1.y; g101 *= norm1.z; g111 *= norm1.w;

    float n000 = dot(g000, Pf0); float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); float n111 = dot(g111, Pf1);

    vec3 fade_xyz = fade(Pf0); vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); return 2.2 * n_xyz; }

    void main() { #include #include #include vec4 modelPosition = modelMatrix * vec4(position, 1.0); float elevation = sin(modelPosition.xuBigWavesFrequency.x + uTimeuBigWaveSpeed)sin(modelPosition.zuBigWavesFrequency.y + uTime * uBigWaveSpeed)

    • uBigWavesElevation;
    for(float i = 1.0; i <= 10.0; i++) { elevation -= abs( cnoise( vec3(modelPosition.xzuSmallWavesFrequencyi, uTime * uSmallWavesSpeed) )
    • uSmallWavesElevation / i
    ); if(i >= uSmallWavesIterations ) { break; } } modelPosition.y += elevation; vec4 viewPosition = viewMatrix * modelPosition; vec4 projectedPosition = projectionMatrix * viewPosition; gl_Position = projectedPosition;

    vElevation = elevation; }

    const fragmentShader =#include precision mediump float; uniform vec3 uDepthColor; uniform vec3 uSurfaceColor;

    uniform float uColorOffset; uniform float uColorMultiplier;

    varying float vElevation;

    void main() { float mixStrength = (vElevation + uColorOffset) * uColorMultiplier; vec3 color = mix(uDepthColor, uSurfaceColor, mixStrength); gl_FragColor = vec4(color, 1.0); #include }

    const gui = new dat.GUI({ closed: false, width: 340 }); const bigWavesFolder = gui.addFolder("Large Waves"); const smallWavesFolder = gui.addFolder("Small Waves"); const colorFolder = gui.addFolder("Colors"); const debugObject = { waveDepthColor: "#b5e4e4", waveSurfaceColor: "#4d9aaa", fogNear: 1, fogFar: 8, fogColor: "#8e99a2" };

    const scene = new THREE.Scene(); scene.fog = new THREE.Fog( debugObject.fogColor, debugObject.fogNear, debugObject.fogFar ); scene.background = new THREE.Color(debugObject.fogColor); const waterGeometry = new THREE.PlaneGeometry(12, 12, 512, 512); const waterMaterial = new THREE.ShaderMaterial({ vertexShader, fragmentShader, transparent: true, fog: true, uniforms: { uTime: { value: 0 }, uMouse: { value: new THREE.Vector2() }, uBigWavesElevation: { value: 0.2 }, uBigWavesFrequency: { value: new THREE.Vector2(4, 2) }, uBigWaveSpeed: { value: 0.75 }, uSmallWavesElevation: { value: 0.15 }, uSmallWavesFrequency: { value: 3 }, uSmallWavesSpeed: { value: 0.2 }, uSmallWavesIterations: { value: 4 }, uDepthColor: { value: new THREE.Color(debugObject.waveDepthColor) }, uSurfaceColor: { value: new THREE.Color(debugObject.waveSurfaceColor) }, uColorOffset: { value: 0.08 }, uColorMultiplier: { value: 5 }, ...THREE.UniformsLib["fog"] } });

    const water = new THREE.Mesh(waterGeometry, waterMaterial); water.rotation.x = -Math.PI * 0.5; scene.add(water);

    const sizes = { width: window.innerWidth, height: window.innerHeight };

    window.addEventListener("resize", () => { sizes.width = window.innerWidth; sizes.height = window.innerHeight; camera.aspect = sizes.width / sizes.height; camera.updateProjectionMatrix() renderer.setSize(sizes.width, sizes.height); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); });

    const camera = new THREE.PerspectiveCamera( 75, sizes.width / sizes.height, 0.1, 100 ); camera.position.set(1, 1, 1); scene.add(camera);

    const renderer = new THREE.WebGLRenderer(); renderer.setSize(sizes.width, sizes.height); renderer.outputColorSpace = THREE.SRGBColorSpace; document.body.appendChild(renderer.domElement);

    const controls = new OrbitControls(camera, renderer.domElement); controls.enableDamping = true;

    bigWavesFolder .add(waterMaterial.uniforms.uBigWavesElevation, "value") .min(0) .max(1) .step(0.001) .name("Elevation"); bigWavesFolder .add(waterMaterial.uniforms.uBigWavesFrequency.value, "x") .min(0) .max(10) .step(0.001) .name("Frequency X"); bigWavesFolder .add(waterMaterial.uniforms.uBigWavesFrequency.value, "y") .min(0) .max(10) .step(0.001) .name("Frequency Y"); bigWavesFolder .add(waterMaterial.uniforms.uBigWaveSpeed, "value") .min(0.25) .max(5) .step(0.001) .name("Speed");

    // Small Waves smallWavesFolder .add(waterMaterial.uniforms.uSmallWavesElevation, "value") .min(0.0) .max(0.3) .step(0.001) .name("Elevation"); smallWavesFolder .add(waterMaterial.uniforms.uSmallWavesFrequency, "value") .min(0) .max(30) .step(0.001) .name("Frequency"); smallWavesFolder .add(waterMaterial.uniforms.uSmallWavesSpeed, "value") .min(0.0) .max(1) .step(0.001) .name("Speed"); smallWavesFolder .add(waterMaterial.uniforms.uSmallWavesIterations, "value") .min(0) .max(10) .step(1) .name("Iterations");

    // Colors colorFolder .add(waterMaterial.uniforms.uColorOffset, "value") .min(0) .max(0.15) .step(0.0001) .name("Color Offset"); colorFolder .add(waterMaterial.uniforms.uColorMultiplier, "value") .min(0.0) .max(10.0) .step(0.001) .name("Color multiplier"); colorFolder .addColor(debugObject, "waveDepthColor") .name("Wave depth color") .onChange(() => { waterMaterial.uniforms.uDepthColor.value.set(debugObject.waveDepthColor); }); colorFolder .addColor(debugObject, "waveSurfaceColor") .name("Wave surface color") .onChange(() => { waterMaterial.uniforms.uSurfaceColor.value.set( debugObject.waveSurfaceColor ); }); colorFolder .addColor(debugObject, "fogColor") .name("Fog Color") .onChange(() => { waterMaterial.uniforms.fogColor.value.set(debugObject.fogColor); scene.background.set(debugObject.fogColor); scene.fog = new THREE.Fog( debugObject.fogColor, debugObject.fogNear, debugObject.fogFar ); });

    /**

    • Animate
    */ const clock = new THREE.Clock();

    const tick = () => { const elapsedTime = clock.getElapsedTime(); controls.update(); waterMaterial.uniforms.uTime.value = elapsedTime; renderer.render(scene, camera); window.requestAnimationFrame(tick); };

    tick();

    完整源码:GitHub

    小结

    • 本文提供波涛海浪完整 Three.js 源码与在线 Demo,建议先运行案例再改 uniform/参数做二次实验
    • 更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库
http://www.jsqmd.com/news/1155768/

相关文章:

  • 2026成都金牛闲置首饰估价技巧,证书齐全大幅提升回收价 - 逸程奢侈品回收中心
  • 2026 高口碑国产白光干涉仪盘点,中图仪器微纳检测方案值得参考 - 深度智识库
  • 三星二季度业绩暴增却股价重挫,AI存储芯片狂欢背后隐忧几何?
  • Claude Code 实战:AI 结对编程如何真正提效-0620
  • Linux 7.2内核slab延迟freelist构建:小对象高频分配性能提升70%
  • GitHub下载慢?这个免费插件让你速度飙升5倍!
  • 《坦克世界闪击战》二维码举报系统:数据驱动的游戏环境维护方案
  • 使用silk-v3-decoder解码微信QQ语音:原理、工具与实战指南
  • 3D硬金黄金回收避雷技巧,天津门店高损耗扣费套路深度拆解 - 奢侈品回收评测
  • 西安黄金回收谨防套路!正规回收注意事项+避坑指南,附靠谱店铺白名单 - 奢侈品回收测评
  • Textual:用Python构建终端与网页双模交互应用
  • DeepXDE 库 2.8 版本:3 步搭建 PINN 求解 2D 热传导方程
  • 两种盒模型计算方式
  • 3种混沌图像加密算法对比:Logistic vs Henon vs 递归置乱,抗攻击性与速度实测
  • 基于节点导纳矩阵的三相配电系统建模附Matlab代码
  • 工业负载控制方案:TPD2017FN与PIC18F86J15应用设计
  • Dify实战指南:从零构建企业级AI应用与智能体工作流
  • 从Excel维护到电价API调用:能源管理系统EMS的电价数据架构改造
  • STM32F765ZI与TB67H480FNG的高精度电机控制方案
  • 抖音内容批量下载终极指南:免费开源工具助你高效管理创作素材
  • SPI-NAND vs SPI-NOR Linux驱动对比:从MTD架构到DTS配置的5点核心差异
  • Vim插件管理终极指南:VAM让你的Vim配置更智能
  • 废旧电动车电机改造风力发电机:DIY低成本离网发电方案
  • 2026 济南驾校怎么挑选?4 家机构客观测评对比 - GrowUME
  • 南宁奢侈品回收怎么选?中检持证+当面鉴定,全品类一站式变现更安心 - 奢侈品回收中心
  • 2026年 7月无锡分赛道装修服务商实测参考,按需挑选更贴合需求 - 装修新知
  • PyTorch 2.0 多头自注意力机制实战:从零实现 8 头注意力模块
  • 当AI不再需要我们:写给中年人的一场“预演”
  • Geoffrey Huntley谈软件开发已死、K型分化与Ralph Loop:AI时代职业生存法则揭秘
  • Cursor Pro免费激活终极指南:三步破解限制与完整配置方案