Eu mencionei AnimationMixer, AnimationAction, etc. no artigo de encadernação óssea avançada three.js . Na verdade, eles devem pertencer ao sistema de animação de Three.js. Este artigo apresentará sistematicamente o sistema de animação (Sistema de Animação).
prefácio
Em geral, raramente usamos o sistema de animação do three.js para criar animações manualmente - porque é realmente problemático. Uma maneira mais eficiente e conveniente é concluir diretamente a produção da animação em um software de modelagem como o Blender e, em seguida, criar animações em três .js .js para jogar. No entanto, aprender o sistema de animação ainda será útil para nós, vamos inserir o texto abaixo.
A criação de uma animação envolve três conceitos: quadros-chave Keyframes
, faixas de quadro-chave KeyframeTrack
e clipes de animação AnimationClip
.
1 quadro-chave Quadros-chave
O conceito de nível mais baixo no sistema de animação é o quadro-chave, cada quadro-chave consiste em três informações: tempo, atributo e valor, por exemplo:
- No 0º segundo,
position
o valor de é(0,0,0)
; - No 3º segundo,
scale
o valor de é(1,1,1)
; - Aos 12 segundos,
material.color
é vermelho.
Cada um desses três quadros-chave descreve o valor de alguma propriedade em um momento específico, mas os quadros-chave não designam nenhum objeto em particular. Os quadros-chave de posição podem ser usados para animar qualquer objeto com uma propriedade .location, os quadros-chave de escala podem animar qualquer objeto com uma propriedade .scale e assim por diante. No entanto, os quadros-chave especificam os tipos de dados. Os quadros-chave acima position
e scale
especificam dados vetoriais, enquanto material.color
os quadros-chave especificam dados de cores. Atualmente, o sistema de animação suporta cinco tipos de dados.
Para criar uma animação, precisamos de pelo menos dois quadros-chave. O exemplo mais simples são dois quadros-chave numéricos, por exemplo, animando a opacidade de um material (quão transparente/transparente ele é):
- No segundo 0,
material.opacity
é 0; - Aos 3 segundos,
material.opacity
é 1;
Uma opacidade de 0 significa completamente invisível e uma opacidade de 1 significa completamente visível. Quando definimos esses dois quadros-chave para um objeto, ele aparecerá gradualmente ao longo de 3 segundos. Independentemente da transparência original do objeto, os quadros-chave substituem seu valor original.
2 faixas de quadro-chave KeyframeTrack
Não há nenhuma classe que represente um único quadro-chave em Three.js, KeyframeTrack
que contém duas matrizes — a matriz de tempo e a matriz de valor.Cada quadro-chave corresponde a um valor na matriz de tempo e na matriz de valor. Além disso, KeyframeTrack
é apenas uma classe base, não use diretamente KeyframeTrack
, existem subclasses correspondentes para cada tipo de dados mencionado acima, você precisa selecionar a subclasse correspondente de acordo com o tipo de dados do valor:
- BooleanKeyframeTrack
- ColorKeyframeTrack
- NumberKeyframeTrack
- QuaternionKeyframeTrack
- StringKeyframeTrack
- VectorKeyframeTrack
2.1 NumberKeyframeTrack
Exemplo usando os quadros-chave de transparência anteriores:
- Em 0 segundos,
material.opacity
é 0 - Em 1 segundo,
material.opacity
é 1 - Em 2 segundos,
material.opacity
é 0 - Em 3 segundos,
material.opacity
é 1 - Aos 4 segundos,
material.opacity
é 0
Como a transparência é numérica, NumberKeyframeTrack
uma classe pode ser usada para armazenar dados de quadro-chave:
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);
Explicação: KeyframeTrack
A função construtora é:
/**
* KeyframeTrack构造函数
* name: 关键帧轨道的名称
* times: 关键帧时间数组,内部转换为Float32Array
* values: 包含与时间数组相关的取值,内部转换为浮点32Array
* interpolation: 要使用的插值类型,默认值为线性插值
*/
KeyframeTrack( name : String, times : Array, values : Array, interpolation : Constant )
2.2 VectorKeyframeTrack
Como NumberKeyframeTrack
existe apenas um valor do tipo numérico em cada ponto do tempo, o comprimento da matriz de tempos e a matriz de valores são os mesmos, e se os dados de cada quadro forem um vetor? Como a matriz de valores deve ser construída? Usamos o seguinte exemplo:
- Em 0 segundos,
position
por(0,0,0)
- Aos 3 segundos,
position
por(2,2,2)
- Em 6 segundos,
position
esses(0,0,0)
três quadros-chave farão com que o objeto comece no centro da cena, mova-se para a direita, para cima e para frente por três segundos, depois inverta a direção e volte para o centro. Em seguida, usaremos esses quadros-chave para criar uma trilha vetorial.
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
Deve-se observar que, como os dados em cada ponto de tempo Vector3
contêm 3 valores e esses dados são diretamente achatados, o comprimento da matriz de valores é três vezes maior que a matriz de tempos e a relação de mapeamento correspondente é:
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 clipes de animação AnimationClip
O modelo dançante na figura abaixo (clique aqui para efeitos dinâmicos ) tem movimentos muito complexos: os pés giram, os joelhos dobram, os braços balançam descontroladamente e as cabeças balançam no ritmo. Cada movimento individual é armazenado em uma faixa de quadro-chave separada , de modo que uma faixa controla a rotação do pé esquerdo do dançarino, outra controla a rotação do pé direito e uma terceira controla a rotação do pescoço etc.
Na verdade, essa animação de dança é feita a partir de 53 faixas de quadro-chave, 52 das quais são faixas de quatérnios que controlam articulações individuais, como os joelhos, cotovelos e tornozelos do dançarino. Em seguida, há uma trilha .location que move o gráfico para frente e para trás no chão.
Essas 53 faixas de quadro-chave são combinadas para criar a animação final, que chamamos de clipe de animação. Um clipe de animação é, portanto, uma coleção de qualquer número de quadros-chave anexados a um único objeto, a classe que representa o clipe é AnimationClip
. Os clipes de animação podem ser repetidos, portanto, embora a animação do dançarino tenha apenas 18 segundos de duração, quando chega ao fim, inicia a próxima rodada do loop, parecendo que o dançarino pode dançar para sempre.
Aqui está AnimationClip
o construtor:
AnimationClip( name : String, duration : Number, tracks : Array )
Pode ser visto no construtor que o clipe de animação armazena três informações: o nome do clipe, a duração do clipe e a matriz de faixas que compõem o clipe. Se definirmos o comprimento como -1, a matriz de trilhas será usada para calcular o comprimento. Criamos um clipe contendo a faixa de posição única anterior:
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);
Assim como os quadros-chave, AnimationClip
eles não são anexados a nenhum objeto específico, portanto, como vincular a animação ao modelo e controlar sua reprodução?
4 Animation Mixer AnimationMixer
Para que os objetos (como o Mesh) acessem o sistema de animação e possam se mover, precisamos estabelecer uma conexão com o mixer de animação AnimationMixer
. Cada objeto animado na cena requer um mixer separado . O mixer é responsável por fazer com que o modelo ajuste o estado de acordo com as configurações do clipe de animação, como mover os pés, braços e quadris de um dançarino ou mover as asas de um pássaro voando.
import {
Mesh, AnimationMixer } from 'three';
// 创建一个静态的Mesh
const mesh = new Mesh();
// 通过将其连接到混合器,将其变为动画网格
const mixer = new AnimationMixer(mesh);
5 ação de animação AnimationAction
AnimationAction
Ele é responsável por conectar o objeto de animação ao clipe de animação AnimationClip
e também é responsável por controlar a pausa, reprodução, reinicialização e outras operações da animação. Vale ressaltar que não iremos criar a ação diretamente, mas sim AnimationMixer.clipAction()
com o auxílio de uma função, que pode ter melhor desempenho, pois o mixer fará o cache da ação.
Veja o exemplo abaixo:
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 Controle de ação múltipla
Suponha que temos um modelo de uma pessoa e esse modelo pode andar, correr e pular, cada animação aparecerá em um clipe separado e cada clipe deve estar conectado a uma ação. Então, assim como existe uma relação um-para-um entre Mixers e Models, também existe uma relação um-para-um entre Actions e AnimationClips:
const mixer = new AnimationMixer(humanModel);
const walkAction = mixer.clipAction(walkClip);
const runnAction = mixer.clipAction(runClip);
const jumpAction = mixer.clipAction(jumpClip);
O próximo passo é escolher qual dessas ações executar. Como você fará isso dependerá do tipo de cena que você está construindo. Por exemplo, no caso de um jogo, você conectaria essas ações aos controles do usuário para que, quando o botão correspondente for pressionado, o personagem caminhe, corra ou pule.