1. 程式人生 > >Cesium官方教程6--相機

Cesium官方教程6--相機

相機(Camera)

相機控制了場景的觀察視角。有很多相機操控方法,比如旋轉、縮放、平移以及飛行定位。Cesium預設支援使用滑鼠和觸控事件控制相機。Cesium也提供了一套可程式設計的相機控制API。這篇教程就是介紹相機相關知識,以及API。

快速開始

開始示例。開啟Sandcastle的 Hello World 示例。預設場景按照下述方式處理滑鼠和處理事件:

左鍵單擊和拖拽 - 沿著地球表面平移(調整相機位置).
右鍵單擊和拖拽 - 相機放大縮小(調整相機距離).
滾輪 - 相機放大縮小(調整相機距離).
中間按下和拖拽 - 圍繞地球表面旋轉相機(調整相機方向)。
使用setView

方法可以設定相機位置和朝向。需要傳遞的引數是目標點和朝向。位置引數需要傳一個Cartesian3 或者 Rectangle類的例項。朝向要麼是 heading/pitch/roll 尤拉角 ,要麼是 朝向向量/向上向量。heading/pitch/roll 的單位是弧度。Heading是當前方向 由北向東旋轉的角度。Pitch 是方向和水平平面的夾角。Pitch為正 表示方向向量指向水平平面上方,反之表示方向向量指向平面下方。Roll 是方向向量以正東方向為軸的旋轉角度。比如我們可以按照下面的程式碼設定相機:

camera.setView({
    destination : new Cesium.Cartesian3(x, y, z),
    orientation: {
        heading : headingAngle,
        pitch : pitchAngle,
        roll : rollAngle
    }
});

位置屬性也可以設定為一個矩形區域:

viewer.camera.setView({
    destination : Cesium.Rectangle.fromDegrees(west, south, east, north), orientation: { heading : headingAngle, pitch : pitchAngle, roll : rollAngle } }); 

所有引數都是可選的,如果哪個引數沒有設定或者設定undefined,那麼就使用當前相機的對應屬性去計算。
把相機垂直向下俯視,Heading設定為正北方向是最常見的設定引數:

camera.setView({
    destination : Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
    orientation: {
        heading : 0.0, pitch : -Cesium.Math.PI_OVER_TWO, roll : 0.0 } }); 

自定義相機的 滑鼠\鍵盤事件

下來,我們建立一個自定義的相機控制方式,滑鼠位置控制了相機前進方向,使用鍵盤來控制相機的前進,後退、向左、向右、向上、向下移動。先把預設的相機事件禁用。在var viewer = ...之後新增下面的程式碼:

var scene = viewer.scene;
var canvas = viewer.canvas;
canvas.setAttribute('tabindex', '0'); // needed to put focus on the canvas canvas.onclick = function() { canvas.focus(); }; var ellipsoid = viewer.scene.globe.ellipsoid; // 禁用預設相機控制事件 scene.screenSpaceCameraController.enableRotate = false; scene.screenSpaceCameraController.enableTranslate = false; scene.screenSpaceCameraController.enableZoom = false; scene.screenSpaceCameraController.enableTilt = false; scene.screenSpaceCameraController.enableLook = false; 

下來,我們建立幾個變數記錄當前相機位置,和一些狀態變數來標記當前相機是如何移動。

var startMousePosition;
var mousePosition;
var flags = {
   looking : false, moveForward : false, moveBackward : false, moveUp : false, moveDown : false, moveLeft : false, moveRight : false }; 

增加一個事件處理器,當滑鼠左鍵點選的時候,儲存當前相機位置,並且設定looking狀態。

var handler = new Cesium.ScreenSpaceEventHandler(canvas);
handler.setInputAction(function(movement) { flags.looking = true; mousePosition = startMousePosition = Cesium.Cartesian3.clone(movement.position); }, Cesium.ScreenSpaceEventType.LEFT_DOWN); handler.setInputAction(function(movement) { mousePosition = movement.endPosition; }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); handler.setInputAction(function(position) { flags.looking = false; }, Cesium.ScreenSpaceEventType.LEFT_UP); 

