经纬志
首页 / three.js / 使用示例

粒子系统

九千片雪花在一个 18 × 14 × 18 的空间里下落,越界后回到顶部重新开始。 位置属性每帧改写一次,是动态缓冲区的最小完整例子。

three.js 0.160.0 · 更新 2026-10-27
源码与运行效果
运行效果

说明

每个粒子有固定的下落速度与摆动相位,存放在两个普通的 Float32Array 里,不进入 GPU。每帧只更新 position 属性,然后置 needsUpdate = true, 驱动层会把整段数据重新上传。

柔边圆点由一张 64 × 64 的 Canvas 生成:从中心白色到边缘透明的径向渐变。 PointsMaterial 读取这张图作为 map, 配合 AdditiveBlending 与 depthWrite: false, 得到叠加发亮的雪片。

设置取值说明
粒子数9000每帧写入约 108 KB 顶点数据
空间尺寸18 × 14 × 18下落高度 14,越界回到顶部
点径0.16可调到 0.32,观察填充率变化
混合Additive重叠处更亮,适合光点与雪
depthWritefalse避免透明点写入深度造成黑边
缓冲用途DynamicDrawUsage提示驱动层这批数据会频繁变化

更新流程

第一步改 CPU 侧的 Float32Array,第二步置 geometry.attributes.position.needsUpdate = true。 两步缺一不可:只改数组,GPU 侧数据不变;只置标志,数组没变,等于空转。

示例没有每帧重算包围球。粒子始终留在固定空间内,初始包围球仍然有效。 如果粒子会飘到远处,需要定期调用 computeBoundingSphere(), 否则视锥裁剪会把它们误判在视野外。

为什么不用一千个精灵

Sprite 每个都是一次独立绘制。上千个精灵会让绘制调用暴涨, 而且每个精灵都要单独维护朝向相机的逻辑。 Points 一次绘制全部粒子,点的大小与柔边由材质统一控制, 数量级上更适合雨雪、粒子流这类效果。

地理场景中的用法

粒子常用于表达流动与密度:风场、污染扩散、人流轨迹。 如果粒子的位置来自地理坐标,同样要先转换到局部平面并统一单位。 粒子的速度也应当按秒计算,而不是按帧,否则帧率变化时流动速度会跟着变。

粒子数不是越多越好。粒子系统最终受限于填充率:屏幕上铺满半透明点, 每个像素都要混合多次。点径调大或粒子数翻倍,帧率下降往往比预期更明显。