粒子系统
九千片雪花在一个 18 × 14 × 18 的空间里下落,越界后回到顶部重新开始。 位置属性每帧改写一次,是动态缓冲区的最小完整例子。
说明
每个粒子有固定的下落速度与摆动相位,存放在两个普通的
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 | 重叠处更亮,适合光点与雪 |
depthWrite | false | 避免透明点写入深度造成黑边 |
| 缓冲用途 | DynamicDrawUsage | 提示驱动层这批数据会频繁变化 |
更新流程
第一步改 CPU 侧的 Float32Array,第二步置
geometry.attributes.position.needsUpdate = true。
两步缺一不可:只改数组,GPU 侧数据不变;只置标志,数组没变,等于空转。
示例没有每帧重算包围球。粒子始终留在固定空间内,初始包围球仍然有效。
如果粒子会飘到远处,需要定期调用 computeBoundingSphere(),
否则视锥裁剪会把它们误判在视野外。
为什么不用一千个精灵
Sprite 每个都是一次独立绘制。上千个精灵会让绘制调用暴涨,
而且每个精灵都要单独维护朝向相机的逻辑。
Points 一次绘制全部粒子,点的大小与柔边由材质统一控制,
数量级上更适合雨雪、粒子流这类效果。
地理场景中的用法
粒子常用于表达流动与密度:风场、污染扩散、人流轨迹。 如果粒子的位置来自地理坐标,同样要先转换到局部平面并统一单位。 粒子的速度也应当按秒计算,而不是按帧,否则帧率变化时流动速度会跟着变。
粒子数不是越多越好。粒子系统最终受限于填充率:屏幕上铺满半透明点, 每个像素都要混合多次。点径调大或粒子数翻倍,帧率下降往往比预期更明显。