增加一個鍵盤事件,去切換相機移動的狀態型別,根據下面鍵盤配置來設定:

  • w 前進
  • s 後退
  • a 向左移動
  • d 向右移動
  • q 向上移動
  • e 向下移動
function getFlagForKeyCode(keyCode) {
    switch (keyCode) { case 'W'.charCodeAt(0): return 'moveForward'; case 'S'.charCodeAt(0): return 'moveBackward'; case 'Q'.charCodeAt(0): return 'moveUp'; case 'E'.charCodeAt(0): return 'moveDown'; case 'D'.charCodeAt(0): return 'moveRight'; case 'A'.charCodeAt(0): return 'moveLeft'; default: return undefined; } } document.addEventListener('keydown', function(e) { var flagName = getFlagForKeyCode(e.keyCode); if (typeof flagName !== 'undefined') { flags[flagName] = true; } }, false); document.addEventListener('keyup', function(e) { var flagName = getFlagForKeyCode(e.keyCode); if (typeof flagName !== 'undefined') { flags[flagName] = false; } }, false); 

現在當這些狀態變數設定為true的時候,就需要更新相機的位置。使用下面程式碼增加一個onTick 事件:

viewer.clock.onTick.addEventListener(function(clock) {
    var camera = viewer.camera;
});

接著,確保相機一直是沿著滑鼠方向。把下面的程式碼新增到上面的事件處理函式裡:

if (flags.looking) {
    var width = canvas.clientWidth;
    var height = canvas.clientHeight;

    // 滑鼠點選時,這個座標計算得到0,0.
    var x = (mousePosition.x - startMousePosition.x) / width; var y = -(mousePosition.y - startMousePosition.y) / height; var lookFactor = 0.05; camera.lookRight(x * lookFactor); camera.lookUp(y * lookFactor); } 

lookRightlookUp方法需要一個旋轉角度的引數,單位是弧度。 我們把滑鼠位置變換到了-1,1之間,0,0座標就是視窗(canvas)的中心點。把滑鼠和中心位置之間的距離當作旋轉的速度。距離中心越近旋轉越慢,距離越遠旋轉越快。
下來我們把相機移動的程式碼也加上:

// 依據相機所在絕對高度來決定相機的執行速度 
var cameraHeight = ellipsoid.cartesianToCartographic(camera.position).height;
var moveRate = cameraHeight / 100.0;

if (flags.moveForward) { camera.moveForward(moveRate); } if (flags.moveBackward) { camera.moveBackward(moveRate); } if (flags.moveUp) { camera.moveUp(moveRate); } if (flags.moveDown) { camera.moveDown(moveRate); } if (flags.moveLeft) { camera.moveLeft(moveRate); } if (flags.moveRight) { camera.moveRight(moveRate); } 

moveForward, moveBackward,moveUp, moveDown, moveLeft, moveRight 這些方法需要傳一個移動距離引數,單位為米。通過相機當前位置的絕對高程決定每次按下按鍵的移動距離。距離地面越近,每次移動的位置就越少。

完整程式碼如下:

var viewer = new Cesium.Viewer('cesiumContainer');

