相机基类CameraBase

  PluginSDK中的相机类CameraBase是三维计算机图形学中的概念。观察者在三维场景中漫游时,通过眼睛看到的场景和相机拍摄过程非常一致。实际上,Direct3D和OpenGL都是先通过对现实世界中的场景先进行世界变换,再通过设置观察矩阵以在场景中安置一个虚拟相机,构建一个视景体来裁剪场景的可见区域,然后在通过投影变换(平行投影或透视投影),获取三维场景的“像”,最后再通过视口变换,将场景的“像”光栅化输出到二维显示屏幕上。如下图所示。

在三维地形系统中,通常定义一个虚拟相机来实现对三维场景的漫游等功能。可以通过操纵虚拟相机来实现在虚拟场景中切换和漫游。这种方式实质上是通过不断改变虚拟相机的相关参数(如位置和朝向等),然后通过这些参数重新构造世界矩阵、观察矩阵或投影矩阵来完成场景漫游的。

  最后,可以用三句话来概括这些变换的作用:世界变换矩阵决定模型的位置,观察变换矩阵决定模型在观察者(相机)眼中的位置,投影变换矩阵决定模型在屏幕空间中的位置。

根据相机的运动特性,可以将相机简单地分为状态型相机、运动型相机和惯性保持型相机。

(1)      状态型相机也叫静态相机,它适合实现稳定静态的空间表达,很多三维应用就是通过状态型相机来实现静态场景的表达的,用户可以通过交互调整相机的位置、方向来实现场景的切换。状态型相机不需要定义相机对象,不适合用于场景漫游表达需要。目前常在部分动态场景中绑定一些功能型状态相机来捕捉场景的变换,如设在机车上的监视相机用于表达从车上观看车内的景观,动的其实不是相机,而是机车模型。

(2)      运动型相机主要针对漫游实现的快捷交互响应实现,通常需要通过相机类对象来完成封装,使它更适合于快速计算视点、视向变化所引起的模型矩阵转换,并通过一定的机制来提供良好的场景交互接口。

(3)      惯性保持型相机则是在运动型相机的基础上,充分利用力学物体运动的知识,使得场景的变化更贴近人的交互感觉,如给地球一个选转力拖动释放后,它不会立即停止旋转,而是会慢慢地停下来,这种相机就像处理物体惯性一样有一个状态保持过程。

无论哪一种类型相机,其实对计算机图形处理运算来说,最终需要两个转换矩阵来完成对模型的取景变化:观察变换矩阵和投影变换矩阵。这里将世界变换矩阵从相机空间中独立出来时希望明确相机空间的操作其实与场景模型表达不同,世界坐标变换针对的是模型,时由模型自身与场景空间组织决定的,而不是由相机空间决定的。相机空间的处理归根结底可以说时围绕着观察变换矩阵和投影变换矩阵这两个变换矩阵进行的。

相机类的类图如下。由相机基类CameraBase派生出世界相机类WorldCamera,再派生出运动相机类MomentumCamera。

其中,基类CameraBase主要成员有:地形高程、(所观察的)星球半径(单位为米)、相机所在经度角度_longitude、纬度角度_latitude、相机的朝向角度_heading、_tilt倾斜角度、_bank横滚角度、_distance眼睛到目标的距离、_altitude相机到海平面的高度、四元数m_Orientation、视景体对象_viewFrustum、从星球设置参数获取的相机视场角,相机的_position向量。

同时设置相机的最小倾斜角度为0度,最大倾斜角度为85度。最小高度为10度,最大高度为无穷大。

m_ProjectionMatrix投影变换矩阵、m_ViewMatrix观察变换矩阵、m_WorldMatrix世界变换矩阵。viewRange可视范围角度,trueViewRange真实的可视范围角度,viewPort视口窗口结构体。lastStepZoomTickCount最后一步缩放计数器(与Environment.TickCount结合),cameraUpVector相机的向上向量,初始化默认为Point3d(0,0,1)。ReferenceCenter相机的参考中心,初始化默认为Point3d(0,0,0),lastResetTime最后的重置时间,DoubleTapDelay双Tap延迟。

m_absoluteViewMatrix、m_absoluteWorldMatrix、m_absoluteProjectionMatrix三个绝对矩阵不清楚与上面的三个相应矩阵有什么区别?还为它们定义了只读的属性AbsoluteViewMatrix、AbsoluteWorldMatrix、AbsoluteProjectionMatrix。

属性Altitude和TargetAltitude分别表示高出海平面的高度和经过移动后目标高出海平面的高度。

属性AltitudeAboveTerrain表示高出地表物体高程的高度。属性TerrainElevation用于设置或获取地表物体高程的高度。

属性Distance和TargetDistance分别表示到目标位置的距离和经过移动后到目标位置的距离。

ComputeDistance( double altitude, Angle tilt )用给定的相机高度和相机倾斜角计算距离。

方法ComputeAltitude( double distance, Angle tilt )的计算示意图如下所示。

