动画系统
概述
在Verge3D动画系统中,您可以为模型的各种属性设置动画:skinnedmesh 的骨骼,geometry.morphtargets
(变形目标),不同的材质属性(颜色,不透明度,布尔值),可见性和变换。动画属性可以淡入、淡出、交叉淡化和扭曲。在相同或不同对象上同时发生的动画的权重和时间比例的变化可以独立地进行。相同或不同物体的动画也可以同步发生。
为了在一个同构系统中实现所有这一切,Verge3D所依赖的Three.JS动画系统[link:https://github.com/mrdoob/three.js/issues/6881
已经在2015年就彻底改变了](要注意过时的信息!),它现在有一个与Unity/虚幻4引擎类似的架构。此页面简要阐述了这个系统中的主要组件以及它们如何协同工作。
动画片段
如果您已成功导入3D动画对象(无论它是否有骨骼或变形目标或两者皆有都不要紧)—— 例如使用glTF Blender导出器从Blender导出它并使用
gltfloader 将其加载到Verge3D场景中 —— 其中一个响应字段应该是一个名为"animations"的数组, 其中包含此模型的
AnimationClips
(请参阅下面可用的加载器列表)。
每个AnimationClip通常保存对象某个活动的数据。举个例子,假如mesh是一个角色,可能有一个AnimationClip实现步行循环,
第二个AnimationClip实现跳跃,第三个AnimationClip实现闪避等等。
关键帧轨道
在这样一个AnimationClip中,每个动画属性的数据存储在一个单独的keyframetrack中。假设一个角色对象有skeleton(骨架),
一个关键帧轨道可以存储下臂骨骼位置随时间变化的数据,
另一个轨道追踪同一块骨骼的旋转变化,第三个追踪另外一块骨骼的位置、转角和尺寸,等等。应该很清楚,AnimationClip可以由许多这样的轨道组成。
假设模型具有Geometry.MorphTargets(变形目标)——
例如一个变形目标显示一个笑脸,另一个显示愤怒的脸 —— 每个轨道都持有某个变形目标在AnimationClip运行期间产生的[page:Mesh.morphTargetInfluences
influence](变形目标影响)如何变化的信息。
动画混合器
存储的数据仅构成动画的基础 —— 实际播放由animationmixer控制。您可以想象这不仅仅是动画的播放器,而是作为硬件的模拟,如真正的调音台,可以同时控制和混合若干动画。
动画行为
AnimationMixer本身只有很少的(大体上)属性和方法, 因为它可以通过[page:AnimationAction AnimationAction]来控制。通过配置AnimationAction,您可以决定何时播放、暂停或停止其中一个混合器中的某个AnimationClip, 这个AnimationClip是否需要重复播放以及重复的频率, 是否需要使用淡入淡出或时间缩放,以及一些其他内容(例如交叉渐变和同步)。
动画对象组
如果希望一组对象接收共享的动画状态,可以使用AnimationObjectGroup。
支持的格式和加载器
注意,并不是所有的模型格式都包含动画(尤其是OBJ不会包含),而且只有部分Verge3D加载器支持AnimationClip序列。以下几个 确实 支持此动画类型:
- v3d.JSONLoader
- v3d.ObjectLoader
- v3d.BVHLoader
- v3d.ColladaLoader
- v3d.FBXLoader
- v3d.GLTFLoader
- v3d.MMDLoader
- v3d.SEA3DLoader
请注意,3ds max和Maya当前无法直接导出多个动画(这意味着动画不是在同一时间线上)到一个文件中。
示例:
var mesh;
// Create an AnimationMixer, and get the list of AnimationClip instances
var mixer = new v3d.AnimationMixer(mesh);
var clips = mesh.animations;
// Update the mixer on each frame
function update () {
mixer.update(deltaSeconds);
}
// Play a specific animation
var clip = v3d.AnimationClip.findByName(clips, 'dance');
var action = mixer.clipAction(clip);
action.play();
// Play all animations
clips.forEach(function(clip) {
mixer.clipAction(clip).play();
});