Files
planet/docs/plans/earth-real-terrain-plan.md
2026-04-21 22:49:39 +08:00

12 KiB
Raw Blame History

Earth Real Terrain Plan

Goal

将 Earth 页当前的“程序噪声假地形”替换成基于真实 DEM 的可用地形层,使 地形 terrain 开关真正显示全球海拔起伏,而不是占位效果。

当前占位实现位于:

具体问题:

  • createTerrain() 直接对球体顶点应用 simplex noise
  • 没有真实海拔数据来源
  • 没有分辨率分层
  • 没有和当前相机/视角配套的性能控制

Constraints

本计划必须贴合当前 Earth 架构,而不是引入一套全新的地形引擎:

  • 地球主体仍然是一个 Three.js sphere
  • 海缆、登陆点、卫星、BGP 都已经建立在当前球体坐标系之上
  • 不能为了地形把整页改成 Cesium/MapLibre Globe 之类的全栈替换
  • 第一阶段优先做“真实可用”,不是一步到位做摄影测量级地形

Primary recommendation

使用公开的 Terrarium 编码高程瓦片作为浏览器端高度来源,第一阶段优先接入:

原因:

  • 已经是全球瓦片化高程
  • 浏览器端按 tile 请求,最适合当前 Earth 这种在线 globe
  • 编码简单稳定:
    • heightMeters = (R * 256 + G + B / 256) - 32768
  • 不需要我们先离线拼整球 DEM

Data quality upgrade path

如果后面第一阶段效果确认可用,再逐步升级到底层源:

  • Copernicus DEM GLO-30
    参考:Copernicus DEM docs
  • 或用 Copernicus / SRTM / ASTER 等离线切成我们自己的 terrain tiles

这条升级路径适合第二阶段,不建议一开始就直接自建全球瓦片服务。

Why Not Replace the Engine

不建议为了地形直接切到 Cesium terrain / quantized mesh 引擎,原因:

  • 现有 Earth 业务对象都依附当前球面坐标
  • 切引擎会同时波及:
    • 海缆绘制
    • 卫星/轨迹
    • BGP 标记
    • HUD 与交互
  • 这是“重做一页”,不是“给地形层接真实数据”

所以推荐路线是:

  • 保持当前 sphere globe
  • 为 sphere 增加真实高度位移层

Implementation Strategy

分三期推进。

Phase 1 — Global Heightmap Terrain Overlay

目标:

  • 地形层切换后显示真实海拔起伏
  • 全球范围可用
  • 性能可控

做法:

  1. 新增 terrain 数据模块

建议文件:

  • frontend/public/earth/js/terrain.js

职责:

  • 选择 DEM zoom level
  • 请求 Terrarium tiles
  • 解码 tile 高程
  • 将高程重采样到当前地形球体网格
  1. 替换 createTerrain()

当前:

  • earth.js 中同步生成噪声地形

调整后:

  • createTerrain() 只负责创建 terrain mesh 骨架
  • 真正的顶点位移由 terrain 模块异步注入
  1. 第一阶段采用“整球低分辨率位移”

不要一上来做动态 patch stitching。第一阶段更稳的办法是

  • 保留一张全球 terrain sphere
  • 使用较低分辨率几何
    • 例如 SphereGeometry(radius, 192, 192)256/256
  • 运行时按一个固定地形 zoomz=4z=5)抓取覆盖全球的 Terrarium tiles
  • 将 tile 解码后重投影到经纬度采样网格
  • 将每个球面顶点按真实高度抬升

这样第一阶段就能做到:

  • 有真实地形
  • 不需要复杂的局部 LOD
  • 不会让现有球体对象体系爆炸

Phase 2 — View-Aware Refinement

目标:

  • 正面可见区域更精细
  • 背面与远处维持低成本

做法:

  • 引入“基础全球地形 + 当前视角高分局部补丁”
  • 正面区域额外抓更高 zoom 的高程 tile
  • 只替换局部顶点位移或局部 overlay mesh

这一阶段适合在第一阶段稳定后做。

Phase 3 — Normals / Shading / Terrain UX

目标:

  • 地形不仅有起伏,还更好看、更可读

