Three.js 骨骼动画深入解析与实践

419 阅读4分钟

在 Three.js 中,骨骼动画为模型赋予了生动的动态效果,使场景更加鲜活。本文假定读者已经具备 Three.js 的基础知识,将直接深入骨骼动画的核心内容。

骨骼动画基础概念回顾

骨骼动画基于骨骼系统,模型的顶点通过蒙皮与骨骼相连。当骨骼移动、旋转或缩放时,与之关联的顶点也会相应地产生变形,从而实现逼真的动画效果。在 Three.js 中,骨骼动画主要涉及到Skeleton(骨骼结构)和SkinnedMesh(蒙皮网格)这两个关键概念。

加载带骨骼动画的模型

在开始使用骨骼动画前,我们需要加载一个带有骨骼动画信息的模型。通常,我们会使用GLTFLoader来加载常见的.gltf或.glb格式模型,这些格式能够很好地保存骨骼动画数据。

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
loader.load('yourModel.glb', (gltf) => {
    const skinnedMesh = gltf.scene.getObjectByName('SkinnedMeshName');
    // 这里SkinnedMeshName是模型中包含骨骼动画的网格名称,需根据实际模型调整
    scene.add(skinnedMesh);
    const animationMixer = new THREE.AnimationMixer(skinnedMesh);
    const clip = gltf.animations[0];
    const action = animationMixer.clipAction(clip);
    action.play();
}, undefined, (error) => {
    console.error('Error loading model:', error);
});

在这段代码中,我们首先创建了一个GLTFLoader实例来加载模型。加载成功后,我们从场景中获取到带有骨骼动画的SkinnedMesh。接着,创建一个AnimationMixer,它负责管理模型的动画状态。然后获取模型自带的动画剪辑clip,并通过clipAction方法创建一个动画动作action,最后调用action.play()来播放动画。

控制动画播放

  1. 播放、暂停与停止
    • 播放动画:如上述代码中action.play()即可启动动画播放。
    • 暂停动画:使用action.paused = true;来暂停当前动画。
    • 停止动画:虽然 Three.js 没有直接的停止方法,但可以通过将动画时间重置到开始位置并暂停来实现类似效果,如action.time = 0; action.paused = true;。
  1. 动画混合与过渡

在复杂的动画场景中,我们可能需要在不同的动画之间进行混合过渡,以实现更自然的效果。例如,从走路动画平滑过渡到跑步动画。

const walkClip = gltf.animations[0];
const runClip = gltf.animations[1];
const walkAction = animationMixer.clipAction(walkClip);
const runAction = animationMixer.clipAction(runClip);
// 从走路动画过渡到跑步动画,过渡时间为0.5秒
walkAction.crossFadeTo(runAction, 0.5);

这里,我们获取了两个不同的动画剪辑(走路和跑步),分别创建对应的动画动作。然后使用crossFadeTo方法实现从走路动画到跑步动画的平滑过渡,过渡时间设置为 0.5 秒。

自定义骨骼动画

除了加载现成的模型动画,我们还可以自定义骨骼动画。这需要我们手动创建骨骼结构和蒙皮网格,并为骨骼添加动画关键帧。

  1. 创建骨骼结构
const bone1 = new THREE.Bone();
const bone2 = new THREE.Bone();
bone1.add(bone2);
const skeleton = new THREE.Skeleton([bone1, bone2], []);

在这段代码中,我们创建了两个骨骼bone1和bone2,并将bone2作为bone1的子骨骼。然后通过这两个骨骼创建了一个Skeleton实例。

  1. 创建蒙皮网格
const geometry = new THREE.BufferGeometry();
// 假设已经设置好顶点数据等几何信息
const material = new THREE.MeshStandardMaterial({ color: 0xffffff });
const skinnedMesh = new THREE.SkinnedMesh(geometry, material);
skinnedMesh.bind(skeleton);

这里,我们创建了一个BufferGeometry作为网格的几何形状,并设置了材质。然后创建SkinnedMesh,并通过bind方法将其与之前创建的骨骼结构绑定。

  1. 添加动画关键帧
const keyframeTracks = [];
const times = [0, 1];
const values1 = [0, Math.PI / 2];
const values2 = [0, Math.PI / 4];
const rotationTrack1 = new THREE.NumberKeyframeTrack(
    '.bones[0].rotation.y',
    times,
    values1
);
const rotationTrack2 = new THREE.NumberKeyframeTrack(
    '.bones[1].rotation.y',
    times,
    values2
);
keyframeTracks.push(rotationTrack1, rotationTrack2);
const clip = new THREE.AnimationClip('customClip', 1, keyframeTracks);
const animationMixer = new THREE.AnimationMixer(skinnedMesh);
const action = animationMixer.clipAction(clip);
action.play();

在这个示例中,我们定义了两个骨骼在不同时间点的旋转关键帧。通过NumberKeyframeTrack创建关键帧轨道,分别描述了bone1和bone2在时间为 0 和 1 时绕 Y 轴的旋转角度。然后将这些关键帧轨道组合成一个动画剪辑clip,并通过AnimationMixer和clipAction来播放自定义的骨骼动画。

通过以上对 Three.js 中骨骼动画的深入介绍,包括加载模型动画、控制动画播放以及自定义骨骼动画,相信你已经对如何在项目中运用骨骼动画有了更清晰的认识。在实际应用中,不断探索和尝试,结合具体需求,你将能够创造出更加精彩的动画效果。