我自己是初学者,入行搞这个差不多两个月了,一直处于迷迷糊糊的状态,今天本着自己学习的态度一起来总结一下OSG/osgEarth的知识体系,希望对初学者有帮助。
假设你有一张纸,纸上画了一棵树,这就是二维(2D)。但是现实生活中,树是立体的,你可以绕着树走,可以从前面看、后面看,还可以飞到天上看,这就是三维(3D)。电脑想要画出这样的世界,需要有人帮它,这个帮手就是OSG。
OSG(OpenSceneGraph)可以理解成:一个会搭积木的机器人。
它负责告诉电脑,这里有一棵树,那里有一个房子,那里有一辆车,然后电脑就能把整个三维世界画出来。实际上电脑里面不是画画,而是很多很多三角形。例如一个正方形,电脑实际上画的是两个三角形。因为GPU最喜欢画三角形。所以,OSG就是负责管理这些三角形。
OSG名字里面有个Graph,其实不是数学里的图,而是一棵树(树状结构)。
例如:
世界 │ ├──天空 │ ├──地面 │ ├──汽车 │ ├──轮子1 │ ├──轮子2 │ ├──轮子3 │ └──轮子4 │ └──房子 ├──门 └──窗户是不是像文件夹?
OSG就是这样管理所有东西。
所以叫:
Scene(场景)
Graph(树状结构)
OSG里面最重要的东西叫Node(节点)。可以理解成乐高积木,节点可以是一棵树,一辆车,一个房子等,所有东西都可以是一个Node。
还有OSG里面的group,group就是一个盒子,他只会装东西不会显示。
Geode:Geometry Node,是真正画东西的节点。Geode里面放Geometry,例如一棵树就是10000个三角形,一个房子就是50000个三角形,Geometry就是真正的模型,OSG最后都是Geometry。
Viewer可以理解成摄像机,例如:如果没有摄像机,你什么都看不到。Viewer负责:
- 打开窗口
- 接收鼠标
- 接收键盘
- 调GPU
- 每秒刷新60次
所以程序一般都有,它就是整个窗口。
Viewer里面还有Camera。Viewer像电影院,Camera像摄影师,摄影师站哪里,观众就看到哪里。Camera移动,观众看到的视角就变化了,看到的世界就变了。
Manipulator就是鼠标控制器,例如:你按住鼠标左键拖一下,地图就会旋转。Manipulator:鼠标移动-> camera移动->画面变化。
OSG负责:建立场景树-> 管理模型->管理灯光->管理纹理->管理相机->交给OpenGL画。
注意:OSG自己不会画。真正画的是:OpenGL。OSG只是帮你组织。就像:导演不会自己去演戏,真正干活的是GPU。
OSG只是会管理GPU画三维世界,但是:不会画地球。因为OSG不知道例如:北京在哪里,东京在哪里,GPS是什么,经纬度又是什么等。于是:有人在OSG上面写了osgEarth。一句话:osgEarth等同于OSG + 地图。例如OSG会画汽车,osgEarth知道汽车的具体位置在地球的那个地方。
osgEarth多了哪些东西?
它认识:地球,经纬度,海拔,地图瓦片,影像,DEM高程,行政区,WMS,WMTS,GeoTIFF,Shapefile等这些OSG 都没有。
为什么需要osgEarth?
例如:你想画北京。如果只有OSG,你要自己画地球- >画中国- >画北京 ->贴地图->算经纬度等,工作量巨大。但是如果有osgEarth,mapNode一句, 整个地球就出来了。
MapNode可以理解成:整个地球,所有东西都放这里。
例如:
MapNode ├──影像 ├──高程 ├──道路 ├──建筑 ├──飞机 ├──无人机 └──标记是不是和OSG树一样?因为:MapNode其实也是Node。
上面是通俗易懂的厘清了OSG和osgEarth相关的一些概念,后续就是实用角度梳理OSG/osgEarth的概念。
一、它们各自解决什么问题
| 库 | 角色 | 一句话 |
|---|---|---|
OpenSceneGraph (OSG) | 通用 3D 场景图引擎 | 管「节点、相机、渲染、事件、文件读写」 |
osgEarth | 基于 OSG 的地理可视化引擎 | 管「地球、地图、瓦片、投影、地形、矢量叠加」 |
关系:osgEarth 依赖并扩展 OSG,不是替代 OSG。写 osgEarth 程序时,渲染循环、相机、节点树仍是 OSG 的;地球内容挂在MapNode等节点上。
二、OSG 核心:场景图思维
2.1 场景图是什么
OSG 用 有向无环图(DAG) 组织场景:
- 叶子节点:几何、文字等「可画的东西」
- 组节点:组织子节点、做变换、做状态切换
- 每帧:从根节点向下遍历(cull),再绘制(draw)
常见节点类型:
| 类型 | 作用 |
|---|---|
| 普通组,挂多个子节点 |
| 局部坐标系变换 |
| 几何数据承载 |
| 开关子节点显隐 |
| 按距离/屏幕大小换细节 |
| 相机(也可嵌套做 RTT) |
| 材质、纹理、光照、shader 等状态 |
入门口诀:节点管结构,StateSet 管怎么画,Drawable 管画什么。
2.2osg::Node/osg::Group/osg::Geode
Node:所有节点基类,可挂UpdateCallback、CullCallback、EventCallback。Group:可addChild/removeChild,是场景组织的主力。Geode(Geometry Node):传统上专门挂Drawable;较新 OSG 也常直接把Geometry当叶子用。
2.3StateSet(状态集)——很容易被忽视但极关键
StateSet描述渲染状态,可挂在任意节点上,向下继承:
- 纹理、材质、光照开关
GL_DEPTH_TEST、混合、面剔除- Shader / Uniform
- 渲染 bin(透明排序等)
要点:
- 子节点可覆盖父节点状态
setMode/setAttribute/setTextureAttribute是三类常用接口- 改状态比改几何更常见,性能也更敏感
2.4 变换节点
MatrixTransform:任意 4×4 矩阵(平移+旋转+缩放)PositionAttitudeTransform:位置 + 姿态四元数 + 缩放,语义更直观CoordinateSystemNode:和地理坐标系相关时常见(osgEarth 大量用地理坐标转换)
注意:OSG 默认常是 右手系,相机看向 -Z,上方向 +Y(具体以你用的 Viewer/相机设置为准)。
三、osgViewer:应用的「外壳」
3.1 Viewer 到底是什么
osgViewer::Viewer(或CompositeViewer)是应用主循环的中枢,负责:
- 创建/管理 GraphicsContext(窗口、OpenGL 上下文)
- 持有 Camera(主相机)
- 挂载 SceneData(场景根节点)
- 驱动 Event / Update / Cull / Draw 四阶段
- 管理 操作器(CameraManipulator):鼠标旋转、平移、缩放
最小心智模型:
Viewer
├── GraphicsWindow / Context
├── Camera(投影 + 视口 + 清屏)
├── SceneData(你的根 Group / MapNode)
├── CameraManipulator(EarthManipulator 等)
└── 事件处理器(键盘、HUD 等)
3.2 典型生命周期
osgViewer::Viewer viewer;
viewer.setSceneData(root); // 挂场景
viewer.setCameraManipulator(...); // 交互
viewer.setUpViewInWindow(x,y,w,h); // 或全屏 / 嵌入 Qt
while (!viewer.done()) {
viewer.frame(); // 一帧:事件→更新→裁剪→绘制
}
或:
viewer.run(); // 内部循环调用 frame()
3.3ViewervsCompositeViewer
| Viewer | CompositeViewer | |
|---|---|---|
场景/视图 | 通常一个主视图 | 多个 |
用途 | 单窗口应用 | 多窗口、多显示器、主从视图 |
复杂度 | 低 | 高(共享场景或独立场景都可) |
入门先用Viewer即可。
3.4 相机(osg::Camera)在 Viewer 里的角色
主相机通常由 Viewer 创建,你可设置:
- 投影:透视
Perspective/ 正交Ortho - 视口:像素区域
- Clear mask / clear color
- Render Order:嵌套相机做 HUD、后处理、RTT
osgEarth 里经常还有:
- 地形相机 / overlay 相机
MapNode内部管理的多 pass 渲染
入门阶段:先把「主相机看向场景」弄清,再碰嵌套相机。
3.5 操作器(Manipulator)
OSG 自带:
TrackballManipulator:绕物体转(模型查看)FlightManipulator/DriveManipulator:飞行/驾驶TerrainManipulator:贴地感更强
osgEarth 常用:
osgEarth::Util::EarthManipulator:地球导航(旋转地球、缩放高度、倾斜)- 支持飞到某经纬度、设置视点、home 位置等
这是「看地球顺不顺手」的关键一环:场景对了,操作器不对,体验也会差。
3.6 事件与回调
GUIEventHandler:键盘鼠标事件- 节点回调:
UpdateCallback:每帧逻辑(动画、仿真)CullCallback:裁剪阶段EventCallback:事件冒泡到节点
和 osgEarth 结合时:拾取地图上的点、查询高程、点选要素,通常走事件处理器 +MapNode工具类。
3.7 线程模型(入门要知道存在)
Viewer 可配置:
- SingleThreaded
- CullDrawThreadPerContext
- DrawThreadPerContext
- CullThreadPerCameraDrawThreadPerContext
入门用默认即可;出黑屏/崩溃时再查线程与上下文问题。
四、OSG 渲染管线四阶段(理解调试必备)
每帧大致:
- Event:处理输入,更新操作器
- Update:执行 UpdateCallback,动画、数据更新
- Cull:视锥裁剪、生成渲染列表、状态排序
- Draw:提交 OpenGL 绘制
PagedLOD/ 瓦片加载常在 update/cull 相关路径触发;osgEarth 的地形瓦片调度也建立在这套机制上。
五、osgEarth 核心:Map / Layer / MapNode
5.1 三层概念(最重要)
Map(数据与配置)
└── 多个 Layer(影像、高程、矢量、模型……)
↓ 被可视化为
MapNode(OSG 节点,真正进场景图)
↓ 挂到
Viewer.setSceneData(mapNode)
| 类 | 是什么 | 不是什么 |
|---|---|---|
| 地图「数据模型」:有哪些图层、剖面、选项 | 不是可渲染节点 |
| 一类数据源(WMTS、本地瓦片、GeoTIFF、矢量等) | 单独不能当场景根 |
| 把 Map 变成可渲染 OSG 子树 | 不只是「一个地球模型文件」 |
口诀:Map 管数据,MapNode 管显示。
5.2Map详解
Map负责:
- 添加/移除 Layer
- 地图剖面(Profile):地理范围、瓦片切分方案、 SRS
- 一些全局选项(如 elevation pooling、插值等,版本间 API 有差异)
常见操作:
map->addLayer(imageLayer);
map->addLayer(elevationLayer);
map->addLayer(featureLayer);
注意:改 Map 后,显示侧由MapNode/ Layer 回调同步;多线程下要遵守 osgEarth 的线程安全约定(很多操作建议在 update 阶段或按文档要求加锁)。
5.3MapNode详解(重点展开)
MapNode是 osgEarth 进入 OSG 场景图的 根入口,通常:
osg::ref_ptr<osgEarth::MapNode> mapNode = new osgEarth::MapNode(map);
viewer.setSceneData(mapNode);
或从.earth文件加载:
osg::Node* node = osgDB::readNodeFile("my.earth");
MapNode* mapNode = MapNode::findMapNode(node);
MapNode 内部大致职责
地形引擎(Terrain Engine)
- 按视点动态生成/回收地形瓦片
- 把影像贴到地形上
- 应用高程(DEM)做起伏
坐标系与椭球
- 地理(经纬高)↔ 世界直角坐标(ECEF 等)
- 和
SpatialReference、GeoPoint、Ellipsoid协作
图层可视化挂载
- Image / Elevation / Model / Annotation 等变成子图或驱动地形
工具入口
- 高程查询、坐标转换、与 Manipulator 配合
常用相关 API 思路(概念级)
MapNode::getMap():取回 MapMapNode::getTerrain()/ terrain engine:访问地形MapNode::findMapNode(node):从场景里找回 MapNode(加载 .earth 后很常用)- 开启/配置一些渲染选项(光照、裙边 skirt、四叉树策略等,随版本变化)
MapNode 和普通 OSG 节点的关系
MapNode就是osg::Group一类节点,可再挂子节点(模型、标注)- 子节点若要用「贴地」「按经纬度放」,常用 osgEarth 的:
GeoTransformAnnotation/PlaceNode/FeatureNodeTerrain::getHeight等做贴地
5.4 Layer 类型图谱
| Layer 类型 | 作用 | 典型数据 |
|---|---|---|
ImageLayer | 贴到地表的影像 | XYZ/WMTS/TMS、GeoTIFF、MBTiles |
ElevationLayer | 高程 DEM | GeoTIFF、S3、本地高程瓦片 |
ModelLayer | 把 OSG 模型当图层放进地图 | 城市模型、机场模型 |
FeatureImageLayer / FeatureModelLayer | 矢量渲染成影像或模型 | Shapefile、GeoJSON、WFS |
AnnotationLayer | 标注层 | 地名、图标 |
VisibleLayer | 可显隐图层基类概念 | 统一 |
入门路径建议:
- 一张在线或本地 ImageLayer 球
- 加 ElevationLayer 看起伏
- 再加矢量/模型
5.5.earth文件
XML/JSON 风格的地图描述(具体格式随版本):
- 声明 Map、Layer、驱动(driver)、URL、缓存策略
- 可用
osgviewer mymap.earth或代码readNodeFile直接打开
价值:把「数据配置」和「C++ 逻辑」分离,适合入门实验和产品配置化。
六、坐标系:osgEarth 最容易卡的地方
6.1 关键对象
| 概念 | 含义 |
|---|---|
SRS / SpatialReference | 空间参考(WGS84、WebMercator、投影坐标等) |
GeoPoint | 带 SRS 的地理点(经纬高或投影 x/y/z) |
GeoExtent | 地理范围 |
Profile | 瓦片金字塔剖分方案(如 global-geodetic、spherical-mercator) |
ECEF | 地心直角坐标系,OSG 世界里地球常用 |
6.2 两套「位置」不要混
- 地理坐标:lon/lat/alt
- 世界坐标:
osg::Vec3d(场景图里 MatrixTransform 用的)
转换路径概念上:
GeoPoint(lon, lat, alt, srs)
→ toWorld(Vec3d)
→ MatrixTransform / GeoTransform
反过来:世界坐标 → 地理坐标(拾取、标注时常用)。
6.3GeoTransform
把子节点放到地理坐标位置,并可选贴地、跟随地形。比手写 ECEF 矩阵更安全,是「在地球上放飞机/标注」的常规做法。
七、地形、瓦片与缓存
7.1 为什么地球必须「瓦片化」
全球影像/DEM 极大,不可能一次加载。osgEarth:
- 按 Profile 切瓦片
- 按相机距离选 LOD 层级
- 异步加载、内存/磁盘缓存、过期回收
这和 OSG 的PagedLOD思想一致,但是地理瓦片专用实现。
7.2 影像 vs 高程
- 影像:改颜色(纹理)
- 高程:改顶点高度(几何)
两者分辨率、覆盖范围常不一致,地形引擎会做采样/混合。
7.3 缓存
常见关注点:
- 内存缓存(已解码瓦片)
- 磁盘缓存(HTTP 瓦片落地)
- 缓存策略影响「第一次慢、第二次快」
调试「花屏/不更新」时,有时是缓存旧数据。
八、交互:EarthManipulator 与拾取
8.1 EarthManipulator
典型能力:
- 旋转、缩放、倾斜、平移
setViewpoint/ 飞行动画到某点- 与地形碰撞/贴地选项(版本相关)
Viewpoint 通常包含:经纬、高度、heading/pitch/range 等。
8.2 地图拾取
常见需求:鼠标点 → 经纬高。
思路:
- 屏幕坐标 → 射线
- 与地形求交
- 交点转 GeoPoint
osgEarth 提供 util/intersection 相关工具(类名随版本略有不同),入门时把它当成「地图专用 intersector」。
九、标注、矢量与模型(进阶入门)
9.1 Annotation
PlaceNode:图标+文字LabelNode:文字CircleNode/LineString等:几何标注
特点:常自动面向屏幕、深度测试策略与普通模型不同。
9.2 Feature
Feature:一条矢量要素(属性 + 几何)FeatureSource:读 shp/geojsonStyle/RenderSymbol:怎么画(线宽、挤出高度、贴地)
9.3 在地球上挂 OSG 模型
两种层次:
- ModelLayer:作为地图图层管理
- GeoTransform + 模型节点:代码里灵活放置
注意缩放单位:ECEF 下模型尺度、朝向(ENU 局部坐标系)非常容易错。
十、一套最小程序的知识清单(对照用)
写第一个地球程序,你会用到:
osgViewer::Viewer— 窗口与主循环Map+ImageLayer— 数据MapNode— 场景根EarthManipulator— 交互- (可选)
ElevationLayer— 地形起伏 - (可选)
GeoTransform— 放一个模型
知识闭环:
数据(Map/Layer) → 节点(MapNode) → 视图(Viewer) → 操作(EarthManipulator)
↑ |
└──────── 坐标(GeoPoint/SRS) ←───────┘