three.js进阶之动画系统

news/2024/12/5 12:45:57/

我曾在three.js进阶之骨骼绑定文章中提到了AnimationMixer、AnimationAction等内容,其实这些应该属于Three.js的动画系统,本文就系统的介绍一下动画系统(Animation System)。

前言

一般情况下,我们很少会使用three.js的动画系统去手动创建动画——因为这真的很麻烦,更高效便捷的做法还是直接在建模软件如Blender中完成动画的制作,然后在three.js中进行播放。不过,学习了动画系统对我们还是会有帮助的,下面进入正文。

创建动画涉及三个概念:关键帧Keyframes,关键帧轨迹KeyframeTrack和动画剪辑AnimationClip

1 关键帧 Keyframes

在动画系统中最低级别的概念就是关键帧,每个关键帧由三个信息组成:时间、属性和值,举个栗子:

  • 在第0秒,position的取值为(0,0,0)
  • 在第3秒,scale的取值为(1,1,1)
  • 在第12秒,material.color为红色。

这三个关键帧分别描述特定时间的某些属性的值,不过关键帧并不指定任何特定对象。位置关键帧可用于为具有.location属性的任何对象设置动画,缩放关键帧可为具有.scale属性的任何对象设置动画,依此类推。但是,关键帧确实指定了数据类型。上面的positionscale关键帧指定矢量数据,而material.color关键帧指定颜色数据。目前,动画系统支持五种数据类型。
在这里插入图片描述
要创建动画,我们至少需要两个关键帧。最简单的例子是两个数字关键帧,例如,动画材质的不透明度(它的透明度/透视程度):

  • 在第0秒,material.opacity为0;
  • 在第3秒,material.opacity为1;

不透明度为0意味着完全不可见,不透明度为1意味着完全可见。当我们为某个对象设置了这两个关键帧后,它将会在3秒内逐渐出现。不管对象原本的透明度为多少,关键帧会覆盖其原本的值。

2 关键帧轨迹 KeyframeTrack

在Three.js中并没有表示单个关键帧的类,KeyframeTrack中包含两个数组——时间数组和取值数组,每一个关键帧就对应时间数组和取值数组中的一个值。另外,KeyframeTrack只是一个基类,不要直接使用KeyframeTrack,,针对前面提到的每种数据类型都有对应的子类,你需要根据取值的数据类型选择对应的子类:

  • BooleanKeyframeTrack
  • ColorKeyframeTrack
  • NumberKeyframeTrack
  • QuaternionKeyframeTrack
  • StringKeyframeTrack
  • VectorKeyframeTrack

2.1 NumberKeyframeTrack

使用前面的透明度关键帧的例子:

  • 在0秒时,material.opacity为0
  • 在1秒时,material.opacity为1
  • 在2秒时,material.opacity为0
  • 在3秒时,material.opacity为1
  • 在4秒时,material.opacity为0

由于透明度为数值型,因此可以使用NumberKeyframeTrack类来存储关键帧数据:

import { NumberKeyframeTrack } from "three";const times = [0, 1, 2, 3, 4];
const values = [0, 1, 0, 1, 0];const opacityKF = new NumberKeyframeTrack(".material.opacity", times, values);

说明:KeyframeTrack的构造函数为:

/*** KeyframeTrack构造函数* name: 关键帧轨道的名称* times: 关键帧时间数组,内部转换为Float32Array* values: 包含与时间数组相关的取值,内部转换为浮点32Array* interpolation: 要使用的插值类型,默认值为线性插值*/
KeyframeTrack( name : String, times : Array, values : Array, interpolation : Constant )

2.2 VectorKeyframeTrack

由于NumberKeyframeTrack在每个时间点都只有一个数值类型的取值,因此times数组和values数组的长度是一样的,那么如果每一帧的数据是一个向量呢?应该如何构造values数组呢?我们使用下面的例子:

  • 在第0秒,position(0,0,0)
  • 在第3秒,position(2,2,2)
  • 在第6秒,position(0,0,0)
    这三个关键帧将使对象从场景的中心开始,在三秒内向右、向上和向前移动,然后反转方向并移动回中心。接下来,我们将使用这些关键帧创建矢量轨迹。