定位与视野控制剖面视图

方法ComputeTilt(_altitude, _distance) 的计算示意图如上所示。

ComputeAbsoluteMatrices()方法里面的矩阵我知道意义,但不太理解其运算的含义。为啥要那样运算?

虚方法Update(Device device)用相机新的世界矩阵、观察矩阵、投影矩阵更新当前场景,同时也更新了视景体的参数、可视范围角度,真实的可视范围角度和World.Settings中关于相机的参数。

虚方法Reset() 用于重置相机的参数。调用了虚方法SetPosition()。

虚方法PointGoto(double lat, double lon)设置相机位置。调用了虚方法SetPosition()。

虚方法SetPosition(double lat, double lon, double heading, double _altitude, double tilt, double bank)真正的被设计用来设置设置相机位置,有三种重载形式。

虚方法virtual void PickingRayIntersection(int screenX, int screenY,out Angle latitude,out Angle longitude)用给定的屏幕像素坐标计算对应位置的经度角度和纬度角度。用构造的向量Vector3 v1 = newVector3(screenX, screenY, Viewport. MinZ)和Vector3 v2 = newVector3(screenX, screenY, Viewport. MinZ)调用了Vector3. Unproject(object viewport, Matrix projection, Matrix view, Matrix world)方法拟向将一个向量从屏幕空间投影到目标空间。实现反算射线上一点的空间坐标,这样反算出来的位置即为空间坐标下的E点或F点,然后再根据模型求交反算出真正需要的地球表面点M。这里针对全球地形表达模型,假设相对于地球半径地面高程可以忽略,认为要求取的这一点来源于标准地球表面,可通过视线与球面的交计算出来。然后再用一元二次方程的判别式(Why?)决定经度角度和纬度角度的取值情况。如果判别式>0,则再通过方法MathEngine.CartesianToSphericalD()计算出点对象Point3d p1 = new Point3d(v1.X, v1.Y, v1.Z)和Point3d p2 = new Point3d(v2.X, v2.Y, v2.Z)所对应的球面坐标,最后调用Angle.FromRadians返回以度表示的经度角度和纬度角度。这实际上是计算机图形学中光线跟踪反算空间模型位置的经典问题,其目的就是通过对屏幕上的鼠标响应到空间场景中进行交互拾取选择。由于投影变换实现的是从三维到二维的非线性映射,所以在进行光线跟踪时信息维度的丢失使得准确的逆向过程比较困难。屏幕交互视点的大地空间反算求解如下图所示。这里可以参考韩元利编著的《DirectX 11高级图形开发技术实战》一书和《光线跟踪技术的原理与实现》和《3D未来争夺战——光线追踪算法浅析》两篇论文。

虚方法ComputeProjectionMatrix(Viewport viewport)计算投影矩阵,用于将三维相机或观察空间坐标转换为二维的屏幕空间坐标。

虚方法RotationYawPitchRoll(Angle yaw, Angle pitch, Angle roll)使相机按照给定的航偏角yaw、倾斜角pitch和横滚角roll旋转,实际上是给相机的经度角度_longitude、纬度角度_latitude、相机的朝向角度_heading重新赋值。如下图所示。

虚方法ZoomStepped通过键盘或鼠标缩放场景。

虚方法Zoom(float percent)在目标距离方向上按指定的百分比缩放。

虚方法Pan(Angle lat, Angle lon)按指定的经、纬度偏移漫游场景。

方法Vector3 Project( Vector3 point ) 用于将世界空间中的三维点转换为二维的屏幕坐标。

静态方法static Angle[] getViewBoundingBox()以度为单位计算可见的外包围盒。

好了,绕了一大圈,总结一下CameraBase类的功能:CameraBase类是管理与虚拟相机位置相关信息的类,内容如下:

a)         设置或获取经纬度、高度、视角角度、倾斜度等位置/视角变量;

b)        各种变化矩阵和计算矩阵的函数,计算的参数由a)中的变量提供;

c)         漫游相关的函数,包括平移、放大、缩小、移位(PointGoto)等功能,而所作的操作也是依据用户操作改变a)中的变量;

d)        刷新/重置的函数;

e)         其他功能。

当需要改变位置时,调用的是WorldWind对象的drawArgs的WorldCamera参数,传入参数即可。在程序窗体Paint函数刷新界面是会自动进行视角变换及相关必须的计算。

世界相机类WorldCamera

  世界相机类WorldCamera 继承自 CameraBase,重载了父类的很多虚方法。

运动相机MomentumCamera

  运动相机MomentumCamera继承自WorldCamera,重载了父类的很多虚方法。

定位与视野控制

  本部分内容请参照《World Wind Java三维地理信息系统开发技术指南》一书。其中不正确或不明确部分已经做了相应的注释。

