在Three.js中使用Points对象创建粒子系统是一种高效渲染大量小图形的技术。以下是创建和定制粒子系统的完整指南:
javascript
// 1. 创建几何体并设置顶点
const geometry = new THREE.BufferGeometry();
const count = 5000;
// 创建顶点数组
const positions = new Float32Array(count * 3); // x, y, z 坐标
for (let i = 0; i < count * 3; i++) {
positions[i] = (Math.random() - 0.5) * 10; // -5 到 5 的范围
}
// 将顶点数据添加到几何体
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
// 2. 创建材质
const material = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.02,
sizeAttenuation: true // 距离远近影响粒子大小
});
// 3. 创建Points对象
const particles = new THREE.Points(geometry, material);
scene.add(particles);
javascript
// 加载纹理
const textureLoader = new THREE.TextureLoader();
const particleTexture = textureLoader.load('path/to/particle.png');
const material = new THREE.PointsMaterial({
size: 0.1,
map: particleTexture, // 使用纹理
transparent: true,
alphaTest: 0.001, // 透明阈值
blending: THREE.AdditiveBlending, // 叠加混合模式
depthWrite: false, // 优化渲染
vertexColors: true // 启用顶点颜色
});
javascript
const vertexShader = `
attribute float size;
attribute vec3 customColor;
varying vec3 vColor;
void main() {
vColor = customColor;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = size * (300.0 / -mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
`;
const fragmentShader = `
varying vec3 vColor;
void main() {
gl_FragColor = vec4(vColor, 1.0);
// 圆形粒子
float distance = length(gl_PointCoord - vec2(0.5));
if (distance > 0.5) {
discard;
}
}
`;
const shaderMaterial = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
uniforms: {
time: { value: 0 }
}
});
javascript
// 在动画循环中更新粒子
function animateParticles() {
requestAnimationFrame(animateParticles);
const positions = particles.geometry.attributes.position.array;
const count = positions.length / 3;
for (let i = 0; i < count; i++) {
const i3 = i * 3;
// 示例:正弦波运动
positions[i3 + 1] = Math.sin(clock.elapsedTime + i * 0.01) * 2;
// 旋转效果
const angle = clock.elapsedTime * 0.5 + i * 0.01;
const radius = Math.sqrt(positions[i3] ** 2 + positions[i3 + 2] ** 2);
positions[i3] = Math.cos(angle) * radius;
positions[i3 + 2] = Math.sin(angle) * radius;
}
particles.geometry.attributes.position.needsUpdate = true;
renderer.render(scene, camera);
}
javascript
// 射线检测与粒子交互
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
window.addEventListener('click', (event) => {
// 计算鼠标归一化坐标
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObject(particles);
if (intersects.length > 0) {
// 获取点击的粒子索引
const index = intersects[0].index;
// 修改被点击的粒子
const positions = particles.geometry.attributes.position.array;
const i3 = index * 3;
positions[i3 + 1] += 1; // 向上移动
particles.geometry.attributes.position.needsUpdate = true;
}
});
javascript
const geometry = new THREE.InstancedBufferGeometry();
geometry.instanceCount = count;
// 实例化位置
const positions = new Float32Array(count * 3);
const offsets = new Float32Array(count * 3); // 每个实例的偏移量
const scales = new Float32Array(count); // 每个实例的缩放
// 填充数据
for (let i = 0; i < count; i++) {
offsets[i * 3] = (Math.random() - 0.5) * 10;
offsets[i * 3 + 1] = (Math.random() - 0.5) * 10;
offsets[i * 3 + 2] = (Math.random() - 0.5) * 10;
scales[i] = Math.random() * 0.5 + 0.5;
}
geometry.setAttribute('offset', new THREE.InstancedBufferAttribute(offsets, 3));
geometry.setAttribute('scale', new THREE.InstancedBufferAttribute(scales, 1));
减少粒子数量:在保持效果的前提下使用最少粒子
简化材质:避免复杂着色器计算
使用实例化:大量相同粒子时使用实例化渲染
层级细节(LOD):根据距离调整粒子密度
视锥体裁剪:只渲染可见区域内的粒子
javascript
function createStarfield(count = 10000) {
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(count * 3);
const colors = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
// 随机位置(球面分布)
const radius = 100 + Math.random() * 900;
const theta = Math.random() * Math.PI * 2;
const phi = Math.acos(2 * Math.random() - 1);
positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta);
positions[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta);
positions[i * 3 + 2] = radius * Math.cos(phi);
// 随机颜色(偏白色)
colors[i * 3] = 0.8 + Math.random() * 0.2;
colors[i * 3 + 1] = 0.8 + Math.random() * 0.2;
colors[i * 3 + 2] = 0.9 + Math.random() * 0.1;
}
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const material = new THREE.PointsMaterial({
size: 0.7,
vertexColors: true,
transparent: true,
opacity: 0.8,
sizeAttenuation: true
});
return new THREE.Points(geometry, material);
}
通过组合这些技术,你可以创建从简单点到复杂动态粒子系统的各种视觉效果。