图像波动 ·Wave Shader· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么ShaderMaterial 自定义着色器实现核心视觉效果OrbitControls 相机轨道交互监听窗口resize同步更新 camera 与 renderer效果说明本案例演示图像波动效果基于 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循环中更新状态并 renderCesium 为viewer.render或自动渲染代码要点import * as THREE from three;import { OrbitControls } from three/examples/jsm/controls/OrbitControls.js; import Stats from three/examples/jsm/libs/stats.module.js;class ThreeCore { dom; scene; camera; defaultCamera; renderer; clock; options; stats; // 要执行动画的对象集合, 子类可以把自己的动画写进 onRender 也可以 this.addAnimate() 添加到父类动画集合里 animates; constructor(dom, options) { this.dom dom; this.options options; this.scene new THREE.Scene(); this.clock new THREE.Clock(); this.animates {}; const k dom.clientWidth / dom.clientHeight; if (fov in options.cameraOptions) { this.defaultCamera new THREE.PerspectiveCamera(options.cameraOptions.fov, k, options.cameraOptions.near, options.cameraOptions.far); } else { const s options.cameraOptions.s; this.defaultCamera new THREE.OrthographicCamera(-sk, sk, s, -s, options.cameraOptions.near, options.cameraOptions.far); } this.camera this.defaultCamera; this.scene.add(this.camera); const rendererOptions { // 抗锯齿 antialias: true, alpha: true, // 深度缓冲, 解决模型重叠部分不停闪烁问题 // 这个属性会导致精灵材质会被后面的物体遮挡 // 只能出现问题的时候, 在那个场景 new ThreeCore继承类的时候, 传入rendererOptions参数, 将此参数改为 false logarithmicDepthBuffer: true }; const renderer new THREE.WebGLRenderer(Object.assign({}, rendererOptions, options.rendererOptions)); renderer.setSize(dom.clientWidth, dom.clientHeight); renderer.setPixelRatio(window.devicePixelRatio); dom.appendChild(renderer.domElement); this.renderer renderer; this.stats new Stats(); dom.appendChild(this.stats.dom); window.addEventListener(resize, this.onResize, false); //利用 setTimeout 宏任务最后执行特性, 使js执行过程要等所有微任务和同步代码执行完再执行, 否则 this.init() 可能会在场景未搭建完毕就执行报错或没有生产对象this.init(); this.animate();} // 提供给子类覆写 onRenderer() { } // 提供给子类覆写 onDestroy() { } animate() { this.renderer.setAnimationLoop(() { this.onRenderer(); this.stats.update(); // 执行动画 for (const key in this.animates) { this.animateskey); } this.renderer.render(this.scene, this.camera); }); } addAnimate(name, func) { this.animates[name] func; } removeAnimate(name) { delete this.animates[name]; } onResize () { const width this.dom.clientWidth; const height this.dom.clientHeight; const k width / height; // 更新相机 if (this.camera instanceof THREE.PerspectiveCamera) { this.camera.aspect k; } else { const s this.options.cameraOptions.s; this.camera.left -s * k; this.camera.right s * k; this.camera.top s; this.camera.bottom -s; } this.camera.updateProjectionMatrix(); // 更新renderer this.renderer.setSize(width, height); this.renderer.setPixelRatio(window.devicePixelRatio); }; destroyed() { // 需要手动移除掉 gui, 否则刷新页面时会出现多个gui document.querySelector(.dg.main.a)?.remove(); window.removeEventListener(resize, this.onResize.bind(this)); this.onDestroy(); this.renderer.setAnimationLoop(null); this.renderer.renderLists.dispose(); this.renderer.dispose(); this.renderer.forceContextLoss(); this.renderer.domElement.innerHTML ; this.scene.clear(); THREE.Cache.clear(); } }class ThreeProject extends ThreeCore { orbit; shadeMaterial; params { uWaresFrequency: 20, uScale: 0.1, uNoiseFrequency: 40, uNoiseScale: 2, uXzScale: 2, uLowColor: 0x000000, uHighColor: 0xffffff, uOpacity: 0.5, }; constructor(dom) { super(dom, { cameraOptions: { fov: 45, near: 0.1, far: 100 } }); this.scene.background new THREE.Color(0x000000); this.camera.position.set(0, 0, 3); this.orbit new OrbitControls(this.camera, this.renderer.domElement); const texture new THREE.TextureLoader().load(FILE_HOST threeExamples/shader/flag.webp); const shadeMaterial new THREE.ShaderMaterial({ vertexShader:precision mediump float; uniform float uTime; varying vec2 vUv; void main() { vUv uv; vec4 modelPosition modelMatrix * vec4(position, 1.0); modelPosition.z sin((modelPosition.x uTime)10.0)0.05; modelPosition.z sin((modelPosition.y uTime)10.0)0.05; gl_Position projectionMatrixviewMatrixmodelPosition; }, fragmentShader:precision mediump float; uniform sampler2D uTexture; varying vec2 vUv; void main() { vec4 textureColor texture2D(uTexture, vUv); gl_FragColor textureColor; }, side: THREE.DoubleSide, uniforms: { uTime: { value: 0 }, uTexture: { value: texture } } }); this.shadeMaterial shadeMaterial; const plane new THREE.Mesh(new THREE.PlaneGeometry(2, 1, 128, 128), shadeMaterial); this.scene.add(plane); } init() { } onRenderer() { const elapsed this.clock.getElapsedTime(); this.orbit.update(); this.shadeMaterial.uniforms.uTime.value elapsed; } }new ThreeProject(document.getElementById(box));完整源码GitHub小结本文提供图像波动完整 Three.js 源码与在线 Demo建议先运行案例再改 uniform/参数做二次实验更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库