第 15 课:综合实战——粒子系统

返回教程目录 · 打开本课练习

为什么选 Points

星空、烟花、萤火虫都需要很多独立的小图形。给每个点创建一个 Mesh 会增加对象数和 draw call;一个 BufferGeometry 加一个 THREE.Points 更适合这些例子。JavaScript 只在初始化时创建 position、aSize、aPhase、aLife、aVelocity 数组并上传 GPU。渲染循环更新统一时间,不逐帧重建几何体。每粒子数据适合 attribute,全体共享数据适合 uniform。

顶点到片元的数据流

顶点 Shader 从 attribute 取得一个点的起点、尺寸、出生相位、寿命和速度。它在局部空间算出当前位置,经 modelViewMatrix 与 projectionMatrix 变换后写入 gl_Position,并用 gl_PointSize 设置屏幕像素大小。vAlpha、vSeed 作为 varying 传到片元;点精灵的片元可读 gl_PointCoord,以中心距离做柔边圆点。由于一个点通常只有一个顶点,这里的 varying 在整个点精灵内基本保持同一粒子值,真正的点内变化来自 gl_PointCoord。

第 1 题先显示纯色点。第 2 题把 sin(uTime * 1.5 + aPhase * 2π) 加到 Y:相位不同的点不会齐刷刷同时升降。第 3 题让 aSize 决定大小;近大远小的缩放由观察空间 Z 控制,但最终仍应给大小设合理范围,避免巨大点片造成 overdraw。

生命周期、烟花和噪声路径

生命周期用 fract(time / life + phase) 变成 0–1 周期。相位分散会让画面中同时存在新生和消退粒子。星空用寿命或相位调亮度;若需要更自然的变化,增加独立速度和色温。烟花用解析运动 position = velocity * age + 0.5 * gravity * age²。练习里的重力系数直接写成 -0.28 * age²,对应 Y 向下的加速度 -0.56 世界单位每秒平方。按“重新发射”只更新 uLaunch,顶点重新从中心计算运动,无需改写每个顶点缓冲。

萤火虫沿一维平滑噪声函数构造的 X/Y 两条路径移动:取相邻格点的伪随机值,用平滑插值连接,可避免每格边界跳变。不同相位让轨迹独立;另一个正弦函数控制呼吸亮度。这里的路径适合视觉效果,不是物理速度积分。需要碰撞或复杂群体行为时,可改用 CPU 更新少量粒子,或在更多粒子时使用 GPU 模拟纹理。

性能、资源与故障

透明柔边点使用 additive blending 和 depthWrite: false,可让发光叠加,但重叠区域会变亮。点越大、密度越高,片元 overdraw 越多;粒子数不是唯一成本指标。限制像素比、粒子尺寸和视野内数量,再固定设备与画布尺寸比较性能。若粒子出现方块边,检查 gl_PointCoord 圆形遮罩;若透明排序异常,检查混合模式和深度写入;若点击重发射无反应,检查 uLaunch 与 uTime 是否同一时间基准。

练习与验收

难度 题目 可观察验收
⭐ Points 纯色粒子 单个 Points 对象显示数百个青色点
⭐⭐ 时间驱动上下浮动 上下往复;暂停后位置冻结
⭐⭐ 随机大小 attribute 大小明显不一;aSize 决定点尺寸
⭐⭐⭐ 随机生命周期 attribute 同时有亮点和暗点,数秒后继续循环
⭐⭐⭐ 闪烁星空 深色背景、独立闪烁,无粒子连线
⭐⭐⭐ 烟花爆发与重力 每次点击重新从中心扩散、下落并淡出
⭐⭐⭐⭐ 噪声路径萤火虫 路径连续且呼吸不同步,运行时无突然跳点