three 附加 动画

639 阅读7分钟

CCDIKSolver 是 Three.js 的扩展库,用于实现反向运动学 (IK) 动画。通过它可以使模型的骨骼链条自动跟随特定的目标位置,适合制作角色动画中的复杂肢体动作(如角色手臂、腿部的移动和弯曲)。CCDIKSolver 使用循环坐标下降(Cyclic Coordinate Descent, CCD)算法,逐步调整骨骼链条中每个关节的角度,以使其末端接近目标位置。
MMDAnimationHelper 是 Three.js 中用于处理 MikuMikuDance (MMD) 动画的工具类。它提供了一些便利的功能,帮助用户在 Three.js 中导入和播放 MMD 模型及其动画。
MMDPhysics 是一个用于在 Three.js 中实现 MikuMikuDance (MMD) 模型的物理模拟的库。它的目的是为 MMD 模型提供物理效果,使得动画看起来更真实,尤其是在涉及到角色的衣物、头发等柔性部件的动态反应时

CCDIKSolver 有两个属性 三个方法

CCDIKSolver( mesh : SkinnedMesh, iks : Array ) mesh — SkinnedMesh 用于 CCDIKSolver 解决 IK 问题 iks — 指定 IK 参数的对象 Object 数组。target、effector 和 link-index 是 .sculptor.bones 中的索引整数。骨骼关系从父级到子级的顺序应为“links[ n ]、 links[ n - 1 ]、...、 links[ 0 ]、effector”。 target — 目标骨骼 effector — 效应器骨 links — 指定链接骨骼的对象Object 数组 index — 链接骨骼 limitation — (可选)旋转轴。默认值 undefined rotationMin — (可选)旋转最小限制。默认值 undefined rotationMax — (可选)旋转最大限制。默认值 undefined enabled — (可选)默认值为 true。 iteration — (可选)计算的迭代次数。越小速度越快,但精度较差。默认值为 1。 minAngle — (可选)一步中的最小旋转角度。默认值 undefined maxAngle — (可选)一步中的最大旋转角度。默认值 undefined 创建一个新的 CCDIKSolver。

属性

  • iks : Array 传递给构造函数的 IK 参数数组。
  • mesh : SkinnedMesh SkinnedMesh 传递给构造函数。

方法

  • createHelper () : CCDIKHelper 返回 CCDIKHelper. 。您可以通过将辅助对象添加到场景来可视化 IK 骨骼。
  • update () : this 通过求解 CCD 算法更新 IK 骨骼四元数。
  • updateOne ( ikParam : Object ) : this 通过求解 CCD 算法来更新一个 IK 骨骼四元数。
    // 创建骨骼(Bones)
    const rootBone = new THREE.Bone();
    const midBone = new THREE.Bone();
    const endBone = new THREE.Bone();
    // 构建骨骼层级关系
    rootBone.add(midBone);
    midBone.add(endBone);
    // 设置骨骼位置
    midBone.position.y = 2;
    endBone.position.y = 2;
    // 创建骨架并附加到骨骼
    const bones = [rootBone, midBone, endBone];
    const skeleton = new THREE.Skeleton(bones);
    // 使用 CylinderGeometry 创建几何体
    const boneGeometry = new THREE.CylinderGeometry(0.2, 0.2, 4, 8);
    const position = boneGeometry.attributes.position;
    const skinIndices = [];
    const skinWeights = [];
    // 为每个顶点设置骨骼权重
    for (let i = 0; i < position.count; i++) {
        const y = position.getY(i);
        if (y < 0) {
            skinIndices.push(0, 0, 0, 0);
            skinWeights.push(1, 0, 0, 0);
        } else if (y < 2) {
            skinIndices.push(1, 0, 0, 0); // midBone
            skinWeights.push(1, 0, 0, 0); // 受 midBone 影响
        } else {
            skinIndices.push(2, 0, 0, 0);
            skinWeights.push(1, 0, 0, 0);
        }
    }
    boneGeometry.setAttribute('skinIndex', new THREE.Uint16BufferAttribute(skinIndices, 4));
    boneGeometry.setAttribute('skinWeight', new THREE.Float32BufferAttribute(skinWeights, 4));
    // 创建骨骼网格并绑定骨架
    const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });
    const boneMesh = new THREE.SkinnedMesh(boneGeometry, material);
    boneMesh.add(rootBone);
    boneMesh.bind(skeleton);
    // 将骨架添加到场景
    scene.add(boneMesh);
    // 定义 IK 配置
    const iks = [
        {
            target: 0, // 目标骨骼(rootBone)
            effector: 2, // 最后一个骨骼(endBone)
            links: [
                { index: 1 }, // midBone
                { index: 0 }, // rootBone
            ],
            iteration: 10,
        },
    ];
    // 创建 CCDIKSolver 实例
    const solver = new CCDIKSolver(boneMesh, iks);
    // 创建目标位置对象
    const target = new THREE.Object3D();
    target.position.set(1, 4, 0);
    scene.add(target);
    camera.position.z = 5;
    // 创建 OrbitControls
    const controls = new OrbitControls(camera, renderer.domElement);
    // 渲染循环
    function animate() {
        requestAnimationFrame(animate);
        controls.update();
        // 使 midBone 旋转和位移以实现弯曲效果
        midBone.rotation.z = Math.sin(Date.now() * 0.001) * 0.5; // 水平旋转
        midBone.position.y = Math.sin(Date.now() * 0.001) * 0.5; // 垂直位移
        // 更新目标位置
        target.position.x = Math.sin(Date.now() * 0.001) * 2; // 目标的 x 坐标
        target.position.y = Math.cos(Date.now() * 0.001) * 2 + 2; // 目标的 y 坐标
        // 将目标位置设置为 midBone
        boneMesh.skeleton.bones[iks[0].target].position.copy(target.position);

        // 更新 CCDIKSolver
        solver.update();
        renderer.render(scene, camera);
    }
    animate();

