过客2024 2024-02-19 15:56 采纳率: 0%
浏览 50
已结题

three.js,给ik驱动fbx模型添加约束

补充:关于乱窜的,主要是大腿乱窜大腿一旦移动到限制就会乱窜
补充:Vector3的起始点在模型背后,目前是mixamorigLeftUpLeg大腿这一块会出现乱窜
我正在尝试为我的 ik 驱动的 fbx 角色模型的四肢添加约束,希望防止模型做出反人类的行为。 我目前正在尝试使用Vector3来约束ik链,但我不知道Vector3如何作用于模型。 我对限制了解不多。 以下是我的 ik 链值中的约束:

leftLeg: {
        target: '左腿控制_L',
        effector: 'mixamorigLeftFoot',
        links: [
          {
            name: 'mixamorigLeftLeg',
            rotationMin: new THREE.Vector3(-Math.PI / 1.2, 0, 0), // 不允许向前弯曲,也不允许围绕Y轴和Z轴的旋转
            rotationMax: new THREE.Vector3(0, 0, 0) // 允许向后弯曲90度,不允许围绕Y轴和Z轴的旋转
            
          },
          {
            name: 'mixamorigLeftUpLeg',
            rotationMin: new THREE.Vector3(-Math.PI / 1.5, -Math.PI, -Math.PI), 
            rotationMax: new THREE.Vector3(Math.PI / 1.7, Math.PI, -Math.PI/2) 
          }
        ]
      },

img


为什么当触及 Vector3 约束时它会鬼畜和乱窜?

