BabylonJS 6.0文档 Deep Dive 动画(四):通过动画排序制作卡通片

一种最为直接的方法是为每个动画剪辑(Animatin Clip)指定开始时间,最终形成一个卡通动画(Cartoon)。

1. 设计

1.1 概述

动画的脚本如下:

  • 摄像机显示了一栋带门的建筑物。
  • 摄像机靠近门并停止。
  • 门打开,摄像机进入房间。
  • 当摄像机进入房间时,房间里的灯亮起。
  • 门关上,摄像机在房间里扫了一圈。

本次目的是展示电影剪辑的制作方式,因此房间和门将简单地用没有纹理的网格制成作为例子。

1.2 执行者

  • 相机(Camera) - Universal Camera
  • 门(Door) - 向内打开, 铰链在右侧(旋转轴)
  • 聚光灯(Lights) - 带有球体以显示位置

1.3 时间排序

以下为各个执行者在不同时间段的执行动作:

对于每个执行者,可以创建一个动画,其中包含每个定时事件的关键点。

1.4 动画

对于相机

移动相机会更改相机的位置,相机绕 y 轴旋转。

由于动画只能更改一个属性,因此相机需要两个动画。

移动摄像机,关键点将在时间 0 处,摄像机将从建筑物开始,朝向和向下移动,直到时间 3 秒刚好在门外。

当门打开时,摄像机将保持其位置 2 秒钟,然后在时间 5 秒以远离门的角度向前移动到房间,在时间 8 秒停止。

相机不会旋转 9 秒,然后相机需要 14 秒才能旋转 180 度以面向门。

相机的key frame将是它在帧 0、3、5 和 8 秒的位置以及它在 0、9 和 14 秒的旋转。

TypeScript 复制代码
//for camera move forward
const movein = new BABYLON.Animation("movein", "position", frameRate, BABYLON.Animation.ANIMATIONTYPE_VECTOR3, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);

const movein_keys = [];

movein_keys.push({
  frame: 0,
  value: new BABYLON.Vector3(0, 5, -30),
});

movein_keys.push({
  frame: 3 * frameRate,
  value: new BABYLON.Vector3(0, 2, -10),
});

movein_keys.push({
  frame: 5 * frameRate,
  value: new BABYLON.Vector3(0, 2, -10),
});

movein_keys.push({
  frame: 8 * frameRate,
  value: new BABYLON.Vector3(-2, 2, 3),
});

movein.setKeys(movein_keys);

//for camera to sweep round
const rotate = new BABYLON.Animation("rotate", "rotation.y", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);

const rotate_keys = [];

rotate_keys.push({
  frame: 0,
  value: 0,
});

rotate_keys.push({
  frame: 9 * frameRate,
  value: 0,
});

rotate_keys.push({
  frame: 14 * frameRate,
  value: Math.PI,
});

rotate.setKeys(rotate_keys);

对于门

门将围绕一个铰链旋转(y 轴)。打开和关闭旋转各需要 2 秒。

Key frame将是 0、3、5 、13 和 15 秒。

扫描的关键值将是它在帧处绕 y 轴的旋转。

TypeScript 复制代码
//for door to open and close
const sweep = new BABYLON.Animation("sweep", "rotation.y", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);

const sweep_keys = [];

sweep_keys.push({
  frame: 0,
  value: 0,
});

sweep_keys.push({
  frame: 3 * frameRate,
  value: 0,
});

sweep_keys.push({
  frame: 5 * frameRate,
  value: Math.PI / 3,
});

sweep_keys.push({
  frame: 13 * frameRate,
  value: Math.PI / 3,
});

sweep_keys.push({
  frame: 15 * frameRate,
  value: 0,
});

sweep.setKeys(sweep_keys);

对于灯光

不同灯光的强度会有所不同,它是一组灯光。

灯的关键点是保持关闭 7 秒,在 10 秒时达到最强,直到 14 秒熄灭。

TypeScript 复制代码
//for light to brighten and dim
const lightDimmer = new BABYLON.Animation("dimmer", "intensity", frameRate, BABYLON.Animation.ANIMATIONTYPE_FLOAT, BABYLON.Animation.ANIMATIONLOOPMODE_CONSTANT);

const light_keys = [];

light_keys.push({
  frame: 0,
  value: 0,
});

light_keys.push({
  frame: 7 * frameRate,
  value: 0,
});

light_keys.push({
  frame: 10 * frameRate,
  value: 1,
});

light_keys.push({
  frame: 14 * frameRate,
  value: 1,
});

light_keys.push({
  frame: 15 * frameRate,
  value: 0,
});

lightDimmer.setKeys(light_keys);

2. 卡通动画

现在只需同时运行所有的动画剪辑:

TypeScript 复制代码
scene.beginDirectAnimation(camera, [movein, rotate], 0, 25 * frameRate, false);
scene.beginDirectAnimation(hinge, [sweep], 0, 25 * frameRate, false);
scene.beginDirectAnimation(spotLights[0], [lightDimmer], 0, 25 * frameRate, false);
scene.beginDirectAnimation(spotLights[1], [lightDimmer.clone()], 0, 25 * frameRate, false);

Playground示例

相关推荐
旧味清欢|6 分钟前
关注分离(Separation of Concerns)在前端开发中的实践演进:从 XMLHttpRequest 到 Fetch API
javascript·http·es6
热爱编程的小曾23 分钟前
sqli-labs靶场 less 8
前端·数据库·less
gongzemin34 分钟前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
Apifox1 小时前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd
-代号95271 小时前
【JavaScript】十四、轮播图
javascript·css·css3
树上有只程序猿1 小时前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼2 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
QTX187302 小时前
JavaScript 中的原型链与继承
开发语言·javascript·原型模式
黄毛火烧雪下2 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox2 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员