Cesium官方教程6--相机

摘要:
默认情况下,Cesium支持使用鼠标和触摸事件来控制相机。铯还提供了可编程的相机控制API。本教程介绍相机相关知识和API。使用setView方法设置相机位置和方向。航向/俯仰/横滚的单位是弧度。最常见的设置参数是在垂直向下查看相机时将“航向”设置为“正北”:camera.setView;自定义相机的鼠标和键盘后,我们创建了一个自定义的相机控制方法。鼠标位置控制相机的前进方向。使用键盘控制相机的向前、向后、向左、向右、向上和向下。
相机(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* 方法的作用:沿着相机方向或者某个给定向量来平移相机的位置。 相机朝向不变。
 
Cesium官方教程6--相机第1张
相机平移
  • look*twist* 方法的作用:旋转相机朝向,向前向量(direction),向上向量(up),向右向量(right)都会改变。相机位置保持不变。
 
Cesium官方教程6--相机第2张
朝向旋转
  • rotate*方法的作用:相对一个给定的向量,旋转相机的位置和朝向。
 
Cesium官方教程6--相机第3张
旋转移动

注意: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 类把屏幕空间的用户输入(鼠标拖拽点击或者触摸事件)转换为三维世界的相机移动 。它包含一些属性,可以启用/禁用某种用户输入,修改惯性、最小最大缩放距离等。



作者:Cesium实验室
链接:https://www.jianshu.com/p/1a31aa57a84b
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

免责声明:文章转载自《Cesium官方教程6--相机》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇SQLMAP注入教程-11种常见SQLMAP使用方法详解Cassandra与职业发展 | 阿里云栾小凡 × 蔚来汽车张旭东 × 网龙阙乃祯下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

数字图像处理_图像基本运算

图像基本运算 1点运算  线性点运算是指输入图像的灰度级与输出图像呈线性关系。s=ar+b  (r为输入灰度值,s为相应点的输出灰度值)。  当a=1,b=0时,新图像与原图像相同;  当a=1,b≠0时,新图像是原图像所有像素的灰度值上移或下移,是整个图像在显示时更亮或更暗;  当a>1时,新图像对比度增加;  当a<1时,新图像对比度降低;  当a<0...

线阵相机参数计算,选择合适的相机与镜头

1、精度=物宽/像素,此公式用来选相机。 如要求精度为0.3mm/pixel,物体宽1200mm,那么像素就是4K,所以选择4K相机。 2、物体最大移速=精度×最大行频,此公式用来计算相机是否满足移动速度。 0.3mm/pixel×26000Hz=7617mm/s,即物体移速低于457m/min即可,所以选的4K相机没有问题。 3、物距=焦距×精度/像元尺...

Android 多媒体------相机

一.拍照 1.设置使用相机权限 为了让用户知道我们的应用需要依赖相机,在Manifest清单文件中添加<uses-feature>标签: <manifest ... > <uses-feature android:name="android.hardware.camera" andr...

深入研究自监督单目深度估计:Monodepth2

作者:Gus 来源:微信公众号|3D视觉工坊(系投稿) 3D视觉精品文章汇总:https://github.com/qxiaofan/awesome-3D-Vision-Papers/ 单目深度估计模型Monodepth2对应的论文为Digging Into Self-Supervised Monocular Depth Estimation,由伦敦大学学...

Unity在UI界面上显示3D模型/物体,控制模型旋转

Unity3D物体在UI界面的显示 本文提供全流程,中文翻译。 Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例) Chinar —— 心分享、心创新!助力快速利用 UGUI 完成 3D 物体在 UI 界面的显示为新手节省宝贵的时间,避免采坑! Chinar 教程效果:...

使用点云数据在Unity中渲染场景

  最近接触了一个用点云数据渲染的方案, 非常给力, 几乎就是毫秒级的加载速度, 特别是在显示一些城市大尺度场景的时候, 简直快的没法形容, 之前的城市场景用了很多重复模型, 并且大量优化之后加载一个城市不仅时间很久, 10分钟级的, 而且内存消耗巨大, 10G级别的, 运行时CPU裁剪都能耗掉40ms, 几乎没有任何意义了...   这个方案好的地方在于...