MMDAnimationHelper 有五个属性 五个方法

MMDAnimationHelper( params : Object ) params — (可选) sync - 添加对象的动画持续时间是否同步。默认为 true afterglow - 默认值为 0.0。 resetPhysicsOnLoop - 默认值为 true pmxAnimation - 如果设置为 true,则帮助程序遵循复杂且昂贵的 PMX 动画系统。仅当您的 PMX 模型动画效果不佳时才尝试此选项。默认为 false。 创建一个新的 MMDAnimationHelper。

属性

  • audio : Audio 添加的 Audio 音频
  • camera : Camera 添加的 Camera 相机
  • meshes : Array 添加的 SkinnedMesh 数组
  • objects : WeakMap 一个 WeakMap ,它保存添加到 helper 的对象的 helper 中使用的动画内容。例如,您可以使用“helper.objects.get(mesh).mixer”访问添加的 AnimationMixer 的 SkinnedMesh。
  • onBeforePhysics : Function 在 SkinnedMesh 的物理计算之前立即执行的可选回调。该函数通过 SkinnedMesh 调用。

方法

  • add ( object : Object3D, params : Object ) : MMDAnimationHelper 添加 SkinnedMesh、Camera 或 Audio 到辅助对象并设置动画。 添加的对象的动画持续时间是同步的。如果已经添加了摄像头/音频,它将被替换为新的。object — SkinnedMesh, Camera 或 Audio params — (可选列表)
    animation - 一个 AnimationClip 或设置为对象的 AnimationClip 数组。仅适用于 SkinnedMeshCamera。默认值 undefined。
    physics - 仅适用于 SkinnedMesh。是否开启物理标志。默认为 true。
    warmup - 仅适用于 SkinnedMesh 并且 physics 为 true。物理参数。默认值为 60。
    unitStep - 仅适用于 SkinnedMesh 并且 physics 为 true。物理参数。默认值为 1 / 65。
    maxStepNum - 仅适用于 SkinnedMesh 并且 physics 为 true。物理参数。默认值为 3。
    gravity - 仅适用于 SkinnedMesh 并且 physics 为 true。物理参数。默认值为 ( 0, - 9.8 * 10, 0 )。
    delayTime - 仅适用于 Audio。默认值为 0.0
  • nable ( key : String, enabled : Boolean ) : MMDAnimationHelper key — 允许的字符串为 'animation'、'ik'、'grant'、'physicals' 和 'cameraAnimation'。enabled — true 表示启用,false 表示禁用。 启用/禁用动画功能
    // 创建 MMDAnimationHelper 实例
    const helper = new MMDAnimationHelper();
    // 添加 MMD 模型
    helper.add(mmd.mesh, {
      animation: mmd.animation,
      physics: true,
    });
    helper.enable("physics", false);  // 禁用物理模拟
  • pose ( mesh : SkinnedMesh, vpd : Object, params : Object ) : MMDAnimationHelper 根据 VPD 内容指定 更改 SkinnedMesh 的姿势。mesh — SkinnedMesh 改变姿势。不需要将其添加到 helper 中。 vpd — 获取由 MMDLoader.loadVPD 加载的 VPD 内容 params — (可选)
    resetPose - 默认为 true
    ik - 默认为 true
    grant - 默认为 true
    // 加载姿势数据(vpd)
    loader.loadVPD('models/mmd/pose.vpd', false, (vpd) => {
        // 应用姿势到模型
        helper.pose(mesh, vpd, {
            preserveMorphs: true,   // 保留当前的 morph 状态
            morphPrecision: 1       // 设置 morph 精度
        });
    });
  • remove ( object : Object3D ) : MMDAnimationHelper object — SkinnedMesh、Camera 或 Audio 从助手中删除 SkinnedMesh、Camera 或 Audio。
  • update ( delta : Number ) : MMDAnimationHelper delta — 秒数 提前混合器时间并更新添加到助手的对象的动画。

