Skip to content

状态: 🟢 已修复

日期: 2026-07-02 严重程度: 🔴 P1 影响范围: frontend/src/scene/perception.ts(视线追踪 / 程序化骨骼旋转) 发现方式: 开发发现 修复方案: 改写 linkedBone.rotationQuaternion(局部旋转)+ 手动触发 updateWorldMatrix 重算骨骼链 + _markAsDirty 刷新渲染矩阵

骨骼变换覆写无效(视线追踪 / 程序化骨骼旋转)

日期:2026-07-02

故障现象

  • 直接写 runtimeBone.worldMatrix 后,骨骼视觉上不动
  • 或骨骼只有旋转变化,位置留在原地(子骨骼不跟随父骨骼)
  • WASM 版 MmdWasmRuntime 下尤其明显

诊断方法

步骤 1:afterRender readback 验证

onAfterRenderObservable 中读回 worldMatrix,与 onBeforeRender 写入值对比:

typescript
scene.onBeforeRenderObservable.add(() => {
    // 写入 worldMatrix
    bone.worldMatrix[0] = newValue;
    console.log('[verify] write=', bone.worldMatrix[0]);
});
scene.onAfterRenderObservable.add(() => {
    console.log('[verify] readback=', bone.worldMatrix[0]);
});
结果结论
readback == write写入生效,问题在别处(如骨骼层级、权重)
readback == 旧值WASM 双缓冲覆盖,需切到 JS 版运行时

步骤 2:子骨骼位置验证

如果写入生效但子骨骼不跟随父骨骼移动,说明绕过了骨骼层级。检查是否直接写 worldMatrix 而非 linkedBone.rotationQuaternion

根因

机制后果
WASM 双缓冲MmdWasmRuntimeworldTransformMatrices 有前后缓冲,update() 用后缓冲覆盖前缓冲直接写 worldMatrix 在下一帧被还原
渲染数据源渲染读 _computeTransformMatrices 输出的 targetMatrix,不直接读 worldMatrix_markAsDirtyupdate() 末尾已执行,之后不重跑
骨骼层级worldMatrix = localMatrix × parentWorldMatrix,直接写 worldMatrix 不影响子骨骼的 parentWorldMatrix子骨骼位置不更新

正确做法

linkedBone.rotationQuaternion(局部旋转),手动触发 updateWorldMatrix 重算骨骼链,最后 _markAsDirty 刷新渲染矩阵。详见 docs/architecture.md §10.1。