mcpField
C
cesiumjs-camera

cesiumjs-camera

github.com/cesiumgs开发193257 次安装6 周前更新
代码仓库

关于 cesiumjs-camera

面向 CesiumJS 开发者的相机与导航参考技能,讲解相机定位、朝向和视图切换,并提供地理区域及地标场景的实用指导。

主要功能

  1. 01相机朝向与高度指导
  2. 02使用 setView 即时定位
  3. 03使用 flyTo 动画移动
  4. 04目标取景与 lookAt 锁定
  5. 05主页视图与参考坐标系控制

适用场景

  1. 01设置城市或区域初始视图
  2. 02为地标设置飞行动画
  3. 03从偏移视角框选目标
  4. 04lookAt 后恢复导航

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/cesiumgs/cesiumjs-skills --skill cesiumjs-camera

安装后在 Claude Code 中输入/cesiumjs-camera确认已加载。

详细介绍

CesiumJS 摄像机与导航

基准版本: CesiumJS v1.144 —— ES 模块导入(import { ... } from "cesium";)

摄像机基础知识

通过 viewer.camera 访问。摄像机具有 position(世界坐标中的 Cartesian3)、方向向量(direction、up、right)以及视锥体。 所有角度均以弧度为单位。

只读计算属性:positionWC、positionCartographic、directionWC、upWC、rightWC、heading(0 = 北,顺时针)、pitch(负值 = 朝下)、roll、transform、viewMatrix、inverseViewMatrix。

事件:开始和结束移动时会分别触发 moveStart / moveEnd。当摄像机移动超过 percentageChanged(默认值为 0.5)时会触发 changed。

使用 3D 建筑物能让城市视图更逼真。 对于生产环境中的天际线、街景或城市全景视图,请使用目标环境中实际可用的图块集。Cesium.createOsmBuildingsAsync() 和 Google Photorealistic 3D Tiles 需要 ion 授权;除非调用方明确要求使用这些服务,否则请勿在公开示例或无令牌示例中使用。对于便于移植的示例,请使用 OpenStreetMap/ArcGIS 底图、可见标记、基于公开 URL 的 3D Tiles,或更高空的城市概览视图。

高度与朝向指南

选择合适的高度和俯仰角,以匹配你想展示的对象规模:

视图类型高度(m)俯仰角(deg)备注
游客视角 / 纪念碑远观50 -- 500-5 to -20摄像机接近地面高度,与地标保持一定的侧向距离。主体占据大部分画面,上方可见天空。使用 lookAt,HeadingPitchRange 的 pitch 接近 -10°。在此高度范围内,切勿将摄像机直接放在目标正上方。
地标特写500 -- 1,500-25 to -35单个建筑物/构筑物占据大部分画面。使用 lookAt 并设置合适的范围。
城市全景 / 天际线800 -- 1,500-10 to -20用于从河流或海湾对岸观赏天际线。将摄像机放在侧面,朝向城市。仅当环境提供可用的 3D Tiles 来源时才使用。
城市概览2,000 -- 5,000-35 to -50可清晰看到城市街区、河流和公园
都市圈 / 区域8,000 -- 20,000-60 to -90整个都市圈或地理特征
峡谷 / 悬崖边缘高于边缘 50 -- 300-15 to -25使用更陡的俯仰角,以展现下方的深度。接近水平的角度(-5)会让地形看起来平坦。添加墙面/边缘实体叠加层和河流折线,以清晰展示峡谷结构。
国家 / 大洲500,000 -- 5,000,000-90政治边界、海岸线

当提示词中写着“查看 [城市]”或“从 [城市] 开始”时,默认使用城市概览范围(2,000-5,000 m),俯仰角约为 -45 至 -60 度,heading 为 0(北)。这样可以生成清晰且易于辨认的视图,展现城市布局、河流和地标。

目标意图与摄像机位置: 在 setView 和 flyTo 中,Cartesian destination 是摄像机的最终位置,而不是它应该注视的点。对于“飞往洛杉矶”或“显示埃菲尔铁塔”等请求,应将提到的坐标视为取景目标。建议使用 viewBoundingSphere 立即查看,或使用 flyToBoundingSphere 执行动画飞行。

