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

18 KiB
Raw Blame History

Earth 天球背景与日月位置实施方案

目标

为 Earth 大屏增加一套真正可用的天文背景层,覆盖三件事:

  1. 用真实天球背景替换当前随机星点
  2. 在当前时间下显示太阳与月亮的相对位置
  3. 让太阳方向同时驱动地球受光,形成更可信的昼夜关系

本方案优先追求:

  • 与当前 Three.js Earth 架构兼容
  • 风险可控
  • 先落地一版真实感明显提升的 V1
  • 为后续更严格的天文参考系升级预留余地

当前现状

当前 Earth 的基础条件已经具备:

这意味着:

  • 不需要重写 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 附着对象的语义

推荐技术选型

天文计算库

推荐:

原因:

  • 有 JavaScript 版本
  • 支持 Sun / Moon 的矢量与坐标变换
  • 精度、可扩展性都比轻量太阳高度角库更适合本项目
  • 后续若要加行星、月相、黄道、赤道网,也能继续沿用

不作为主选的库:

原因:

  • 更偏本地观察者视角的太阳/月亮高度角
  • 用于“地面日出日落”很好
  • 但不如 Astronomy Engine 适合做真实天球与后续空间参考系扩展

Three.js 表现层

推荐组合:

  • 天球:内翻球壳 + 星图纹理
  • 太阳:THREE.Sprite
  • 月亮:THREE.Sprite 或小型 THREE.Mesh
  • 太阳光:THREE.DirectionalLight

参考:

天球背景资源与星体数据来源

为避免把“视觉背景”和“可计算天体位置”混为一谈,本方案明确分成两类资源:

1. 背景资源:全天星图贴图

用于 Phase 1 的“真实天空背景”。

推荐优先来源:

  • NASA SVS 的 Tycho 全天星图
  • 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 资源

建议要求:

  • 使用官方来源或官方衍生可复用资源
  • 等距矩形投影equirectangular
  • 坐标定义尽量明确为赤道坐标展开
  • 分辨率建议至少 4k
  • 颜色不要过亮,避免压过 Earth HUD 前景
  • 尽量优先选择官方天文机构已经生产好的深空图,而不是自行拼接低质量星空纹理

建议本地资源目录:

  • frontend/public/earth/assets/celestial/starmap_equatorial_4k.jpg

2. 位置数据:星表与天体计算

用于 Phase 2+ 的“位置正确的星体”。

推荐来源分两层:

建议策略:

  • V1背景球壳只用全天星图不立即生成全量恒星点
  • V2只挑选亮星例如星等 < 5.5)生成恒星点层
  • V3如果确实需要更丰富的星场再逐步扩展到更深星等

这样做的原因:

  • 背景球壳负责“天球真实感”
  • 亮星点负责“位置正确、可后续标注和高亮”
  • 不需要一开始就处理数十万甚至数百万颗星

3. 对外部成熟实现的参考结论

SatelliteMap.space 的公开 credits 提供了一个很有价值的参考样板:

  • 图形渲染使用 TWGL.js
  • 天文计算使用 SkyfieldAstronomia
  • 星空/天球视觉资源使用 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 当前世界坐标定义做轴向映射

参考公式:

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

建议把这层显式封装在:

function equatorialToWorldVector(raRad, decRad)

不要把轴映射散落在不同模块里。

4. 背景球壳与恒星点的关系

推荐最终组合:

  • 背景层:全天星图球壳
  • 点位层:亮星点
  • 动态层:太阳 / 月亮

这样有三个好处:

  • 背景层提供密集真实的天空纹理
  • 亮星点提供位置正确、可扩展的标注基础
  • 太阳/月亮提供与时间相关的真实动态对象

数据与资源建议清单

推荐首批引入资源

  1. 全天星图
  • 来源NASA Tycho all-sky map
  • 用途:背景球壳纹理
  1. 月亮纹理
  • 用途Phase 4 月相表现
  • 路径建议:
    • frontend/public/earth/assets/celestial/moon_albedo_2k.jpg
  1. 太阳 glow 贴图
  • 用途:太阳 sprite halo
  • 路径建议:
    • frontend/public/earth/assets/celestial/sun_glow.png

推荐首批数据文件

如果要上亮星层,建议新增一个预处理后的轻量数据文件:

  • frontend/public/earth/assets/celestial/bright-stars.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

    • 资源路径
    • 星图方向配置
    • 亮星数据加载(后续)

建议函数设计

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 叠加
  1. 新增天球球壳

新增模块:

