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 数组。仅适用于 SkinnedMesh 和 Camera。默认值 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次预热循环
减少初始化突变:避免模型在物理模拟启动时发生大幅度的跳动或晃动。
获得自然的姿势:通过提前计算物理效果,使骨骼和蒙皮达到更自然的初始状态。
模拟开始前稳定:对动态物体进行稳定,使模拟的起点更加平滑自然。