Three.js 粒子地球教程

📅 2026/7/4 11:29:17
Three.js 粒子地球教程
粒子地球 ·Points Earth· ▶ 在线运行案例案例合集三维可视化功能案例threehub.cn开源仓库github地址https://github.com/z2586300277/three-cesium-examples400个案例代码:网盘链接你将学到什么ShaderMaterial 自定义着色器实现核心视觉效果OrbitControls 相机轨道交互THREE.Points 粒子点渲染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 等交互控件若源码包含在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;let gui new GUI(); // 初始化场景、相机和渲染器 const gl { clearColor: #332148, shadows: false, alpha: false, outputColorSpace: THREE.SRGBColorSpace, };const scene new THREE.Scene(); const camera new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 100 );const renderer new THREE.WebGLRenderer({ alpha: gl.alpha }); renderer.setClearColor(gl.clearColor); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement);const controls new OrbitControls(camera, renderer.domElement); controls.target.set(0, 0, 0); controls.update(); // 设置相机位置 camera.position.z 2; // Shaders const vertexShader uniform float size; uniform sampler2D elevTexture; uniform sampler2D alphaTexture; uniform float uTime; uniform float uWaveHeight; uniform float uWaveSpeed;varying vec2 vUv; varying float vVisible; varying float vAlpha; varying float vElevation; // Function to generate fBm with vec3 input float random(vec3 st) { return fract(sin(dot(st.xyz, vec3(12.9898,78.233,45.164))) * 43758.5453123); }float noise(vec3 st) { vec3 i floor(st); vec3 f fract(st);// Eight corners in 3D of a tile float a random(i); float b random(i vec3(1.0, 0.0, 0.0)); float c random(i vec3(0.0, 1.0, 0.0)); float d random(i vec3(1.0, 1.0, 0.0)); float e random(i vec3(0.0, 0.0, 1.0)); float f1 random(i vec3(1.0, 0.0, 1.0)); float g random(i vec3(0.0, 1.0, 1.0)); float h random(i vec3(1.0, 1.0, 1.0));vec3 u ff(3.0 - 2.0 * f);return mix(mix(mix(a, b, u.x), mix(c, d, u.x), u.y), mix(mix(e, f1, u.x), mix(g, h, u.x), u.y), u.z); }float fbm(vec3 st) { float value 0.0; float amplitude 0.5;for (int i 0; i 5; i) { value amplitude * noise(st); st * 2.0; amplitude * 0.5; } return value; }void main() { vUv uv; float alphaLand 1.0 - texture2D(alphaTexture, vUv).r; vAlpha alphaLand; vec3 newPosition position;if(alphaLand 0.5) { // Sea // fBm for wave-like displacement float waveHeight uWaveHeight; // Adjust wave height as needed float waveSpeed uWaveSpeed; // Adjust wave speed as needed float displacement (fbm(newPosition5.0 uTimewaveSpeed)2.0 - 1.0)waveHeight; vElevation displacement; newPosition normal * displacement ; }vec4 mvPosition modelViewMatrix * vec4( newPosition, 1.0 ); float elv texture2D(elevTexture, vUv).r; vec3 vNormal normalMatrix * normal; vVisible step(0.0, dot( -normalize(mvPosition.xyz), normalize(vNormal))); mvPosition.z 0.45 * elv;// 求出 mvPosition 距离相机的距离 float dist length(mvPosition.xyz); // 根据距离调整 size float pointSize size * (1.0 - dist / 10.0); gl_PointSize max(pointSize, 1.0); gl_PointSize pointSize; gl_Position projectionMatrix * mvPosition; }; // 将pointsEarth.vert的内容粘贴在这里 const fragmentShader uniform sampler2D colorTexture; // uniform sampler2D alphaTexture; uniform sampler2D earthTexture; uniform sampler2D starTexture;varying vec2 vUv; varying float vVisible; varying float vAlpha; varying float vElevation;void main() { if (floor(vVisible 0.1) 0.0) discard; vec2 coord gl_PointCoord; float alpha texture2D(starTexture, coord).a; // 根据 alpha 值来裁剪形状 if (alpha 0.1) discard;// float alphaLand 1.0 - texture2D(alphaTexture, vUv).r; vec3 color texture2D(colorTexture, vUv).rgb; vec3 earth texture2D(earthTexture, vUv).rgb; color mix(color, earth, 0.65); if( vAlpha 0.5 ) { gl_FragColor vec4(color, vAlpha); }else { // 对于海洋部分根据 vElevation 调整颜色 float elevationEffect clamp(vElevation*30.0, -1.0, 1.0); // 将 vElevation 限制在 [-1, 1] 范围内 vec3 deep_sea_blue vec3(0.004, 0.227, 0.388); vec3 adjustedColor mix(deep_sea_blue, earth1.75, (elevationEffect 1.0)0.5); // 根据 vElevation 调整颜色 gl_FragColor vec4(adjustedColor, 1.0-vAlpha); } }; // 将pointsEarth.frag的内容粘贴在这里const params { color: #17c5a9, pointSize: 4.0, };const wireframeMaterial { color: params.color, wireframe: true, transparent: true, opacity: 0.2, };const textures { earthMap: new THREE.TextureLoader().load(FILE_HOST threeExamples/shader/earth1.jpg), starSprite: new THREE.TextureLoader().load(FILE_HOST threeExamples/particle/particleEarth/circle.png), colorMap: new THREE.TextureLoader().load(FILE_HOST threeExamples/particle/particleEarth/04_rainbow1k.jpg), elevMap: new THREE.TextureLoader().load(FILE_HOST threeExamples/particle/particleEarth/01_earthbump1k.jpg), alphaMap: new THREE.TextureLoader().load(FILE_HOST threeExamples/particle/particleEarth/02_earthspec1k.jpg), };const pointsShader { uniforms: { size: { type: f, value: params.pointSize }, uTime: { type: f, value: 0.0 }, uWaveHeight: { type: f, value: 0.075 }, uWaveSpeed: { type: f, value: 0.2 }, colorTexture: { type: t, value: textures.colorMap }, elevTexture: { type: t, value: textures.elevMap }, alphaTexture: { type: t, value: textures.alphaMap }, earthTexture: { type: t, value: textures.earthMap }, starTexture: { type: t, value: textures.starSprite }, }, vertexShader: vertexShader, fragmentShader: fragmentShader, transparent: true, side: THREE.FrontSide, };const group new THREE.Group(); const wireframeMaterialRef new THREE.MeshBasicMaterial(wireframeMaterial); const pointsMaterial new THREE.ShaderMaterial(pointsShader);const geometry new THREE.IcosahedronGeometry(1, 4); const wireframeMesh new THREE.Mesh(geometry, wireframeMaterialRef); group.add(wireframeMesh);const pointsGeometry new THREE.IcosahedronGeometry(1, 128); const pointsMesh new THREE.Points(pointsGeometry, pointsMaterial); group.add(pointsMesh);const hemisphereLight new THREE.HemisphereLight(#ffffff, #080820, 3); scene.add(hemisphereLight);scene.add(group);// 创建一个名为 Debug 的文件夹 let debugFolder gui.addFolder(Debug);// 添加颜色控制器 debugFolder.addColor(params, color).name(color).onChange((value) { wireframeMaterialRef.color.set(value); });// 添加粒子大小控制器 debugFolder.add(params, pointSize, 0.1, 10, 0.1).name(粒子大小).onChange((value) { pointsShader.uniforms.size.value value; });// 添加海浪高度控制器 debugFolder.add(pointsShader.uniforms.uWaveHeight, value, 0.01, 0.5, 0.01).name(海浪高度).onChange((value) { pointsShader.uniforms.uWaveHeight.value value; });// 添加海浪变化速度控制器 debugFolder.add(pointsShader.uniforms.uWaveSpeed, value, 0.01, 1, 0.01).name(海浪变化速度).onChange((value) { pointsShader.uniforms.uWaveSpeed.value value; });// 渲染循环 function animate() { pointsShader.uniforms.uTime.value 10 * 0.016; group.rotation.y 0.002; requestAnimationFrame(animate); renderer.render(scene, camera); } animate();完整源码GitHub小结本文提供粒子地球完整 Three.js 源码与在线 Demo建议先运行案例再改 uniform/参数做二次实验更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库