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示例

相关推荐
code_Bo2 分钟前
使用micro-app 多层嵌套的问题
前端·javascript·架构
小灰2 分钟前
VS Code 插件 Webview 热更新配置
前端·javascript
进击的明明7 分钟前
前端监控与前端兜底:那些我们平常没注意,但真正决定用户体验的“小机关”
前端·面试
前端老宋Running8 分钟前
我只改了个头像,为什么整个后台系统都闪了一下?
前端·react.js·面试
r***01389 分钟前
SpringBoot3 集成 Shiro
android·前端·后端
八哥程序员9 分钟前
深入理解 JavaScript 作用域与作用域链
前端·javascript
前端一课11 分钟前
【vue高频面试题】第 11 题:Vue 的 `nextTick` 是什么?为什么需要它?底层原理是什么?
前端·面试
前端一课12 分钟前
【vue高频面试题】第 10 题:`watch` VS `watchEffect` 的区别是什么?触发时机有什么不同?
前端·面试
Robet14 分钟前
WebGL2D渲染引擎
webgl
h***346317 分钟前
SpringBoot3.3.0集成Knife4j4.5.0实战
android·前端·后端