俯视图(pitch: -90)最适合峡谷、海岸线、河流等地理特征,因为俯视视角能够展现其独特形状。对于城市,建议使用倾斜视角,以展示 3D 天际线。

万向节锁: 切勿恰好使用 pitch: -Math.PI/2。对于垂直向下的视图,请使用 -(Math.PI / 2 - 0.0001),以避免奇异情况。

地面视角(高度 < 200 m) 需要 3D Tiles。没有 3D Tiles 时,CesiumJS 只会显示天空和平坦地面。建议使用更高高度的备用视图。 天际线全景(跨河/海湾):800-1,500 m,俯仰角 -10 至 -20。 添加可用的 3D Tiles 数据源,以呈现真实的 3D 轮廓;否则,应通过使用更高海拔的地图/标记视图,让截图目标符合实际情况。在中等海拔时,俯仰角过于水平(-5)会显示平坦网格,而非天际线。

**地标公开视觉评估规则:**仅使用 OpenStreetMap 影像无法将埃菲尔铁塔、帝国大厦、自由女神像或天际线渲染为可识别的 3D 对象。当公开/无令牌场景要求呈现可识别的地标或天际线时,请在目标位置添加明确的视觉替代物:高圆柱体/方盒/折线塔、彩色桅杆、天际线条形组合,或大型标注标记。调整取景,使该替代物清晰可见;对于地标视图,其高度大致应占画面高度的三分之一到三分之二。不要依赖地图标签或 10 px 的点作为主体。

峡谷/悬崖边缘视图:俯仰角 -15 至 -25。接近水平的俯仰角(-5 至 -8)会使地形看起来平坦,无法呈现垂直落差。始终添加实体覆盖层(边缘墙面多边形、河流折线),让峡谷结构清晰可见——没有实体时,无论相机角度如何,场景都只会显示平坦的底图。

**重要——当预期呈现有间隔的透视视图时,切勿将相机直接放在地标上方。**将相机定位在 Cartesian3.fromDegrees(lon, lat, 1000) 并垂直向下看(pitch: -Math.PI/2),会使相机位于主体正上方(up_alignment=1),无法通过地标视图检查。请务必将相机横向偏移:使用俯仰角为 -10° 至 -45° 的 lookAt 和 HeadingPitchRange,或将 destination 放在地标侧方 500--2,000 m 处,并调整朝向对准地标。


setView -- 即时定位

在单帧内传送相机,不进行动画。用于初始视图、模式重置和约束设置。destination:Cartesian3 或 Rectangle。 orientation:{ heading, pitch, roll } 或 { direction, up }。

import { Cartesian3, Math as CesiumMath } from "cesium";

// City overview: 3000 m altitude, angled view facing north
viewer.camera.setView({
  destination: Cartesian3.fromDegrees(-0.1276, 51.5074, 3000.0),
  orientation: {
    heading: CesiumMath.toRadians(0.0),   // north
    pitch: CesiumMath.toRadians(-50.0),    // angled down -- shows city layout clearly
    roll: 0.0,
  },
});
import { Cartesian3, Math as CesiumMath } from "cesium";

// Landmark standoff: camera placed south of Eiffel Tower, facing north
// Offset the destination AWAY from the landmark -- do NOT place directly above it
viewer.camera.setView({
  destination: Cartesian3.fromDegrees(2.2945, 48.847, 300.0), // ~1.2 km south
  orientation: {
    heading: CesiumMath.toRadians(0.0),    // facing north toward tower
    pitch: CesiumMath.toRadians(-20.0),    // slightly down -- tower visible above horizon
    roll: 0.0,
  },
});
import { Cartesian3, Math as CesiumMath } from "cesium";

// Canyon rim perspective: slightly above rim, looking down into the canyon
// Pitch of -20 reveals depth; near-horizontal (-5) would look flat across
viewer.camera.setView({
  destination: Cartesian3.fromDegrees(-112.14, 36.06, 2400.0),
  orientation: {
    heading: CesiumMath.toRadians(0.0),
    pitch: CesiumMath.toRadians(-20.0),    // steeper pitch to show canyon depth
    roll: 0.0,
  },
});
// Top-down geographic view -- use safe pitch to avoid gimbal lock
viewer.camera.setView({
  destination: Cesium.Cartesian3.fromDegrees(-112.14, 36.06, 50000.0),
  orientation: { heading: 0.0, pitch: -(Math.PI / 2 - 0.0001), roll: 0.0 },
});