建议接口:

export function initCelestialLayer(scene)
export function updateCelestialLayer(date, camera, earth)
export function disposeCelestialLayer()
  1. 实现一个大半径内翻球体

建议参数:

  • 半径:600 ~ 900
  • 材质:MeshBasicMaterial
  • side: THREE.BackSide
  • 不受场景光照影响
  • 始终围绕场景中心

验收标准

  • 初始加载后背景不再是随机星点
  • 旋转地球时,背景保持为稳定天球而不是跟地球一起转
  • 不明显干扰海缆/卫星/BGP 的前景识别

Phase 2太阳与月亮真实位置

目标

在当前 UTC 时间下,计算太阳与月亮在天球中的方向,并显示出来。

做法

  1. celestial.js 内封装天体位置计算

建议函数:

function getSunDirection(date)
function getMoonDirection(date)

输出统一为 world-space THREE.Vector3

  1. 太阳显示
  • 一个暖色发光 sprite
  • 比月亮更大、更亮
  • 可选添加柔和 halo
  1. 月亮显示
  • 一个较小 sprite 或 sphere
  • 灰白偏冷色
  • 后续 Phase 3 再做月相
  1. 更新频率

不要每帧重新做完整天文计算,建议:

  • 每 30 秒或 60 秒重算一次真实位置
  • 渲染帧内做平滑过渡

验收标准

  • 页面可见太阳与月亮两个对象
  • 时间变化时位置会更新
  • 日月不会跟随地球局部旋转而错误附着

Phase 3太阳驱动地球受光

目标

让地球光照方向与太阳方向一致,不再使用写死的固定主光。

做法

  1. 替换或接管当前主定向光

当前 frontend/public/earth/js/main.jsaddLights() 里用了固定方向的 DirectionalLight

建议改为:

  • 保留环境补光
  • 主太阳光方向由 sunDirection 决定
  1. 太阳光参数建议
  • DirectionalLight 颜色偏暖白
  • 强度略高于当前主光
  • 保留一个弱背光作为氛围补偿,避免背面过死黑
  1. 先不做物理级大气散射

第一版只要求:

  • 亮面与暗面方向真实
  • 云层和大气仍保持当前风格

验收标准

  • 地球明暗面会随太阳方向改变
  • 太阳 sprite 和地球亮面方向一致
  • 不破坏现有海缆、卫星、BGP 的可见性

Phase 4月相与天文细节增强

目标

在日月真实位置基础上增加更强的“天文可信度”。

可选项

  1. 月相
  • 根据日月夹角计算 illuminated fraction
  • 用月相纹理或 shader 表达盈亏
  1. 赤道/黄道辅助线
  • 可作为开发调试层,不默认显示
  1. 太阳 terminator 增强
  • 给地球夜面加入更自然的 night tint
  • 未来可叠加城市夜光纹理
  1. 天文时间入口
  • 设置中加入“当前时刻 / 指定时刻 / 加速时间”模式

验收标准

  • 月亮不再只是一个静态圆点
  • 后续扩展行星或观测模式时无需推倒重来

Phase 5严格参考系升级可选不作为 V1 必做)

目标

把 Earth 从“用户旋转球体”升级为“真实地球姿态 + 用户观察姿态”的双层模型。

需要处理的问题

  • 地球自转角与 UTC 的一致性
  • 赤道坐标系、地固坐标系、相机交互层分离
  • 卫星轨道显示与 Earth 旋转同步关系
  • resetView 和 autoRotate 的语义重定

风险

这一步会影响:

因此不建议与 V1 同时推进。

代码改造清单

1. 新增文件

职责:

  • 管理天球背景、太阳、月亮
  • 对外暴露 init/update/dispose

2. 修改 constants.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

文件:

主要改动:

  1. init() 中:
    • 初始化 celestial layer
  2. addLights() 中:
    • 把固定太阳光改成可更新的 celestial sun light
  3. animate() 中:
    • 每帧调 updateCelestialLayer()
  4. destroy() 中:
    • 清理 celestial 资源

4. 修改 earth.js

文件:

主要改动:

  • createStars() 逐步退役
  • 第一阶段可先保留作为 fallback
  • 当真实星图加载成功后,不再显示随机星点

5. 新增资源

目录建议:

  • frontend/public/earth/assets/celestial/

建议至少包含:

  • starmap_equatorial_4k.jpg
  • sun_glow.png
  • moon_albedo_2k.jpg

数据流设计

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 交互和图层系统的前提下,显著提升空间感、真实感和演示说服力。