使用 Mapbox 创建电影路线动画

如何为路线跟踪场景设置动画

每个人都在 Mapbox 等上发推文。你看过介绍每个阶段的动画视频吗?平滑的摄像机移动、卫星图像和 3D 地形有助于让观众沉浸其中,并轻松了解运动员将克服的海拔差异和距离。

今天第17阶段进入比利牛斯山脉,戏剧性地攀登到詹姆斯邦德电影《明日不死》中的一个高海拔机场。

——地图盒(@Mapbox)

这一次,我将介绍制作这部动画所涉及的所有过程。① 从高处扩大路线周围区域② 用动画在地图上显示路线③ 用相机跟随动画路线的尖端④使相机平滑或缓慢旋转以创建视觉上更易于理解的场景⑤ 导出视频

本视频中的方法可以在 Mapbox GL JS 文档中找到用作参考。它在 3D 地形上进行类似的路线跟踪,但没有精确的相机控制。

路线显示

为了使玩家从编码的路线中移动动画,我们需要以小的增量更改线条的长度。

由于动画是静态图像的连续播放,因此挑战在于以编程方式构造每一帧,并与前一帧相比进行必要的更改。为此,请使用 window.requestAnimationFrame()。

更改线条的渐变属性以使线条随时间移动。

首先,通过将动画开始后经过的时间除以动画的预设时间来计算 animationPhase 值。这将为您从开始到结束的每一帧提供一个介于 0 和 1 之间的值。

然后用 setPaintProperty() 应用它。

const animationPhase = (currentTime - startTime) / duration;
...
map.setPaintProperty(
    "line",
    "line-gradient", [
        "step", ["line-progress"],
        "yellow",
        animationPhase,
        "rgba(0, 0, 0, 0)",
    ]
);

公式说“如果在当前进度点之前,将线上的每个点涂成黄色,如果在它之后,则将其涂成透明”。 animationPhase 每帧接近 1,因此每次调用此 setPaintProperty() 方法时,您都会看到另一小块笔画。

这是一个使用这种技术在三秒内逐渐移动简单的两点 LineString 的示例。

见笔克里斯·黄) 上.

运行相机

线条现在出现在每一帧。现在让我们让相机跟随。这里和 Mapbox GL JS 的三角函数是必须的。使用 turf.distance() 和 turf.along() 与 animationFrame 沿线的长度选择正确的点。

// アニメーションの前に、線の長さを計算します。
const pathDistance = turf.lineDistance(path);

...

// animationPhase に基づいて、パスに沿った距離を計算します。

const[lng, lat] = turf.along(path, pathDistance * animationPhase).geometry
    .coordinates;

控制相机·位置・海拔高度(你在哪里)·沥青・方向(你面向哪个方向?)需要准备四个在这个视频中,高度和俯仰是恒定的,所以我们只需要计算方位角和相机位置。摄像机的方向与路线无关,它以恒定的速率变化以产生微妙的电影旋转效果。

const bearing = startBearing - animationPhase * 200.0;

如果你知道方位角、高度、俯仰角和你想看的点,你可以用三角函数来猜测相机的位置。

const computeCameraPosition = (
 pitch,
 bearing,
 targetPosition,
 altitude,
 smooth = false
) => {
 var bearingInRadian = bearing / 57.29;
 var pitchInRadian = (90 - pitch) / 57.29;
 
 var lngDiff =
   ((altitude / Math.tan(pitchInRadian)) *
     Math.sin(-bearingInRadian)) /
   70000; // ~70km/degree longitude
 var latDiff =
   ((altitude / Math.tan(pitchInRadian)) *
     Math.cos(-bearingInRadian)) /
   110000 // 110km/degree latitude
 
 var correctedLng = targetPosition.lng + lngDiff;
 var correctedLat = targetPosition.lat - latDiff;
 
 const newCameraPosition = {
   lng: correctedLng,
   lat: correctedLat
 };
 
 ...
 
 return newCameraPosition
}

注意:从经度到米的转换取决于纬度。上面的函数使用了每度 70km 的固定换算,这对法国来说已经足够准确了,但不是到处都可以。

这是一个解释在 computeCameraPosition() 中进行的计算的视觉效果。下图显示了你想在地面上看到的地方、高度、方位角和俯仰角。相机的新位置计算为相对于目标位置的 x 和 y 偏移量。

▲ 使用 GeoGebra 创建的 3D 图表()

平滑

在到目前为止我向您展示的代码中,相机直接跟随包含急转弯的路径。在这种情况下,相机聚焦在直线的尖端,动画是模糊的。线性插值,也称为“relp”,可以通过防止相机运动在帧之间突然变化来平滑相机运动。

//https://codepen.io/ma77os/pen/OJPVrPを参照
function lerp(start, end, amt) {
 return (1 - amt) * start + amt * end
}

这个例子是使用跳跃函数平滑圆的一个很好的例子。当您移动鼠标时,请注意圆圈如何平滑地跟随指针。 Mapbox 使用相同的 lerp 函数为路由动画添加了平滑度。

看笔通过安德烈马托斯() 上.

放大地球

每个视频都以从高空飞入开始,包括在路线动画中的位置。. flyTo() 的退出状态和 FreeCamera API 控件的启动状态之间很难无缝转换,所以我们不能在这里使用它。相反,在初始地球视图和轨迹动画的开始视图之间转换相机.

将先前的位置和新的线提示传递给 lerp 函数将为您提供“更平滑”的新位置。也就是说,你想看到的线条的尖端可能不在画面的中心,但随着时间的推移,它往往会向中心移动,除非移动太突然。

导出视频

导出视频,Mapbox GL JS.它使用 mp4-encoder javascript 库并在每次渲染 Mapbox GL JS 画布时保存一个帧。

function frame() {
  // スタブ時間を 16.6ms 増やす(60fps)。
 
  now += 1000 / 60;
 
  mapboxgl.setNow(now);
 
  const pixels = encoder.memory().subarray(ptr); // エンコーダのメモリにアクセスする

 
  gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels); // エンコーダにピクセルを読み込む
 
  encoder.encodeRGBPointer(); // フレームをエンコードする
}
 
map.on("render", frame); // フレーム単位の録画の設定

屏幕显示的大小由地图容器的 CSS 控制。对于 16:9 的视频,以 1280 像素 x 720 像素的尺寸显示。对于方形视频(Instagram 等使用),尺寸为 1080 像素 x 1080 像素。

后期制作

动画完成后,组装这些帧并下载为 mp4。最后,输出 mp4 是 CLI 工具的被压缩使用

% ffmpeg -i my_video.mp4 my_video_compressed.mp4

用于创建这些动画的代码在 GitHub 上,并且可以被引用和重用。使用 FreeCamera API,从您的 Mapbox GL JS 项目中导出高质量视频,并在 Twitter 上发布标记和分享!

*这篇文章是是一篇翻译文章。

原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308628414.html

39人参与, 0条评论 登录后显示评论回复

你需要登录后才能评论 登录/ 注册