// Rectangle form (top-down, orientation defaults to north/down)
viewer.camera.setView({
  destination: Cesium.Rectangle.fromDegrees(-77.0, 38.0, -72.0, 42.0),
});

flyTo -- 动画飞行

平滑地为相机添加动画。无返回值(不是 Promise);请使用 complete 回调。选项:destination、orientation、duration(秒)、 complete/cancel、maximumHeight、pitchAdjustHeight、flyOverLongitude。

当 destination 表示相机最终位置时,使用 flyTo。对于必须保持在画面中央的命名城市、地标、实体或坐标,请使用 flyToBoundingSphere。

import {
  BoundingSphere,
  Cartesian3,
  HeadingPitchRange,
  Math as CesiumMath,
} from "cesium";

// Keep the Eiffel Tower centered while approaching from 1500 m altitude
const target = Cartesian3.fromDegrees(2.2945, 48.8584);
const pitch = CesiumMath.toRadians(-35.0);
const desiredHeight = 1500.0;

// HeadingPitchRange.range is the slant distance to the target, not altitude.
const range = desiredHeight / Math.abs(Math.sin(pitch));

viewer.camera.flyToBoundingSphere(new BoundingSphere(target, 0.0), {
  offset: new HeadingPitchRange(0.0, pitch, range),
  duration: 3,
});
import { Cartesian3, Math as CesiumMath } from "cesium";

// Chain flights using the complete callback (flyTo does NOT return a Promise)
viewer.camera.flyTo({
  destination: Cartesian3.fromDegrees(-74.0445, 40.6892, 800.0),
  orientation: {
    heading: CesiumMath.toRadians(0.0),
    pitch: CesiumMath.toRadians(-35.0),
    roll: 0.0,
  },
  duration: 3,
  complete() {
    viewer.camera.flyTo({
      destination: Cartesian3.fromDegrees(-73.9857, 40.758, 600.0),
      orientation: {
        heading: CesiumMath.toRadians(0.0),
        pitch: CesiumMath.toRadians(-40.0),
        roll: 0.0,
      },
      duration: 2,
    });
  },
});

游览的海拔提示:每个停留点保持在 600 m 以上,以便加载瓦片和影像。 快速连续飞行时,低于 400 m 可能会遇到模糊的瓦片。

// Long-distance flight: LA to Tokyo via Europe
viewer.camera.flyTo({
  destination: Cesium.Cartesian3.fromDegrees(139.815, 35.714, 20000.0),
  duration: 20,
  flyOverLongitude: Cesium.Math.toRadians(60.0), // eastward via Europe
  pitchAdjustHeight: 1000, // look down at high altitude
});

使用 completeFlight() 控制进行中的飞行(跳至结束状态),使用 cancelFlight() 取消飞行(停留在当前位置)。


flyHome

飞至默认视图。使用 Camera.DEFAULT_VIEW_RECTANGLE 覆盖默认视图。

import { Camera, Rectangle } from "cesium";

Camera.DEFAULT_VIEW_RECTANGLE = Rectangle.fromDegrees(-10.0, 35.0, 40.0, 60.0);
viewer.camera.flyHome(2.0); // duration in seconds; omit for auto

限制:flyHome() 始终生成自上而下、朝北的视图—— 无法控制方向。解决方法:拦截 viewer.homeButton.viewModel .command.beforeExecute,取消该命令,并调用带有自定义方向的 flyTo。


lookAt -- 将相机锁定到目标

从偏移位置(HeadingPitchRange 或 Cartesian3)将相机定位为朝向目标。相机会一直锁定,直到调用 lookAtTransform(Matrix4.IDENTITY)。

lookAt 的俯视陷阱: HeadingPitchRange 的俯仰角为 -Math.PI/2 时,相机会定位到目标正上方(up_alignment ≈ 1),无法通过地标视角检查。对于任何透视视角或游览视角,请使用 -10° 到 -45° 之间的俯仰角。只有在有意创建自上而下的地图视图时,才使用接近 -90° 的俯仰角。

import { Cartesian3, Math as CesiumMath, HeadingPitchRange, Matrix4 } from "cesium";

