附录 A:粒子效果排错路线
先确定故障属于没有生成、状态没有更新、几何没有提交、深度挡住、混合不对,还是设备成本过高。逐项缩小范围,比同时改贴图、透明度和相机更容易找到原因。本附录以本仓库 Three.js r174 示例为准;教程目录可以返回各章原理与运行页面。
1. 页面空白或粒子不出现
| 现象 | 优先检查 | 对应章节 |
|---|---|---|
| 模块加载失败 | 从 particle-system/examples/ 目录启动静态服务器;不要通过 file:// 打开;看 Network 的 JS 路径与控制台错误 |
00 运行说明、13 Points |
| 有动画时间但没有像素 | drawRange、属性长度、相机近远面、发射位置、点大小、材质 Alpha、discard 条件 |
09 发射器、16 GPU 解析式 |
| 改了 TypedArray 画面没动 | 是否改了 BufferAttribute 持有的同一数组;是否设置 needsUpdate;更新范围是否覆盖被改元素 |
15 动态 BufferGeometry |
| 某批点突然整批消失 | 动态位置超出旧包围球,frustumCulled 误判;维护保守边界或暂时关闭对象级剔除验证 |
13 Points、16 GPU 解析式 |
| 点比预期小/被截断 | PointsMaterial 与自定义 gl_PointSize 的像素比处理不同;查 ALIASED_POINT_SIZE_RANGE |
13 Points、16 GPU 解析式 |
第 16 章的 ShaderMaterial 每帧按初始状态重新算位置,不会自动保存前一帧速度。碰撞、持续受力变化与鸟群邻居规则需要 CPU 状态或 GPU 反馈存储。若修改风后粒子“跳到另一条轨迹”,先确认用的是解析式还是状态积分。
2. 透明、颜色和深度看起来不对
透明贴图有黑边: 检查源 RGB 在透明边缘的颜色、插值、是否误用预乘 Alpha,以及 texture.colorSpace。Alpha 通道不靠把整张纹理改为线性颜色空间来修复。参考第 02 章混合与第 03 章透明纹理。
烟被另一片烟硬裁: 检查透明材质是否仍 depthWrite:true。depthTest:true 保留与已有不透明场景的遮挡;depthWrite:false 避免透明片互相写硬深度。它不解决同一个 Points 内部的逐点排序,也不会自动产生与地面相交的软边。参考第 05 章、第 07 章、第 17 章。
看不见遮挡代理却挡住后物体: colorWrite:false 只是不写颜色;若 depthTest:true,depthWrite:true,代理仍可改写深度。用第 06 章核对绘制顺序、深度缓冲与 renderOrder。renderOrder 调整对象提交顺序,不对对象内部粒子排序。
墙后粒子不见了却仍卡: 视觉物体遮挡由深度测试完成;对象级遮挡剔除必须另行决定不提交墙后整批对象。一个跨墙的大 Points 对象不能只因部分点被挡就跳过整个对象。参考第 08 章。
3. 软粒子交界失效
- 先显示不透明场景深度预览。地面、墙或方块若不在纹理里,检查预通道包含的对象、渲染顺序和
DepthTexture附着。 - 对比
gl_FragCoord.xy的物理像素与深度目标尺寸。画布 CSS 像素、DPR 后物理尺寸、渲染目标尺寸必须对应;窗口 resize 后全部同步。 - 用同一相机近远面把透视窗口深度
sceneDepth与gl_FragCoord.z分别转为视空间距离,再求差。正交相机应使用对应的正交深度转换。 - 烟片仍应
depthTest:true,depthWrite:false。深度纹理通常只记录不透明场景;透明玻璃或另一团烟不会自动参与淡化。
若交界出现整片消失或错位,先检查第 1—3 步,再调淡化距离;直接把 depthWrite 关掉不会制造软交界。可用第 17 章示例作对照。
4. 动画速度、GC 与更新范围
高刷屏上飞得快、低帧率下飞得慢,通常是把“每帧移动固定距离”当成速度。使用以秒为单位的 deltaTime,并明确后台恢复时是否限幅或使用固定子步。发射率用余数累计,生命周期用 age += dt;参考第 11 章和第 12 章。
每帧大量 new Vector3()、新数组或新材质可能引发 GC 抖动。先预分配并复用槽位和临时对象。setDrawRange() 只控制绘制数量,不会自动缩小属性上传;局部上传使用 addUpdateRange(start,count),其中 count 是 TypedArray 元素数。很多零碎范围也有提交成本,尽可能使活跃区连续。参考第 15 章。
5. 怎么判断瓶颈
记录点数、画布 CSS 尺寸、渲染像素比、点屏幕尺寸、浏览器和 GPU/设备。再分别看 JS 位置循环、理论/实际属性上传、绘制调用、GPU 填充与 overdraw。renderer.info.render.calls 不是帧耗时;动画回调间隔也不是 GPU 耗时。大柔边点即使数量不多也可能覆盖大量像素。用第 20 章性能实验一次只改一个参数。
若 three.quarks 示例不出现,先核对 0.16.0 对照页使用的本地 import map:three 指向 r174,quarks.core 与 three.quarks 是同一 0.16.0 系列;system.emitter 和 BatchedRenderer 都加入场景,addSystem(system) 后每帧调用 batch.update(dt)。库负责模拟组织和批渲染,但不自动消除透明排序、深度、片元覆盖或设备瓶颈。