粒子烟花 ·Fire· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么ShaderMaterial 自定义着色器实现核心视觉效果OrbitControls 相机轨道交互THREE.Points 粒子点渲染GSAP 时间轴与补间动画BufferGeometry 自定义顶点/索引数据requestAnimationFrame渲染循环与resize自适应效果说明本案例演示粒子烟花效果基于 WebGL 实现「粒子烟花」可视化效果附完整可运行源码核心用到 ShaderMaterial、OrbitControls、THREE.Points。建议先打开文首在线案例查看动态画面再对照下方源码逐步理解。核心概念Scene / Camera / WebGLRenderer构成最小渲染闭环大场景可开logarithmicDepthBuffer缓解 Z-fighting。ShaderMaterial通过uniforms 自定义 GLSL 控制逐像素/逐点效果透明粒子常配合depthTest: false。OrbitControls提供轨道旋转/缩放开启enableDamping后需在 animate 中controls.update()。THREE.Points将每个顶点渲染为可控大小的粒子可用自定义 attribute如u_index驱动片元/顶点动画。实现步骤搭建 Scene、PerspectiveCamera、WebGLRenderer挂载 canvas 并处理resize定义 uniforms / onBeforeCompile 或 ShaderMaterial编写 GLSL 与材质参数创建 OrbitControls及 Raycaster 等交互控件若源码包含在定时器或 GSAP 时间轴中更新 uniform / 变换驱动特效播放在requestAnimationFrame循环中更新状态并 renderCesium 为viewer.render或自动渲染代码要点import * as THREE from three;import { OrbitControls } from three/examples/jsm/controls/OrbitControls.js; import gsap from gsap;const scene new THREE.Scene();const sizes { width: window.innerWidth, height: window.innerHeight, resolution: null, pixelRatio: Math.min(window.devicePixelRatio, 2), }; sizes.resolution new THREE.Vector2( window.innerWidth * sizes.pixelRatio, window.innerHeight * sizes.pixelRatio );const textureLoader new THREE.TextureLoader();const textures [ textureLoader.load(FILE_HOST threeExamples/particle/particleFire/1.png), textureLoader.load(FILE_HOST threeExamples/particle/particleFire/10.png), textureLoader.load(FILE_HOST threeExamples/particle/particleFire/3.png), textureLoader.load(FILE_HOST threeExamples/particle/particleFire/4.png), textureLoader.load(FILE_HOST threeExamples/particle/particleFire/5.png), textureLoader.load(FILE_HOST threeExamples/particle/particleFire/6.png), textureLoader.load(FILE_HOST threeExamples/particle/particleFire/7.png), textureLoader.load(FILE_HOST threeExamples/particle/particleFire/8.png), ];/*** param {粒子数目} countparam {烟花位置} positionparam {烟花粒子大小} sizeparam {纹理} textureparam {烟花半径} radiusparam {颜色}color*/ const createFireWork async ( count, position, size, texture, radius 1, color ) { if (!texture texture instanceof THREE.Texture) return; // 反转纹理 texture.flipY false; const positionsArray new Float32Array(count * 3);// 粒子的随机大小 const sizeArray new Float32Array(count); // 粒子的随机存在寿命 const lifeArray new Float32Array(count); for (let index 0; index count; index) { const spherical new THREE.Spherical( radius(0.75 (Math.random() - 0.5)0.25), Math.random() * Math.PI, Math.random()Math.PI2 ); const position new THREE.Vector3(); position.setFromSpherical(spherical);positionsArray[index * 3] position.x; positionsArray[index * 3 1] position.y; positionsArray[index * 3 2] position.z;sizeArray[index] Math.random(); // 粒子的寿命只能够在原有的基础上的更短 //这样烟花粒子就消失的更快,会在vs基于原有的寿命乘上这个值 lifeArray[index] Math.random() 1; } const geometry new THREE.BufferGeometry(); geometry.setAttribute( position, new THREE.Float32BufferAttribute(positionsArray, 3) ); geometry.setAttribute( aSize, new THREE.Float32BufferAttribute(sizeArray, 1) ); geometry.setAttribute( aLife, new THREE.Float32BufferAttribute(lifeArray, 1) ); const material new THREE.ShaderMaterial({ fragmentShader:precision mediump float; uniform sampler2D uTexture; uniform vec3 uColor; varying vec2 vUv; uniform float uTime; varying vec3 vPosition; varying vec3 vNormal; void main(){ // 注意开启材质透明 float textureAlphatexture(uTexture,gl_PointCoord).r; gl_FragColorvec4(uColor, textureAlpha); // 引入three.js的内置shader代码。开启toneMapping和colorSpace #include #include }, vertexShader:#include precision mediump float; attribute float aSize; attribute float aLife; uniform float uTime; uniform float uSize; uniform vec2 uResolution; uniform float uProgress; varying vec3 vPosition; varying vec3 vNormal; float linearFunction (float x,float x1,float y1,float x2,float y2) { return x((y2-y1)/(x2-x1))(y2-((y2-y1)/(x2-x1))x2); } void main(){ /**Position*/ vec3 newPositionposition; newPositionnewPosition; float vProgressuProgress; vProgress*aLife; //Explding // float explodingProgressuProgress*((1.0-0)/(0.1-0)); float explodingProgressvProgress*10.0; explodingProgressclamp(explodingProgress,0.0 ,1.0 ); explodingProgress1.0-pow(1.0-explodingProgress,3.0); newPosition*explodingProgress; // Falling // fallingProgress float fallingProgresslinearFunction(vProgress,0.1,0.0,1.0,1.0); fallingProgressclamp(fallingProgress,0.0 ,1.0 ); fallingProgress(1.0-pow(1.0-fallingProgress,3.0))*0.2; newPosition.y-fallingProgress; //scalling float sizeOpenProgresslinearFunction(vProgress,0.0,0.0,0.125,1.0); float sizecCloseProgresslinearFunction(vProgress,0.125,1.0,1.0,0.0); float scallingProcess min(sizecCloseProgress,sizecCloseProgress ) ; scallingProcessclamp(scallingProcess,0.0 ,1.0 ); //Twinkling float twinkProgreesslinearFunction(vProgress,0.2,0.0,0.8,1.0); twinkProgreessclamp(twinkProgreess,0.0 ,1.0 ); float twinkSizesin(vProgress30.0)0.50.5; twinkSize1.0-twinkProgreess*twinkSize; vec4 modelPositionmodelMatrix*vec4(newPosition,1.); vec4 viewPositionviewMatrix*modelPosition; vec4 projectedPositionprojectionMatrix*viewPosition; gl_PositionprojectedPosition; gl_PointSizeuSize*uResolution.y; gl_PointSize*aSize; gl_PointSize*scallingProcess; gl_PointSize*twinkSize; // 实现粒子的透视效果viewPosition为是模型视图变化后的posotion gl_PointSize*(1.0/-viewPosition.z); if(gl_PointSize1.0) gl_Positionvec4(9999.9); }, uniforms: { uSize: new THREE.Uniform(size), // 屏幕分辨率 uResolution: new THREE.Uniform(sizes.resolution), uTexture: new THREE.Uniform(texture), uColor: new THREE.Uniform(color), uProgress: new THREE.Uniform(0), }, transparent: true, // 关闭粒子深度测试 depthTest: false, // 开启混合 blending: THREE.AdditiveBlending, }); const fireWork new THREE.Points(geometry, material); fireWork.position.copy(position); scene.add(fireWork);//Destory const destory () { scene.remove(fireWork); geometry.dispose(); material.dispose(); };// Animate gsap.to(material.uniforms.uProgress, { value: 1, duration: 3, ease: linear, onComplete: destory, }); };const radomCreateFireWork () { const count Math.round(400 Math.random() * 1000); const position new THREE.Vector3( (Math.random() - 0.5) * 2, Math.random(), (Math.random() - 0.5) * 2 ); const size 0.1 Math.random() * 0.1; const texture textures[Math.floor(Math.random() * textures.length)]; const radius 1 Math.random(); const color new THREE.Color(); color.setHSL(Math.random(), 1, 0.7); createFireWork(count, position, size, texture, radius, color); };setInterval(radomCreateFireWork, 600);const camera new THREE.PerspectiveCamera( 75, sizes.width / sizes.height, 0.1, 100 ); camera.position.set(0, 0, 2); scene.add(camera);const renderer new THREE.WebGLRenderer({ antialias: true, }); renderer.setSize(sizes.width, sizes.height); renderer.setPixelRatio(sizes.pixelRatio); document.getElementById(box).appendChild(renderer.domElement);const controls new OrbitControls(camera, renderer.domElement); controls.enableDamping true;const tick () {controls.update()renderer.render(scene, camera)requestAnimationFrame(tick)};tick();完整源码GitHub小结本文提供粒子烟花完整 Three.js 源码与在线 Demo建议先运行案例再改 uniform/参数做二次实验更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库