7.3粒子系统 (Particles) 7.3 粒子系统 (Particles) 粒子系统是 Three.js 中一种强大的技术,用于模拟大量的微小物体,例如烟雾、火焰、星尘、水花等。通过控制每个粒子的属性(位置、颜色、大小、生命周期等),可以创造出各种各样令人惊叹的视觉效果。 7.3.1 粒子系统的基本概念 粒子(Particle): 构成粒子系统的基本单元,通常是一个小的几何体(例如 或 )或一个像素。 粒子系统(Particle System): 一个包含大量粒子的集合,以及控制这些粒子行为的规则和参数。在 Three.js 中,粒子系统通常由 对象表示。 粒子材质(Particle Material): 定义粒子的外观,包括颜色、纹理、透明度等。
粒子系统是 Three.js 中一种强大的技术,用于模拟大量的微小物体,例如烟雾、火焰、星尘、水花等。通过控制每个粒子的属性(位置、颜色、大小、生命周期等),可以创造出各种各样令人惊叹的视觉效果。
粒子(Particle): 构成粒子系统的基本单元,通常是一个小的几何体(例如 THREE.SphereGeometry 或 THREE.PlaneGeometry)或一个像素。
粒子系统(Particle System): 一个包含大量粒子的集合,以及控制这些粒子行为的规则和参数。在 Three.js 中,粒子系统通常由 THREE.Points 对象表示。
粒子材质(Particle Material): 定义粒子的外观,包括颜色、纹理、透明度等。常用的粒子材质有 THREE.PointsMaterial 和 THREE.ShaderMaterial(用于自定义着色器)。
粒子发射器(Particle Emitter): 负责创建和初始化粒子。发射器可以定义粒子的生成速率、初始位置、速度、方向等。
粒子属性(Particle Attributes): 每个粒子都具有的属性,例如位置、速度、颜色、大小、生命周期等。这些属性可以通过 JavaScript 代码或着色器程序进行控制和更新。
在 Three.js 中,创建粒子系统通常涉及以下步骤:
创建粒子几何体(Particle Geometry): 定义粒子的形状和顶点数据。通常使用 THREE.BufferGeometry 来存储大量的粒子数据。
创建粒子材质(Particle Material): 定义粒子的外观,例如颜色、纹理、大小等。
创建粒子系统(Particle System): 使用几何体和材质创建一个 THREE.Points 对象。
配置粒子属性(Particle Attributes): 设置每个粒子的初始属性,例如位置、速度、颜色、大小等。
创建粒子发射器(Particle Emitter): 定义粒子的生成规则和参数。
更新粒子状态(Update Particle State): 在渲染循环中,更新每个粒子的属性,例如位置、速度、颜色、大小、生命周期等。
以下是一个简单的 Three.js 粒子系统的示例代码,用于创建一个随机分布的粒子云:
import * as THREE from 'three'; // 创建场景、相机和渲染器 const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // 创建粒子几何体 const particleCount = 10000; const particleGeometry = new THREE.BufferGeometry(); const positions = new Float32Array(particleCount * 3); // 每个粒子有 x, y, z 三个坐标 for (let i = 0; i < particleCount * 3; i++) { positions[i] = (Math.random() - 0.5) * 50; // 随机位置,范围 -25 到 25 } particleGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); // 创建粒子材质 const particleMaterial = new THREE.PointsMaterial({ color: 0xffffff, // 白色 size: 0.1, // 粒子大小 sizeAttenuation: true // 远小近大 }); // 创建粒子系统 const particleSystem = new THREE.Points(particleGeometry, particleMaterial); scene.add(particleSystem); // 设置相机位置 camera.position.z = 10; // 渲染循环 function animate() { requestAnimationFrame(animate); // 旋转粒子系统 particleSystem.rotation.x += 0.001; particleSystem.rotation.y += 0.002; renderer.render(scene, camera); } animate();
代码解释:
创建场景、相机和渲染器: 这是 Three.js 的标准流程,用于初始化场景、相机和渲染器。
创建粒子几何体:
particleCount 定义了粒子的数量。
THREE.BufferGeometry 用于存储大量的粒子数据,相比于 THREE.Geometry 效率更高。
positions 是一个 Float32Array 类型的数组,用于存储每个粒子的 x, y, z 坐标。
循环遍历 positions 数组,为每个粒子生成随机的位置坐标。
particleGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) 将 positions 数组绑定到几何体的 position 属性上,告诉 Three.js 如何解释这些数据。
创建粒子材质:
THREE.PointsMaterial 是用于渲染粒子的材质。
color 定义了粒子的颜色。
size 定义了粒子的大小。
sizeAttenuation 设置为 true 表示粒子的大小会随着距离相机的距离而变化,产生远小近大的效果。
创建粒子系统:
THREE.Points 对象将几何体和材质结合起来,创建粒子系统。
scene.add(particleSystem) 将粒子系统添加到场景中。
设置相机位置: 设置相机的位置,以便可以看到粒子系统。
渲染循环:
requestAnimationFrame(animate) 创建一个循环,不断调用 animate 函数。
particleSystem.rotation.x += 0.001; 和 particleSystem.rotation.y += 0.002; 旋转粒子系统。
renderer.render(scene, camera) 渲染场景。
除了上述基本示例,Three.js 还支持许多高级粒子技术,例如:
纹理粒子: 使用纹理图像作为粒子的外观,可以创建更复杂的视觉效果。
自定义着色器: 使用 THREE.ShaderMaterial 和 GLSL 着色器语言,可以完全控制粒子的渲染方式,实现各种自定义的视觉效果。
粒子动画: 通过在渲染循环中更新粒子的属性,可以创建各种粒子动画效果,例如粒子爆炸、粒子流动等。
粒子碰撞: 检测粒子之间的碰撞,可以模拟更真实的物理效果。
粒子力场: 使用力场来影响粒子的运动,例如引力、斥力、风力等。
以下是一个使用纹理和自定义着色器的粒子系统示例,用于创建一个火焰效果:
import * as THREE from 'three'; // 创建场景、相机和渲染器 const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerWidth); document.body.appendChild(renderer.domElement); // 创建粒子几何体 const particleCount = 500; const particleGeometry = new THREE.BufferGeometry(); const positions = new Float32Array(particleCount * 3); const sizes = new Float32Array(particleCount); const colors = new Float32Array(particleCount * 3); for (let i = 0; i < particleCount; i++) { positions[i * 3] = 0; positions[i * 3 + 1] = 0; positions[i * 3 + 2] = 0; sizes[i] = Math.random() * 5 + 2; const color = new THREE.Color(Math.random(), Math.random(), Math.random()); colors[i * 3] = color.r; colors[i * 3 + 1] = color.g; colors[i * 3 + 2] = color.b; } particleGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); particleGeometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1)); particleGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); // 加载火焰纹理 const textureLoader = new THREE.TextureLoader(); const fireTexture = textureLoader.load('fire.png'); // 替换为你的火焰纹理路径 // 创建自定义着色器材质 const particleMaterial = new THREE.ShaderMaterial({ uniforms: { color: { value: new THREE.Color(1.0, 0.2, 0.0) }, pointTexture: { value: fireTexture } }, vertexShader: ` attribute float size; attribute vec3 color; varying vec3 vColor; void main() { vColor = color; vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); gl_PointSize = size * (300.0 / -mvPosition.z); gl_Position = projectionMatrix * mvPosition; } `, fragmentShader: ` uniform vec3 color; uniform sampler2D pointTexture; varying vec3 vColor; void main() { gl_FragColor = vec4(color * vColor, 1.0); gl_FragColor = gl_FragColor * texture2D(pointTexture, gl_PointCoord); } `, blending: THREE.AdditiveBlending, depthTest: false, transparent: true }); // 创建粒子系统 const particleSystem = new THREE.Points(particleGeometry, particleMaterial); scene.add(particleSystem); // 设置相机位置 camera.position.z = 10; // 渲染循环 function animate() { requestAnimationFrame(animate); // 更新粒子位置和大小 const positions = particleGeometry.attributes.position.array; const sizes = particleGeometry.attributes.size.array; for (let i = 0; i < particleCount; i++) { positions[i * 3 + 1] += 0.01; // 向上移动 if (positions[i * 3 + 1] > 5) { positions[i * 3 + 1] = 0; } sizes[i] = Math.random() * 5 + 2; } particleGeometry.attributes.position.needsUpdate = true; particleGeometry.attributes.size.needsUpdate = true; renderer.render(scene, camera); } animate();
代码解释:
创建粒子几何体: 除了位置,还添加了 size 和 color 属性,用于控制每个粒子的大小和颜色。
加载火焰纹理: 使用 THREE.TextureLoader 加载火焰纹理图像。
创建自定义着色器材质:
THREE.ShaderMaterial 允许使用自定义的顶点着色器和片元着色器来渲染粒子。
uniforms 定义了着色器可以访问的全局变量,例如颜色和纹理。
vertexShader 是顶点着色器代码,负责计算每个粒子的位置和大小。
fragmentShader 是片元着色器代码,负责计算每个粒子的颜色。
blending: THREE.AdditiveBlending 使用加法混合,使火焰效果更亮。
depthTest: false 关闭深度测试,使粒子可以重叠。
transparent: true 启用透明度。
更新粒子位置和大小: 在渲染循环中,更新每个粒子的位置和大小,创建火焰动画效果。
Vertex Shader (顶点着色器):
attribute float size; attribute vec3 color; varying vec3 vColor; void main() { vColor = color; vec4 mvPosition = modelViewMatrix * vec4(position, 1.0); gl_PointSize = size * (300.0 / -mvPosition.z); gl_Position = projectionMatrix * mvPosition; }
attribute float size;: 从几何体中读取每个粒子的大小。
attribute vec3 color;: 从几何体中读取每个粒子的颜色。
varying vec3 vColor;: 将颜色传递给片元着色器。
gl_PointSize = size * (300.0 / -mvPosition.z);: 设置粒子的大小,并根据距离进行衰减。
gl_Position = projectionMatrix * mvPosition;: 计算粒子的最终位置。
Fragment Shader (片元着色器):
uniform vec3 color; uniform sampler2D pointTexture; varying vec3 vColor; void main() { gl_FragColor = vec4(color * vColor, 1.0); gl_FragColor = gl_FragColor * texture2D(pointTexture, gl_PointCoord); }
uniform vec3 color;: 从 JavaScript 代码中传递的颜色。
uniform sampler2D pointTexture;: 从 JavaScript 代码中传递的纹理。
varying vec3 vColor;: 从顶点着色器接收的颜色。
gl_FragColor = vec4(color * vColor, 1.0);: 设置粒子的颜色。
gl_FragColor = gl_FragColor * texture2D(pointTexture, gl_PointCoord);: 将纹理应用到粒子上。
粒子系统通常需要处理大量的粒子数据,因此性能优化非常重要。以下是一些优化粒子系统性能的技巧:
使用 THREE.BufferGeometry: THREE.BufferGeometry 比 THREE.Geometry 更高效,因为它直接使用 GPU 内存。
减少粒子数量: 尽量减少粒子数量,只使用必要的粒子。
使用 LOD(Level of Detail): 根据距离相机的距离,使用不同细节级别的粒子系统。
使用 Instancing: 使用 THREE.InstancedMesh 可以减少 draw call,提高渲染性能。
优化着色器代码: 尽量简化着色器代码,避免复杂的计算。
使用 GPU 粒子: 将粒子系统的逻辑放在 GPU 上执行,可以显著提高性能。
粒子系统是 Three.js 中一种强大的技术,可以用于模拟各种各样的视觉效果。通过控制每个粒子的属性,可以创造出令人惊叹的视觉体验。掌握粒子系统的基本概念和实现方法,可以为你的 Three.js 项目增添更多创意和可能性。
希望这篇文章能够帮助你理解 Three.js 中的粒子系统。通过实践和探索,你可以创造出更多令人惊叹的视觉效果。