Three.js 地球粒子教程 地球粒子 ·Globe Particle· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么ShaderMaterial 自定义着色器实现核心视觉效果EffectComposer 多 Pass 后期处理管线UnrealBloomPass 辉光 Bloom 效果OrbitControls 相机轨道交互THREE.Points 粒子点渲染BufferGeometry 自定义顶点/索引数据requestAnimationFrame渲染循环与resize自适应效果说明本案例演示地球粒子效果原场景渲染后经 EffectComposer 叠加 Bloom/模糊等全屏后期核心用到 ShaderMaterial、EffectComposer、UnrealBloomPass。建议先打开文首在线案例查看动态画面再对照下方源码逐步理解。核心概念Scene / Camera / WebGLRenderer构成最小渲染闭环大场景可开logarithmicDepthBuffer缓解 Z-fighting。ShaderMaterial通过uniforms 自定义 GLSL 控制逐像素/逐点效果透明粒子常配合depthTest: false。EffectComposer以多 Pass 链式渲染RenderPass → 特效 Pass → 输出屏幕替代直接renderer.render。OrbitControls提供轨道旋转/缩放开启enableDamping后需在 animate 中controls.update()。实现步骤搭建 Scene、PerspectiveCamera、WebGLRenderer挂载 canvas 并处理resize定义 uniforms / onBeforeCompile 或 ShaderMaterial编写 GLSL 与材质参数组装 EffectComposer Pass 链在 animate 中调用composer.render()创建 OrbitControls及 Raycaster 等交互控件若源码包含在requestAnimationFrame循环中更新状态并 renderCesium 为viewer.render或自动渲染代码要点import * as THREE from three;import { OrbitControls } from three/addons/controls/OrbitControls.js; import { EffectComposer } from three/addons/postprocessing/EffectComposer.js; import { RenderPass } from three/addons/postprocessing/RenderPass.js; import { ShaderPass } from three/addons/postprocessing/ShaderPass.js; import { UnrealBloomPass } from three/addons/postprocessing/UnrealBloomPass.js; import { GammaCorrectionShader } from three/addons/shaders/GammaCorrectionShader.js;const scene new THREE.Scene(); scene.background new THREE.Color(0x020209); scene.fog new THREE.Fog(0x020209, 15, 60);const camera new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000); camera.position.z 6;const renderer new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); renderer.toneMapping THREE.ACESFilmicToneMapping; renderer.toneMappingExposure 1.2; document.getElementById(box).appendChild(renderer.domElement);const ambientLight new THREE.AmbientLight(0x333366, 0.8); scene.add(ambientLight);const directionalLight new THREE.DirectionalLight(0xffffcc, 1.2); directionalLight.position.set(1, 3, 2); scene.add(directionalLight);const pointLight new THREE.PointLight(0x3366ff, 2, 10); pointLight.position.set(0, 0, 0); scene.add(pointLight);const composer new EffectComposer(renderer); const renderPass new RenderPass(scene, camera); composer.addPass(renderPass);const bloomPass new UnrealBloomPass( new THREE.Vector2(window.innerWidth, window.innerHeight), 1.2, 0.7, 0.5 ); composer.addPass(bloomPass);const gammaCorrectionPass new ShaderPass(GammaCorrectionShader); composer.addPass(gammaCorrectionPass);const controls new OrbitControls(camera, renderer.domElement); controls.enableDamping true; controls.dampingFactor 0.1; controls.rotateSpeed 0.5; controls.minDistance 3; controls.maxDistance 15; controls.autoRotate true; controls.autoRotateSpeed 0.5;controls.addEventListener(start, () { document.body.style.cursor grabbing; controls.autoRotate false; });controls.addEventListener(end, () { document.body.style.cursor grab; setTimeout(() { controls.autoRotate true; }, 3000); });const numParticles 30000; const geometry new THREE.BufferGeometry(); const positions new Float32Array(numParticles * 3); const colors new Float32Array(numParticles * 3); const sizes new Float32Array(numParticles); const speeds new Float32Array(numParticles);for (let i 0; i numParticles; i) { if (i numParticles * 0.8) { const phi Math.acos(-1 (2i) / (numParticles0.8)); const theta Math.sqrt(numParticlesMath.PI)phi; const radius 1.8 Math.random() * 0.4; const x Math.sin(phi)Math.cos(theta)radius; const y Math.sin(phi)Math.sin(theta)radius; const z Math.cos(phi) * radius; positions[i * 3] x; positions[i * 3 1] y; positions[i * 3 2] z; } else { const angle Math.random()Math.PI2; const radius 2 Math.random() * 3; const height (Math.random() - 0.5) * 2; positions[i3] Math.cos(angle)radius; positions[i * 3 1] height; positions[i3 2] Math.sin(angle)radius; }const distance Math.sqrt( positions[i3]* 2 positions[i3 1]* 2 positions[i3 2]* 2 );let color; if (distance 1.5) { color new THREE.Color(0x4466ff); } else if (distance 2.5) { color new THREE.Color(0x9944ff); } else { color new THREE.Color(0xff44aa); }color.offsetHSL(0, (Math.random() - 0.5)0.3, (Math.random() - 0.5)0.2); colors[i * 3] color.r; colors[i * 3 1] color.g; colors[i * 3 2] color.b;sizes[i] distance 2 ? 0.05 Math.random()0.04 : 0.03 Math.random()0.03; speeds[i] 0.4 Math.random() * 0.6; }geometry.setAttribute(position, new THREE.BufferAttribute(positions, 3)); geometry.setAttribute(color, new THREE.BufferAttribute(colors, 3)); geometry.setAttribute(size, new THREE.BufferAttribute(sizes, 1));const particleMaterial new THREE.ShaderMaterial({ uniforms: { time: { value: 0 }, pixelRatio: { value: window.devicePixelRatio } }, vertexShader:attribute float size; uniform float time; uniform float pixelRatio; varying vec3 vColor; void main() { vColor color; float pulse 1.0 0.2 * sin(time position.x position.z); vec4 mvPosition modelViewMatrix * vec4(position, 1.0); gl_PointSize sizepulsepixelRatio * (300.0 / -mvPosition.z); gl_Position projectionMatrix * mvPosition; }, fragmentShader:varying vec3 vColor; void main() { float distanceToCenter length(gl_PointCoord - vec2(0.5)); if (distanceToCenter 0.5) discard; float alpha 1.0 - smoothstep(0.4, 0.5, distanceToCenter); gl_FragColor vec4(vColor, alpha); }, transparent: true, vertexColors: true, depthWrite: false, blending: THREE.AdditiveBlending });const particleSystem new THREE.Points(geometry, particleMaterial); scene.add(particleSystem);window.addEventListener(resize, () { camera.aspect window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); composer.setSize(window.innerWidth, window.innerHeight); particleMaterial.uniforms.pixelRatio.value window.devicePixelRatio; }, false);renderer.domElement.addEventListener(dblclick, () { camera.position.set(0, 0, 6); camera.lookAt(0, 0, 0); controls.reset(); });const clock new THREE.Clock();function animate() { requestAnimationFrame(animate); const delta clock.getDelta(); const elapsedTime clock.getElapsedTime(); particleSystem.rotation.y delta * 0.05; particleMaterial.uniforms.time.value elapsedTime; particleSystem.scale.x 1 Math.sin(elapsedTime0.3)0.05; particleSystem.scale.y 1 Math.sin(elapsedTime0.4)0.05; particleSystem.scale.z 1 Math.sin(elapsedTime0.5)0.05; controls.update(); composer.render(); }animate();完整源码GitHub小结本文提供地球粒子完整 Three.js 源码与在线 Demo建议先运行案例再改 uniform/参数做二次实验更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库