Three.js 粒子泡泡教程 粒子泡泡 ·Bubble· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么ShaderMaterial 自定义着色器实现核心视觉效果OrbitControls 相机轨道交互BufferGeometry 自定义顶点/索引数据requestAnimationFrame渲染循环与resize自适应效果说明本案例演示粒子泡泡效果基于 WebGL 实现「粒子泡泡」可视化效果附完整可运行源码核心用到 ShaderMaterial、OrbitControls、BufferGeometry。建议先打开文首在线案例查看动态画面再对照下方源码逐步理解。核心概念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/addons/controls/OrbitControls.js; import { GUI } from three/addons/libs/lil-gui.module.min.js; const { Color, ShaderMaterial, BufferGeometry, Points, Vector3, Float32BufferAttribute, } THREEclass Bubble extends Points {_count 0;_size 10;_color #ff0000;_speed 0.8;_maxHeight 10;_radius 10;_radius2 10;isBubble true;_emitter cone_emitters [ cone, cylinder, box, sphere ];type Bubble;/*** param emitter {string}*/ set emitter(emitter) { if (this._emitters.indexOf(emitter) ! -1) { this._emitter emitter; this.setPoints(); this.uniforms.emitter.value this._emitters.indexOf(this._emitter); } }set radius(radius) { this._radius radius; this.setPoints(); }set radius2(radius) { this._radius2 radius; this.setPoints(); }set count(count) { this._count count; this.setPoints(); }set maxHeight(maxHeight) { this._maxHeight maxHeight; this.uniforms.maxHeight.value maxHeight || 10; this.setPoints(); }set speed(speed) { this._speed speed; this.uniforms.speed.value speed; }set size(size) { this._size size; this.uniforms.size.value size; }set color(color) { this._color color; this.uniforms.color.value new Color(color); }get radius() { return this._radius; }get emitter() { return this._emitter; }get emitters() { return this._emitters; }get count() { return this._count; }get speed() { return this._speed; }get size() { return this._size; }get maxHeight() { return this._maxHeight; }vertexShader varying vec2 vUv; //创建uv变量,用于给片元着色器传递uv uniform float u_time; //从前端接收u_time uniform float speed; //从前端接收speed uniform float size; //从前端接收size uniform float emitter;//发射器类型 uniform float maxHeight;//从前端接收maxHeight attribute float data1; attribute float data2; void main(){ //从顶点着色器取uv给片元着色器 vUv vec2(uv.x,uv.y); //用一个变量复制当前位置,用于计算最终位置 vec3 u_position position; if(emitter 3.0){ //粒子y轴变化 float t fract( u_time * speed position.y/maxHeight); //对u_time取小数,使得数据一直在0~1按顺序变化,减y轴是用于移动图像 u_position.y t * maxHeight; //圆锥模式 if(emitter 0.0){ u_position.x cos(data2)data1u_position.y/maxHeight; u_position.z sin(data2)data1u_position.y/maxHeight; } //圆柱模式 if(emitter 1.0){ u_position.x cos(data2) * data1; u_position.z sin(data2) * data1; } //立方体模式 if(emitter 2.0){ u_position.x data1; u_position.z data2; } }else{ //球体模式 if(emitter 3.0){ float r length(u_position); float t fract(u_time * speed r/maxHeight); r t * maxHeight; u_position.x rsin(data1)cos(data2); u_position.y r * sin(data2); u_position.z rcos(data1)cos(data2); } } //设定粒子大小 gl_PointSize size; //固定写法,将最终计算完成的顶点位置传递给显卡并交由显卡计算 gl_Position projectionMatrixmodelViewMatrixvec4( u_position, 1.0 ); };fragmentShader uniform vec3 color;//从前端接收颜色 varying vec2 vUv; //获取从顶点着色器传递过来的uv void main(){ //气泡计算公式, 根据中心到边缘的距离设定透明度 float dis pow( distance( gl_PointCoord , vec2(0.5,0.5) ) ,2.0); //透明度高于0.2的部分舍弃,用于舍弃边缘方形的区域 if(dis 0.2){ discard; } //固定写法,将计算后的颜色渲染出来 gl_FragColor vec4(color,dis * 2.0); };uniforms { u_time: { value: 0 }, speed: { value: 0.8 }, size: { value: 10 }, color: { value: new Color(#2acdf9) }, maxHeight: { value: 10 }, emitter: { value: this._emitters.indexOf(this._emitter) } };/*** param config {Object}count:粒子数量xArea:x随机范围zArea:z随机范围maxHeight:最大升腾高度speed:升腾速度size:气泡大小color:气泡颜色*/ constructor(config) { super(); this.material this.initLineMaterial(); this.geometry new BufferGeometry(); config config || { color: #2acdf9 }; this.uniforms.speed.value config.speed || this.uniforms.speed.value this.uniforms.size.value config.size || this.uniforms.size.value this.uniforms.maxHeight.value config.maxHeight || this.uniforms.maxHeight.value this.uniforms.color.value new Color(config.color); this._count config.count || 100; this.setPoints(); }initLineMaterial () { return new ShaderMaterial({ uniforms: this.uniforms, vertexShader: this.vertexShader, fragmentShader: this.fragmentShader, transparent: true, }); }setPoints () { let points []; let data1Array []; let data2Array []; for (let i 0; i this._count; i) {//圆柱模式下,生成的数据用于半径和角度 if (this._emitter cone || this._emitter cylinder) { let data1 Math.random() * this._radius; let data2 Math.PI2Math.random(); data1Array.push(data1); data2Array.push(data2); } if (this._emitter box) { let data1 Math.random() * this._radius - this._radius / 2; let data2 Math.random() * this._radius2 - this._radius2 / 2; data1Array.push(data1); data2Array.push(data2); } if (this._emitter sphere) { let data1 Math.PI2Math.random(); let data2 Math.PI2Math.random(); data1Array.push(data1); data2Array.push(data2); } let y i / this._count * this.uniforms.maxHeight.value; points.push(new Vector3(0, y, 0)); } this.geometry.setFromPoints(points); this.geometry.setAttribute(data1, new Float32BufferAttribute(data1Array, 1)); this.geometry.setAttribute(data2, new Float32BufferAttribute(data2Array, 1)); console.log(this.geometry); }onBeforeRender () { this.uniforms.u_time.value 0.01; } }window.addEventListener(load, e { init(); addMesh(); render(); })let scene, renderer, camera; let orbit;let gui new GUI();let bubble;function init() {scene new THREE.Scene(); renderer new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.append(renderer.domElement);camera new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 2000); camera.add(new THREE.PointLight()); camera.position.set(10, 10, 10); scene.add(camera);orbit new OrbitControls(camera, renderer.domElement); orbit.enableDamping true;scene.add(new THREE.AxesHelper(10)); }function addMesh() { let size 1; let geometry new THREE.BoxGeometry(size, size, size).translate(0, size / 2, 0); let material new THREE.MeshBasicMaterial({ color: #00ffff, transparent: true, opacity: 0.5 }); let mesh new THREE.Mesh(geometry, material); scene.add(mesh);bubble new Bubble({ speed: 0.8, size: 30, maxHeight: 10, color: #ff0000 }); scene.add(bubble);let params { speed: 0.8, size: 30, maxHeight: 10, color: #1acdf9, count: 100, radius: 10, rotateSpeed: 0.01, backgroundColor: #000000, emitter: cone, emitterOptions: [cone, cylinder, box, sphere] };gui.add(params, speed, -2, 2).step(0.01).onChange(v bubble.speed v); gui.add(params, size).onChange(v bubble.size v); gui.add(params, maxHeight).onChange(v bubble.maxHeight v); gui.addColor(params, color).onChange(v bubble.color v); gui.add(params, count).onChange(v bubble.count v); gui.add(params, radius).onChange(v bubble.radius v); gui.add(params, rotateSpeed).onChange(v bubble.rotateSpeed v); gui.addColor(params, backgroundColor).name(背景色).onChange(v { scene.background new THREE.Color(v); }); gui.add(params, emitter, params.emitterOptions).name(粒子发射方式).onChange(v { bubble.emitter v; }); }function render() { renderer.render(scene, camera); orbit.update(); requestAnimationFrame(render); }完整源码GitHub小结本文提供粒子泡泡完整 Three.js 源码与在线 Demo建议先运行案例再改 uniform/参数做二次实验更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库

相关新闻

最新新闻

工程项目管理破局:用数字化闭环解决管控乱象

工程项目管理破局:用数字化闭环解决管控乱象

一、工程项目普遍存在的管理痛点:全周期信息断层工程行业从业者普遍有一个共性体验:项目中标初期,整体规划、流程、节点都清晰明确,但随着施工推进,各项工作会逐渐陷入信息不透明、状态不清晰的被动局面。材料进场状态…

2026/8/25 23:15:14
国产AI数据大屏生成工具对比分析与数字孪生大屏选型指南

国产AI数据大屏生成工具对比分析与数字孪生大屏选型指南

我们公司最近在筹备一个智慧园区的数字孪生展厅,需要一块能展示园区实时运营数据的大屏。本来打算找外包公司做,但报价动辄几十万,而且后续数据更新还得依赖他们,想想就不划算。于是我开始研究市面上国产的AI数据大屏生成工具&…

2026/8/25 23:15:14
嘎嘎降AI和火龙果写作哪个好:2026年博士论文降AI工具亲测深度对比分析

嘎嘎降AI和火龙果写作哪个好:2026年博士论文降AI工具亲测深度对比分析

嘎嘎降AI和火龙果写作哪个好:2026年博士论文降AI工具亲测深度对比分析 总有人问嘎嘎降AI和火龙果写作对比,这篇把主流几款对比清楚。 综合推荐嘎嘎降AI(www.aigcleaner.com),4.8元,99.26%达标率。不同需求…

2026/8/25 23:15:14
C++结构体排序实战:重载、函数与Lambda三种方式详解

C++结构体排序实战:重载、函数与Lambda三种方式详解

1. 项目概述:为什么结构体排序是编程中的“家常便饭”?在C的实际开发里,尤其是处理业务数据时,我们很少只对单一的基本类型(比如一个整数、一个字符串)进行排序。更多时候,我们面对的是一个个“…

2026/8/25 23:15:14
FMA|完整音乐分析数据集,106,574 首曲目与 917 GiB CC 音频

FMA|完整音乐分析数据集,106,574 首曲目与 917 GiB CC 音频

FMA|完整音乐分析数据集,106,574 首曲目与 917 GiB CC 音频 【免费下载链接】fma FMA: A Dataset For Music Analysis 项目地址: https://gitcode.com/gh_mirrors/fm/fma FMA 是一个开源音乐分析数据集,收录 106,574 首 Creative Comm…

2026/8/25 23:15:14
不是定时狂点,而是“看懂屏幕“的智能自动点击器

不是定时狂点,而是“看懂屏幕“的智能自动点击器

手游里重复刷材料,手指点到抽筋;某个App每天固定流程操作,枯燥又费时;测试软件时需要模拟用户点击,传统自动点击器只会无脑乱点——如果你需要的是能"看懂"屏幕、按条件触发的自动化工具,Klickr&…

2026/8/25 23:10:14