模型解构为粒子:交互、释放与自适应性能
从模型表面采样粒子,组织排斥、释放和回归状态,并根据设备条件分配粒子预算。
系统拆分
模型粒子化可以拆成四个相互独立的部分:
- 采样:从模型表面取得粒子的初始位置和颜色。
- 状态:根据滚动进度计算实体、柔化、静止和释放阶段。
- 交互:把指针命中、距离和运动速度转换为局部影响。
- 渲染:使用 GPU 侧位移和透明度变化绘制粒子。
这种拆分让“粒子在哪里”“粒子现在处于什么阶段”和“粒子如何被指针影响”不会混在同一个循环里。调节视觉效果时,也不必重新组织模型载入和缓冲区创建。
从模型表面取得粒子
使用 MeshSurfaceSampler 采样模型表面,可以让粒子初始状态天然对应实体模型。每个粒子至少需要保存初始位置、随机相位、颜色和用于释放的方向或权重。初始位置作为回归目标,随机相位则用于避免所有粒子以相同节奏运动。
位置数据写入 BufferGeometry 后,运行时尽量保持缓冲区结构稳定。连续变化的部分交给 shader,通过 uniform 传递时间、滚动阶段、指针位置和释放强度,减少 JavaScript 逐粒子写入带来的 CPU 压力。
排斥不是瞬移
指针排斥可以按距离衰减计算:距离越近,位移越强;超过影响半径后,粒子回到原来的状态。为了让动作有重量,还需要把指针运动速度作为额外输入,让快速划过产生短暂的方向性反馈。
实际体验中,排斥和回归需要同时存在。排斥力负责表达“用户碰到了这里”,回归力负责保持模型的整体可读性。如果只增加排斥,不设置稳定的回归目标,粒子场会逐渐漂移;如果回归过强,用户又会感觉粒子没有被真正推动。
释放与反向恢复
释放状态不应只是把所有粒子乘以一个越来越大的坐标系数。更完整的过程通常包含:
- 从实体模型过渡到可分辨的粒子形态。
- 在释放前短暂收束,形成一个可感知的蓄力阶段。
- 沿各自方向展开,同时保留少量旋转和噪声差异。
- 进入稳定的扩散状态。
滚动驱动的状态必须可逆。向上滚动时,释放进度应回退,粒子重新接近采样位置,而不是继续沿上一次的速度向外飞行。这样用户可以把滚动当成探索,而不是只能观看一次的动画。
自适应粒子预算
粒子数量采用自适应策略,不在所有设备上使用同一个固定值。预算可以由设备类型、视口尺寸和像素比共同决定:移动端优先保证触控反馈和稳定帧率,桌面端在剩余性能允许时增加粒子密度。
NAIWA 的基础预算约为移动端 12,000 个、桌面端 22,000 个粒子,并同时限制高密度屏幕的渲染像素比。预算调整的优先级是:先保留轮廓,再保留释放动作,最后才增加细节密度。这样即使减少粒子,用户仍能理解页面正在发生什么。
命中检测和性能边界
实体阶段需要知道指针是否命中真实模型,不能只依据屏幕中心或一个固定圆形区域判断。复杂网格的 raycast 可以使用 three-mesh-bvh 加速;粒子阶段则可以使用模型空间或屏幕空间的影响范围,避免为每个粒子执行昂贵的完整几何检测。
性能检查应覆盖载入、实体展示、粒子展开、指针快速移动和移动端滚动。重点观察主线程任务、GPU 帧时间、内存增长和移动端像素比。粒子系统的目标不是“永远最大”,而是在完整体验中保持可预测的成本。
相关项目:NAIWA / 奶蛙。