Three.js:重新连接骨骼后如何重新绑定Mesh网格的Skeleton骨架?
问题根因
你通过scene.attach(bone)临时修改骨骼父级、再附着回原父节点的操作,破坏了蒙皮绑定的核心依赖:Three.js的Skeleton实例会基于骨骼初始的父子层级预计算bindMatrixInverse,绑定后的骨骼层级不允许随意修改,否则会导致骨架层级映射断裂,表现为SkeletonHelper显示断线、子骨骼不跟随父骨骼运动。
修复方案
完全删除修改骨骼父级的逻辑,直接通过矩阵运算将世界坐标系下的目标方向转换为骨骼局部坐标系下的旋转值即可,修改后代码如下:
function boneLookAtLocal(bone, localDir) { const direction = localDir.clone().normalize() const pitch = Math.asin(-direction.y) const yaw = Math.atan2(direction.x, direction.z); const roll = Math.PI; bone.rotation.set(roll, yaw, pitch); } function boneLookAtWorld(bone, worldDir) { // 刷新骨骼世界矩阵确保计算准确 bone.updateMatrixWorld(true) // 计算父骨骼世界矩阵的逆矩阵,用于将世界方向转成父骨骼局部空间的方向 const parentWorldInv = bone.parent.matrixWorld.clone().invert() const localDir = worldDir.clone().applyMatrix4(parentWorldInv) boneLookAtLocal(bone, localDir) // 刷新骨骼矩阵 bone.updateMatrixWorld() }
在所有骨骼旋转操作完成后,调用对应蒙皮网格的骨架更新方法即可生效:
// 你的原调用逻辑不变 boneLookAtWorld(hand.wrist, new THREE.Vector3(1, 1, 1)) boneLookAtWorld(hand.index[0], new THREE.Vector3(1, 1, 1)) boneLookAtWorld(hand.index[1], new THREE.Vector3(1, 0, 1)) boneLookAtWorld(hand.index[2], new THREE.Vector3(0, -1, 0)) // 假设handMesh是你绑定了骨骼的手部蒙皮网格,调用这句更新骨架 handMesh.skeleton.update()
注意事项
- 若你需要骨骼看向三维空间中的某一点而非朝向某方向,只需将传入的
worldDir改为目标点.clone().sub(bone.getWorldPosition(new THREE.Vector3()))即可 - 代码中的
roll = Math.PI是适配你当前手部模型的骨骼初始朝向,若换用其他模型可根据骨骼初始朝向调整该值 - 不要随意修改已绑定蒙皮的骨骼的父子层级,若必须修改则需要重新调用
skeleton.calculateInverses()重新计算绑定逆矩阵,成本远高于直接做坐标空间转换
内容的提问来源于stack exchange,提问作者Ietpt123
相关产品推荐
相关产品推荐