包括:

  • 根据高度生成更合理的 normals
  • 调整 terrain material使山脉/高原更易读
  • 可选加入:
    • hillshade
    • contour lines
  • snowline / bathymetry tint

Calibration Overlay Before More Terrain Tuning

在当前项目里terrain 看起来“不像真地形”,不一定只是 DEM 或 exaggeration 不够,也可能是因为缺少稳定参照物。

没有清晰的海岸线、国界线和地表分层时,人眼很难判断:

  • 山脉是不是在应该高的地方高
  • terrain 是否真的贴在正确的大陆位置上
  • 地球纹理、本初子午线、terrain 采样之间是否存在偏移

这里要明确区分两件事:

  • 国界线不会修好错误的 terrain
  • 但海岸线 / 国界线会让我们更容易判断 terrain 有没有贴准

所以在继续盲调 terrain 参数之前,建议先插入一个“校准参照层”阶段。

  1. 海岸线
  2. 国界线
  3. 再继续调 terrain

原因:

  • 海岸线比国界线更基础,也更接近真实地表边界
  • 判断 terrain 是否贴准,最重要的是大陆边缘和山脉/海岸关系
  • 国界线更多是政治边界,只能作为辅助参照

如果只加国界线,不加海岸线,效果仍然可能会怪,因为:

  • 很多国界线本来就是人为直线
  • 它们并不总是跟真实地形走

Suggested layer order during debugging

建议调试期临时把地球层次明确成:

  1. base earth texture
  2. coastline / borders overlay
  3. terrain relief
  4. cables / landing points / bgp / satellites

这样会比现在更容易判断:

  • 山脉是否位于正确区域
  • terrain 是否和地表对齐
  • 国界/海岸是否漂移

Suggested data source for the calibration overlay

优先用 Natural Earth 的轻量全球矢量数据:

  • 海岸线coastline
  • Admin 0 国界线country borders

优点:

  • 全球一致
  • 轻量
  • 很适合当前 Three.js globe 做 overlay

Phase A — Add reference overlays

先加两层可开关的参考线:

  • 海岸线
  • 国界线

这两层的目标不是最终美术表现,而是调试 / 校准。

Phase B — Recalibrate terrain against coastline

有了海岸线以后,再重新看 terrain

  • terrain 是否和大陆边缘错位
  • 地球纹理、本初子午线、terrain 采样之间是否有固定偏移

Phase C — Decide whether to keep the current terrain path

这时再决定后面的路线:

  • 如果发现真实高程整体是对的,只是缺少 shading / readability
    继续保留当前 DEM + terrain overlay 路线
  • 如果发现整球采样投影、本初子午线或 overlay 关系本身就很别扭
    再考虑重做 terrain pipeline

Practical recommendation

当前阶段不建议“从头开始重做 terrain”。

更稳的策略是:

  • 暂停继续盲调 terrain 参数
  • 先补海岸线 / 国界线作为校准参照层
  • 再基于参照层判断 terrain 是“参数没调好”,还是“整条实现路径有偏移”

First usable model

保留一层独立 terrain sphere

  • base earth sphere贴纹理、昼夜、海洋
  • terrain sphere略高于地球半径真实高程位移

建议:

  • terrainBaseRadius = CONFIG.earthRadius + 0.2
  • 高度缩放使用真实米制换算,再乘一个可调 exaggeration

示例关系:

  • heightWorld = (elevationMeters / 6371000) * CONFIG.earthRadius * exaggeration

建议第一阶段 exaggeration = 1.3 ~ 1.8

因为完全真实比例在全球球体上会太平,看不出来。

Tile Decoding Plan

Terrarium decode

对于每个高程 tile 像素:

heightMeters = (R * 256 + G + B / 256) - 32768

Sampling path

对于 terrain mesh 上每个顶点:

  1. 将顶点方向转成经纬度
  2. 将经纬度映射到 Web Mercator tile 坐标
  3. 找到对应的 tile 和像素
  4. 解码高程
  5. 将顶点沿法线方向抬升

Needed helpers

