如果目标是做一个:
全国范围
高程 + 着色 + 矢量
三维地图
运行在 SylixOS 开发板
使用 OSMesa CPU 渲染
参考 Garmin 航电三维地形显示
我建议不要按普通桌面 GIS 或 Cesium 方式设计,而应该按航电嵌入式离线三维地形系统来设计。
核心原则是:
数据离线预处理
运行时只加载 GPS 附近小范围
CPU 渲染只画必要区域
地形显示服务于导航和态势感知
不是做通用三维地球
1. 总体推荐方案
高程数据
推荐使用:
标准 TMS TIFF 高程瓦片
或者进一步离线预处理成你自己的:
自定义二进制高程瓦片
第一阶段可以用:
水经微图 -> 导出瓦片 -> 标准 TMS 瓦片 -> TIFF(*.tif)
后续性能优化时转成:
.demtile / .bin
不推荐全国范围使用单块 GeoTIFF。
矢量数据
推荐:
离线矢量分层 + 分块 + 简化 + 空间索引
不要运行时直接读取全国 SHP。
建议离线处理成:
vector/L8/L9/L10/L11/
或者:
SQLite / SpatiaLite / 自定义二进制矢量瓦片
渲染方式
推荐:
OSMesa CPU 离屏渲染
地形 mesh 低面数
高程色带 + 简单光照
矢量贴地 / 悬浮
固定视角或航向跟随视角
不要追求真实影像贴图,也不要追求通用 3D 地球效果。
2. 系统目标应该像 Garmin,而不是 Cesium
Garmin 航电三维地图的重点通常不是“照片级真实”,而是:
飞行员能快速理解前方地形、障碍、航线、机场、空域、水系、边界
所以视觉目标应该是:
- 地形轮廓清晰。
- 高低起伏明显。
- 危险地形颜色醒目。
- 航向前方显示范围更大。
- 矢量要素简洁清楚。
- 帧率稳定优先于细节。
- 数据必须离线可靠。
- GPS 移动时不能频繁卡顿。
3. 推荐整体架构
离线数据处理工具↓
全国高程瓦片库
全国矢量瓦片库
索引文件
配置文件↓
SylixOS 运行时地图引擎↓
GPS / 航向 / 高度↓
加载当前附近数据↓
构建低面数地形 mesh↓
CPU OSMesa 渲染↓
显示到 Qt / framebuffer / 显示设备
运行时不要做复杂 GIS 处理,尽量把复杂工作放到 PC 端离线工具。
4. 数据组织推荐
4.1 高程目录
建议:
data/elevation/L7/x/y.tifL8/x/y.tifL9/x/y.tifL10/x/y.tifL11/x/y.tifL12/x/y.tif
或者 TMS 行列目录:
data/elevation/L11/3311/1415.tif1416.tif1417.tif
需要统一清楚:
level / row / column
并确认是:
TMS 行号
还是 XYZ 行号
这一点必须在项目早期固定下来。
4.2 高程级别建议
全国地图不要只做一个级别,建议至少准备多级:
| 用途 | 推荐级别 |
|---|---|
| 全国/省级远景 | L6-L8 |
| 中距离巡航 | L9-L10 |
| 近距离低空 | L11-L12 |
| 机场附近细节 | L13 可选 |
第一版可以先做:
L10 + L11 + L12
或者只做:
L11
先跑通系统。
4.3 矢量目录
建议分层:
data/vector/water/road/railway/airport/boundary/airspace/obstacle/
如果参考航电系统,重点不是所有 POI,而是:
水系
主要道路
铁路
机场
导航点
航线
行政边界
空域
障碍物
禁飞区 / 限制区
不要把餐饮、商店、生活 POI 全部加载进系统。
5. 运行时采用瓦片方案,而不是 GPS 固定大窗口
对于全国地图,我推荐:
GPS 当前 tile↓
显示 5x5↓
缓存 7x7
或者根据开发板性能调整为:
显示 3x3
缓存 5x5
不要全国场景仍然采用“从一个巨大 DEM 中按 GPS 裁剪窗口”的方案。
全国地图适合瓦片化,因为:
- 数据量巨大。
- GPS 连续移动时瓦片复用好。
- 容易按高度切换 LOD。
- 容易做磁盘缓存。
- 容易和矢量分块统一。
- 嵌入式系统更可控。
6. 推荐显示 / 缓存范围
开发板性能一般时
建议:
显示 3x3
缓存 5x5
也就是:
displayRadius = 1
cacheRadius = 2
优点:
- mesh 数量少。
- CPU 渲染压力低。
- 适合 OSMesa。
开发板性能较好时
可以:
显示 5x5
缓存 7x7
也就是:
displayRadius = 2
cacheRadius = 3
更适合视野开阔、飞机前方需要更大范围的场景。
航电推荐变体:前向窗口
Garmin 类三维地图不一定非要中心对称,可以做成:
前方多加载
后方少加载
左右适中
例如:
前方 5 行
后方 2 行
左右各 3 列
示意:
航向前方+----------------+| || || || GPS || |+----------------+
这比单纯 5x5 更符合飞行场景。
第一版可以先做中心 5x5,后续再改成航向前向窗口。
7. 地形 mesh 设计
不要每个高程像素都生成顶点。
每个 tile 生成固定网格 mesh:
| 模式 | 每 tile 网格 |
|---|---|
| 交互 / 移动中 | 17x17 |
| 正常 | 33x33 |
| 高清 | 65x65 可选 |
对 OSMesa CPU 渲染,推荐第一版:
每 tile 17x17 或 33x33
如果显示 5x5,33x33 的顶点数量约:
25 * 33 * 33 ≈ 27225 顶点
CPU 渲染基本可控。
如果显示 7x7,33x33:
49 * 33 * 33 ≈ 53361 顶点
也还能接受,但要看开发板 CPU。
8. 高程着色设计
参考 Garmin,应支持两类着色:
8.1 普通地形色带
用于常规三维地图:
低海拔:绿色
中海拔:黄绿 / 土黄
高海拔:棕色
极高:灰白
配合:
环境光 + 简单方向光
让地形起伏明显。
8.2 地形告警色带
航电系统非常重要。
根据飞机当前高度 AGL/MSL 与地形高度差:
terrainDelta = aircraftAltitude - terrainElevation
颜色可以类似:
| 高度差 | 颜色 |
|---|---|
| 地形高于飞机或接近 | 红色 |
| 低于飞机但危险 | 黄色 |
| 安全 | 绿色 / 棕色正常色 |
| 很远 / 不相关 | 暗化 |
例如:
terrainDelta < 0m -> 红色
0m ~ 300m -> 红色
300m ~ 700m -> 黄色
> 700m -> 正常地形色
具体阈值根据需求调整。
这样比单纯漂亮地形更接近 Garmin 航电系统。
9. 矢量渲染设计
矢量不要一股脑全部画。
应该按航电优先级分层:
9.1 必须显示
机场
跑道
航路点
航线
禁飞区 / 限制区
重要水系
国界 / 省界
9.2 可选显示
道路
铁路
城市轮廓
湖泊
河流
9.3 不建议第一版显示
餐饮
商店
小 POI
详细建筑
小区边界
9.4 矢量贴地方式
线和面要素:
根据高程采样贴地
加 1~5m 偏移避免 z-fighting
点和标签:
悬浮显示
根据距离控制大小和显隐
建议偏移:
| 类型 | 高度偏移 |
|---|---|
| 水系面 | +0.5m |
| 道路 | +2m |
| 边界线 | +5m |
| 机场/导航点 | +20m |
| 标签 | +50m 或屏幕空间绘制 |
10. OSMesa CPU 渲染的关键限制
OSMesa 是 CPU OpenGL,开发板上不能按桌面 GPU 思路做。
必须控制:
三角形数量
纹理数量
透明叠加
抗锯齿
频繁状态切换
动态内存分配
建议:
- 不用影像纹理。
- 地形用顶点色。
- 矢量尽量简化。
- 标签用 2D overlay 绘制。
- 移动时用低 LOD。
- 停止或低频时升级高 LOD。
- 尽量复用 mesh。
- OSMesa 渲染线程独立运行。
- 主线程只显示最后一帧图像。
- 避免每帧重建全部数据。
11. 推荐运行时线程模型
主线程 / UI↓
GPS 输入线程↓
Scene 构建线程↓
OSMesa 渲染线程↓
显示输出
典型流程:
GPS 更新↓
判断是否跨 tile / 是否需要重建↓
后台加载高程和矢量↓
构建 SceneBuffer↓
主线程切换 front buffer↓
渲染线程渲染↓
输出 QImage / framebuffer
必须使用:
front buffer / back buffer
不要让渲染线程读到正在修改的数据。
12. 推荐 SceneBuffer 结构
struct SceneBuffer
{int level;TileKey centerTile;QList<TileKey> displayTileKeys;QList<TileKey> cacheTileKeys;QList<TerrainMesh> terrainMeshes;QHash<TileKey, int> terrainMeshIndex;QList<VectorFeature> vectorFeatures;SceneCoordinateFrame coordinateFrame;double centerLon;double centerLat;double aircraftAltitude;double headingDegrees;bool finalLodReady;
};
terrainMeshes 可以包含缓存范围,比如 7x7。
渲染时只选:
displayTileKeys
或者选:
航向前方窗口 tile keys
13. 高程瓦片缓存
需要至少三层缓存:
13.1 文件路径索引缓存
启动时扫描或读取索引:
TileKey -> file path
不要每次 GPS 更新都遍历目录。
13.2 高程数据缓存
QHash<TileKey, ElevationTile>
缓存最近使用的高程 tile。
13.3 Mesh 缓存
QHash<TileKey, TerrainMesh>
GPS 跨 tile 后复用重叠部分。
例如 5x5 移动一格,只新增一列或一行。
14. LOD 设计
全国地图必须做 LOD,但第一版可以简化。
第一版
固定 level
固定网格 33x33
显示 5x5
缓存 7x7
第二版
根据飞机高度选择 level:
| 飞机高度 / 视距 | 高程 level |
|---|---|
| 高空 | L8-L9 |
| 中空 | L10-L11 |
| 低空 | L12-L13 |
第三版
视野中心近处高精度,远处低精度:
中心 tile:高 LOD
外围 tile:低 LOD
远方:更低 LOD
但是这会增加边界拼接复杂度,后期再做。
15. Garmin 风格视角设计
建议支持三种模式:
15.1 North Up
北向上
适合地图浏览
15.2 Track Up
航向向上
适合导航
15.3 3D Forward View
相机位于飞机后上方
看向航向前方
类似航电三维地形
3D Forward View 推荐:
camera behind aircraft
camera above aircraft
look ahead 5~20km
pitch down 20~45 degrees
16. 坐标系统建议
全国范围数据通常是 WGS84 经纬度。
运行时不要直接把经纬度当三维坐标。
推荐每个 SceneBuffer 建立局部 ENU 坐标框架:
origin = 当前 GPS 或 centerTile 中心
X = east meters
Y = up meters
Z = north meters / forward
转换:
lon/lat -> local east/north meters
elevation -> up meters
对于局部几十公里范围,可以用近似:
metersPerDegreeLat = 111320
metersPerDegreeLon = 111320 * cos(originLat)
如果要求更高精度,再引入投影库。
17. 数据离线预处理建议
强烈建议做一个 PC 端预处理工具,不要把水经微图导出的原始文件直接给开发板使用。
预处理内容:
- 统一目录结构。
- 生成 tile index。
- 检查缺失瓦片。
- 统计每 tile min/max elevation。
- 压缩高程数据。
- 简化矢量。
- 按 tile 切分矢量。
- 生成 manifest。
例如:
{"dataset": "china-terrain","projection": "WGS84","tileScheme": "TMS","levels": [8, 9, 10, 11, 12],"tileSize": 256,"bounds": [73.0, 18.0, 135.0, 54.0]
}
运行时先读 manifest.json,不要靠猜目录。
18. 是否使用 .terrain CES 瓦片?
不推荐作为第一选择。
原因:
.terrain 更适合 Cesium
自研解析复杂
CPU OSMesa 不需要 Cesium quantized mesh 那套复杂能力
你更适合:
TMS TIFF 高程瓦片
或者自定义轻量二进制高程瓦片。
后期如果要进一步优化,可以把每个 .tif 离线转成:
header + height array + min/max
例如:
struct DemTileHeader
{char magic[8];int level;int row;int column;int width;int height;double minLon;double minLat;double maxLon;double maxLat;float minElevation;float maxElevation;
};
后面跟:
int16 / float32 高程数组
这样 SylixOS 端读取比 TIFF 更轻。
19. 全国地图推荐开发路线
阶段 1:小区域原型
使用:
单块 GeoTIFF
GPS 中心范围
一个 TerrainMesh
高程着色
简单矢量
目的:
验证渲染、相机、着色、矢量贴地
阶段 2:局部瓦片化
使用:
一个省或一个区域的 TMS TIFF 高程瓦片
显示 3x3 / 缓存 5x5
目的:
验证瓦片加载、缓存、跨 tile 复用
阶段 3:全国瓦片库
使用:
全国 L8-L12 高程瓦片
矢量分块
manifest 索引
目的:
完整全国 GPS 定位加载
阶段 4:航电显示优化
加入:
地形告警色
航向前方窗口
机场/航线/空域
标签避让
低帧率稳定优化
20. 最终推荐方案一句话
如果最终目标是全国范围的 Garmin 风格航电三维地图,我推荐:
高程使用标准 TMS TIFF 瓦片或离线转换后的自定义高程瓦片;
运行时根据 GPS 计算当前瓦片;
显示 3x3 或 5x5,缓存 5x5 或 7x7;
每个瓦片生成低面数 TerrainMesh;
使用高程色带 + 地形告警色 + 简单光照;
矢量数据离线分层分块,运行时只加载当前周边;
OSMesa CPU 渲染只画必要 mesh 和必要矢量。
更具体的第一版全国架构建议是:
数据:TMS TIFF 高程瓦片 L10-L12分块 SHP/GeoJSON/自定义矢量瓦片manifest 索引运行:GPS -> 当前 tiledisplay 3x3 或 5x5cache 5x5 或 7x7高程 tile -> TerrainMesh 33x33高程色带 + 航电告警色矢量贴地/悬浮OSMesa 后台线程渲染
这样既适合全国数据规模,也适合 SylixOS 开发板和 CPU 渲染约束。
