三维场景中常用的路径动画
前言
在三维场景中,除了用逼近真实的模型代表现实中的设备、标识物外,通常还会使用一些动画来表示模型在现实中一些行为和作用。常见的动画比如路径动画、旋转动画、发光动画、流动动画等。本文将为大家介绍几种常用的路径动画。首先,最简单的自然是直线路径动画。
直线路径动画
比如以下场景,地铁需要从上一站A驶入当前站B,在此过程中,我们将AB组合成一条路径(假设路径为直线),使用动画,不停的设置地铁(模型)的在路径上的位置,就可以实现地铁从A站-B站的动画过程。
1const points = [A, B]; 2// 创建路径 3let path = new mono.Path(); 4points.forEach((point, i) => { 5 const { x, y, z } = point; 6 if (i == 0) { 7 path.moveTo(x, y, z); 8 } else { 9 path.lineTo(x, y, z); 10 } 11}); 12// 动画 13const instance = new mono.Animate({ 14 form: 0, 15 to:1, 16 dur:3000, 17 delay, 18 reverse:false, 19 repeat: 1, 20 easing, 21 onPlay, 22 onUpdate: (val) => { 23 // 获取路径上的点 24 const point = path.getPointAt(val); 25 // 设置实体位置 26 entity.setPosition(point); 27 }, 28 onDone, 29}); 30instance.play();
动画效果:
至此,直线路径动画就实现了。那么现在想想,现实场景中不可能只有直线运动这种场景,比如小车巡检,就属于一个折线场景,那么我们就需要使用折线动画来完成。
折线路径动画
小车在房间内不间断的通过巡检监控,记录设备状态及检测相关数据。模拟小车巡检动画,我们需要采集巡检小车核心点位:A、B、C、D。同样的将ABCD组合成路径,不停的设置小车(模型)位置
1const points = [A, B, C, D]; 2// 创建路径 3let path = new mono.Path(); 4points.forEach(...); 5// 动画 6const instance = new mono.Animate({ 7 ... 8 onUpdate: (val) => { 9 // 获取路径上的点 10 const point = path.getPointAt(val); 11 // 设置实体位置 12 entity.setPosition(point); 13 }, 14 onDone, 15}); 16instance.play();
动画效果:折线路径
当然,巡检过程可能是一个循环的闭合路径,所以可以满足使用四个点创建闭环路径,行形成闭环路径动画
折线路径闭环动画
1const points = [A, B, C, D]; 2// 创建路径 3let path = new mono.Path(); 4points.forEach(...); 5// 闭环路径 6path.closePath() 7// 动画 8const instance = new mono.Animate({ 9 ... 10}); 11instance.play();
动画效果:闭环路径
上面的折线动画是完成了,但是转弯的时候似乎略显生硬,接下来我们再尝试如何让转弯更自然。
圆润的折线路径动画
其实很简单,在已有的折线动画基础上,对路径先进行一步拐角处理,让路径整体显得很趋于自然。
1const points = [A, B, C, D]; 2// 创建路径 3let path = new mono.Path(); 4points.forEach(...); 5// 闭环路径 6path.closePath() 7// 获取平滑路径, 让转弯更自然 8path = mono.PathNode.prototype.adjustPath(path, 20, 1); 9// 动画 10const instance = new mono.Animate({ 11 ... 12}); 13instance.play();
动画效果:转弯更加的自然
自然弯道.gif
实现了圆润的折线路径动画后,貌似看起来已经完工了。其实再仔细观察下,可以发现,在转弯的时候,模型没有同步转向,那么我们需要如何处理呢。
模型与路径动画同步旋转
在折线动画中,将模型绕对应的旋转旋转方向即可
1const points = [A, B, C, D]; 2// 创建路径 3let path = new mono.Path(); 4points.forEach(...); 5// 闭环路径 获取平滑路径, 让转弯更自然 6... 7// 旋转向量 8const rotate = new mono.Vec3(); 9// 动画 10const instance = new mono.Animate({ 11 onUpdate: (val) => { 12 // 位置 13 ... 14 // 模型同步旋转 15 const tangent = path.getTangentAt(val); 16 var normal = new mono.Vec3(0, 0, -1); 17 rotate.rotationTowards(normal, tangent); 18 entity.setRotation(rotate); 19 } 20}); 21instance.play();
动画效果:可以看到小车上的摄像头是一直朝向设备
同步.gif
那么,直线路径动画和折线路径动画介绍完了。从上面动画截图中可以看出,我们是在一个固定的位置查看动画,那么,能让镜头沿着路径一起移动么
镜头沿路径动画一起移动
显然,镜头是可以沿着路径同时移动的。通常用于巡航(自动巡检)中.主要是在折线动画的基础上,同步设置镜头动画的位置和朝向点。
1const points = [....]; 2// 创建路径 3let path = new mono.Path(); 4points.forEach(...); 5// 闭环路径 获取平滑路径, 让转弯更自然 6... 7// 镜头初始位置 8 const pos = camera.p(), 9 target = camera.t(); 10 const length = pos.clone().sub(target).length(); 11// 动画 12const instance = new mono.Animate({ 13 onUpdate: (val) => { 14 // 模型 15 ... 16 // 镜头沿路径动画 17 const tangent = path.getTangentAt(value); 18 point = path.getPointAt(value); 19 ntarget = point.clone().add(tangent.multiplyScalar(length)); 20 camera.p(point); 21 camera.lookAt(ntarget); 22 } 23}); 24instance.play();
动画效果:
既然能让镜头沿着路径同步移动,那么是否能让镜头与路径保持平行移动呢
镜头与路径保持平行一起移动
保持平行移动,其实是在点位的基础上,将镜头位置设置到对应距离点位置。以下是基于动画链完成的某流水线作业动画,需要路径动画同时,镜头同步移动。对动画链有兴趣的可参考前文《基于路径集合的三维动画链》
1const points = [....]; 2// 创建路径 3let path = new mono.Path(); 4points.forEach(...); 5// 闭环路径 获取平滑路径, 让转弯更自然 6... 7// 镜头相对于目标动画模型的距离 8const dis = 550 9// 动画 10const instance = new mono.Animate({ 11 onUpdate: (val) => { 12 // 模型 13 ... 14 // 镜头平行X轴动画 15 point = path.getPointAt(value); 16 camera.setPosition(point.clone().add(new mono.Vec3(0, dis, dis))); 17 camera.lookAt(point); 18 } 19}); 20instance.play();
动画效果:
案例说明
上面举例说明动画的示意图,来自两个案例,一个是地铁站三维可视化,可以认为是一个轨道交通方面的;另外一个是实验室车间 流水线可视化,主要用于流水线,设备监控等三维可视化呈现。
结语
至此,路径动画已经介绍的差不多了。利用常用的动画能够让整个三维场景更丰满,写实。希望在项目中可以多多利用起来。