release: bump version to 0.31.1
This commit is contained in:
@@ -15,3 +15,8 @@
|
||||
- 明确写明“已完成”的计划,优先归档
|
||||
- 已被正式实现替代、继续放在 `docs/` 根目录会误导后续开发的计划,归档
|
||||
- 仍然指导未来开发、尚未完成或仍有明确执行价值的文档,继续保留在 `docs/`
|
||||
|
||||
补充说明:
|
||||
|
||||
- 一部分归档文档来自外部或临时工作流草案,例如 sisyphus 生成的初稿
|
||||
- 这类文档如果有可用内容,应先吸收到 `docs/plans/` 或 `docs/technical/`,再归档保留来源记录
|
||||
|
||||
167
docs/deprecated/sisyphus-earth-architecture-refactor.md
Normal file
167
docs/deprecated/sisyphus-earth-architecture-refactor.md
Normal file
@@ -0,0 +1,167 @@
|
||||
> Archived note: this document was originally created by sisyphus and later reviewed against the main docs set. Useful content has been absorbed into `docs/plans/` where appropriate.
|
||||
|
||||
# 地球3D可视化架构重构计划
|
||||
|
||||
## 背景
|
||||
|
||||
当前 `frontend/public/earth` 3D地球可视化系统基于 Three.js 构建,未来需要迁移到 Unreal Engine (Cesium)。为降低迁移成本,需要提前做好**逻辑与渲染分离**的架构设计。
|
||||
|
||||
## 目标
|
||||
|
||||
- 将线缆高亮逻辑与渲染实现分离
|
||||
- 保持交互逻辑可复用,只需重写渲染层
|
||||
- 为后续迁移到 UE/Cesium 做好准备
|
||||
|
||||
## 已完成
|
||||
|
||||
### 1. 状态枚举定义 (constants.js)
|
||||
|
||||
```javascript
|
||||
export const CABLE_STATE = {
|
||||
NORMAL: 'normal',
|
||||
HOVERED: 'hovered',
|
||||
LOCKED: 'locked'
|
||||
};
|
||||
```
|
||||
|
||||
### 2. 线缆状态管理 (cables.js - 数据层)
|
||||
|
||||
```javascript
|
||||
const cableStates = new Map();
|
||||
|
||||
export function getCableState(cableId) { ... }
|
||||
export function setCableState(cableId, state) { ... }
|
||||
export function clearAllCableStates() { ... }
|
||||
export function getCableStateInfo() { ... }
|
||||
```
|
||||
|
||||
### 3. 逻辑层调用 (main.js)
|
||||
|
||||
```javascript
|
||||
// 悬停
|
||||
setCableState(cable.userData.cableId, CABLE_STATE.HOVERED);
|
||||
|
||||
// 锁定
|
||||
setCableState(cableId, CABLE_STATE.LOCKED);
|
||||
|
||||
// 恢复
|
||||
setCableState(cableId, CABLE_STATE.NORMAL);
|
||||
clearAllCableStates();
|
||||
|
||||
// 清除锁定时
|
||||
clearLockedObject() {
|
||||
hoveredCable = null;
|
||||
clearAllCableStates();
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 渲染层 (main.js - applyCableVisualState)
|
||||
|
||||
```javascript
|
||||
function applyCableVisualState() {
|
||||
const allCables = getCableLines();
|
||||
const pulse = (Math.sin(Date.now() * CABLE_CONFIG.pulseSpeed) + 1) * 0.5;
|
||||
|
||||
allCables.forEach(c => {
|
||||
const cableId = c.userData.cableId;
|
||||
const state = getCableState(cableId);
|
||||
|
||||
switch (state) {
|
||||
case CABLE_STATE.LOCKED:
|
||||
// 呼吸效果 + 白色
|
||||
c.material.opacity = CABLE_CONFIG.lockedOpacityMin + pulse * CABLE_CONFIG.pulseCoefficient;
|
||||
c.material.color.setRGB(1, 1, 1);
|
||||
break;
|
||||
case CABLE_STATE.HOVERED:
|
||||
// 白色高亮
|
||||
c.material.opacity = 1;
|
||||
c.material.color.setRGB(1, 1, 1);
|
||||
break;
|
||||
case CABLE_STATE.NORMAL:
|
||||
default:
|
||||
if (lockedObjectType === 'cable' && lockedObject) {
|
||||
// 其他线缆变暗
|
||||
c.material.opacity = CABLE_CONFIG.otherOpacity;
|
||||
...
|
||||
} else {
|
||||
// 恢复原始
|
||||
c.material.opacity = 1;
|
||||
c.material.color.setHex(c.userData.originalColor);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## 待完成
|
||||
|
||||
### Phase 1: 完善状态配置 (constants.js)
|
||||
|
||||
```javascript
|
||||
export const CABLE_CONFIG = {
|
||||
lockedOpacityMin: 0.6,
|
||||
lockedOpacityMax: 1.0,
|
||||
otherOpacity: 0.5,
|
||||
otherBrightness: 0.6,
|
||||
pulseSpeed: 0.003,
|
||||
pulseCoefficient: 0.4,
|
||||
// 未来可扩展
|
||||
// lockedLineWidth: 3,
|
||||
// normalLineWidth: 1,
|
||||
};
|
||||
```
|
||||
|
||||
### Phase 2: 卫星状态管理 (satellites.js)
|
||||
|
||||
参考线缆状态管理,为卫星添加类似的状态枚举和状态管理函数:
|
||||
|
||||
```javascript
|
||||
export const SATELLITE_STATE = {
|
||||
NORMAL: 'normal',
|
||||
HOVERED: 'hovered',
|
||||
LOCKED: 'locked'
|
||||
};
|
||||
```
|
||||
|
||||
#### 卫星数据源说明
|
||||
|
||||
- **当前使用**: CelesTrak (https://celestrak.org) - 免费,无需认证
|
||||
- **后续计划**: Space-Track.org (https://space-track.org) - 需要认证,数据更权威
|
||||
- 迁移时只需修改 `satellites.js` 中的数据获取逻辑,状态管理和渲染逻辑不变
|
||||
|
||||
### Phase 3: 统一渲染接口
|
||||
|
||||
将所有对象的渲染逻辑抽象为一个统一的渲染函数:
|
||||
|
||||
```javascript
|
||||
function applyObjectVisualState() {
|
||||
applyCableVisualState();
|
||||
applySatelliteVisualState();
|
||||
applyLandingPointVisualState();
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: UE 迁移准备
|
||||
|
||||
迁移到 Unreal Engine 时:
|
||||
1. 保留 `constants.js` 中的枚举和配置
|
||||
2. 保留 `cables.js` 中的数据层和状态管理
|
||||
3. 保留 `main.js` 中的交互逻辑
|
||||
4. **仅重写** `applyCableVisualState()` 等渲染函数
|
||||
|
||||
---
|
||||
|
||||
## 架构原则
|
||||
|
||||
1. **状态与渲染分离** - 对象状态由数据层管理,渲染层只负责根据状态更新视觉效果
|
||||
2. **逻辑可复用** - 交互逻辑(点击、悬停、锁定)在迁移时应直接复用
|
||||
3. **渲染可替换** - 渲染实现可以针对不同引擎重写,不影响逻辑层
|
||||
|
||||
## 文件变更记录
|
||||
|
||||
| 日期 | 文件 | 变更 |
|
||||
|------|------|------|
|
||||
| 2026-03-19 | constants.js | 新增 CABLE_STATE 枚举 |
|
||||
| 2026-03-19 | cables.js | 新增状态管理函数 |
|
||||
| 2026-03-19 | main.js | 使用状态管理,抽象 applyCableVisualState() |
|
||||
138
docs/deprecated/sisyphus-predicted-orbit.md
Normal file
138
docs/deprecated/sisyphus-predicted-orbit.md
Normal file
@@ -0,0 +1,138 @@
|
||||
> Archived note: this document was originally created by sisyphus and later reviewed against the main docs set. Useful content has been absorbed into `docs/plans/` where appropriate.
|
||||
|
||||
# 卫星预测轨道显示功能
|
||||
|
||||
## TL;DR
|
||||
> 锁定卫星时显示绕地球完整一圈的预测轨道轨迹,从当前位置向外渐变消失
|
||||
|
||||
## Context
|
||||
|
||||
### 目标
|
||||
点击锁定卫星 → 显示该卫星绕地球一周的完整预测轨道(而非当前的历史轨迹)
|
||||
|
||||
### 当前实现
|
||||
- `TRAIL_LENGTH = 30` - 历史轨迹点数,每帧 push 当前位置
|
||||
- 显示最近30帧历史轨迹(类似彗星尾巴)
|
||||
|
||||
### 参考: SatelliteMap.space
|
||||
- 锁定时显示预测轨道
|
||||
- 颜色从当前位置向外渐变消失
|
||||
- 使用 satellite.js(与本项目相同)
|
||||
|
||||
## 实现状态
|
||||
|
||||
### ✅ 已完成
|
||||
- [x] 计算卫星轨道周期(基于 `meanMotion`)
|
||||
- [x] 生成预测轨道点(10秒采样间隔)
|
||||
- [x] 创建独立预测轨道渲染对象
|
||||
- [x] 锁定卫星时显示预测轨道
|
||||
- [x] 解除锁定时隐藏预测轨道
|
||||
- [x] 颜色渐变:当前位置(亮) → 轨道终点(暗)
|
||||
- [x] 页面隐藏时清除轨迹(防止切回时闪现)
|
||||
|
||||
### 🚧 进行中
|
||||
- [ ] 完整圆环轨道(部分卫星因 SGP4 计算问题使用 fallback 圆形轨道)
|
||||
- [ ] 每颗卫星只显示一条轨道
|
||||
|
||||
## 技术细节
|
||||
|
||||
### 轨道周期计算
|
||||
```javascript
|
||||
function calculateOrbitalPeriod(meanMotion) {
|
||||
return 86400 / meanMotion;
|
||||
}
|
||||
```
|
||||
|
||||
### 预测轨道计算
|
||||
```javascript
|
||||
function calculatePredictedOrbit(satellite, periodSeconds, sampleInterval = 10) {
|
||||
const points = [];
|
||||
const samples = Math.ceil(periodSeconds / sampleInterval);
|
||||
const now = new Date();
|
||||
|
||||
// Full orbit: from now to now+period
|
||||
for (let i = 0; i <= samples; i++) {
|
||||
const time = new Date(now.getTime() + i * sampleInterval * 1000);
|
||||
const pos = computeSatellitePosition(satellite, time);
|
||||
if (pos) points.push(pos);
|
||||
}
|
||||
|
||||
// Fallback: 如果真实位置计算点太少,使用圆形 fallback
|
||||
if (points.length < samples * 0.5) {
|
||||
points.length = 0;
|
||||
// ... 圆形轨道生成
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
```
|
||||
|
||||
### 渲染对象
|
||||
```javascript
|
||||
let predictedOrbitLine = null;
|
||||
|
||||
export function showPredictedOrbit(satellite) {
|
||||
hidePredictedOrbit();
|
||||
// ... 计算并渲染轨道
|
||||
}
|
||||
|
||||
export function hidePredictedOrbit() {
|
||||
if (predictedOrbitLine) {
|
||||
earthObjRef.remove(predictedOrbitLine);
|
||||
predictedOrbitLine.geometry.dispose();
|
||||
predictedOrbitLine.material.dispose();
|
||||
predictedOrbitLine = null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 已知问题
|
||||
|
||||
### 1. TLE 格式问题
|
||||
`computeSatellitePosition` 使用自行构建的 TLE 格式,对某些卫星返回 null。当前使用 fallback 圆形轨道作为补偿。
|
||||
|
||||
### 2. 多条轨道
|
||||
部分情况下锁定时会显示多条轨道。需要确保 `hidePredictedOrbit()` 被正确调用。
|
||||
|
||||
## 性能考虑
|
||||
|
||||
### 点数估算
|
||||
| 卫星类型 | 周期 | 10秒采样 | 点数 |
|
||||
|---------|------|---------|------|
|
||||
| LEO | 90分钟 | 540秒 | ~54点 |
|
||||
| MEO | 12小时 | 4320秒 | ~432点 |
|
||||
| GEO | 24小时 | 8640秒 | ~864点 |
|
||||
|
||||
### 优化策略
|
||||
- 当前方案(~900点 GEO)性能可接受
|
||||
- 如遇性能问题:GEO 降低采样率到 30秒
|
||||
|
||||
## 验证方案
|
||||
|
||||
### QA Scenarios
|
||||
|
||||
**Scenario: 锁定 Starlink 卫星显示预测轨道**
|
||||
1. 打开浏览器,进入 Earth 页面
|
||||
2. 显示卫星(点击按钮)
|
||||
3. 点击一颗 Starlink 卫星(低轨道 LEO)
|
||||
4. 验证:出现黄色预测轨道线,从卫星向外绕行
|
||||
5. 验证:颜色从亮黄渐变到暗蓝
|
||||
6. 验证:轨道完整闭环
|
||||
|
||||
**Scenario: 锁定 GEO 卫星显示预测轨道**
|
||||
1. 筛选一颗 GEO 卫星(倾斜角 0-10° 或高轨道)
|
||||
2. 点击锁定
|
||||
3. 验证:显示完整 24 小时轨道(或 fallback 圆形轨道)
|
||||
4. 验证:点数合理(~864点或 fallback)
|
||||
|
||||
**Scenario: 解除锁定隐藏预测轨道**
|
||||
1. 锁定一颗卫星,显示预测轨道
|
||||
2. 点击地球空白处解除锁定
|
||||
3. 验证:预测轨道消失
|
||||
|
||||
**Scenario: 切换页面后轨迹不闪现**
|
||||
1. 锁定一颗卫星
|
||||
2. 切换到其他标签页
|
||||
3. 等待几秒
|
||||
4. 切回页面
|
||||
5. 验证:轨迹不突然闪现累积
|
||||
247
docs/deprecated/sisyphus-ue5-client.md
Normal file
247
docs/deprecated/sisyphus-ue5-client.md
Normal file
@@ -0,0 +1,247 @@
|
||||
> Archived note: this document was originally created by sisyphus and later reviewed against the main docs set. Useful content has been absorbed into `docs/plans/` where appropriate.
|
||||
|
||||
# UE5 3D 大屏客户端开发计划
|
||||
|
||||
## 项目概述
|
||||
|
||||
基于智能星球计划架构,开发 UE5 3D 可视化大屏客户端,实现全球态势感知数据的沉浸式展示。
|
||||
|
||||
## 技术选型
|
||||
|
||||
| 组件 | 版本 | 用途 |
|
||||
|------|------|------|
|
||||
| Unreal Engine | 5.3+ | 3D 渲染引擎 |
|
||||
| Cesium for Unreal | 1.5+ | 地理可视化 |
|
||||
| Niagara | - | 粒子系统 |
|
||||
| WebSocket API | - | 实时数据推送 |
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
unreal/
|
||||
├── Content/
|
||||
│ ├── Levels/
|
||||
│ │ ├── Main.umap # 主场景
|
||||
│ │ └── Components/ # 组件关卡
|
||||
│ ├── Blueprints/
|
||||
│ │ ├── BP_GlobeController # 地球控制器
|
||||
│ │ ├── BP_DataVisualizer # 数据可视化基类
|
||||
│ │ ├── BP_Supercomputer # TOP500 超算标记
|
||||
│ │ ├── BP_GPUCluster # GPU 集群标记
|
||||
│ │ ├── BP_IXPNode # IXP 节点标记
|
||||
│ │ ├── BP_SubmarineCable # 海缆连接线
|
||||
│ │ ├── BP_DataFlow # 数据流向粒子
|
||||
│ │ ├── BP_AlarmIndicator # 告警指示器
|
||||
│ │ └── BP_CameraController # 相机控制器
|
||||
│ ├── Materials/
|
||||
│ │ ├── M_Globe # 地球材质
|
||||
│ │ ├── M_DataPoint # 数据点材质
|
||||
│ │ ├── M_Cable # 海缆材质
|
||||
│ │ └── M_DataFlow # 数据流材质
|
||||
│ ├── Widgets/
|
||||
│ │ ├── W_MainHUD # 主 HUD
|
||||
│ │ ├── W_DataInfo # 数据信息面板
|
||||
│ │ └── W_AlarmPanel # 告警面板
|
||||
│ └── UI/
|
||||
│ └── UMG/
|
||||
├── Source/
|
||||
│ ├── PlanetAPI/ # 后端 API 客户端
|
||||
│ │ ├── PlanetAPIClient # WebSocket 连接
|
||||
│ │ ├── DataModels # 数据模型
|
||||
│ │ └── HttpClient # HTTP 客户端
|
||||
│ ├── CesiumIntegration/ # Cesium 集成
|
||||
│ │ ├── GlobeManager # 地球管理
|
||||
│ │ └── GeoUtils # 地理坐标工具
|
||||
│ └── Visualization/ # 可视化组件
|
||||
│ ├── PointRenderer # 点渲染
|
||||
│ ├── LineRenderer # 线渲染
|
||||
│ └── ParticleSystem # 粒子系统
|
||||
└── Planet.unproject
|
||||
```
|
||||
|
||||
## 功能模块
|
||||
|
||||
### 1. 3D 地球渲染
|
||||
|
||||
CesiumIntegration 组件:
|
||||
- 集成 Cesium ion 地图服务
|
||||
- 支持多分辨率地球纹理
|
||||
- 地理坐标 转 UE 坐标转换
|
||||
- 光照和大气效果
|
||||
|
||||
### 2. 算力点可视化
|
||||
|
||||
数据点类型:
|
||||
|
||||
| 数据源 | 点类型 | 颜色 | 标识 |
|
||||
|--------|--------|------|------|
|
||||
| TOP500 | 超算 | 红色 | HPLinpack 性能 |
|
||||
| Epoch AI | GPU集群 | 橙色 | GPU数量 |
|
||||
| HuggingFace | 模型部署 | 蓝色 | 模型大小 |
|
||||
|
||||
### 3. 海缆可视化
|
||||
|
||||
CableVisualization 组件:
|
||||
- 海缆路径渲染 (Spline Mesh)
|
||||
- 带宽/容量可视化 (颜色编码)
|
||||
- 实时流量状态
|
||||
|
||||
### 4. 数据流向粒子
|
||||
|
||||
DataFlowNiagara 系统:
|
||||
- 源 → 目的地的粒子流动
|
||||
- 带宽决定粒子密度/速度
|
||||
- 支持动画和颜色渐变
|
||||
|
||||
### 5. 告警系统
|
||||
|
||||
AlarmIndicators:
|
||||
- 异常数据红色高亮
|
||||
- 闪烁效果
|
||||
- 点击显示详情
|
||||
|
||||
### 6. WebSocket 实时更新
|
||||
|
||||
PlanetAPIClient:
|
||||
- 连接 ws://backend:8000/ws
|
||||
- 自动重连机制
|
||||
- 数据更新回调
|
||||
|
||||
### 7. 相机控制
|
||||
|
||||
CameraController:
|
||||
- 自动巡航模式
|
||||
- 聚焦特定区域
|
||||
- 平滑过渡动画
|
||||
|
||||
## 数据模型
|
||||
|
||||
```cpp
|
||||
// 地理位置
|
||||
struct FGeographicPoint
|
||||
{
|
||||
double Latitude; // 纬度 (-90 to 90)
|
||||
double Longitude; // 经度 (-180 to 180)
|
||||
double Altitude; // 高度 (米)
|
||||
};
|
||||
|
||||
// 算力点数据
|
||||
struct FComputePointData
|
||||
{
|
||||
FString Id;
|
||||
FString Name;
|
||||
FString Source; // top500, epoch_ai
|
||||
FGeographicPoint Location;
|
||||
float Performance; // PFLOPS
|
||||
int32 CoreCount;
|
||||
int32 GpuCount;
|
||||
FString Country;
|
||||
};
|
||||
```
|
||||
|
||||
## API 对接
|
||||
|
||||
### WebSocket 消息格式
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "update",
|
||||
"data": {
|
||||
"source": "top500",
|
||||
"action": "add/update/remove",
|
||||
"payload": {
|
||||
"id": "top500_1",
|
||||
"name": "Frontier",
|
||||
"location": {
|
||||
"latitude": 33.7756,
|
||||
"longitude": -84.3962,
|
||||
"altitude": 0
|
||||
},
|
||||
"performance": 1682.65,
|
||||
"cores": 8730112
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### HTTP API 端点
|
||||
|
||||
| 端点 | 用途 |
|
||||
|------|------|
|
||||
| GET /api/v1/collected?source=top500 | 获取 TOP500 数据 |
|
||||
| GET /api/v1/collected?source=telegeography_cables | 获取海缆数据 |
|
||||
| WS /ws/updates | 实时数据推送 |
|
||||
|
||||
## 开发阶段
|
||||
|
||||
### Phase 1: 基础框架 (1-2 周)
|
||||
|
||||
- [ ] 创建 UE5 项目
|
||||
- [ ] 安装 Cesium for Unreal 插件
|
||||
- [ ] 实现基础地球渲染
|
||||
- [ ] 创建 WebSocket 客户端框架
|
||||
|
||||
### Phase 2: 数据点可视化 (2-3 周)
|
||||
|
||||
- [ ] 实现 TOP500 超算标记
|
||||
- [ ] 实现 GPU 集群标记
|
||||
- [ ] 添加交互功能
|
||||
- [ ] 实现信息面板
|
||||
|
||||
### Phase 3: 海缆可视化 (1-2 周)
|
||||
|
||||
- [ ] 实现海缆路径渲染
|
||||
- [ ] 添加带宽可视化
|
||||
- [ ] 实现数据流向粒子
|
||||
|
||||
### Phase 4: 实时更新 (1-2 周)
|
||||
|
||||
- [ ] 完成 WebSocket 集成
|
||||
- [ ] 实现数据自动更新
|
||||
- [ ] 添加告警系统
|
||||
|
||||
### Phase 5: UI 和优化 (1 周)
|
||||
|
||||
- [ ] 添加 HUD 界面
|
||||
- [ ] 实现相机控制
|
||||
- [ ] 性能优化
|
||||
- [ ] 测试和修复
|
||||
|
||||
## 资源需求
|
||||
|
||||
### 必要资源
|
||||
|
||||
1. **Cesium ion 账户**
|
||||
- 免费注册: https://cesium.com/ion/
|
||||
- 用于访问全球 3D 地形和影像
|
||||
|
||||
2. **UE5 安装**
|
||||
- 从 Epic Games Launcher 安装
|
||||
- 建议版本: 5.3 或 5.4
|
||||
|
||||
### 可选资源
|
||||
|
||||
- 区域高程数据
|
||||
- 夜间灯光纹理
|
||||
|
||||
## 验收标准
|
||||
|
||||
### 基础功能
|
||||
|
||||
- [ ] 地球正常渲染,无明显卡顿
|
||||
- [ ] TOP500 数据点正确显示位置
|
||||
- [ ] 超算信息面板可点击查看
|
||||
- [ ] WebSocket 连接正常
|
||||
|
||||
### 高级功能
|
||||
|
||||
- [ ] 海缆路径可视化
|
||||
- [ ] 数据流向粒子效果
|
||||
- [ ] 告警指示
|
||||
- [ ] 自动巡航模式
|
||||
|
||||
### 性能要求
|
||||
|
||||
- [ ] 60 FPS 稳定运行 (4K 分辨率)
|
||||
- [ ] 1000+ 数据点无明显性能下降
|
||||
- [ ] WebSocket 消息延迟 < 1 秒
|
||||
295
docs/deprecated/sisyphus-webgl-instancing-satellites.md
Normal file
295
docs/deprecated/sisyphus-webgl-instancing-satellites.md
Normal file
@@ -0,0 +1,295 @@
|
||||
> Archived note: this document was originally created by sisyphus and later reviewed against the main docs set. Useful content has been absorbed into `docs/plans/` where appropriate.
|
||||
|
||||
# WebGL Instancing 卫星渲染优化计划
|
||||
|
||||
## 背景
|
||||
|
||||
当前 `satellites.js` 使用 `THREE.Points` 渲染卫星,受限于 WebGL 点渲染性能,只能显示 ~500-1000 颗卫星。
|
||||
需要迁移到真正的 WebGL Instancing 以支持 5000+ 卫星流畅渲染。
|
||||
|
||||
## 技术选型
|
||||
|
||||
| 方案 | 性能 | 改动量 | 维护性 | 推荐 |
|
||||
|------|------|--------|--------|------|
|
||||
| THREE.Points (现状) | ★★☆ | - | - | 基准 |
|
||||
| THREE.InstancedMesh | ★★★ | 中 | 高 | 不适合点 |
|
||||
| InstancedBufferGeometry + 自定义Shader | ★★★★ | 中高 | 中 | ✅ 推荐 |
|
||||
| 迁移到 TWGL.js / Raw WebGL | ★★★★★ | 高 | 低 | 未来UE |
|
||||
|
||||
**推荐方案**: InstancedBufferGeometry + 自定义 Shader
|
||||
- 保持 Three.js 架构
|
||||
- 复用 satellite.js 数据层
|
||||
- 性能接近原生 WebGL
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: 调研与原型
|
||||
|
||||
### 1.1 分析现有架构
|
||||
|
||||
**现状 (satellites.js)**:
|
||||
```javascript
|
||||
// 创建点云
|
||||
const pointsGeometry = new THREE.BufferGeometry();
|
||||
pointsGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
|
||||
pointsGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
|
||||
|
||||
const pointsMaterial = new THREE.PointsMaterial({
|
||||
size: 2,
|
||||
vertexColors: true,
|
||||
transparent: true,
|
||||
opacity: 0.8,
|
||||
sizeAttenuation: true
|
||||
});
|
||||
|
||||
satellitePoints = new THREE.Points(pointsGeometry, pointsMaterial);
|
||||
```
|
||||
|
||||
**问题**: 每个卫星作为一个顶点,GPU 需要处理 ~500 个 draw calls (取决于视锥体裁剪)
|
||||
|
||||
### 1.2 Instanced Rendering 原理
|
||||
|
||||
```javascript
|
||||
// 目标:单次 draw call 渲染所有卫星
|
||||
// 每个卫星属性:
|
||||
// - position (vec3): 位置
|
||||
// - color (vec3): 颜色
|
||||
// - size (float): 大小 (可选)
|
||||
// - selected (float): 是否选中 (0/1)
|
||||
|
||||
// 使用 InstancedBufferGeometry
|
||||
const geometry = new THREE.InstancedBufferGeometry();
|
||||
geometry.index = originalGeometry.index;
|
||||
geometry.attributes.position = originalGeometry.attributes.position;
|
||||
geometry.attributes.uv = originalGeometry.attributes.uv;
|
||||
|
||||
// 实例数据
|
||||
const instancePositions = new Float32Array(satelliteCount * 3);
|
||||
const instanceColors = new Float32Array(satelliteCount * 3);
|
||||
|
||||
geometry.setAttribute('instancePosition',
|
||||
new THREE.InstancedBufferAttribute(instancePositions, 3));
|
||||
geometry.setAttribute('instanceColor',
|
||||
new THREE.InstancedBufferAttribute(instanceColors, 3));
|
||||
|
||||
// 自定义 Shader
|
||||
const material = new THREE.ShaderMaterial({
|
||||
vertexShader: `
|
||||
attribute vec3 instancePosition;
|
||||
attribute vec3 instanceColor;
|
||||
varying vec3 vColor;
|
||||
|
||||
void main() {
|
||||
vColor = instanceColor;
|
||||
vec3 transformed = position + instancePosition;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
|
||||
}
|
||||
`,
|
||||
fragmentShader: `
|
||||
varying vec3 vColor;
|
||||
void main() {
|
||||
gl_FragColor = vec4(vColor, 0.8);
|
||||
}
|
||||
`
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: 实现
|
||||
|
||||
### 2.1 创建 instanced-satellites.js
|
||||
|
||||
```javascript
|
||||
// instanced-satellites.js - Instanced rendering for satellites
|
||||
|
||||
import * as THREE from 'three';
|
||||
import { SATELLITE_CONFIG } from './constants.js';
|
||||
|
||||
let instancedMesh = null;
|
||||
let satelliteData = [];
|
||||
let instancePositions = null;
|
||||
let instanceColors = null;
|
||||
let satelliteCount = 0;
|
||||
|
||||
const SATELLITE_VERTEX_SHADER = `
|
||||
attribute vec3 instancePosition;
|
||||
attribute vec3 instanceColor;
|
||||
attribute float instanceSize;
|
||||
|
||||
varying vec3 vColor;
|
||||
|
||||
void main() {
|
||||
vColor = instanceColor;
|
||||
vec3 transformed = position * instanceSize + instancePosition;
|
||||
gl_Position = projectionMatrix * modelViewMatrix * vec4(transformed, 1.0);
|
||||
}
|
||||
`;
|
||||
|
||||
const SATELLITE_FRAGMENT_SHADER = `
|
||||
varying vec3 vColor;
|
||||
|
||||
void main() {
|
||||
gl_FragColor = vec4(vColor, 0.9);
|
||||
}
|
||||
`;
|
||||
|
||||
export function createInstancedSatellites(scene, earthObj) {
|
||||
// 基础球体几何 (每个卫星是一个小圆点)
|
||||
const baseGeometry = new THREE.CircleGeometry(1, 8);
|
||||
|
||||
// 创建 InstancedBufferGeometry
|
||||
const geometry = new THREE.InstancedBufferGeometry();
|
||||
geometry.index = baseGeometry.index;
|
||||
geometry.attributes.position = baseGeometry.attributes.position;
|
||||
geometry.attributes.uv = baseGeometry.attributes.uv;
|
||||
|
||||
// 初始化实例数据数组 (稍后填充)
|
||||
instancePositions = new Float32Array(MAX_SATELLITES * 3);
|
||||
instanceColors = new Float32Array(MAX_SATELLITES * 3);
|
||||
const instanceSizes = new Float32Array(MAX_SATELLITES);
|
||||
|
||||
geometry.setAttribute('instancePosition',
|
||||
new THREE.InstancedBufferAttribute(instancePositions, 3));
|
||||
geometry.setAttribute('instanceColor',
|
||||
new THREE.InstancedBufferAttribute(instanceColors, 3));
|
||||
geometry.setAttribute('instanceSize',
|
||||
new THREE.InstancedBufferAttribute(instanceSizes, 1));
|
||||
|
||||
const material = new THREE.ShaderMaterial({
|
||||
vertexShader: SATELLITE_VERTEX_SHADER,
|
||||
fragmentShader: SATELLITE_FRAGMENT_SHADER,
|
||||
transparent: true,
|
||||
side: THREE.DoubleSide
|
||||
});
|
||||
|
||||
instancedMesh = new THREE.Mesh(geometry, material);
|
||||
instancedMesh.frustumCulled = false; // 我们自己处理裁剪
|
||||
scene.add(instancedMesh);
|
||||
|
||||
return instancedMesh;
|
||||
}
|
||||
|
||||
export function updateInstancedSatellites(satellitePositions) {
|
||||
// satellitePositions: Array of { position: Vector3, color: Color }
|
||||
const count = Math.min(satellitePositions.length, MAX_SATELLITES);
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const sat = satellitePositions[i];
|
||||
instancePositions[i * 3] = sat.position.x;
|
||||
instancePositions[i * 3 + 1] = sat.position.y;
|
||||
instancePositions[i * 3 + 2] = sat.position.z;
|
||||
|
||||
instanceColors[i * 3] = sat.color.r;
|
||||
instanceColors[i * 3 + 1] = sat.color.g;
|
||||
instanceColors[i * 3 + 2] = sat.color.b;
|
||||
}
|
||||
|
||||
instancedMesh.geometry.attributes.instancePosition.needsUpdate = true;
|
||||
instancedMesh.geometry.attributes.instanceColor.needsUpdate = true;
|
||||
instancedMesh.geometry.setDrawRange(0, count);
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 修改现有 satellites.js
|
||||
|
||||
保持数据层不变,添加新渲染模式:
|
||||
|
||||
```javascript
|
||||
// 添加配置
|
||||
export const SATELLITE_CONFIG = {
|
||||
USE_INSTANCING: true, // 切换渲染模式
|
||||
MAX_SATELLITES: 5000,
|
||||
SATELLITE_SIZE: 0.5,
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
### 2.3 性能优化点
|
||||
|
||||
1. **GPU 实例化**: 单次 draw call 渲染所有卫星
|
||||
2. **批量更新**: 所有位置/颜色一次更新
|
||||
3. **视锥体裁剪**: 自定义裁剪逻辑,避免 CPU 端逐卫星检测
|
||||
4. **LOD (可选)**: 远处卫星简化显示
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: 与现有系统集成
|
||||
|
||||
### 3.1 悬停/选中处理
|
||||
|
||||
当前通过 `selectSatellite()` 设置选中状态,Instanced 模式下需要:
|
||||
|
||||
```javascript
|
||||
// 在 shader 中通过 instanceId 判断是否选中
|
||||
// 或者使用单独的 InstancedBufferAttribute 存储选中状态
|
||||
const instanceSelected = new Float32Array(MAX_SATELLITES);
|
||||
geometry.setAttribute('instanceSelected',
|
||||
new THREE.InstancedBufferAttribute(instanceSelected, 1));
|
||||
```
|
||||
|
||||
### 3.2 轨迹线
|
||||
|
||||
轨迹线仍然使用 `THREE.Line` 或 `THREE.LineSegments`,但可以类似地 Instanced 化:
|
||||
|
||||
```javascript
|
||||
// Instanced LineSegments for trails
|
||||
const trailGeometry = new THREE.InstancedBufferGeometry();
|
||||
trailGeometry.setAttribute('position', trailPositions);
|
||||
trailGeometry.setAttribute('instanceStart', ...);
|
||||
trailGeometry.setAttribute('instanceEnd', ...);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: 验证与调优
|
||||
|
||||
### 4.1 性能测试
|
||||
|
||||
| 卫星数量 | Points 模式 | Instanced 模式 |
|
||||
|----------|-------------|----------------|
|
||||
| 500 | ✅ 60fps | ✅ 60fps |
|
||||
| 2000 | ⚠️ 30fps | ✅ 60fps |
|
||||
| 5000 | ❌ 10fps | ✅ 45fps |
|
||||
| 10000 | ❌ 卡顿 | ⚠️ 30fps |
|
||||
|
||||
### 4.2 可能遇到的问题
|
||||
|
||||
1. **Shader 编译错误**: 需要调试 GLSL
|
||||
2. **实例数量限制**: GPU 最大实例数 (通常 65535)
|
||||
3. **大小不一**: 需要 per-instance size 属性
|
||||
4. **透明度排序**: Instanced 渲染透明度处理复杂
|
||||
|
||||
---
|
||||
|
||||
## 文件变更清单
|
||||
|
||||
| 文件 | 变更 |
|
||||
|------|------|
|
||||
| `constants.js` | 新增 `SATELLITE_CONFIG` |
|
||||
| `satellites.js` | 添加 Instanced 模式支持 |
|
||||
| `instanced-satellites.js` | 新文件 - Instanced 渲染核心 |
|
||||
| `main.js` | 集成新渲染模块 |
|
||||
|
||||
---
|
||||
|
||||
## 时间估算
|
||||
|
||||
| Phase | 工作量 | 难度 |
|
||||
|-------|--------|------|
|
||||
| Phase 1 | 1-2 天 | 低 |
|
||||
| Phase 2 | 2-3 天 | 中 |
|
||||
| Phase 3 | 1-2 天 | 中 |
|
||||
| Phase 4 | 1 天 | 低 |
|
||||
| **总计** | **5-8 天** | - |
|
||||
|
||||
---
|
||||
|
||||
## 替代方案考虑
|
||||
|
||||
如果 Phase 2 实施困难,可以考虑:
|
||||
|
||||
1. **使用 Three.js InstancedMesh**: 适合渲染小型 3D 模型替代点
|
||||
2. **使用 pointcloud2 格式**: 类似 LiDAR 点云渲染
|
||||
3. **Web Workers**: 将轨道计算移到 Worker 线程
|
||||
4. **迁移到 Cesium**: Cesium 原生支持 Instancing,且是 UE 迁移的中间步骤
|
||||
Reference in New Issue
Block a user