建议新增:

  • latLonToTileXY(lat, lon, z)
  • tilePixelFromLatLon(lat, lon, z, tileSize)
  • decodeTerrariumHeight(r, g, b)

Caching Strategy

为了不让地形开关每次重开都重新抓全量 tile

  • terrain tile 按 z/x/y 存到内存缓存
  • terrain mesh 结果也缓存一份
  • 当用户关闭/开启 terrain
    • 直接复用已有位移结果

建议:

  • Map<string, Float32Array | ImageBitmap>

Material Strategy

第一阶段不要复杂化。

建议 terrain material

  • 半透明低饱和地形色
  • 比 base earth 稍亮或稍偏冷
  • 保留当前 HUD 风格下的可读性

第一阶段不需要:

  • 真实土地覆被纹理
  • 独立卫星影像贴 terrain

因为那会和现有地球纹理、云层、昼夜 shader 打架。

Integration Points

Files to change

  • earth.js
    • 重写 createTerrain()
    • 删除 simplex noise 占位逻辑
  • main.js
    • 初始化 terrain 数据加载
    • 控制 terrain readiness / loading message
  • controls.js
    • toggleTerrain 逻辑保持,但应能区分:
      • mesh 已就绪
      • 正在加载
  • constants.js
    • 新增 TERRAIN_CONFIG
  • 新文件:
    • frontend/public/earth/js/terrain.js

Suggested new config

建议新增:

export const TERRAIN_CONFIG = {
  enabled: true,
  tileSize: 256,
  baseZoom: 4,
  baseRadiusOffset: 0.2,
  exaggeration: 1.5,
  opacity: 0.55,
  color: 0x6c876f,
  maxConcurrentRequests: 8,
  cacheEnabled: true,
};

Loading UX

地形第一次开启时,不能像现在一样瞬时切换。

建议:

  • 如果地形数据尚未准备:
    • 顶部状态条显示:正在加载真实地形数据...
  • 完成后:
    • 真实地形已就绪

如果加载失败:

  • 保留 base earth
  • 显示轻量错误提示
  • 不要让 terrain 开关卡死在“开”状态

Risks

1. Global tile count too high

即使 z=5 全球 tile 数也不少。

缓解:

  • 第一阶段限定低 zoom
  • 并发上限
  • 缓存

2. Mesh resolution too low

如果球面分段太低,山脉会被抹平。

缓解:

  • 第一阶段先选一个中等分辨率
  • 用 exaggeration 保证可见性

3. Existing overlays may z-fight with terrain

海缆、登陆点、BGP、卫星相关对象都假设地球半径固定。

缓解:

  • terrain sphere 单独作为 overlay
  • overlay 保持略低或略高的固定 offset
  • 必要时局部调整 landing point / cable altitude offset

4. Mercator sampling distortion near poles

Web Mercator 在高纬会有失真。

缓解:

  • 第一阶段接受
  • 后续若需要更严格极区质量,再上 geodetic reprojection pipeline

Acceptance Criteria

第一阶段完成后,应满足:

  1. 地形 terrain 开关开启时,地表起伏明显不再是随机噪声
  2. 喜马拉雅、安第斯、落基山、东非高原等全球大尺度地形可辨认
  3. 关闭/重新开启 terrain 不重复全量请求
  4. 不破坏:
    • 海缆
    • 卫星
    • BGP
    • 地球昼夜
    • 天球层

Suggested Execution Order

  1. 引入 TERRAIN_CONFIG
  2. 新建 terrain.js
  3. 实现 Terrarium tile 请求与 decode
  4. 用低 zoom 全球 tile 构建真实 terrain sphere
  5. 接管 toggleTerrain()
  6. 调整 terrain material 和高度 exaggeration
  7. 做缓存
  8. 再考虑第二阶段局部高分 refinement

Source References

Recommendation Summary

如果现在就要开始做,我建议直接按这条路线开工:

  • 第一阶段接入 Terrarium 全球高程 tile
  • 替换掉当前 simplex 假地形
  • 先做一层真实可见的全球 terrain overlay
  • 等第一阶段稳定,再做视角高分 refinement

这是对当前项目风险最低、最贴合现有 Earth 架构的一条路。