把原始地图数据整理成 OpenGL 能画的三维地形数据
SceneBuilder::build() 可以理解成:把原始地图数据整理成 OpenGL 能画的三维地形数据。
它的整体流程是:
SceneBuildRequest
-> 读取高程/矢量数据
-> 生成 TerrainMesh
-> 塞进 SceneData
-> 返回 SceneBuildResult
build() 主逻辑
- 检查数据配置是否有效
if (!request.dataset.isValid())
比如高程 TIFF 路径为空,就直接失败。
- 读取地图数据
MapDataResult dataResult =
m_mapDataReader.read(request.dataset, request.mapBounds);
这里会读取:
高程 GeoTIFF
水系 SHP
机场 SHP
并按 request.mapBounds 裁剪。
- 判断数据是否读取成功
if (!dataResult.success)
如果高程读失败,场景就没法构建。
- 创建
SceneData
SceneData scene;
scene.elevationBlock = dataResult.elevationBlock;
scene.waterFeatures = dataResult.waterFeatures;
scene.airportFeatures = dataResult.airportFeatures;
scene.mapBounds = dataResult.resolvedMapBounds;
这一步只是把原始数据放进场景对象。
- 构建地形网格
buildTerrainMesh(scene.elevationBlock,
scene.mapBounds,
&scene.terrainMesh,
&meshError)
这一步最关键:把高程矩阵变成 OpenGL 更容易画的网格。
- 校验场景
if (!scene.isValid())
确保高程和 mesh 都有效。
- 返回结果
result.scene = scene;
result.success = true;
OpenGL 里什么是顶点、索引、法线
假设你的高程块是 3 x 3:
A --- B --- C
| | |
D --- E --- F
| | |
G --- H --- I
每个格点都有一个地图坐标和高程。
这些点变成三维坐标后,就是 顶点 vertices:
A = (x, y, z)
B = (x, y, z)
C = (x, y, z)
...
z 就是高程。
但是 OpenGL 画面通常画的是三角形,不是直接画一个“格子”。
一个四边形格子要拆成两个三角形:
A --- B
| / |
| / |
D --- E
可以拆成:
A, D, B
B, D, E
这些点在 vertices 数组里有编号:
A=0, B=1, C=2
D=3, E=4, F=5
G=6, H=7, I=8
所以三角形不用重复存点,只存点的编号。这就是 索引 indices:
indices = {
0, 3, 1,
1, 3, 4
};
意思是:
第一个三角形:顶点 0、3、1
第二个三角形:顶点 1、3、4
法线 normals 是什么
法线就是每个顶点“朝哪个方向”。
它主要用于光照。
比如一块平地,法线大概朝上:
(0, 0, 1)
山坡上的法线会斜一点。OpenGL 根据法线判断:
这个面朝不朝光源?
亮一点还是暗一点?
没有法线,地形可能看起来很平、很假,或者光照不对。
TerrainMesh 里各数组含义
vertices
三维顶点坐标。OpenGL 主要画它。
indices
三角形索引。告诉 OpenGL 哪三个顶点组成一个三角形。
normals
每个顶点的法线,用于光照。
texCoords
纹理坐标。如果后面把影像贴到地形上,用它决定图片贴在哪里。
mapCoords
每个顶点对应的真实地图坐标,比如经纬度。用于调试、拾取、叠加矢量。
elevations
每个顶点的原始高程值。
validMask
标记这个点是不是有效高程:
1 有效
0 NoData / 无效
一句话总结:
build() 是把“地图数据”变成“场景数据”;buildTerrainMesh() 是把“高程像素矩阵”变成 OpenGL 能画的“三角形网格”。

浙公网安备 33010602011771号