动画系统

概述

在 three.js 的动画系统中,你可以为模型的多种属性制作动画: 例如蒙皮绑定模型的骨骼、形态目标(morph targets)、不同材质属性 (颜色、不透明度、布尔值)、可见性与变换。动画属性可以淡入、 淡出、交叉淡化(crossfade)和变速。即使是同一对象上的多个动画, 或不同对象上的多个动画,也可以独立调整权重和时间缩放, 并进行同步。

为了在一个统一系统中实现这些功能,three.js 动画系统在 2015 年[link:https://github.com/mrdoob/three.js/issues/6881 发生了彻底重构] (注意甄别过时资料)。当前架构与 Unity / Unreal Engine 4 更接近。本页将简要介绍该系统的核心组件,以及它们如何协同工作。

动画片段(Animation Clips)

当你成功导入一个带动画的 3D 对象后(无论它使用骨骼、形态目标,或两者兼有), 比如通过 [link:https://github.com/KhronosGroup/glTF-Blender-IO glTF Blender 导出器] 从 Blender 导出,再使用 `GLTFLoader` 加载到 three.js 场景中, 返回结果中通常会有一个名为 "animations" 的数组字段, 其中包含该模型的动画片段(下文会列出支持此能力的加载器)。

每个 `AnimationClip` 一般表示对象的一种动作数据。以角色模型为例, 可以有一个片段表示走路,第二个表示跳跃,第三个表示侧移,等等。

关键帧轨道(Keyframe Tracks)

在 `AnimationClip` 内部,每个被动画驱动的属性都会存储在独立的 `KeyframeTrack` 中。假设角色有骨架,那么一条轨道可以记录前臂骨骼 随时间变化的位置数据,另一条记录同一骨骼的旋转变化,第三条记录 其他骨骼的位置、旋转或缩放,依此类推。也就是说, 一个 AnimationClip 通常由大量此类轨道组成。

如果模型有形态目标(比如一个表示微笑,另一个表示愤怒), 每条相关轨道会描述某个形态目标在该片段播放过程中, 其影响权重如何随时间变化。

动画混合器(Animation Mixer)

这些存储的数据只是动画基础,真正的播放控制由 `AnimationMixer` 完成。 你可以把它理解成不只是一个“播放器”,更像一台真实的混音台: 能够同时控制多个动画,并对它们进行混合与融合。

动画动作(Animation Actions)

`AnimationMixer` 本身只有少量通用属性和方法, 因为它主要通过动画动作来驱动。通过配置 `AnimationAction`, 你可以决定某个 `AnimationClip` 在某个 mixer 上何时播放、暂停或停止, 是否循环、循环次数、是否淡入淡出、是否进行时间缩放, 以及更多高级控制(如交叉淡化与同步)。

动画对象组(Animation Object Groups)

如果你希望一组对象共享同一套动画状态, 可以使用 `AnimationObjectGroup`。

支持的格式与加载器

请注意,并非所有模型格式都包含动画(例如 OBJ 就不包含), 而且只有部分 three.js 加载器支持 `AnimationClip` 序列。 下面这些加载器支持这种动画数据:

  • THREE.ObjectLoader
  • THREE.BVHLoader
  • THREE.ColladaLoader
  • THREE.FBXLoader
  • THREE.GLTFLoader

另外,3ds Max 和 Maya 目前还不能直接将多个动画 (即不在同一时间轴上的动画)导出到同一个文件中。

示例

let mesh;

// 创建 AnimationMixer,并获取 AnimationClip 列表
const mixer = new THREE.AnimationMixer( mesh );
const clips = mesh.animations;

// 每帧更新 mixer
function update () {
  mixer.update( deltaSeconds );
}

// 播放指定动画
const clip = THREE.AnimationClip.findByName( clips, 'dance' );
const action = mixer.clipAction( clip );
action.play();

// 播放全部动画
clips.forEach( function ( clip ) {
  mixer.clipAction( clip ).play();
} );