MMDPhysics 有一个属性五个方法

MMDPhysics( mesh : SkinnedMesh, rigidBodyParams : Array, constraintParams : Array, [param:Object params] ) mesh — SkinnedMesh, MMDPhysics 为其计算物理。 rigidBodyParams — 指定刚体参数的 Object 数组。 constraintParams — 可选)指定约束参数的 Object 数组。 params — (可选) unitStep - 默认为 1 / 65 maxStepNum - 默认为 3 gravity - 默认为 ( 0, - 9.8 * 10, 0 ) 创建一个新的 MMDPhysics。

属性

  • mesh : Array SkinnedMesh 传递给构造函数。

方法

  • createHelper () : MMDPhysicsHelper 返回 MMDPhysicsHelper。您可以通过将辅助对象添加到场景来可视化刚体。
  • reset () : this 重置刚体变换为当前骨骼的刚体。
  • setGravity ( gravity : Vector3 ) : this gravity — 重力的方向和体积。设置重力。
    // 设置重力方向 
    helper.setGravity(new THREE.Vector3(0, -9.8, 0)); // 重力向下
    x 分量
    含义:表示水平方向的重力分量。
    正值:使重力向右偏移,推动物体向右侧运动。
    负值:使重力向左偏移,推动物体向左侧运动。
    用途:用于模拟物体在倾斜平面上的滑动效果,或产生水平风的效果。
    y 分量
    含义:表示垂直方向的重力分量,通常用于控制向下的重力。
    正值:重力向上,通常较少使用。
    负值:重力向下,常用于模拟地心引力,比如 -9.8 表示正常的重力加速度。
    用途:主要用于模拟下落、自由落体等符合现实的重力效果。y 分量越大(绝对值),物体下落得越快。
    z 分量
    含义:表示深度方向的重力分量,即模型朝向/远离屏幕的运动方向。
    正值:使重力朝向观察者,使物体向屏幕前方运动。
    负值:使重力背离观察者,使物体向屏幕后方运动。
    用途:用于控制物体的前后移动,适用于模拟三维场景中的特定力场方向。
    
    x 分量:控制水平左右方向的重力。
    y 分量:控制垂直上下方向的重力,通常为负数表示地心引力。
    z 分量:控制深度方向的重力(朝向或远离屏幕)。
  • update ( delta : Number ) : this delta — 时间(以秒为单位) 高级物理计算和更新骨骼。
  • warmup ( cycles : Integer ) : this delta — 时间(以秒为单位) 热身刚体。计算循环步数。physics.warmup(100); // 进行100次预热循环
    减少初始化突变:避免模型在物理模拟启动时发生大幅度的跳动或晃动。
    获得自然的姿势:通过提前计算物理效果,使骨骼和蒙皮达到更自然的初始状态。
    模拟开始前稳定:对动态物体进行稳定,使模拟的起点更加平滑自然。