补充代码

    function loadModel(modelPath = '/static/resources/models/pg_ik_Modify_parent.fbx') {
      console.log(upload_file_name)
      const loader = new FBXLoader();
      loader.load(modelPath, function (pg) {
        // 将模型的初始位置设置为 (x, y, z)
        pg.rotation.set(0, 0.7, 0);
        pg.position.set(0, -100, 0);
        scene.add(pg);
        console.log(scene);
        pg.traverse(function (child) {
          if (child.isMesh) {
            // 检查子对象是否有材质和贴图
            const material = child.material;
            if (material && material.map) {
              console.log(material.map.name);
              textureFileName = material.map.name
            } else {
              console.log('此子对象没有贴图。');
            }
          }

          if (child.isSkinnedMesh) {
            skinnedMesh = child; // 确保这一行成功找到了网格
            // 找到skinnedMesh后,再将pg添加到场景中
            scene.add(pg);
            IKsolvers = setupIKSolvers(skinnedMesh, limbIKData); // 现在可以初始化IKsolvers
            // 创建并添加表示本地轴的辅助对象
            const axesHelper2 = new THREE.AxesHelper(50); // 长度为5的轴辅助对象
            pg.add(axesHelper2); // 将辅助对象添加到立方体上
            // 使用新的创建控制器的方法
            rootbones.右手控制_R = pg.getObjectByName('右手控制_R');
            rootbones.左手控制_L = pg.getObjectByName('左手控制_L');
            rootbones.右腿控制_R = pg.getObjectByName('右腿控制_R');
            rootbones.左腿控制_L = pg.getObjectByName('左腿控制_L');

            transformControls = createTransformControl('右手控制_R');
            transformControls2 = createTransformControl('左手控制_L');
            transformControls3 = createTransformControl('右腿控制_R');
            transformControls4 = createTransformControl('左腿控制_L');
          }
        }
        );
      });
    }

    function createTransformControl(boneName) {
      let control = new TransformControls(camera, renderer.domElement);
      control.size = 0.75;
      control.attach(rootbones[boneName]);
      scene.add(control);
      control.setMode('translate');

      // 添加鼠标事件监听器,用于开启和关闭IK更新
      control.addEventListener('mouseDown', () => {
        controls.enabled = false;
        shouldUpdateIK = true;
      });
      control.addEventListener('mouseUp', () => {
        controls.enabled = true;
        shouldUpdateIK = false;
      });

      return control;
    }

    const limbIKData = {
      leftArm: {
        target: '左手控制_L',
        effector: 'mixamorigLeftHand',
        links: [
          {
            name: 'mixamorigLeftForeArm',
            rotationMin: new THREE.Vector3(-Math.PI / 20, -Math.PI / 8, -1), // 限制小臂向下的旋转范围,防止反折
            rotationMax: new THREE.Vector3(Math.PI / 1.8, Math.PI / 2, 1) // 保持其他轴向的旋转范围不变
          },
          {
            name: 'mixamorigLeftArm',
            rotationMin: new THREE.Vector3(-Math.PI / 2, -Math.PI / 8, -1), // 减小向上和内侧旋转的范围
            rotationMax: new THREE.Vector3(Math.PI / 2, Math.PI / 2, 1) // 保持向下和向外的旋转范围不变
          }
        ]
      },
      rightArm: {
        target: '右手控制_R',
        effector: 'mixamorigRightHand',
        links: [
          {
            name: 'mixamorigRightForeArm',
            rotationMin: new THREE.Vector3(-Math.PI / 20, -Math.PI / 8, -1), // 限制小臂向下的旋转范围,防止反折
            rotationMax: new THREE.Vector3(Math.PI / 1.8, Math.PI / 2, 1) // 保持其他轴向的旋转范围不变
          },
          {
            name: 'mixamorigRightArm',
            rotationMin: new THREE.Vector3(-Math.PI / 2, -Math.PI / 8, -1), // 减小向上和内侧旋转的范围
            rotationMax: new THREE.Vector3(Math.PI / 2, Math.PI / 2, 1) // 保持向下和向外的旋转范围不变
          }
        ]
      },
      leftLeg: {
        target: '左腿控制_L',
        effector: 'mixamorigLeftFoot',
        links: [
          {
            name: 'mixamorigLeftLeg',
            rotationMin: new THREE.Vector3(-Math.PI / 1.2, 0, 0), // 不允许向前弯曲,也不允许围绕Y轴和Z轴的旋转
            rotationMax: new THREE.Vector3(0, 0, 0) // 允许向后弯曲90度,不允许围绕Y轴和Z轴的旋转

          },
          {
            name: 'mixamorigLeftUpLeg',
            rotationMin: new THREE.Vector3(-Math.PI , -Math.PI / 2, -Math.PI), // 不允许向前弯曲,也不允许围绕Y轴和Z轴的旋转
            rotationMax: new THREE.Vector3(Math.PI , Math.PI / 2, Math.PI) // 允许向后弯曲90度,不允许围绕Y轴和Z轴的旋转
          }
        ]
      },

      // rotationMin: new THREE.Vector3(-Math.PI / 1.5, -Math.PI, -Math.PI), // 不允许向前弯曲,也不允许围绕Y轴和Z轴的旋转
      // rotationMax: new THREE.Vector3(Math.PI / 1.7, Math.PI, -Math.PI / 2) // 允许向后弯曲90度,不允许围绕Y轴和Z轴的旋转


      rightLeg: {
        target: '右腿控制_R',
        effector: 'mixamorigRightFoot',
        links: [
          {
            name: 'mixamorigRightLeg',
            rotationMin: new THREE.Vector3(-Math.PI / 1.2, 0, 0), // 不允许向前弯曲,也不允许围绕Y轴和Z轴的旋转
            rotationMax: new THREE.Vector3(0, 0, 0) // 允许向后弯曲90度,不允许围绕Y轴和Z轴的旋转
          },
          {
            name: 'mixamorigRightUpLeg',
            rotationMin: new THREE.Vector3(-Math.PI, -Math.PI / 2, -Math.PI), // 不允许向前弯曲,也不允许围绕Y轴和Z轴的旋转
            rotationMax: new THREE.Vector3(Math.PI, Math.PI / 2, Math.PI) // 允许向后弯曲90度,不允许围绕Y轴和Z轴的旋转
          }
        ]
      }
      //rotationMin: new THREE.Vector3(-Math.PI / 2, -Math.PI / 20, Math.PI / 2), // 不允许向前弯曲,也不允许围绕Y轴和Z轴的旋转
      //rotationMax: new THREE.Vector3(Math.PI / 2, Math.PI / 20, Math.PI * 1.5) // 允许向后弯曲90度,不允许围绕Y轴和Z轴的旋转
      // 更多的肢体可以按照此格式继续添加
    };

    function setupIKSolvers(skinnedMesh, limbIKData) {
      const IKsolvers = {};

      for (const limbName in limbIKData) {
        const limb = limbIKData[limbName];
        const boneArray = skinnedMesh.skeleton.bones;

        const targetBone = boneArray.find(bone => bone.name === limb.target);
        const effectorBone = boneArray.find(bone => bone.name === limb.effector);
        if (!targetBone || !effectorBone) {
          console.error(`IK setup error: Target or effector bone not found for ${limbName}.`);
          continue;
        }

        const links = limb.links.map(link => {
          const linkBone = boneArray.find(bone => bone.name === link.name);
          if (!linkBone) {
            console.error(`IK setup error: Link bone not found: ${link.name}.`);
            return null;
          }
          const linkData = {
            index: boneArray.indexOf(linkBone)
          };
          // 仅当旋转最小和最大约束存在时才添加到链环节
          if (link.rotationMin) {
            linkData.rotationMin = link.rotationMin;
          }
          if (link.rotationMax) {
            linkData.rotationMax = link.rotationMax;
          }
          return linkData;
        }).filter(link => link !== null);

        if (links.length !== limb.links.length) {
          console.error(`IK setup error: Not all link bones found for ${limbName}.`);
          continue;
        }

        const ik = {
          target: boneArray.indexOf(targetBone),
          effector: boneArray.indexOf(effectorBone),
          links: links,
        };

        // 在这里,你需要一个支持旋转约束的IK解算器。这可能需要对CCDIKSolver进行扩展或使用其他支持约束的解算器
        IKsolvers[limbName] = new CCDIKSolver(skinnedMesh, [ik]);
        const ccdikhelper = new CCDIKHelper(skinnedMesh, [ik], 0.01);
        scene.add(ccdikhelper);
      }

      return IKsolvers;
    }



  • 写回答