import { VectorKeyframeTrack } from "three";const times = [0, 3, 6];
const values = [0, 0, 0, 2, 2, 2, 0, 0, 0];const positionKF = new VectorKeyframeTrack(".position", times, values);

需要留意,由于每个时间点的position数据都是一个Vector3包含3个数值,而且这些数据是直接平铺开来的,因此values数组的长度是times数组的3倍,对应的映射关系为:

const times = [0, 3, 6];
const values = [0,0,0, // (x, y, z) at t = 02,2,2, // (x, y, z) at t = 30,0,0, // (x, y, z) at t = 6
];

3 动画剪辑 AnimationClip

如下图(动态效果可点击这里查看)中跳舞的模型动作非常复杂:双脚旋转,膝盖弯曲,手臂疯狂摆动,头部随着节拍点头。每个单独的动作都存储在一个单独的关键帧轨道中,因此有一个轨道控制舞者左脚的旋转,另一个轨道控制他的右脚的旋转,第三个轨道控制他的脖子的旋转,等等。
在这里插入图片描述

事实上,这个舞蹈动画是由53个关键帧轨道制成的,其中52个是控制舞者膝盖、肘部和脚踝等单个关节的四元数轨道。然后,有一个.location轨迹,可以在地板上来回移动图形。

这53个关键帧轨道结合在一起创建出的最终动画,我们称之为动画剪辑。因此,动画剪辑是附加到单个对象的任意数量关键帧的集合,表示剪辑的类是AnimationClip。动画剪辑可以循环播放,所以,虽然这个舞者的动画只有18秒长,但当它到达终点时,它开始下一轮的循环,因此看起来舞者似乎可以一直跳下去。

下面是AnimationClip的构造函数:

AnimationClip( name : String, duration : Number, tracks : Array )

从构造函数中能够看出动画剪辑存储三个信息:剪辑的名称、剪辑的长度和组成剪辑的轨道数组。如果我们将长度设置为-1,则轨道数组将用于计算长度。我们创建一个包含前面的单个位置轨迹的剪辑:

import { AnimationClip, VectorKeyframeTrack } from "three";const times = [0, 3, 6];
const values = [0, 0, 0, 2, 2, 2, 0, 0, 0];const positionKF = new VectorKeyframeTrack(".position", times, values);// 当前只有一个关键帧轨道
const tracks = [positionKF];// 将length设置为-1可以自动从tracks中计算长度,在本例中为6秒
const length = -1;const clip = new AnimationClip("slowmove", length, tracks);

和关键帧一样,AnimationClip不会被附着到任何特定的对象上,那么应该如何将动画绑定到模型身上并且控制其进行播放呢?

4 动画混合器 AnimationMixer

为了让物体(如Mesh)接入动画系统并且能够动起来,我们需要将其和动画混合器AnimationMixer建立联系。场景中的每个动画对象都需要使用一个单独的混合器。混合器负责使模型按照动画剪辑的设定进行状态调整,如移动舞者的脚、手臂和臀部,或者是移动飞鸟的翅膀。

import { Mesh, AnimationMixer } from 'three';// 创建一个静态的Mesh
const mesh = new Mesh();// 通过将其连接到混合器,将其变为动画网格
const mixer = new AnimationMixer(mesh);

5 动画动作 AnimationAction

AnimationAction负责将动画对象连接到动画剪辑AnimationClip,同时也负责控制动画的暂停、播放、重置等操作。需要注意的是,我们不会直接创建action,而是借助AnimationMixer.clipAction()函数创建,这样能够有更好的性能,因为mixer会对action进行缓存。

看下面的例子:

