物理系统
一句话总览
three 官方 MMDPhysics.js(Ammo.js / Bullet wasm 后端)+ 运行时参数调校。
没有自研物理内核——所有"手感"来自对 Bullet 刚体/约束参数的二次调校(重力、风力、约束松绑)。
参数面板(PHYS)
所有参数在 PHYS 面板实时可调,支持 localStorage 持久化。
基本参数
| 参数 | 默认 | 说明 |
|---|---|---|
| enabled | ✅ | 物理总开关 |
| gravity | 1.0 | 全局重力倍率 |
| sway | ❌ | 待机摇摆(根骨骼正弦摆动) |
| amp | 0.06 | 摇摆幅度 |
| speed | 1.0 | 摇摆速度 |
| fpsCap | 0 | 帧率上限(0=不限) |
| autoRotate | ✅ | 相机自动环绕 |
| debug | ❌ | 刚体线框可视化 |
风力系统
| 参数 | 默认 | 说明 |
|---|---|---|
| windOn | ❌ | 风力总开关 |
| wind | 0.12 | 风力强度 |
| windAuto | ✅ | 自动风向(0°~360° 循环扫动) |
| windAngle | 0° | 手动风向(关闭 windAuto 时生效) |
TIP
风向罗盘 + 3D 场景中的箭头实时同步,0°=朝观众方向,顺时针旋转。
三个核心调校(缺一不可)
① 配件微重力
头发/领带刚体只有 15% 重力,裙子 30%。原因是 PMX 模型的头发刚体质量远大于裙子,Bullet 默认重力会把卷发拉直。
② 裙摆/袖口约束松绑
Sour 式裙子约束被作者焊死(旋转限制 ±1.1°),运行时重设为:
- 裙子:旋转限制 ×4、旋转弹簧 ×0.25、位置弹簧 ×0.1
- 袖子:旋转限制 ×3、旋转弹簧 ×0.5、位置弹簧 ×0.5
③ 风只吹头发/领带(防走光)
风力正则排除了裙子和袖子,它们只靠约束松绑后的惯性摆动。这是刻意设计——裙摆被风吹起会走光。
踩坑记录
CAUTION
以下是历次调参踩过的坑,改参数前必读。
| # | 坑 | 结论 |
|---|---|---|
| 1 | 弹簧刚度 ×2 反而更糟 | 非线性,调手感走重力/风力路线 |
| 2 | setDamping 甩动更大 | 弹簧相关参数维持模型原始值 |
| 3 | "头发拉长"不是位移问题 | 是旋转累积把卷发甩直(伸直比 0.906→0.942) |
| 4 | ammo.wasm.js 是精简构建 | 无 getCollisionObjectArray(),释放必须逐个 destroy |
| 5 | Ammo 异步加载 | 模块顶层禁止 new Ammo.btVector3(...) |
| 6 | mmd-parser morph bug | 类型 4~7 和 9 不消费字节,新模型需验证 |
| 7 | dt 必须限幅 1/30 | 后台切回/掉帧时大步长让 Bullet 爆炸 |
| 8 | 摇摆只摇安全骨骼 | 有 type1/2 刚体的骨骼会被物理回写覆盖 |
技术架构
点击展开(开发者阅读)
每帧流程(顺序不能乱)
动作/待机摇摆 → 微风施力 applyWind(t) → mesh.updateMatrixWorld(true) → physics.update(dt)关键约束
updateMatrixWorld(true)必须在physics.update前:MMDPhysics 读bone.matrixWorld驱动 kinematic 刚体warmup(60)创建后立即执行:让裙摆/头发落定- 单位制:MMD 1 单位 = 10cm,重力
(0, -98, 0)
必须保留的设计
unitStep: 1/65, maxStepNum: 3- 配件微重力 / 约束松绑 / 风豁免三件套
- 模型切换完整释放(Ammo 对象 + GPU 资源)
- 风向罗盘 + 3D 箭头共用
getWindAngle(t)单一来源