WorldWind源码剖析系列:相机类CameraBase的更多相关文章

  1. WorldWind源码剖析系列:星球类World

    星球类World代表通用的星球类,因为可能需要绘制除地球之外的其它星球,如月球.火星等.该类的类图如下. 需要说明的是,在WorldWind中星球球体的渲染和经纬网格的渲染时分别绘制的.经纬网格的渲染 ...

  2. WorldWind源码剖析系列:星球球体的加载与渲染

    WorldWind源码剖析系列:星球球体的加载与渲染 WorldWind中主函数Main()的分析 在文件WorldWind.cs中主函数Main()阐明了WorldWind的初始化运行机制(如图1所 ...

  3. WorldWind源码剖析系列:影像存储类ImageStore、Nlt影像存储类NltImageStore和WMS影像存储类WmsImageStore

    影像存储类ImageStore 影像存储类ImageStore提供了计算本地影像路径和远程影像影像URL访问的各种接口,是WmsImageStore类和NltImageStore类的基类.当划分完层次 ...

  4. WorldWind源码剖析系列:设置类SettingsBase

    PluginSDK中的星球设置类WorldSettings 和WorldWind.程序设置类WorldWindSettings均继承自父类SettingsBase.类图如下所示.其中父类Setting ...

  5. WorldWind源码剖析系列:表面影像类SurfaceImage

    表面影像类SurfaceImage描述星球类(如地球)表面纹理影像.该类的类图如下. 表面影像类SurfaceImage包含的主要的字段.属性和方法如下: string m_ImageFilePath ...

  6. WorldWind源码剖析系列:表面瓦片类SurfaceTile

    表面瓦片类SurfaceTile描述星球类(如地球)表面纹理影像的瓦片模型.其类图如下. 表面瓦片类SurfaceTile包含的主要的字段.属性和方法如下: int m_Level;//该瓦片所属金字 ...

  7. WorldWind源码剖析系列:星球表面渲染类WorldSurfaceRenderer

    星球表面渲染类WorldSurfaceRenderer描述如何渲染星球类(如地球)表面影像纹理.该类的类图如下. 星球类World包含的主要的字段.属性和方法如下: public const int ...

  8. WorldWind源码剖析系列:绘制参数类DrawArgs

    绘制参数类DrawArgs主要对绘制时需要的对象如:设备对象Microsoft.DirectX.Direct3D.Device.Microsoft.DirectX.Direct3D.Font字体对象. ...

  9. WorldWind源码剖析系列:挂件类Widgets

    WorldWindow用户定制控件类中所包含的的挂件类Widgets控件主要有如下图所示的派生类.它们的类图如下所示. 鉴于挂件类Widgets及其派生类,相对简单,基本上都是些利用DirectX3D ...

随机推荐

  1. 多线程系列(1)多线程基础和Thread

    因为现项目中有用到多线程和并发的知识,所以打算近期补习一下多线程相关的内容.第一篇文章从最基础的开始,就是如何开启一个线程,如何启动线程和阻塞线程等,这篇文章分以下几点进行总结. 多线程初印象 多线程 ...

  2. Codeforces35E(扫描线)

    E. Parade time limit per test:2 seconds memory limit per test:64 megabytes input:input.txt output:ou ...

  3. 08:Vigenère密码

    08:Vigenère密码 查看 提交 统计 提问 总时间限制:  1000ms 内存限制:  65536kB 描述 16世纪法国外交家Blaise de Vigenère设计了一种多表密码加密算法— ...

  4. 工作记录(JS向textarea添加固定内容、通过固定字符将字符串分割为数组)

    第一个是在 textarea 输入框中添加固定的内容. 代码如下: <textarea id="text" cols="30" rows="10 ...

  5. 基于 Web 的 Go 语言 IDE - Wide 1.5.2 发布!

    这个版本由热心的开源贡献者加入了韩语支持,欢迎各位 gophers 加入到 Wide 的开源开发中.另外,这个版本还改进了 Playground,使其更稳定和易用.目前黑客派社区已经支持嵌入 Wide ...

  6. PostGIS安装

    PostGIS安装 1.软件下载 postgresql-9.6.1-1-windows-x64-binaries.zip https://www.postgresql.org/download/win ...

  7. Python 基于python实现的http接口自动化测试框架(含源码)

    基于python实现的http+json协议接口自动化测试框架(含源码) by:授客 QQ:1033553122      欢迎加入软件性能测试交流 QQ群:7156436  由于篇幅问题,采用百度网 ...

  8. 【Python】定时调度

    from datetime import datetime from apscheduler.schedulers.blocking import BlockingScheduler def tick ...

  9. 【JAVA】什么是冒泡排序?——面试加分题

    冒泡排序是一种计算机科学领域的较简单的排序算法,有心人将代码不断优化改良,本人特摘抄部分代码进行学习. 文章来自开源中国,转载自:程序员小灰.原文:漫画:什么是冒泡排序? 冒泡排序第一版 public ...

  10. fab提供远程IP和账号密码

    #!/usr/bin/python #-*- coding: UTF-8 -*- from fabric.api import * from fabric.context_managers impor ...