如果你和我一样,第一次编译完 osgearth2.10 后打开 bin 目录,大概率会被一排osgearth_开头的 exe 震住——这些就是官方 demo。这个版本里的 demo 数量不少,从最基础的地形球显示,到视域分析、测量标绘、天气粒子、路径动画,基本把引擎的核心能力都覆盖了。本文就以 osgearth2.10 的 demo 为主线,按功能把这一批示例程序逐个讲清楚,告诉你每个 demo 是干什么的、怎么运行、改哪个 earth 文件能玩出花,以及看完之后怎么在真实项目里复用里面的思路。
如果你是刚接触三维 GIS 渲染的新人,这批 demo 就是最好的入门教材;如果你已经用 osgEarth 做过项目,2.10 的 demo 依然值得翻一遍,尤其是那些动画、编辑、分析相关的例子,能帮你省掉不少自己造轮子的时间。我尽量用实际跑过的经验来讲,不堆官方文档里那种干巴巴的说明。
1. 为什么 2.10 的 demo 值得挨个看一遍
1.1 demo 是学习引擎 API 和 earth 语法的双重入口
很多人的误区是"demo 不就是个能跑的小程序吗",其实 osgEarth 的 demo 远不止演示功能那么简单。每个osgearth_xxx可执行文件背后,都对应源码里examples/目录下的一个独立工程,工程里通常只有一个主 cpp 文件,代码量从一两百行到上千行不等。这意味着你看到一个功能跑起来之后,可以直接打开对应源码,看它到底调了哪些类、哪些接口。
举个例子,你想搞清楚标注是怎么做出来的,跑一下osgearth_labels,屏幕上出现一堆可以随地形起伏的标签,然后打开examples/osgearth_labels/osgearth_labels.cpp,你会发现核心就是在场景根节点下挂了一个osgEarth::Annotation::LabelNode,再设置好样式和位置。这种"先看现象、再追源码"的学习路径,比对着 API 文档硬啃效率高得多。
2.10 的 demo 还有一个特殊价值:它连 earth 文件的写法一起演示了。osgEarth 的三维场景描述是靠 XML 格式的 earth 文件组织的,demo 运行时会加载对应的 earth 文件,文件里写了数据源、图层类型、坐标系、缓存策略等配置。你不需要去翻厚厚的 XML Schema 文档,直接看 demo 自带的 earth 文件,就能学到各种 driver 的用法。
1.2 对项目选型来说,demo 就是功能验收清单
我接触过不少团队,选 osgEarth 之前会先问"能不能做视域分析""能不能量测""能不能编辑矢量数据"。与其听销售和文档吹,不如直接把对应 demo 跑起来看效果。osgEarth 2.10 的 demo 覆盖了当时引擎的绝大部分能力,逐个试一遍,项目的技术可行性基本就有数了。
这也让我养成了一个习惯:评估一个开源引擎时,先看它的 demo 覆盖度,demo 多不多、功能全不全,直接反映了这个引擎的成熟度和社区活跃度。2.10 之所以在我这边一直留作备用版本,就是因为它的 demo 体系非常完整,很多后续版本的示例其实都是在 2.10 的基础上扩展的。
2. 先搞懂这批 demo 的构成与运行方式
2.1 bin 目录里的命名规律
编译完 osgEarth 2.10 之后,bin 目录下会出现大量可执行文件,命名规律非常统一:osgearth_加上功能关键词。这个命名不是随便起的,它精确对应源码里examples/下的同名子目录。
| demo 名称 | 核心功能 |
|---|---|
| osgearth_osgviewer | 最基础的地形球浏览 |
| osgearth_terrain | 地形渲染参数调试 |
| osgearth_viewshed | 视域分析 |
| osgearth_measure | 距离/面积量测 |
| osgearth_labels | 文本标注 |
| osgearth_features | 矢量要素加载 |
| osgearth_editable | 矢量要素编辑 |
| osgearth_weather | 天气特效 |
| osgearth_path | 路径动画 |
| osgearth_manip | 相机操作模式 |
这些 demo 大多数在编译时默认就会生成,不需要额外开关。个别依赖第三方库的例子(比如依赖 osgQt 的 UI 示例)可能需要你编译时开了对应选项才会有,跑不起来也不用急,批量认识这批 demo 从最常见的开始就行。
2.2 数据从哪来:earth 文件里的门道
第一次运行 demo,最容易卡住的就是数据。大多数 demo 会有一个默认加载逻辑:如果你不带参数直接运行,比如执行osgearth_viewshed,可能会提示Usage: osgearth_viewshed <earthfile>,然后退出;也有的 demo 会尝试加载它内部写死的 earth 文件路径,找不到就报错。
所以正确姿势是准备好一个 earth 文件再运行。osgEarth 2.10 的源码包tests/和examples/目录下本身带了不少 earth 文件,比如tests/readymap.earth、tests/azavea.earth这类,它们引用的是在线 TMS 切片服务。如果你编译时没下载这些测试数据,也可以自己写一个最简单的 earth 文件,用本地影像和高程数据来跑:
<map name="my_earth" type="geocentric"> <image name="local_image" driver="gdal"> <url>D:/data/imagery.tif</url> </image> <elevation name="local_dem" driver="gdal"> <url>D:/data/dem.tif</url> </elevation> </map>把这个文件存成simple.earth,然后运行任意一个接受 earth 参数的 demo,比如osgearth_osgviewer simple.earth,一个三维地球就会转起来。这一步通了,后面所有的 demo 你都可以用类似方式驱动。
3. 按功能拆解:这批 demo 各自能干什么
3.1 地形显示与基础浏览
你要真正入门 osgEarth,第一个该跑的是osgearth_osgviewer。这个 demo 本质上就是把 earth 文件加载成场景图然后丢给 OSG 渲染,代码极其简单,但它能验证你整个编译安装环境是不是好的。我用它测试过自己的数据能不能正常出地形,每次改完 earth 文件配置,都会顺手用这个 demo 快速预览。
osgearth_terrain比 osgviewer 稍微进阶一点,它暴露了更多地形渲染的控制参数,比如可见距离、细节层次策略、法线生成方式。跑起来之后,你可以用键盘上的特定按键切换调试模式,屏幕上会实时显示当前帧的三角形数量、瓦片加载状态这类信息。对于要调地形性能的人来说,这个 demo 非常有用。
osgearth_graticule是画经纬网的,表面上看起来只是加了一层网格线,但它内部用到了 osgEarth 的装饰器机制,把经纬网作为一个场景图节点挂在地形上,并且跟随地球曲率、视角变化自动调整网格密度。想理解 osgEarth 如何在渲染层叠加非地理数据的,看这个 demo 比看文档直观得多。
osgearth_manip则是专门测试相机操作器的,内置了地球、轨道、飞行等多种操作模式。你按对应快捷键切换,能明显感觉到不同模式下鼠标拖拽的差异。做项目的时候,如果需要自定义相机交互,我通常先拿它做实验,确认手感后再移植到自己的工程里。
3.2 纹理合成、天空与地表细节
地形光秃秃的肯定不行,osgearth_splat就是处理地表纹理混合的。它能把同一块地形上不同种类的纹理(草地、岩石、雪地)按高程、坡度、遮罩图做平滑过渡。这个 demo 我强烈建议跑一次,因为它展示的效果非常惊艳:一座山从山谷到山顶,地表材质会自然地从绿色过渡到灰色再到白色。理解 splat 的关键在于它本质上是在 GPU 上做多层纹理的权重混合,而不是简单贴一张大图。
osgearth_sky负责天空环境。它生成一个覆盖整个视界的天空穹顶,支持太阳位置计算、大气散射近似模拟、昼夜变化时的光照同步。跑这个 demo 的时候,你可以拖拽相机观察地平线附近的渐变色,再按下某个功能键让太阳移动,地面上的阴影方向会跟着变化。对于有飞行漫游或者大场景观感需求的项目,这个 demo 是一个很好的参考起点。
osgearth_clamp是讲"模型贴地"的,它把任意模型放置到地形表面上,并且让模型自动贴合地形的起伏和朝向。别小看这个功能,很多 GIS 项目里放个车辆、雷达、房屋都需要它。这个 demo 内部用的是地图边形体适配机制,读一遍它的源码,你就知道MapNode和模型节点之间是怎么建立空间关系的。
3.3 矢量、标注与要素编辑
矢量数据在 osgEarth 里使用频率非常高。osgearth_features是最基础的矢量加载示例,它读取 shp、GeoJSON 等格式的要素,按属性字段渲染成不同颜色和线宽。这个 demo 适合拿来测试自己的矢量数据能不能被正确读取、坐标系转换对不对。
osgearth_labels是文本标注的集中展示,包含了普通标签、带引线标签、屏幕空间标注等多种样式。我实际项目里做地名标注、设备编号显示,基本都是套用这个 demo 的样式配置。它让我明白了 osgEarth 的标注系统里 "annotation" 和 "label" 的区别:annotation 是贴在地理位置上的节点,label 是更具体的文本表现,两者是继承关系。
osgearth_extrude是把矢量面拉伸成三维建筑的例子。程序读取带有高度属性的多边形要素,然后沿着 Z 轴方向挤出实体,看起来就像一片城市白模。做城市级三维场景、通视分析、噪声传播分析之前,我都先用这个 demo 验证数据能不能正确拉伸,再考虑后续的复杂渲染。
osgearth_editable的定位更进一步,它在场景里提供了绘制和编辑矢量的交互能力。你可以用鼠标在地形上画点、画线、画面,拖拽顶点修改形状,甚至把编辑结果保存成 OGR 支持的文件格式。这已经接近一个轻量级 GIS 编辑器了,凡是需要在三维场景里做标绘、采集、修图的业务,基本都绕不开这个 demo 里的技术。
3.4 空间分析:视域、量测与拾取
osgearth_viewshed是很多用户重点关注的对象。运行它之后,你在场景里点击一个位置作为观察点,程序会实时计算当前点的可视范围,并把可见区域用半透明绿色显示在地形表面上。背面的区域被山体挡住,则保持原样。这个分析能力对选址评估、军事仿真、基站规划都很有价值。
它的代码实现基于osgEarth::Util::Viewshed类,原理是在观察点周围按一定采样密度发射射线,与地形做相交测试,把结果整理成多边形再叠加显示。这个 demo 真正演示的不只是"能不能做视域分析",还包括了如何在地形上动态叠加分析结果而不破坏场景结构。
osgearth_measure是量测工具集。运行时你在地形上左键连续点击,它会实时计算累计距离,右键可以闭合多边形测面积,测出来的数据会以一串数字的形式显示在地形上方。我经常在项目演示阶段直接用这个 demo 给客户看"地形上两点之间能不能看见""这条路大概多长",比专门写一个测量模块快很多。
osgearth_pick则展示鼠标拾取能力。它在地形上选中一个点时,会高亮显示该点的经纬度和高程,这个功能在信息查询、属性关联场景里是刚需。它的实现依赖 OSG 的相交检测机制,但在 osgEarth 里,拾取点会额外转化为精确的地图坐标,所以你必须看它源码里如何处理坐标转换,才能在自己项目里把"点屏幕坐标"变成"点经纬度高程"。
3.5 动态效果:路径、轨迹、天气与粒子
动态效果是 osgEarth 2.10 比较吸引人的一部分。osgearth_path演示了对象沿路径运动的机制。它预先定义一组经纬度航点,然后让一个模型或相机按时间参数沿着路径移动,还能控制移动速度、朝向与地形坡度。这个能力做飞行漫游、车辆巡航非常合适,我在做无人机航线三维可视化时就参考了它的思路。
osgearth_tracks和路径类似,但专门用于回放轨迹数据,比如 GPS 轨迹点序列。它会把点按时间顺序展现成运动线条,并且可以控制播放速度。做船舶、车辆、人员的历史轨迹回放项目时,这个 demo 就是现成的骨架。
osgearth_weather是天气系统,提供雪、雨、雾、云等效果。跑起来后地形上会飘下雨丝或雪花,甚至能控制降雨强度。它不是简单地在场景里加粒子,而是把天气效果与相机状态、时间绑定在一起,做数字孪生和仿真的朋友可以多看两眼。
osgearth_particle更专注于粒子系统本身,它用 OSG 的粒子系统在地形上生成火焰、喷泉之类的效果。如果你需要做地物对象的动态效果(比如烟囱排烟、爆炸特效),这个 demo 可以作为粒子参数调优的试验场。
3.6 渲染进阶:着色器、多线程与多视图
osgearth_shaders是专门给图形程序员看的 demo。它演示了如何在地形渲染管线中注入自定义 GLSL 着色器代码,实现特效、滤镜、风格化渲染。我试过在这个 demo 基础上改一个"冰冻地形"的着色器,效果还不错。它能让你很快理解 osgEarth 的渲染器在哪些阶段可以挂钩子。
osgearth_many在一个窗口里创建了多个地球视图,每个地球加载不同的 earth 文件或同一 earth 的不同视图,适合做多屏对比、场景联动演示。做指挥调度大屏、多区域态势展示时,这个 demo 提供了不错的参考。
osgearth_multithread关注多线程渲染和线程配置,它展示如何在多核环境下合理分配渲染线程、资源加载线程。虽然 2.10 的线程模型和现在最新版本有差异,但理解它依然能帮你避免不少运行时崩溃和卡顿问题。
4. 三个典型 demo 的实战拆解
4.1 osgearth_viewshed:视域分析是怎么跑起来的
先准备一个带高程的 earth 文件,然后运行:
osgearth_viewshed simple.earth程序启动后,默认会加载一个视图分析工具。鼠标左键点在地面上,当前位置会生成一个观察点,程序开始计算该点的可视域,并在场景中叠加显示。计算范围默认是有限半径,你可以在程序运行后用键盘快捷键调整搜索半径。
从源码角度看,核心逻辑在osgearth_viewshed.cpp中:它创建了一个osgEarth::Util::Viewshed节点,设置了分析半径和采样间距,并将该节点挂到场景中。每次鼠标点击,程序把点击位置解析成地图坐标,然后调用视图节点的更新接口重新计算可视区域。
我在实际使用时最常调的参数有三个:采样间距、最大半径和可视颜色透明度。采样间距越小,结果越精细,但计算量成倍上涨;对于大范围分析,我会把采样间距从默认值加大不少,只求快速给出一个感性结果,精确分析再另外用离线算法做。这个取舍思路同样适用于其他做实时分析的场景。
4.2 osgearth_measure:量测工具里的坐标转换逻辑
osgearth_measure运行后可以直接在地形上量测距离和面积。左键逐点点击,会显示一条折线并实时更新总距离;右键闭合,会量测多边形覆盖的面积,数字悬浮在场景中。
这个 demo 里最值得学习的不是量测算法本身(那很简单),而是它如何把鼠标点击的屏幕坐标精确映射到地形表面坐标。它使用 OSG 的拾取能力获得命中点后,再通过 osgEarth 的GeoPoint类把三维坐标转换为经纬度高程。这一步理解透了,后续你做任何"点击地形获取坐标"的功能都会非常顺畅。
源码里还展示了一个细节:量测结果不是画在屏幕上,而是以三维注记的方式放在地形上,这样视角转动时数字始终贴在测量位置,不会漂移。这个交互细节我后来直接搬到了项目里。
4.3 osgearth_editable:要素编辑的完整交互闭环
osgearth_editable是一个功能相对完整的矢量编辑 demo。启动后你可以选择绘制模式(点、线、面),在地形上左键依次添加顶点,双击完成绘制。绘制完成后,还可以进入编辑模式,拖拽顶点改变形状,或者选中整个要素移动位置。
我跑这个 demo 时最关注的是它保存数据的部分。源码里通过 OGR 接口把编辑结果写回本地文件,这一步打通了"加载矢量-编辑-保存矢量"的闭环。很多项目只需要这个闭环,不需要更复杂的 GIS 桌面软件级功能,所以这个 demo 基本上是一个轻量标绘模块的起点。
从学习角度,我建议你把它的代码和osgearth_features对照着看:一个管静态展示,一个管动态编辑,两者配合就能理解 osgEarth 中矢量数据从模型生成到交互修改的整个链路。
5. 读透 earth 文件,才算真正会用 demo
5.1 一个典型 earth 文件的骨架
osgEarth 2.10 的 earth 文件本质是 XML,根节点是map,下面挂各种图层。最常用的是三类:image影像图层、elevation高程图层、model模型图层。
<map name="demo" type="geocentric"> <options> <cache type="filesystem"> <path>D:/cache</path> </cache> </options> <image name="basemap" driver="tms"> <url>https://example.com/tiles</url> <profile>global-geodetic</profile> </image> <elevation name="srtm" driver="tms"> <url>https://example.com/elevation</url> </elevation> <model name="buildings" driver="features"> <url>buildings.shp</url> <style type="text/css"> building { extrusion-height: 20; } </style> </model> </map>options 里可以配置缓存、光照等全局参数;image 和 elevation 决定地形长什么样;model 里可以加载矢量数据并做样式化渲染。demo 里凡是涉及特定数据加载的,通常都有自己的 earth 文件,你把它的数据源换成自己的,就能直接体验同样的功能。
5.2 把远程数据源换成自己的本地数据
我最常做的一步操作,就是把 demo 自带的远程数据源替换成本地文件。原因很简单:远程 TMS 服务受网络影响大,调试时不稳定;而且很多内网环境根本访问不了外网,离线数据才是常态。
替换方法很直接,把<image>或<elevation>里的 driver 从tms改成gdal,url指向本地影像或 DEM 文件:
<elevation name="local_dem" driver="gdal"> <url>D:/data/srtm_30m.tif</url> <height_offset>0</height_offset> </elevation>这里有个关键点:driver决定 osgEarth 用什么方式读取数据,不同 driver 对应不同数据源协议。gdal对应 GDAL/OGR 支持的光栅格式,tms对应在线瓦片服务,wms对应 WMS 服务。demo 里展示什么 driver,你就照着换成自己能访问的数据源即可。
5.3 用 options 控制 demo 的运行时行为
很多 demo 在运行时支持额外的命令行参数,也可以通过在 earth 文件的 options 区添加配置来改变行为。比如关闭地形光照:
<options> <lighting>false</lighting> </options>这个在调试纹理原始颜色的时候特别有用。开着光照,地形会因为太阳方向产生明暗变化,影响你对纹理真实色彩的判断;关掉光照后,纹理显示的就是原始采样颜色,排查影像偏移、颜色异常的问题效率高很多。
另外,想控制屏幕空间误差、远近裁剪面、地形细节层次等参数,也都可以在 options 里调整。我建议你在跑每个 demo 之前先看一眼它的 earth 文件里 options 写了什么,那通常就是该 demo 为了展示特定效果而设的关键参数。
6. 从 demo 到真实项目:移植思路与常见坑
6.1 demo 代码怎么搬进自己的 CMake 工程
看完 demo 之后,下一个问题必然是"怎么把功能搬到我自己的工程里"。最简单的方法是把 demo 的 cpp 文件直接复制过来,然后检查三点:头文件路径、链接库、earth 文件路径。
osgEarth 2.10 的 CMake 配置里,最简单的方式是用:
find_package(osgEarth REQUIRED) add_executable(my_app main.cpp) target_link_libraries(my_app osgEarth osgEarthUtil)如果你是直接引用源码里的例子工程,那更简单,直接把它整个 add_subdirectory 进来即可。不过要注意,例子工程默认会链接动态库,运行时需要把 osgEarth 的 DLL 或 so 路径加到环境变量里,否则会报找不到模块。
6.2 我踩过的运行环境坑
运行 osgEarth 2.10 的 demo,最常见的问题排行大致是:
- 缺少 DLL / 共享库。解决办法是把 osgEarth 和 OSG 的库目录加到系统 PATH,或者把库复制到 exe 同级目录。
- 插件目录找不到。osgEarth 和 OSG 都依赖插件机制,GDAL 读取、VPB 读取等都通过插件实现。如果运行时提示"no plugin found",需要检查 OSG 插件路径是否配置正确。
- 显卡和 OpenGL 版本问题。老笔记本或者远程桌面环境下,OpenGL 上下文可能受限制,现象是窗口黑屏或者直接崩溃。这个问题没有代码层面的通用解法,优先保证驱动更新到支持 OpenGL 3.3 以上。
- earth 文件路径里有中文或空格。在部分 Windows 环境下,这会造成数据加载失败,我的习惯是工程路径、数据路径全部用英文目录。
6.3 性能调优围绕 demo 参数的几点提示
demo 跑通了,接下来就是性能问题。2.10 版本的 osgEarth 对大数据量支持不错,但默认参数偏保守。我有几个调参经验:
- 如果地形场景加载缓慢,优先检查缓存配置,让瓦片能落到本地磁盘缓存,二次启动速度会快很多。
- 如果相机飞行时卡顿,观察屏幕空间误差设置,适当调大可以让地形细节层次切换更平滑。
- 如果叠加了大量矢量要素导致帧率下降,优先检查是否开启了要素抽稀和 LOD,很多情况下可以把不可见层级的要素直接剔除。
这些参数大部分都能在 earth 文件的 options 里配置,不用改代码。这也是 osgEarth 比较舒服的一点:数据与逻辑分离,调参不改代码,适合现场快速优化。
我个人在这些 demo 上花了不少时间,印象最深的反而不是某个酷炫效果,而是它们展现出来的工程化思路——每个功能都保持独立、可组合,场景数据用文本化 earth 文件描述,功能模块用节点方式挂载。这种设计让我后来在自己的项目里也养成了"功能节点化、配置外置化"的习惯。如果你刚接触 osgEarth,不要急着写大系统,先把 bin 目录里这些 demo 逐个跑一遍,再挑两三个和业务最相关的源码精读,比闷头看文档管用得多。