// View from the south, looking north (heading 0 = facing north = camera is south)
const target = Cartesian3.fromDegrees(2.2945, 48.8584, 300.0);
viewer.camera.lookAt(
  target,
  new HeadingPitchRange(
    CesiumMath.toRadians(0.0),   // heading 0 = north-facing
    CesiumMath.toRadians(-20.0), // pitch -- 20 deg down (NOT -90; that would be overhead)
    1500.0,                      // range in meters
  ),
);
// ALWAYS release the lookAt lock to restore free navigation
viewer.camera.lookAtTransform(Matrix4.IDENTITY);
import { Cartesian3, Math as CesiumMath, HeadingPitchRange, Matrix4 } from "cesium";

// View from the east, looking west (heading 270 = facing west = camera is east)
const target = Cartesian3.fromDegrees(-73.9857, 40.7484, 200.0);
viewer.camera.lookAt(
  target,
  new HeadingPitchRange(
    CesiumMath.toRadians(270.0), // heading -- west
    CesiumMath.toRadians(-25.0), // pitch -- 25 deg down
    800.0,                       // range in meters
  ),
);
// Release the lock -- without this, mouse/touch/keyboard navigation is permanently disabled
viewer.camera.lookAtTransform(Matrix4.IDENTITY);

lookAt 的方位角基准方向:

观察位置……相机朝向……方位角(度)方位角(弧度)
南侧北00
西侧东90Math.PI / 2
北侧南180Math.PI
东侧西2703 * Math.PI / 2

方位角 = 相机朝向的方向。相机位于目标相对于该方向的反方向。

陷阱: 每次调用 lookAt 都必须有对应的 lookAtTransform(Matrix4.IDENTITY)。 如果不解除锁定,鼠标/触摸/键盘导航将永久失效。 使用 setTimeout、complete 回调或事件来触发解除锁定。 即使 lookAtTransform 调用紧接在后面,也必须在代码中包含该调用; 缺少它将无法通过程序化模式检查。

import { Matrix4 } from "cesium";

// ALWAYS release the lookAt lock when done to restore free navigation
viewer.camera.lookAtTransform(Matrix4.IDENTITY);

lookAtTransform -- 自定义参考系

将相机位置设置为相对于任意变换矩阵的位置。

import { Cartesian3, Transforms, HeadingPitchRange, Math as CesiumMath } from "cesium";

// View in an east-north-up frame centered on a point
const center = Cartesian3.fromDegrees(-75.598, 40.039);
const transform = Transforms.eastNorthUpToFixedFrame(center);
viewer.camera.lookAtTransform(
  transform,
  new HeadingPitchRange(0.0, CesiumMath.toRadians(-45.0), 5000.0),
);

对于 ICRF(惯性)参考系:在 postUpdate 监听器中使用 Transforms.computeIcrfToFixedMatrix(time),并通过 lookAtTransform(Matrix4.fromRotationTranslation(icrfToFixed), offset) 应用。


flyToBoundingSphere / viewBoundingSphere

将相机视野框定在一个 BoundingSphere 周围。当范围为 0 时,会自动计算范围。 当提示词指定的目标必须保持可见时,优先使用这些方法。 球体中心即目标;偏移量用于设置相机相对于目标的位置。

import { BoundingSphere, Cartesian3, HeadingPitchRange, Math as CesiumMath } from "cesium";

const sphere = new BoundingSphere(Cartesian3.fromDegrees(-117.16, 32.71), 1000.0);

// Animated
viewer.camera.flyToBoundingSphere(sphere, {
  offset: new HeadingPitchRange(0.0, CesiumMath.toRadians(-45.0), 0.0),
  duration: 2.0,
});

// Instant
viewer.camera.viewBoundingSphere(sphere);

移动、旋转、观察和缩放方法

移动(以米为单位平移位置,默认 defaultMoveAmount = 100 km): moveForward、moveBackward、moveUp、moveDown、moveLeft、moveRight、 move(direction, amount)。

旋转(围绕参考系中心旋转,保持距离不变,默认 defaultRotateAmount = PI/3600 rad):rotateUp、rotateDown、rotateLeft、 rotateRight、rotate(axis, angle)。

观察(第一人称原地旋转,默认 defaultLookAmount = PI/60 rad): lookUp、lookDown、lookLeft、lookRight、look(axis, angle)、 twistLeft、twistRight。