import { AnimationClip, AnimationMixer } from "three";const positionKF = new VectorKeyframeTrack(".position",[0, 3, 6],[0, 0, 0, 2, 2, 2, 0, 0, 0]
);const opacityKF = new NumberKeyframeTrack(".material.opacity",[0, 1, 2, 3, 4, 5, 6],[0, 1, 0, 1, 0, 1, 0]
);const moveBlinkClip = new AnimationClip("move-n-blink", -1, [positionKF,opacityKF,
]);const mesh = new Mesh();const mixer = new AnimationMixer(mesh);
const action = mixer.clipAction(moveBlinkClip);

5.1 多动作控制

假设我们有一个人的模型,并且这个模型可以走路、跑步和跳,每个动画都将在一个单独的剪辑中出现,每个剪辑必须连接到一个动作。因此,就像混合器和模型之间存在一对一的关系一样,动作和动画剪辑之间也存在一对一的关系:

const mixer = new AnimationMixer(humanModel);const walkAction = mixer.clipAction(walkClip);
const runnAction = mixer.clipAction(runClip);
const jumpAction = mixer.clipAction(jumpClip);

下一步是选择要执行这些操作中的哪一个。你怎么做将取决于你正在建造什么样的场景。例如,如果是游戏,您将将这些操作连接到用户控件,这样当按下相应的按钮时,角色将行走、运行或跳跃。

参考资料

The three.js Animation System


http://www.ppmy.cn/news/57339.html

相关文章

[Eigen中文文档] 编译器对堆栈对齐做出了错误的假设

文档总目录 本文目录 局部解决方案全局解决方案 英文原文(Compiler making a wrong assumption on stack alignment) 这是 GCC 的错误,已在 GCC 4.5 中修复。如果遇到此问题,请升级到 GCC 4.5 。 到目前为止,我们只在 Windows 上遇到过 GC…

【Unity-UGUI控件全面解析】| Text文本组件详解

🎬【Unity-UGUI控件全面解析】| Text文本组件详解一、组件介绍二、组件属性面板三、代码操作组件四、组件常用方法示例4.1 改变Text文本颜色4.2 文本换行问题4.3 空格自动换行问题4.4 逐字显示效果五、组件相关扩展使用5.1 文本描边组件(Outline)5.2 阴影组件(Shadow)5.3…

Java每日一练(20230503)

1. 外观数列 给定一个正整数 n ,输出外观数列的第 n 项。 「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。 你可以将其视作是由递归公式定义的数字字符串序列: countAndSay(1) "1"c…

【Linux从入门到精通】vim的基本使用各种操作详解

文章目录 一、vim编辑器简单介绍 二、vim编辑器的四种模式 2、1 正常/普通/命令模式(Normal mode) 2、2 插入模式(Insert mode) 2、3 末行模式(last line mode) 三、命令模式的相关操作实例 3、1 光标的相关操作 3、2 文本操作 四、插入模式下的相关操作 五、末行模式下的相关操…

Node第三方包 【Request】

文章目录 🌟前言🌟Request🌟安装与使用🌟流(stream)操作🌟Form表单🌟application/x-www-form-urlencoded (URL编码的Form)🌟multipart/form-data (Multipart Form 上传) …

内网穿透NPS和宝塔Nginx配合使用,开启SSL访问本地局域网网络

并非为了教学,仅供自己记录,方便下次用。所以内容不会刻意花时间写的很细节详细。 1. 服务器NPS配置 NPS install安装后,配置文件会在其他位置,通过是 /etc/nps/nps.conf目录。 找到进行修改,主要修改的是http_proxy_p…

本地安装kibana kibana设置中文汉化

一、Kibana简介 Kibana是一个开源的基于浏览器的分析和可视化平台,可以用于搜索,查看,删除Elasticsearch索引并与存储在Elasticsearch索引中的数据进行交互。可以执行高级数据分析,并且以各种图标、表格和地图的形式可视化数据。…

AWE2023什么值得看?智哪儿带你五大关键词读懂AWE2023

4月27至30日,2023年中国家电及消费电子博览会(AWE 2023)在上海浦东新国际博览中心开展。 作为与德国IFA、美国CES并肩的全球前三国际家电及消费电子展览会,时隔两年AWE终于重启。沉淀两年,它的规模也是历年最大&#x…