var scene = viewer.scene;
var canvas = viewer.canvas; canvas.setAttribute('tabindex', '0'); // needed to put focus on the canvas canvas.onclick = function() { canvas.focus(); }; var ellipsoid = viewer.scene.globe.ellipsoid; // disable the default event handlers scene.screenSpaceCameraController.enableRotate = false; scene.screenSpaceCameraController.enableTranslate = false; scene.screenSpaceCameraController.enableZoom = false; scene.screenSpaceCameraController.enableTilt = false; scene.screenSpaceCameraController.enableLook = false; var startMousePosition; var mousePosition; var flags = { looking : false, moveForward : false, moveBackward : false, moveUp : false, moveDown : false, moveLeft : false, moveRight : false }; var handler = new Cesium.ScreenSpaceEventHandler(canvas); handler.setInputAction(function(movement) { flags.looking = true; mousePosition = startMousePosition = Cesium.Cartesian3.clone(movement.position); }, Cesium.ScreenSpaceEventType.LEFT_DOWN); handler.setInputAction(function(movement) { mousePosition = movement.endPosition; }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); handler.setInputAction(function(position) { flags.looking = false; }, Cesium.ScreenSpaceEventType.LEFT_UP); function getFlagForKeyCode(keyCode) { switch (keyCode) { case 'W'.charCodeAt(0): return 'moveForward'; case 'S'.charCodeAt(0): return 'moveBackward'; case 'Q'.charCodeAt(0): return 'moveUp'; case 'E'.charCodeAt(0): return 'moveDown'; case 'D'.charCodeAt(0): return 'moveRight'; case 'A'.charCodeAt(0): return 'moveLeft'; default: return undefined; } } document.addEventListener('keydown', function(e) { var flagName = getFlagForKeyCode(e.keyCode); if (typeof flagName !== 'undefined') { flags[flagName] = true; } }, false); document.addEventListener('keyup', function(e) { var flagName = getFlagForKeyCode(e.keyCode); if (typeof flagName !== 'undefined') { flags[flagName] = false; } }, false); viewer.clock.onTick.addEventListener(function(clock) { var camera = viewer.camera; if (flags.looking) { var width = canvas.clientWidth; var height = canvas.clientHeight; // Coordinate (0.0, 0.0) will be where the mouse was clicked. var x = (mousePosition.x - startMousePosition.x) / width; var y = -(mousePosition.y - startMousePosition.y) / height; var lookFactor = 0.05; camera.lookRight(x * lookFactor); camera.lookUp(y * lookFactor); } var cameraHeight = ellipsoid.cartesianToCartographic(camera.position).height; var moveRate = cameraHeight / 100.0; if (flags.moveForward) { camera.moveForward(moveRate); } if (flags.moveBackward) { camera.moveBackward(moveRate); } if (flags.moveUp) { camera.moveUp(moveRate); } if (flags.moveDown) { camera.moveDown(moveRate); } if (flags.moveLeft) { camera.moveLeft(moveRate); } if (flags.moveRight) { camera.moveRight(moveRate); } }); 

可以看下Sandcastle的 完整例項

Camera類

Camera類描述了相機的當前狀態,包包括 位置( position),朝向( orientation), 參考空間( reference frame), 視錐體(view frustum).

  • move*zoom* 方法的作用:沿著相機方向或者某個給定向量來平移相機的位置。 相機朝向不變。
  相機平移
  • look*twist* 方法的作用:旋轉相機朝向,向前向量(direction),向上向量(up),向右向量(right)都會改變。相機位置保持不變。
  朝向旋轉
  • rotate*方法的作用:相對一個給定的向量,旋轉相機的位置和朝向。
  旋轉移動

注意:Cesium每幀會保證相機的三個朝向向量是正交的。
Note: The camera vectors above are orthonormal in each frame.

  • 修改相機位置,設定一個物件位置或者範圍:

    var west = Cesium.Math.toRadians(-77.0);
    var south = Cesium.Math.toRadians(38.0);
    var east = Cesium.Math.toRadians(-72.0); var north = Cesium.Math.toRadians(42.0); var extent = new Cesium.Extent(west, south, east, north); camera.viewExtent(extent, Cesium.Ellipsoid.WGS84); 
  • 根據一個螢幕座標建立一個從相機位置發出的射線。在拾取過程中非常有用:

    // 計算相機射線和橢球體相交點
    var ray = camera.getPickRay(mousePosition);
    var intersection = Cesium.IntersectionTests.rayEllipsoid(ray, Cesium.Ellipsoid.WGS84);
    
    

螢幕控制元件相機控制器

ScreenSpaceCameraController 類把螢幕空間的使用者輸入(滑鼠拖拽點選或者觸控事件)轉換為三維世界的相機移動 。它包含一些屬性,可以啟用/禁用某種使用者輸入,修改慣性、最小最大縮放距離等。

資源

Sandcastle中關於相機的示例:

  • 相機教程 - 本教程對應的程式碼。
  • 相機 - 相機飛行定位,可視範圍修改以及相機的參考空間等示例。

也可以檢視下相機相關類的使用者手冊:

  中國最專業的Cesium開發者社群