以编程方式在 Three.js 中创建骨架

Programatically create skeleton in Three.js

我正在 Three.js 中加载机械模型(例如机械臂)。遗憾的是,我使用的模型没有骨架,但我有关节的位置、轴等。为了使用例如像 Three-IK 这样的反向运动学解算器,我想根据这些参数创建骨架。因为我想使用许多不同的模型,所以我宁愿不要手动创建骨架,而是用代码创建。

我已经尝试了一个多星期,以根据这些反映模型的值创建有效的骨骼结构,但没有成功。例如,如果我使用关节的位置创建骨骼链,我会得到一个非常长的骨架,这与我使用的位置完全不匹配。

let boneParent;
let bonepos = [];
let bones = [];
model.traverse(child => {
    switch(child.type) {
        case "joint":
            let p = new Vector3();
            child.getWorldPosition(p);
            bonepos.push(p);

            let bone = new Bone();
            boneParent && boneParent.add(p);
            bone.worldToLocal(p.clone());
            bone.position.copy(p);
            bone.rotation.copy(child.rotation);
            bone.scale.copy(child.scale);

            boneParent = bone;
            bones.push(bone);
            break;
    }
});
showPoints(scene, bonepos, 0xff0000);

const skeletonHelper = new SkeletonHelper(bones[0]);
skeletonHelper.visible = true;
scene.add(skeletonHelper);

上面的代码生成下面的屏幕截图。红色标记是我从机器人关节得到的位置,蜿蜒到远处的线是 SkeletonHelper 可视化的骨架。

所以我的问题是:我似乎不太了解Three.js中骨骼是如何处理的。我怎样才能创建一个骨架来反映我现有模型的关节位置和方向?

提前致谢!

child.getWorldPosition(p);

恐怕将世界 space 中的位置应用到 Bone.position 代表 local space 中的位置是不正确的。

boneParent = bone;

这一行看起来也有问题。一个骨骼可以有多个子元素。在我看来,您的代码没有考虑这个用例。

经过一番折腾,我找到了解决办法:

let root = new Bone();
let parent = root;
let pos = new Vector3();

for (let joint of robot.arm.movable) {
    let link = robot.getLinkForJoint(joint);
    link.getWorldPosition(pos);
    
    let bone = new Bone();
    parent.add(bone);
    parent.lookAt(pos);
    parent.updateMatrixWorld();  // crucial for worldToLocal!
    bone.position.copy(bone.worldToLocal(pos));
        
    parent = bone;
}

重要的部分是在 lookAt() 之后 调用 updateMatrixWOrld() 以便 bone.worldToLocal() 正常工作。另外 lookAt() 省去了很多矩阵麻烦 :)