# Earth 天球背景与日月位置实施方案 ## 目标 为 Earth 大屏增加一套真正可用的天文背景层,覆盖三件事: 1. 用真实天球背景替换当前随机星点 2. 在当前时间下显示太阳与月亮的相对位置 3. 让太阳方向同时驱动地球受光,形成更可信的昼夜关系 本方案优先追求: - 与当前 Three.js Earth 架构兼容 - 风险可控 - 先落地一版真实感明显提升的 V1 - 为后续更严格的天文参考系升级预留余地 ## 当前现状 当前 Earth 的基础条件已经具备: - 地球、云层、地形、网格都基于 Three.js,主渲染入口在 [frontend/public/earth/js/main.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/main.js) - 地球实体创建在 [frontend/public/earth/js/earth.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/earth.js) - 当前所谓“宇宙背景”只是 `createStars()` 生成的随机星点,不是真实星图 - Earth 已有倾角常量 `EARTH_CONFIG.tiltRad`,位于 [frontend/public/earth/js/constants.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/constants.js) - 主循环 `animate()` 已稳定运行,可在其中接入天体更新逻辑 这意味着: - 不需要重写 Earth - 可以在现有 scene/world 层新增一个 celestial layer - 第一阶段不必拆 Earth / satellite / cable 的参考系 ## 总体策略 采用“两层现实”设计: ### 1. 世界层(world-space celestial layer) 用于放置: - 天球背景 - 太阳 - 月亮 - 太阳光方向 这些对象不挂在 `earthObj` 上,而是直接放在 `scene` 中。 ### 2. 地球层(earth-fixed layer) 继续保持当前结构: - 海缆 - 登陆点 - 卫星点与轨迹 - BGP 覆盖 - 地球纹理、云层、地形 这些对象继续挂在 `earthObj` 下,不打断现有交互。 ## 为什么先这样做 当前用户交互是“拖动地球本体”,而不是“移动相机绕惯性系观测”。 如果现在直接做严格惯性参考系改造,会同时影响: - `earthObj.rotation` - 卫星轨迹与锁定逻辑 - 海缆与登陆点附着关系 - resetView / autoRotate / hover / click 等交互链路 所以第一阶段只做: - 真正的天空 - 真正的日月方向 - 不碰现有 Earth 附着对象的语义 ## 推荐技术选型 ### 天文计算库 推荐: - [Astronomy Engine](https://github.com/cosinekitty/astronomy) 原因: - 有 JavaScript 版本 - 支持 Sun / Moon 的矢量与坐标变换 - 精度、可扩展性都比轻量太阳高度角库更适合本项目 - 后续若要加行星、月相、黄道、赤道网,也能继续沿用 不作为主选的库: - [SunCalc](https://github.com/mourner/suncalc) 原因: - 更偏本地观察者视角的太阳/月亮高度角 - 用于“地面日出日落”很好 - 但不如 Astronomy Engine 适合做真实天球与后续空间参考系扩展 ### Three.js 表现层 推荐组合: - 天球:内翻球壳 + 星图纹理 - 太阳:`THREE.Sprite` - 月亮:`THREE.Sprite` 或小型 `THREE.Mesh` - 太阳光:`THREE.DirectionalLight` 参考: - [Three.js SpriteMaterial](https://threejs.org/docs/pages/SpriteMaterial.html) ## 天球背景资源与星体数据来源 为避免把“视觉背景”和“可计算天体位置”混为一谈,本方案明确分成两类资源: ### 1. 背景资源:全天星图贴图 用于 Phase 1 的“真实天空背景”。 推荐优先来源: - NASA SVS 的 Tycho 全天星图 - [The Tycho Catalog Skymap - Version 2.0](https://svs.gsfc.nasa.gov/3572/) - NASA Deep Star Maps 2020 - SatelliteMap.space 在 credits 中明确提到其使用了 `NASA Deep Star Maps 2020 - High-resolution star field (1.7 billion stars from Gaia DR2)` 作为星空视觉资源 - 这说明行业内成熟实现并不一定直接渲染全部星表点,而很可能先使用一张高质量官方深空星图作为背景层 - 如需后续替换,也可评估 ESA / Gaia 的全天 sky map 资源 - [Gaia DR3 stories](https://www.cosmos.esa.int/web/gaia/dr3-stories) 建议要求: - 使用官方来源或官方衍生可复用资源 - 等距矩形投影(equirectangular) - 坐标定义尽量明确为赤道坐标展开 - 分辨率建议至少 `4k` - 颜色不要过亮,避免压过 Earth HUD 前景 - 尽量优先选择官方天文机构已经生产好的深空图,而不是自行拼接低质量星空纹理 建议本地资源目录: - `frontend/public/earth/assets/celestial/starmap_equatorial_4k.jpg` ### 2. 位置数据:星表与天体计算 用于 Phase 2+ 的“位置正确的星体”。 推荐来源分两层: - 太阳、月亮位置 - 使用 [Astronomy Engine](https://github.com/cosinekitty/astronomy) - 恒星位置 - 第一优先:Hipparcos / Tycho - [Hipparcos overview](https://www.cosmos.esa.int/web/Hipparcos) - [Hipparcos catalogues](https://www.cosmos.esa.int/web/hipparcos/catalogues) - 第二优先:Gaia - [Gaia DR3 stories](https://www.cosmos.esa.int/web/gaia/dr3-stories) 建议策略: - V1:背景球壳只用全天星图,不立即生成全量恒星点 - V2:只挑选亮星(例如星等 `< 5.5`)生成恒星点层 - V3:如果确实需要更丰富的星场,再逐步扩展到更深星等 这样做的原因: - 背景球壳负责“天球真实感” - 亮星点负责“位置正确、可后续标注和高亮” - 不需要一开始就处理数十万甚至数百万颗星 ### 3. 对外部成熟实现的参考结论 `SatelliteMap.space` 的公开 credits 提供了一个很有价值的参考样板: - 图形渲染使用 `TWGL.js` - 天文计算使用 `Skyfield` 与 `Astronomia` - 星空/天球视觉资源使用 `NASA Deep Star Maps 2020` 这给本项目的启发是: - “真实感强的天球背景”完全可以先依赖官方高质量深空图 - “位置正确的动态天体”则应依赖单独的天文计算链路 - 没有必要在第一版就直接渲染完整星表 因此本项目推荐继续坚持两层拆分: - 背景层:官方深空图 / 全天星图 - 计算层:太阳、月亮与后续亮星点 ## 如何保证星体位置正确 位置正确不是只看“图看起来像”,而是要统一参考系和转换链路。 ### 1. 统一坐标基准 本方案推荐统一使用: - `J2000` 赤道坐标系作为恒星位置基准 原因: - Hipparcos / Tycho 资料和大量天文可视化都容易映射到该基准 - 太阳、月亮也可以通过 Astronomy Engine 转到同一坐标系 - 这样背景、恒星点、太阳、月亮就能共用一套 sky orientation ### 2. 背景贴图与点位必须使用同一展开逻辑 如果背景球壳使用赤道坐标全天图,那么: - 亮星点也必须按赤道坐标贴到同一球面方向 - 太阳/月亮 sprite 也必须按赤道坐标转换后落到同一 world-space 否则会出现: - 背景银河带是对的 - 但太阳/月亮或亮星点飘到不匹配的位置 ### 3. RA / Dec 到 Three.js 坐标的落点方式 亮星点和日月方向最终都要转成单位球面向量。 概念步骤: 1. 读取赤经 `RA` 2. 读取赤纬 `Dec` 3. 转成弧度 4. 映射到单位球面向量 5. 再根据 Three.js 当前世界坐标定义做轴向映射 参考公式: ```text x = cos(dec) * cos(ra) y = sin(dec) z = cos(dec) * sin(ra) ``` 实际接入 Three.js 时,需要做一次项目内坐标轴校准: - 验证 `RA = 0h` - 验证 `RA = 6h` - 验证北天极 - 验证银河带主方向 然后确定最终的: - `x/y/z` 对应 Three.js 哪个轴 - 是否需要 `z` 取反 - 是否需要整体再做一个固定 `rotation` 建议把这层显式封装在: ```js function equatorialToWorldVector(raRad, decRad) ``` 不要把轴映射散落在不同模块里。 ### 4. 背景球壳与恒星点的关系 推荐最终组合: - 背景层:全天星图球壳 - 点位层:亮星点 - 动态层:太阳 / 月亮 这样有三个好处: - 背景层提供密集真实的天空纹理 - 亮星点提供位置正确、可扩展的标注基础 - 太阳/月亮提供与时间相关的真实动态对象 ## 数据与资源建议清单 ### 推荐首批引入资源 1. 全天星图 - 来源:NASA Tycho all-sky map - 用途:背景球壳纹理 2. 月亮纹理 - 用途:Phase 4 月相表现 - 路径建议: - `frontend/public/earth/assets/celestial/moon_albedo_2k.jpg` 3. 太阳 glow 贴图 - 用途:太阳 sprite halo - 路径建议: - `frontend/public/earth/assets/celestial/sun_glow.png` ### 推荐首批数据文件 如果要上亮星层,建议新增一个预处理后的轻量数据文件: - `frontend/public/earth/assets/celestial/bright-stars.json` 建议字段: ```json [ { "id": 32349, "name": "Sirius", "raDeg": 101.2875, "decDeg": -16.7161, "mag": -1.46, "colorIndex": 0.00 } ] ``` 建议不要在浏览器里直接吞原始 Gaia 大表,而是先离线裁剪成: - 只保留亮星 - 只保留渲染必需字段 - JSON 或二进制轻量格式 ## 资源与数据实施路线 ### 路线 A:先做可用版本(推荐) 1. 引入 NASA Tycho 全天图 - 或评估替换为更接近 SatelliteMap.space 路线的 `NASA Deep Star Maps 2020` 2. 实现背景球壳 3. 用 Astronomy Engine 计算太阳/月亮方向 4. 暂不做亮星点 优点: - 最快见效 - 风险最低 - 就能明显提升天球真实感 ### 路线 B:在 A 基础上增强 1. 离线生成 `bright-stars.json` 2. 浏览器端渲染亮星点 3. 后续可加: - 星座线 - 亮星名称 - 特定星体高亮 优点: - 背景真实感和“位置正确的可交互星体”同时兼顾 ## 代码模块建议细化 ### 新增模块 - `frontend/public/earth/js/celestial.js` - 管理天球背景 - 管理太阳/月亮 - 管理亮星层(后续) - `frontend/public/earth/js/celestial-data.js` - 资源路径 - 星图方向配置 - 亮星数据加载(后续) ### 建议函数设计 ```js export function initCelestialLayer(scene) export function updateCelestialLayer(date) export function setCelestialVisibility(visible) export function disposeCelestialLayer() function loadStarMapTexture() function createSkySphere(texture) function createSunSprite() function createMoonSprite() function getSunEquatorialPosition(date) function getMoonEquatorialPosition(date) function equatorialToWorldVector(raRad, decRad) ``` ### 推荐后续预处理脚本 如要引入亮星层,建议单独做离线脚本: - `scripts/build_bright_stars.py` 职责: - 从 Hipparcos / Tycho 源数据读取 - 过滤亮星 - 生成 `bright-stars.json` 这样浏览器端只消费轻量结果,不承担大表解析成本。 ## 分阶段实施 ## Phase 1:真实天球背景 ### 目标 用真实全天星图替换当前随机星点背景。 ### 做法 1. 新增一张全天星图纹理 建议路径: - `frontend/public/earth/assets/celestial/starmap_equatorial_4k.jpg` 纹理要求: - 等距矩形投影 - 赤经/赤纬坐标展开 - 无地平线、无地景遮挡 - 尽量深色、弱干扰,适合大屏 HUD 叠加 2. 新增天球球壳 新增模块: - [frontend/public/earth/js/celestial.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/celestial.js) 建议接口: ```js export function initCelestialLayer(scene) export function updateCelestialLayer(date, camera, earth) export function disposeCelestialLayer() ``` 3. 实现一个大半径内翻球体 建议参数: - 半径:`600 ~ 900` - 材质:`MeshBasicMaterial` - `side: THREE.BackSide` - 不受场景光照影响 - 始终围绕场景中心 ### 验收标准 - 初始加载后背景不再是随机星点 - 旋转地球时,背景保持为稳定天球而不是跟地球一起转 - 不明显干扰海缆/卫星/BGP 的前景识别 ## Phase 2:太阳与月亮真实位置 ### 目标 在当前 UTC 时间下,计算太阳与月亮在天球中的方向,并显示出来。 ### 做法 1. 在 `celestial.js` 内封装天体位置计算 建议函数: ```js function getSunDirection(date) function getMoonDirection(date) ``` 输出统一为 world-space `THREE.Vector3` 2. 太阳显示 - 一个暖色发光 sprite - 比月亮更大、更亮 - 可选添加柔和 halo 3. 月亮显示 - 一个较小 sprite 或 sphere - 灰白偏冷色 - 后续 Phase 3 再做月相 4. 更新频率 不要每帧重新做完整天文计算,建议: - 每 30 秒或 60 秒重算一次真实位置 - 渲染帧内做平滑过渡 ### 验收标准 - 页面可见太阳与月亮两个对象 - 时间变化时位置会更新 - 日月不会跟随地球局部旋转而错误附着 ## Phase 3:太阳驱动地球受光 ### 目标 让地球光照方向与太阳方向一致,不再使用写死的固定主光。 ### 做法 1. 替换或接管当前主定向光 当前 [frontend/public/earth/js/main.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/main.js) 中 `addLights()` 里用了固定方向的 `DirectionalLight`。 建议改为: - 保留环境补光 - 主太阳光方向由 `sunDirection` 决定 2. 太阳光参数建议 - `DirectionalLight` 颜色偏暖白 - 强度略高于当前主光 - 保留一个弱背光作为氛围补偿,避免背面过死黑 3. 先不做物理级大气散射 第一版只要求: - 亮面与暗面方向真实 - 云层和大气仍保持当前风格 ### 验收标准 - 地球明暗面会随太阳方向改变 - 太阳 sprite 和地球亮面方向一致 - 不破坏现有海缆、卫星、BGP 的可见性 ## Phase 4:月相与天文细节增强 ### 目标 在日月真实位置基础上增加更强的“天文可信度”。 ### 可选项 1. 月相 - 根据日月夹角计算 illuminated fraction - 用月相纹理或 shader 表达盈亏 2. 赤道/黄道辅助线 - 可作为开发调试层,不默认显示 3. 太阳 terminator 增强 - 给地球夜面加入更自然的 night tint - 未来可叠加城市夜光纹理 4. 天文时间入口 - 设置中加入“当前时刻 / 指定时刻 / 加速时间”模式 ### 验收标准 - 月亮不再只是一个静态圆点 - 后续扩展行星或观测模式时无需推倒重来 ## Phase 5:严格参考系升级(可选,不作为 V1 必做) ### 目标 把 Earth 从“用户旋转球体”升级为“真实地球姿态 + 用户观察姿态”的双层模型。 ### 需要处理的问题 - 地球自转角与 UTC 的一致性 - 赤道坐标系、地固坐标系、相机交互层分离 - 卫星轨道显示与 Earth 旋转同步关系 - resetView 和 autoRotate 的语义重定 ### 风险 这一步会影响: - [frontend/public/earth/js/main.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/main.js) - [frontend/public/earth/js/satellites.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/satellites.js) - [frontend/public/earth/js/cables.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/cables.js) - [frontend/public/earth/js/controls.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/controls.js) 因此不建议与 V1 同时推进。 ## 代码改造清单 ## 1. 新增文件 - [frontend/public/earth/js/celestial.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/celestial.js) 职责: - 管理天球背景、太阳、月亮 - 对外暴露 init/update/dispose ## 2. 修改 `constants.js` 文件: - [frontend/public/earth/js/constants.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/constants.js) 新增: ```js export const CELESTIAL_CONFIG = { sphereRadius: 800, updateIntervalMs: 60000, sunSpriteScale: 28, moonSpriteScale: 16, sunLightIntensity: 1.25, ambientIntensity: 0.28, backLightIntensity: 0.18, }; ``` ## 3. 修改 `main.js` 文件: - [frontend/public/earth/js/main.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/main.js) 主要改动: 1. `init()` 中: - 初始化 celestial layer 2. `addLights()` 中: - 把固定太阳光改成可更新的 celestial sun light 3. `animate()` 中: - 每帧调 `updateCelestialLayer()` 4. `destroy()` 中: - 清理 celestial 资源 ## 4. 修改 `earth.js` 文件: - [frontend/public/earth/js/earth.js](/home/ray/dev/linkong/planet/frontend/public/earth/js/earth.js) 主要改动: - `createStars()` 逐步退役 - 第一阶段可先保留作为 fallback - 当真实星图加载成功后,不再显示随机星点 ## 5. 新增资源 目录建议: - `frontend/public/earth/assets/celestial/` 建议至少包含: - `starmap_equatorial_4k.jpg` - `sun_glow.png` - `moon_albedo_2k.jpg` ## 数据流设计 ```mermaid flowchart TD A["main.js:init()"] --> B["initCelestialLayer(scene)"] B --> C["创建天球球壳"] B --> D["创建太阳 sprite + 主定向光"] B --> E["创建月亮 sprite"] F["animate()"] --> G["updateCelestialLayer(now, camera, earth)"] G --> H["Astronomy Engine 计算 Sun/Moon 方向"] H --> I["更新 sun sprite / moon sprite 位置"] H --> J["更新太阳 DirectionalLight 方向"] J --> K["地球昼夜方向变化"] ``` ## 风险与注意事项 ### 1. 星图投影方向容易反 这会表现为: - 星图左右镜像 - 赤经方向颠倒 - 日月位置和背景对不上 建议: - 先做一个开发调试模式 - 显示赤经/赤纬参考点,快速校正纹理朝向 ### 2. 不要让天球跟随 Earth 旋转 天球背景和日月必须属于 scene/world,而不是 `earthObj`。 ### 3. 不要每帧做重型天文计算 真实位置更新应节流,否则会浪费 CPU。 ### 4. 月亮先求“方向正确”,再求“月相精致” 月相属于第二步优化,不应阻塞 V1 上线。 ## 推荐实施顺序 1. 新建 `celestial.js` 2. 用星图球壳替换随机星点 3. 接入 Astronomy Engine 4. 加太阳/月亮 sprite 5. 用太阳方向驱动主光 6. 再决定要不要做月相和更严格参考系 ## 最终建议 对于当前 Planet Earth,最稳妥的方案是: - 先做真实天球背景 - 再做真实太阳/月亮方向 - 再让太阳驱动地球受光 - 暂时不做 Earth 参考系重构 这样可以在不破坏现有 Earth 交互和图层系统的前提下,显著提升空间感、真实感和演示说服力。