18 KiB
Earth 天球背景与日月位置实施方案
目标
为 Earth 大屏增加一套真正可用的天文背景层,覆盖三件事:
- 用真实天球背景替换当前随机星点
- 在当前时间下显示太阳与月亮的相对位置
- 让太阳方向同时驱动地球受光,形成更可信的昼夜关系
本方案优先追求:
- 与当前 Three.js Earth 架构兼容
- 风险可控
- 先落地一版真实感明显提升的 V1
- 为后续更严格的天文参考系升级预留余地
当前现状
当前 Earth 的基础条件已经具备:
- 地球、云层、地形、网格都基于 Three.js,主渲染入口在 frontend/public/earth/js/main.js
- 地球实体创建在 frontend/public/earth/js/earth.js
- 当前所谓“宇宙背景”只是
createStars()生成的随机星点,不是真实星图 - Earth 已有倾角常量
EARTH_CONFIG.tiltRad,位于 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 附着对象的语义
推荐技术选型
天文计算库
推荐:
原因:
- 有 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)作为星空视觉资源 - 这说明行业内成熟实现并不一定直接渲染全部星表点,而很可能先使用一张高质量官方深空星图作为背景层
- SatelliteMap.space 在 credits 中明确提到其使用了
- 如需后续替换,也可评估 ESA / Gaia 的全天 sky map 资源
建议要求:
- 使用官方来源或官方衍生可复用资源
- 等距矩形投影(equirectangular)
- 坐标定义尽量明确为赤道坐标展开
- 分辨率建议至少
4k - 颜色不要过亮,避免压过 Earth HUD 前景
- 尽量优先选择官方天文机构已经生产好的深空图,而不是自行拼接低质量星空纹理
建议本地资源目录:
frontend/public/earth/assets/celestial/starmap_equatorial_4k.jpg
2. 位置数据:星表与天体计算
用于 Phase 2+ 的“位置正确的星体”。
推荐来源分两层:
- 太阳、月亮位置
- 恒星位置
- 第一优先:Hipparcos / Tycho
- 第二优先:Gaia
建议策略:
- 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 坐标的落点方式
亮星点和日月方向最终都要转成单位球面向量。
概念步骤:
- 读取赤经
RA - 读取赤纬
Dec - 转成弧度
- 映射到单位球面向量
- 再根据 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. 背景球壳与恒星点的关系
推荐最终组合:
- 背景层:全天星图球壳
- 点位层:亮星点
- 动态层:太阳 / 月亮
这样有三个好处:
- 背景层提供密集真实的天空纹理
- 亮星点提供位置正确、可扩展的标注基础
- 太阳/月亮提供与时间相关的真实动态对象
数据与资源建议清单
推荐首批引入资源
- 全天星图
- 来源:NASA Tycho all-sky map
- 用途:背景球壳纹理
- 月亮纹理
- 用途:Phase 4 月相表现
- 路径建议:
frontend/public/earth/assets/celestial/moon_albedo_2k.jpg
- 太阳 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:先做可用版本(推荐)
- 引入 NASA Tycho 全天图
- 或评估替换为更接近 SatelliteMap.space 路线的
NASA Deep Star Maps 2020
- 或评估替换为更接近 SatelliteMap.space 路线的
- 实现背景球壳
- 用 Astronomy Engine 计算太阳/月亮方向
- 暂不做亮星点
优点:
- 最快见效
- 风险最低
- 就能明显提升天球真实感
路线 B:在 A 基础上增强
- 离线生成
bright-stars.json - 浏览器端渲染亮星点
- 后续可加:
- 星座线
- 亮星名称
- 特定星体高亮
优点:
- 背景真实感和“位置正确的可交互星体”同时兼顾
代码模块建议细化
新增模块
-
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:真实天球背景
目标
用真实全天星图替换当前随机星点背景。
做法
- 新增一张全天星图纹理
建议路径:
frontend/public/earth/assets/celestial/starmap_equatorial_4k.jpg
纹理要求:
- 等距矩形投影
- 赤经/赤纬坐标展开
- 无地平线、无地景遮挡
- 尽量深色、弱干扰,适合大屏 HUD 叠加
- 新增天球球壳
新增模块:
建议接口:
export function initCelestialLayer(scene)
export function updateCelestialLayer(date, camera, earth)
export function disposeCelestialLayer()
- 实现一个大半径内翻球体
建议参数:
- 半径:
600 ~ 900 - 材质:
MeshBasicMaterial side: THREE.BackSide- 不受场景光照影响
- 始终围绕场景中心
验收标准
- 初始加载后背景不再是随机星点
- 旋转地球时,背景保持为稳定天球而不是跟地球一起转
- 不明显干扰海缆/卫星/BGP 的前景识别
Phase 2:太阳与月亮真实位置
目标
在当前 UTC 时间下,计算太阳与月亮在天球中的方向,并显示出来。
做法
- 在
celestial.js内封装天体位置计算
建议函数:
function getSunDirection(date)
function getMoonDirection(date)
输出统一为 world-space THREE.Vector3
- 太阳显示
- 一个暖色发光 sprite
- 比月亮更大、更亮
- 可选添加柔和 halo
- 月亮显示
- 一个较小 sprite 或 sphere
- 灰白偏冷色
- 后续 Phase 3 再做月相
- 更新频率
不要每帧重新做完整天文计算,建议:
- 每 30 秒或 60 秒重算一次真实位置
- 渲染帧内做平滑过渡
验收标准
- 页面可见太阳与月亮两个对象
- 时间变化时位置会更新
- 日月不会跟随地球局部旋转而错误附着
Phase 3:太阳驱动地球受光
目标
让地球光照方向与太阳方向一致,不再使用写死的固定主光。
做法
- 替换或接管当前主定向光
当前 frontend/public/earth/js/main.js 中 addLights() 里用了固定方向的 DirectionalLight。
建议改为:
- 保留环境补光
- 主太阳光方向由
sunDirection决定
- 太阳光参数建议
DirectionalLight颜色偏暖白- 强度略高于当前主光
- 保留一个弱背光作为氛围补偿,避免背面过死黑
- 先不做物理级大气散射
第一版只要求:
- 亮面与暗面方向真实
- 云层和大气仍保持当前风格
验收标准
- 地球明暗面会随太阳方向改变
- 太阳 sprite 和地球亮面方向一致
- 不破坏现有海缆、卫星、BGP 的可见性
Phase 4:月相与天文细节增强
目标
在日月真实位置基础上增加更强的“天文可信度”。
可选项
- 月相
- 根据日月夹角计算 illuminated fraction
- 用月相纹理或 shader 表达盈亏
- 赤道/黄道辅助线
- 可作为开发调试层,不默认显示
- 太阳 terminator 增强
- 给地球夜面加入更自然的 night tint
- 未来可叠加城市夜光纹理
- 天文时间入口
- 设置中加入“当前时刻 / 指定时刻 / 加速时间”模式
验收标准
- 月亮不再只是一个静态圆点
- 后续扩展行星或观测模式时无需推倒重来
Phase 5:严格参考系升级(可选,不作为 V1 必做)
目标
把 Earth 从“用户旋转球体”升级为“真实地球姿态 + 用户观察姿态”的双层模型。
需要处理的问题
- 地球自转角与 UTC 的一致性
- 赤道坐标系、地固坐标系、相机交互层分离
- 卫星轨道显示与 Earth 旋转同步关系
- resetView 和 autoRotate 的语义重定
风险
这一步会影响:
- frontend/public/earth/js/main.js
- frontend/public/earth/js/satellites.js
- frontend/public/earth/js/cables.js
- frontend/public/earth/js/controls.js
因此不建议与 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
文件:
主要改动:
init()中:- 初始化 celestial layer
addLights()中:- 把固定太阳光改成可更新的 celestial sun light
animate()中:- 每帧调
updateCelestialLayer()
- 每帧调
destroy()中:- 清理 celestial 资源
4. 修改 earth.js
文件:
主要改动:
createStars()逐步退役- 第一阶段可先保留作为 fallback
- 当真实星图加载成功后,不再显示随机星点
5. 新增资源
目录建议:
frontend/public/earth/assets/celestial/
建议至少包含:
starmap_equatorial_4k.jpgsun_glow.pngmoon_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 上线。
推荐实施顺序
- 新建
celestial.js - 用星图球壳替换随机星点
- 接入 Astronomy Engine
- 加太阳/月亮 sprite
- 用太阳方向驱动主光
- 再决定要不要做月相和更严格参考系
最终建议
对于当前 Planet Earth,最稳妥的方案是:
- 先做真实天球背景
- 再做真实太阳/月亮方向
- 再让太阳驱动地球受光
- 暂时不做 Earth 参考系重构
这样可以在不破坏现有 Earth 交互和图层系统的前提下,显著提升空间感、真实感和演示说服力。