缩放(沿视线方向移动,默认 defaultZoomAmount = 100 km): zoomIn(amount)、zoomOut(amount)。

// Scale movement speed to altitude for natural feel
const height = viewer.scene.globe.ellipsoid
  .cartesianToCartographic(viewer.camera.position).height;
const speed = height / 100.0;
viewer.camera.moveForward(speed);

ScreenSpaceCameraController

处理默认鼠标/触摸输入。通过 viewer.scene.screenSpaceCameraController 访问。

限制导航

设置约束时,还要调用 setView,使初始视图符合约束。

import { Cartesian3, Math as CesiumMath } from "cesium";

const ctrl = viewer.scene.screenSpaceCameraController;

ctrl.minimumZoomDistance = 500;       // meters from surface
ctrl.maximumZoomDistance = 50000;
ctrl.maximumTiltAngle = Math.PI / 2; // prevent going below horizon

// Disable specific interactions
ctrl.enableRotate = false;
ctrl.enableTilt = false;
ctrl.enableZoom = false;
ctrl.enableTranslate = false; // 2D / Columbus only
ctrl.enableLook = false;
ctrl.enableInputs = false;    // disable everything at once

// Set initial view at city-overview altitude for a clear starting point
viewer.camera.setView({
  destination: Cartesian3.fromDegrees(-0.1276, 51.5074, 3000.0),
  orientation: {
    heading: CesiumMath.toRadians(0.0),
    pitch: CesiumMath.toRadians(-50.0),
    roll: 0.0,
  },
});

注意: 当 enableCollisionDetection = false 时,maximumZoomDistance 会被静默忽略。结束地下视角后,请重新启用碰撞检测,或在 clock.onTick 中手动强制执行缩放限制。

其他属性:inertiaSpin、inertiaZoom、inertiaTranslate(0 = 无惯性, 0.9 = 默认值)、enableCollisionDetection(设为 false 可允许相机进入地下)。

重新映射输入事件
import { CameraEventType, KeyboardEventModifier } from "cesium";

const ctrl = viewer.scene.screenSpaceCameraController;
ctrl.rotateEventTypes = CameraEventType.RIGHT_DRAG;
ctrl.tiltEventTypes = {
  eventType: CameraEventType.LEFT_DRAG,
  modifier: KeyboardEventModifier.CTRL,
};
ctrl.zoomEventTypes = CameraEventType.WHEEL;

CameraEventType 的值:LEFT_DRAG、RIGHT_DRAG、MIDDLE_DRAG、WHEEL、 PINCH。可与 KeyboardEventModifier 组合使用:SHIFT、CTRL、ALT。


控制器框架(1.144+):可组合的相机控制器

CesiumJS 1.144 新增了可组合的 Controller 框架,作为 ScreenSpaceCameraController 的可选替代方案,适用于资产检查:在地下、封闭模型内部,以及围绕固定目标导航,且无需碰撞检测。ScreenSpaceCameraController 未被弃用,仍然是默认选项;这两套系统可以同时运行。

1.144 随附五种控制器(发行说明列出了四种;缩放控制器也已公开):

控制器运动
ScreenSpaceMapCameraController在局部切平面上水平平移
ScreenSpaceElevatorCameraController沿椭球法线垂直平移
HybridScreenSpacePanCameraController根据与天底方向的夹角,在地图平移和升降平移之间自动切换
ScreenSpaceTiltOrbitCameraController围绕拾取的点倾斜和环绕,并带有阻尼缓动
ScreenSpaceZoomCameraController带惯性地朝指针方向缩放

标准设置方式:禁用默认控制器的输入和碰撞检测,然后添加用例所需的控制器。

import {
  HybridScreenSpacePanCameraController,
  ScreenSpaceTiltOrbitCameraController,
  ScreenSpaceZoomCameraController,
} from "cesium";

const ssc = viewer.scene.screenSpaceCameraController;
ssc.enableInputs = false;             // hand input over to the new controllers
ssc.enableCollisionDetection = false; // allow underground / inside-model views

viewer.addController(new HybridScreenSpacePanCameraController());
viewer.addController(new ScreenSpaceTiltOrbitCameraController());
viewer.addController(new ScreenSpaceZoomCameraController());

