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

相关推荐
kyriewen11 分钟前
我扫了 422 个仓库,近一半 CI 在裸装依赖
前端·javascript·程序员
鹏多多22 分钟前
AI 到底能不能独立做个产品?我拿一个小程序验证了
前端·微信小程序·ai编程
火山引擎开发者社区27 分钟前
构建专属的飞书群开发助手,AgentKit Studio 一键部署 DeepSeek Harness Super-Agent
前端
啾咪orJIUMI3 小时前
Vue 3 + 高德地图封装实战:点位、区域、Loca 流光与 Vue 信息窗体
前端·vue.js
掘金者阿豪3 小时前
AI数据库不只是向量检索:从数据语义到库内智能计算的技术演进
前端
梦里寻码3 小时前
我用 NestJS + Vue3 写了一套完整的研发管理系统(58 表 / 229 API / 130 项断言,全开源
前端·后端·算法
FRing033 小时前
vue2干货总结8(路由模块封装、router-link、声明式导航、路由重定向、编程式导航)
开发语言·前端·vue.js·vue2
SameX3 小时前
HarmonyOS ArkUI Dialog 复用 用 @CustomDialog + enum type 做多类型统一管理的做法
前端
AiMan3 小时前
豆包爱学还是不可以导出左侧所有对话,只能单容导出,方法不对吗
前端
AiMan4 小时前
Grok生成的内容能否下载吗?
前端