21条回答 默认 最新

  • 叫兽-郭老师 新星创作者: Java技术领域 2024-02-19 15:58
    关注
    获得0.75元问题酬金

    参考通义千问和郭老师的小迷弟雅思莫了-编写提供,您可以参考如下,如果回答的不正确,及时评论区回复我,我会根据你错误描述追加回复。如果解决了您的问题,麻烦帮我点击个采纳!谢谢您的肯定!

    在Three.js中,你使用的是CCDIKSolver,它本身并不直接支持旋转约束。要实现你的需求,你可以通过自定义解决方法或者找到一个支持约束的IK库。在这里,我建议你使用一个名为THREE.IK的库,它是一个基于Three.js的逆动力学解决方案,并且支持约束。

    首先,安装THREE.IK库:

    npm install three-i-k
    

    然后导入库并使用它替换你的现有IK设置:

    import * as THREE from 'three';
    import { CCDIkChain, IKBone } from 'three-i-k';
    
    // ...
    
    function setupIKSolvers(skinnedMesh, limbIKData) {
      const IKsolvers = {};
    
      for (const limbName in limbIKData) {
        const limb = limbIKData[limbName];
        const boneArray = skinnedMesh.skeleton.bones;
        const targetBone = boneArray.find((bone) => bone.name === limb.target);
        const effectorBone = boneArray.find((bone) => bone.name === limb.effector);
    
        if (!targetBone || !effectorBone) {
          console.error(`IK setup error: Target or effector bone not found for ${limbName}.`);
          continue;
        }
    
        const chain = [];
        limb.links.forEach((link) => {
          const linkBone = boneArray.find((bone) => bone.name === link.name);
          if (!linkBone) {
            console.error(`IK setup error: Link bone not found: ${link.name}.`);
            return;
          }
          const ikBone = new IKBone(linkBone, link.rotationMin, link.rotationMax);
          chain.push(ikBone);
        });
    
        if (chain.length !== limb.links.length) {
          console.error(`IK setup error: Not all link bones found for ${limbName}.`);
          continue;
        }
    
        const ikChain = new CCDIkChain(effectorBone, targetBone, chain);
        skinnedMesh.addConstraint(ikChain);
    
        IKsolvers[limbName] = ikChain;
      }
    
      return IKsolvers;
    }
    

    请注意,这个库要求你传入一个旋转限制的对象,而不是Three.js的Vector3。所以,你需要调整你的limbIKData以适应新的结构:

    const limbIKData = {
      // ...
      leftLeg: {
        target: '左腿控制_L',
        effector: 'mixamorigLeftFoot',
        links: [
          {
            name: 'mixamorigLeftLeg',
            rotationMin: { x: -Math.PI / 1.2, y: 0, z: 0 },
            rotationMax: { x: 0, y: 0, z: 0 },
          },
          {
            name: 'mixamorigLeftUpLeg',
            rotationMin: { x: -Math.PI, y: -Math.PI / 2, z: -Math.PI },
            rotationMax: { x: Math.PI, y: Math.PI / 2, z: Math.PI },
          },
        ],
      },
      // ...
    };
    

    现在,你应该能够看到更自然的运动,因为每个关节都有了适当的旋转限制。不过,记得根据实际效果微调这些限制。

    评论

报告相同问题?

问题事件

  • 系统已结题 2月27日
  • 修改了问题 2月20日
  • 修改了问题 2月20日
  • 修改了问题 2月19日
  • 展开全部