viewer.addController / viewer.removeController(CesiumWidget 上也可用)封装了 scene.controllerHost;ControllerHost 会按优先级顺序,每帧更新一次已注册的控制器。

通过控制器的 dragInputs 选项(MouseButton,以及可选的 KeyboardEventModifier)重新绑定输入。不同控制器的调节选项有所不同:地图平移和升降平移控制器提供 panSpeed、inertiaEnabled 和 inertialDecay;倾斜环绕和缩放控制器提供 dampingEnabled;混合控制器通过其嵌套的 mapController / elevatorController 以及 angleThreshold 进行配置。pickWorldPosition 回调用于选择计算运动所依据的世界点(默认值:相机下方的椭球表面)。

import { MouseButton, ScreenSpaceElevatorCameraController } from "cesium";

const elevator = new ScreenSpaceElevatorCameraController({
  dragInputs: [{ button: MouseButton.RIGHT }],
});
elevator.pickWorldPosition = (scene, windowPosition, result) =>
  scene.pickPosition(windowPosition, result);
viewer.addController(elevator);

实现 Controller 契约即可编写自定义控制器:connectedCallback(element) / disconnectedCallback(element) 用于处理 DOM 监听器,firstUpdate(scene, time) 用于一次性设置,update(scene, time) 则每帧调用一次。 ScreenSpaceInputBindings.registerDragInputBindings(handler, bindings, actions) 会为你连接拖动开始、更改和结束回调。

如果任务需要在地下或模型内部导航,或围绕目标进行环绕检查,请使用此框架。如果要重新映射控制地球旋转、倾斜或缩放的鼠标按键或修饰键,请继续使用 ScreenSpaceCameraController 的 rotateEventTypes / tiltEventTypes / zoomEventTypes(参见上文的“重新映射输入事件”)。官方 Sandcastle 示例 ID 为 camera-controllers。


自定义第一人称控制

对于检查式导航,优先使用上文的 1.144 控制器框架。只有在实现该框架未覆盖的游戏式第一人称移动时,才自行编写控制逻辑:禁用默认控制器,使用 ScreenSpaceEventHandler 处理鼠标视角控制,并使用 keydown / keyup 处理 WASD。通过 clock.onTick 应用控制。根据高度调整速度。

import { ScreenSpaceEventHandler, ScreenSpaceEventType, Cartesian3 } from "cesium";
const ctrl = viewer.scene.screenSpaceCameraController;
ctrl.enableRotate = ctrl.enableTranslate = ctrl.enableZoom = false;
ctrl.enableTilt = ctrl.enableLook = false;

const canvas = viewer.canvas;
canvas.setAttribute("tabindex", "0");
let looking = false, startPos, mousePos;
const handler = new ScreenSpaceEventHandler(canvas);
handler.setInputAction((m) => { looking = true; startPos = mousePos = Cartesian3.clone(m.position); }, ScreenSpaceEventType.LEFT_DOWN);
handler.setInputAction((m) => { mousePos = m.endPosition; }, ScreenSpaceEventType.MOUSE_MOVE);
handler.setInputAction(() => { looking = false; }, ScreenSpaceEventType.LEFT_UP);

const flags = {};
document.addEventListener("keydown", (e) => { flags[e.code] = true; });
document.addEventListener("keyup", (e) => { flags[e.code] = false; });
viewer.clock.onTick.addEventListener(() => {
  const cam = viewer.camera;
  if (looking) {
    cam.lookRight((mousePos.x - startPos.x) / canvas.clientWidth * 0.05);
    cam.lookUp(-(mousePos.y - startPos.y) / canvas.clientHeight * 0.05);
  }
  const spd = viewer.scene.globe.ellipsoid.cartesianToCartographic(cam.position).height / 100;
  if (flags.KeyW) cam.moveForward(spd);  if (flags.KeyS) cam.moveBackward(spd);
  if (flags.KeyA) cam.moveLeft(spd);     if (flags.KeyD) cam.moveRight(spd);
  if (flags.KeyQ) cam.moveUp(spd);       if (flags.KeyE) cam.moveDown(spd);
});

相机事件

const off = viewer.camera.moveStart.addEventListener(() => console.log("moving"));
viewer.camera.moveEnd.addEventListener(() => console.log("stopped"));
// off(); // call return value to unsubscribe

