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