# Earth Real Terrain Plan ## Goal 将 Earth 页当前的“程序噪声假地形”替换成基于真实 DEM 的可用地形层,使 `地形 terrain` 开关真正显示全球海拔起伏,而不是占位效果。 当前占位实现位于: - [earth.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/earth.js) 具体问题: - `createTerrain()` 直接对球体顶点应用 `simplex noise` - 没有真实海拔数据来源 - 没有分辨率分层 - 没有和当前相机/视角配套的性能控制 ## Constraints 本计划必须贴合当前 Earth 架构,而不是引入一套全新的地形引擎: - 地球主体仍然是一个 Three.js sphere - 海缆、登陆点、卫星、BGP 都已经建立在当前球体坐标系之上 - 不能为了地形把整页改成 Cesium/MapLibre Globe 之类的全栈替换 - 第一阶段优先做“真实可用”,不是一步到位做摄影测量级地形 ## Recommended Data Source ### Primary recommendation 使用公开的 Terrarium 编码高程瓦片作为浏览器端高度来源,第一阶段优先接入: - Mapzen/AWS `Terrarium` elevation tiles 参考:[Mapzen terrain tile format / Terrarium](https://www.mapzen.com/blog/terrain-tile-service/) 原因: - 已经是全球瓦片化高程 - 浏览器端按 tile 请求,最适合当前 Earth 这种在线 globe - 编码简单稳定: - `heightMeters = (R * 256 + G + B / 256) - 32768` - 不需要我们先离线拼整球 DEM ### Data quality upgrade path 如果后面第一阶段效果确认可用,再逐步升级到底层源: - Copernicus DEM GLO-30 参考:[Copernicus DEM docs](https://documentation.dataspace.copernicus.eu/APIs/SentinelHub/Data/DEM.html) - 或用 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 高程 - 将高程重采样到当前地形球体网格 2. 替换 `createTerrain()` 当前: - 在 [earth.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/earth.js) 中同步生成噪声地形 调整后: - `createTerrain()` 只负责创建 terrain mesh 骨架 - 真正的顶点位移由 terrain 模块异步注入 3. 第一阶段采用“整球低分辨率位移” 不要一上来做动态 patch stitching。第一阶段更稳的办法是: - 保留一张全球 terrain sphere - 使用较低分辨率几何 - 例如 `SphereGeometry(radius, 192, 192)` 或 `256/256` - 运行时按一个固定地形 zoom(如 `z=4` 或 `z=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 参数之前,建议先插入一个“校准参照层”阶段。 ### Recommended order for the calibration layer 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 ### Recommended execution path #### 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 是“参数没调好”,还是“整条实现路径有偏移” ## Recommended Geometry Model ### 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 像素: ```text 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` ## Material Strategy 第一阶段不要复杂化。 建议 terrain material: - 半透明低饱和地形色 - 比 base earth 稍亮或稍偏冷 - 保留当前 HUD 风格下的可读性 第一阶段不需要: - 真实土地覆被纹理 - 独立卫星影像贴 terrain 因为那会和现有地球纹理、云层、昼夜 shader 打架。 ## Integration Points ### Files to change - [earth.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/earth.js) - 重写 `createTerrain()` - 删除 simplex noise 占位逻辑 - [main.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/main.js) - 初始化 terrain 数据加载 - 控制 terrain readiness / loading message - [controls.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/controls.js) - `toggleTerrain` 逻辑保持,但应能区分: - mesh 已就绪 - 正在加载 - [constants.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/constants.js) - 新增 `TERRAIN_CONFIG` - 新文件: - `frontend/public/earth/js/terrain.js` ### Suggested new config 建议新增: ```js 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 - Mapzen Terrarium / AWS terrain tiles [Mapzen Terrain Tile Service](https://www.mapzen.com/blog/terrain-tile-service/) - Terrarium tile experiments / format background [mapzen/terrarium](https://github.com/mapzen/terrarium) - Copernicus DEM overview [Copernicus DEM docs](https://documentation.dataspace.copernicus.eu/APIs/SentinelHub/Data/DEM.html) ## Recommendation Summary 如果现在就要开始做,我建议直接按这条路线开工: - 第一阶段接入 Terrarium 全球高程 tile - 替换掉当前 simplex 假地形 - 先做一层真实可见的全球 terrain overlay - 等第一阶段稳定,再做视角高分 refinement 这是对当前项目风险最低、最贴合现有 Earth 架构的一条路。