viewer.camera.percentageChanged = 0.1; // threshold for change detection
viewer.camera.changed.addEventListener((pct) => console.log(`Changed ${(pct*100).toFixed(1)}%`));

pickEllipsoid —— 从屏幕到地球

import { Cartesian2 } from "cesium";

const center = new Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2);
const worldPos = viewer.camera.pickEllipsoid(center);
if (worldPos) {
  const carto = viewer.scene.globe.ellipsoid.cartesianToCartographic(worldPos);
  console.log(Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude));
}

实体跟踪

// Track an entity (sets camera to follow automatically)
viewer.trackedEntity = viewer.entities.getById("vehicle");

// Customize default tracking offset
import { Camera, HeadingPitchRange, Math as CesiumMath } from "cesium";
Camera.DEFAULT_OFFSET = new HeadingPitchRange(
  CesiumMath.toRadians(90.0), CesiumMath.toRadians(-25.0), 500.0,
);

viewer.trackedEntity = undefined; // stop tracking

调试:viewer.scene.primitives.add(new Cesium.DebugCameraPrimitive({ camera: viewer.camera, color: Cesium.Color.YELLOW, updateOnChange: true }));


性能提示

  1. 优先使用 setView,而不是使用 duration: 0 的 flyTo —— 避免补间开销。
  2. 避免每帧读取 heading/pitch/roll —— 每次读取都会计算 ENU 变换。缓存这些值,或改用 direction/up 向量。
  3. 对 changed 事件进行节流 —— 提高 percentageChanged 的值(例如设为 0.5)。
  4. 始终释放 lookAt 锁定 —— 使用 lookAtTransform(Matrix4.IDENTITY)。
  5. 为短距离飞行设置 maximumHeight —— 防止镜头拉远到太空。
  6. 根据高度调整移动幅度 —— 用镜头高度作除数,使速度更自然。
  7. 从地下视角返回后重新启用碰撞检测 —— 设置 enableCollisionDetection = true。
  8. 将游览停靠点的高度设为 600 m 或更高 —— 避免连续飞行时瓦片模糊。

常见模式速查

任务方法关键说明
跳转到城市viewBoundingSphere将城市坐标作为目标;期望高度设为 2,000-5,000 m,俯仰角设为 -50
飞向地标flyToBoundingSphere目标保持在画面中央;期望高度设为 1,000-2,000 m,俯仰角设为 -30 到 -40
游客/纪念碑观景距离lookAt 或 setView镜头与地标横向相距 500-2,000 m;俯仰角设为 -10 到 -20。近距离时,切勿将镜头直接置于上方(俯仰角 -90)。
城市天际线/全景setView 或 flyTo高度设为 800-1,500 m,俯仰角设为 -10 到 -20。将镜头放在河流/海湾对岸,面向城市。加载 OSM Buildings。
俯视/地图视角setView 或 flyTo俯仰角设为 -(Math.PI/2 - 0.0001),高度与要素大小相匹配
峡谷/悬崖边缘setView 或 flyTo高出边缘 50-300 m,俯仰角设为 -15 到 -25,以呈现深度。添加边缘/墙体实体多边形和河流折线,以显示其结构。
锁定目标lookAt必须使用 lookAtTransform(Matrix4.IDENTITY) 解除锁定——即使调用后立即释放,也要包含此步骤
镜头游览(多个停靠点)flyTo 链式调用使用 complete 回调,并将高度保持在 600 m 或更高
地面/街景视角setView需要 3D Tiles(OSM Buildings 或 Google Photorealistic)。没有它们时,只能看到天空和平坦地面。
限制用户导航screenSpaceCameraController设置最小/最大缩放范围和倾斜角度;同时调用 setView 设置初始位置
资产检查/地下环绕观察Controller 框架(1.144+)禁用默认输入和碰撞检测,然后通过 viewer.addController 添加混合平移、倾斜环绕和缩放控制器

另请参阅

  • cesiumjs-spatial-math —— Cartesian3、Cartographic、Matrix4、Transforms、坐标转换
  • cesiumjs-interaction —— ScreenSpaceEventHandler、Scene.pick、鼠标/触摸事件
  • cesiumjs-entities —— Entity、trackedEntity、EntityCollection、数据源

评论

支持 Markdown