three.js advanced animation system

I mentioned AnimationMixer, AnimationAction, etc. in the three.js advanced bone binding article. In fact, these should belong to the animation system of Three.js. This article will introduce the animation system (Animation System) systematically.

foreword

In general, we rarely use the animation system of three.js to create animations manually - because it is really troublesome. A more efficient and convenient way is to directly complete the animation production in modeling software such as Blender, and then create animations in three.js .js to play. However, learning the animation system will still be helpful to us, let's enter the text below.

Creating an animation involves three concepts: keyframes Keyframes, keyframe tracks KeyframeTrack, and animation clips AnimationClip.

1 key frame Keyframes

The lowest level concept in the animation system is the keyframe, each keyframe consists of three pieces of information: time, attribute and value, for example:

  • At the 0th second, positionthe value of is (0,0,0);
  • In the 3rd second, scalethe value of is (1,1,1);
  • At 12 seconds, material.colorit's red.

These three keyframes each describe the value of some property at a specific time, but the keyframes do not designate any particular object. Position keyframes can be used to animate any object with a .location property, scale keyframes can animate any object with a .scale property, and so on. However, keyframes do specify data types. The above positionand scalekeyframes specify vector data, while material.colorkeyframes specify color data. Currently, the animation system supports five data types.
insert image description here
To create an animation, we need at least two keyframes. The simplest example is two numeric keyframes, e.g. animating a material's opacity (how transparent/see-through it is):

  • At second 0, material.opacityit is 0;
  • At 3 seconds, material.opacityit is 1;

An opacity of 0 means completely invisible, and an opacity of 1 means completely visible. When we set these two keyframes for an object, it will gradually appear over 3 seconds. Regardless of the object's original transparency, keyframes override its original value.

2 key frame track KeyframeTrack

There is no class representing a single keyframe in Three.js, KeyframeTrackwhich contains two arrays—the time array and the value array. Each keyframe corresponds to a value in the time array and the value array. In addition, KeyframeTrackit is only a base class, do not use it directly KeyframeTrack, there are corresponding subclasses for each data type mentioned above, you need to select the corresponding subclass according to the data type of the value:

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

2.1 NumberKeyframeTrack

Example using the previous transparency keyframes:

  • At 0 seconds, material.opacityit is 0
  • At 1 second, material.opacityit is 1
  • At 2 seconds, material.opacityit is 0
  • At 3 seconds, material.opacityit is 1
  • At 4 seconds, material.opacityit is 0

Since transparency is numeric, NumberKeyframeTracka class can be used to store keyframe data:

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);

Explanation: KeyframeTrackThe constructor function is:

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

2.2 VectorKeyframeTrack

Since NumberKeyframeTrackthere is only one numeric value at each time point, the length of the times array and the values ​​array are the same, so what if the data of each frame is a vector? How should the values ​​array be constructed? We use the following example:

  • At 0 seconds, positionfor(0,0,0)
  • At 3 seconds, positionfor(2,2,2)
  • At 6 seconds, positionthese (0,0,0)
    three keyframes will cause the object to start at the center of the scene, move right, up, and forward for three seconds, then reverse direction and move back to the center. Next, we'll use these keyframes to create a vector track.
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);

positionIt should be noted that since the data at each time point Vector3contains 3 values, and these data are directly flattened, the length of the values ​​array is three times that of the times array, and the corresponding mapping relationship is:

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

3 animation clip AnimationClip

The dancing model in the picture below (click here for dynamic effects ) has very complex movements: feet rotate, knees bend, arms swing wildly, and heads nod to the beat. Each individual movement is stored in a separate keyframe track , so one track controls the rotation of the dancer's left foot, another controls the rotation of his right foot, and a third controls the rotation of his neck, etc.
insert image description here

In fact, this dance animation is made from 53 keyframe tracks, 52 of which are quaternion tracks that control individual joints like the dancer's knees, elbows and ankles. Then, there is a .location track that moves the graphic back and forth on the floor.

These 53 keyframe tracks are combined to create the final animation, which we call an animation clip. An animation clip is thus a collection of any number of keyframes attached to a single object, the class representing the clip is AnimationClip. Animation clips can be looped, so even though this animation of the dancer is only 18 seconds long, when it reaches the end, it starts the next loop, so it looks like the dancer can keep going forever.

Here's AnimationClipthe constructor:

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

It can be seen from the constructor that the animation clip stores three pieces of information: the name of the clip, the length of the clip, and the array of tracks that make up the clip. If we set the length to -1, the track array will be used to calculate the length. We create a clip containing the previous single position track:

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);

Like keyframes, AnimationClipthey are not attached to any specific object, so how do you bind the animation to the model and control its playback?

4 Animation Mixer AnimationMixer

In order for objects (such as Mesh) to access the animation system and be able to move, we need to establish a connection with the animation mixer AnimationMixer. Each animated object in the scene requires a separate mixer . The mixer is responsible for making the model adjust the state according to the settings of the animation clip, such as moving the feet, arms and hips of a dancer, or moving the wings of a flying bird.

import {
    
     Mesh, AnimationMixer } from 'three';

// 创建一个静态的Mesh
const mesh = new Mesh();

// 通过将其连接到混合器,将其变为动画网格
const mixer = new AnimationMixer(mesh);

5 animation action AnimationAction

AnimationActionIt is responsible for connecting the animation object to the animation clip AnimationClip, and is also responsible for controlling the pause, play, reset and other operations of the animation. It should be noted that we will not create the action directly, but AnimationMixer.clipAction()create it with the help of a function, which can have better performance, because the mixer will cache the action.

See the example below:

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 Multi-action control

Suppose we have a model of a person, and this model can walk, run and jump, each animation will appear in a separate clip, and each clip must be connected to an action. So, just like there is a one-to-one relationship between Mixers and Models, there is also a one-to-one relationship between Actions and AnimationClips:

const mixer = new AnimationMixer(humanModel);

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

The next step is to choose which of these actions to perform. How you do it will depend on what kind of scene you are building. For example, in the case of a game, you would connect these actions to user controls so that when the corresponding button is pressed, the character will walk, run, or jump.

References

The three.js Animation System

Guess you like

Origin blog.csdn.net/qq_26822029/article/details/130441062