cosmo/CAMERA_FOCUS_ALGORITHMS.md

393 lines
12 KiB
Markdown
Raw Normal View History

2025-12-08 10:55:38 +00:00
# Cosmo 相机聚焦算法文档
本文档详细说明了Cosmo项目中两种视图模式下的相机聚焦算法实现。
---
## 目录
- [1. 概述](#1-概述)
- [2. 太阳系模式Solar System Mode](#2-太阳系模式solar-system-mode)
- [3. 银河系模式Galaxy Mode](#3-银河系模式galaxy-mode)
- [4. 算法对比](#4-算法对比)
- [5. 关键参数调优建议](#5-关键参数调优建议)
---
## 1. 概述
Cosmo项目包含两种视图模式每种模式都有独特的相机聚焦算法
- **太阳系模式**:用于观察太阳系内的天体(行星、卫星、探测器等)
- **银河系模式**:用于观察恒星际空间中的恒星系统和系外行星
两种模式的聚焦算法设计理念不同,以适应各自的尺度和用户体验需求。
---
## 2. 太阳系模式Solar System Mode
### 2.1 实现位置
**文件**: `/frontend/src/components/CameraController.tsx`
**组件**: `CameraController`
### 2.2 算法原理
太阳系模式采用**固定偏移量**的聚焦策略,相机位置相对于目标天体有固定的空间偏移。
### 2.3 核心算法
```typescript
// 1. 获取目标天体的渲染位置
const renderPos = calculateRenderPosition(focusTarget, allBodies);
const currentTargetPos = new Vector3(renderPos.x, renderPos.z, renderPos.y);
// 2. 计算目标到原点的距离
const pos = focusTarget.positions[0];
const distance = Math.sqrt(pos.x ** 2 + pos.y ** 2 + pos.z ** 2);
// 3. 根据天体类型确定偏移量
let offset: number;
let heightMultiplier = 1;
let sideMultiplier = 1;
if (focusTarget.type === 'planet') {
offset = 4;
heightMultiplier = 1.5;
sideMultiplier = 1;
} else if (focusTarget.type === 'probe') {
if (parentInfo) {
// 探测器在行星附近
offset = 3;
heightMultiplier = 0.8;
sideMultiplier = 1.2;
} else if (distance < 10) {
// 近距离探测器
offset = 5;
heightMultiplier = 0.6;
sideMultiplier = 1.5;
} else if (distance > 50) {
// 远距离探测器
offset = 4;
heightMultiplier = 0.8;
sideMultiplier = 1;
} else {
// 中距离探测器
offset = 6;
heightMultiplier = 0.8;
sideMultiplier = 1.2;
}
} else {
// 其他天体类型
offset = 10;
heightMultiplier = 1;
sideMultiplier = 1;
}
// 4. 计算相机目标位置(简单的坐标偏移)
targetPosition.current.set(
currentTargetPos.x + (offset * sideMultiplier),
currentTargetPos.y + (offset * heightMultiplier),
currentTargetPos.z + offset
);
```
### 2.4 动画实现
使用帧动画进行平滑过渡:
```typescript
// 使用 easing 函数实现平滑动画
const eased = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
// 线性插值相机位置
camera.position.lerpVectors(startPosition.current, targetPosition.current, eased);
// 相机始终看向目标
camera.lookAt(renderPos.x, renderPos.z, renderPos.y);
```
### 2.5 特点
-**固定偏移量**:相机距离目标的偏移量是预设的常量
-**类型感知**:不同类型的天体使用不同的偏移参数
-**上下文感知**:探测器根据其位置(近行星、近太阳、远距离)调整相机距离
-**简单直观**:适合太阳系内的小尺度观察
-**动画平滑**使用ease-in-out缓动函数
### 2.6 偏移量参数表
| 天体类型 | offset | heightMultiplier | sideMultiplier | 相机高度 | 相机侧向距离 | 相机深度距离 |
|---------|--------|------------------|----------------|----------|-------------|-------------|
| 行星 (planet) | 4 | 1.5 | 1 | 6 | 4 | 4 |
| 探测器-近行星 (probe near planet) | 3 | 0.8 | 1.2 | 2.4 | 3.6 | 3 |
| 探测器-近距离 (probe < 10 AU) | 5 | 0.6 | 1.5 | 3 | 7.5 | 5 |
| 探测器-远距离 (probe > 50 AU) | 4 | 0.8 | 1 | 3.2 | 4 | 4 |
| 探测器-中距离 (probe 10-50 AU) | 6 | 0.8 | 1.2 | 4.8 | 7.2 | 6 |
| 其他天体 | 10 | 1 | 1 | 10 | 10 | 10 |
**计算公式**
```
相机X = 目标X + offset × sideMultiplier
相机Y = 目标Y + offset × heightMultiplier
相机Z = 目标Z + offset
```
---
## 3. 银河系模式Galaxy Mode
### 3.1 实现位置
**文件**: `/frontend/src/components/GalaxyScene.tsx`
**组件**: `CameraAnimator`
### 3.2 算法原理
银河系模式采用**向量方向聚焦**策略,相机沿着"太阳→目标恒星"的方向向量定位,确保:
1. 目标恒星始终在屏幕正前方
2. 相机在目标的远端(远离太阳的一侧)
3. 距离根据目标的远近动态调整
### 3.3 核心算法
```typescript
// 1. 计算目标恒星到太阳的距离
const targetDistanceFromSun = Math.sqrt(x * x + y * y + z * z);
// 2. 动态计算相机拉远距离
const basePullBack = 150;
const pullBackDistance = targetDistanceFromSun < 500
? basePullBack
: basePullBack + (targetDistanceFromSun - 500) * 0.08;
// 3. 计算方向向量(从太阳指向目标恒星,已归一化)
const dirX = x / targetDistanceFromSun;
const dirY = y / targetDistanceFromSun;
const dirZ = z / targetDistanceFromSun;
// 4. 计算相机位置:目标位置 + 方向向量 × 拉远距离
// 相机在目标的"后方"(远离太阳的一侧)
const cameraX = x + dirX * pullBackDistance;
const cameraY = y + dirY * pullBackDistance + 30; // 额外的垂直偏移
const cameraZ = z + dirZ * pullBackDistance;
```
### 3.4 图解说明
```
相机位置
太阳 (0,0,0) ----方向向量----> 目标恒星 ------拉远距离-----> 📷
Origin Target Star (x,y,z) (x+dirX×d, y+dirY×d+30, z+dirZ×d)
相机看向目标 ←
```
**关键点**
- 相机位置 = `目标位置 + 方向向量 × pullBackDistance`
- **不是**`目标位置 - 方向向量 × pullBackDistance`(这会把相机放在太阳和目标之间,导致聚焦错误)
### 3.5 动画实现
```typescript
// 使用 easeInOutCubic 缓动函数
const eased = progress < 0.5
? 4 * progress * progress * progress
: 1 - Math.pow(-2 * progress + 2, 3) / 2;
// 插值相机位置
camera.position.x = startPos.x + (cameraX - startPos.x) * eased;
camera.position.y = startPos.y + (cameraY - startPos.y) * eased;
camera.position.z = startPos.z + (cameraZ - startPos.z) * eased;
// 插值 OrbitControls 的目标点
controls.target.x = startTarget.x + (x - startTarget.x) * eased;
controls.target.y = startTarget.y + (y - startTarget.y) * eased;
controls.target.z = startTarget.z + (z - startTarget.z) * eased;
controls.update();
```
### 3.6 特点
-**方向向量驱动**:基于太阳→目标的方向计算相机位置
-**动态距离**:根据目标距离自动调整拉远距离
-**正确定位**:相机在目标的远端,确保目标在屏幕正前方
-**尺度感知**:近距离恒星和远距离恒星有明显的视觉差异
-**平滑过渡**同时插值相机位置和OrbitControls的target
### 3.7 距离计算公式
| 目标距离 (AU单位) | 拉远距离计算 | 示例 |
|------------------|------------|------|
| < 500 | 150 () | (~130 AU)150 |
| ≥ 500 | 150 + (distance - 500) × 0.08 | 距离2000 AU的系统拉远150 + 1500×0.08 = 270单位 |
| ≥ 500 | 150 + (distance - 500) × 0.08 | 距离5000 AU的系统拉远150 + 4500×0.08 = 510单位 |
**公式**
```
pullBackDistance = distance < 500 ? 150 : 150 + (distance - 500) × 0.08
```
### 3.8 坐标系说明
在银河系模式中,使用的坐标系统:
- **X轴**指向银道坐标系的X方向
- **Y轴**:垂直于银道平面(向上为正)
- **Z轴**指向银道坐标系的Z方向
- **原点**太阳系Solar System
- **单位**秒差距Parsec× 100渲染缩放
**坐标转换**
```typescript
// 数据库中的坐标(秒差距)
const db_x = star.position_x; // 单位: pc
const db_y = star.position_y; // 单位: pc
const db_z = star.position_z; // 单位: pc
// 渲染坐标Three.js场景坐标SCALE=100
const render_x = db_x * 100;
const render_y = db_y * 100;
const render_z = db_z * 100;
```
---
## 4. 算法对比
| 特性 | 太阳系模式 | 银河系模式 |
|------|----------|----------|
| **聚焦策略** | 固定偏移量 | 方向向量 + 动态距离 |
| **相机定位方式** | 目标 + 常量偏移 | 目标 + 方向 × 动态距离 |
| **尺度范围** | 0.1 - 100 AU | 100 - 5000+ AU (pc级别) |
| **距离感** | 偏移量固定,距离感弱 | 动态调整,距离感强 |
| **类型感知** | 强(根据天体类型调整) | 无(所有恒星系统相同策略) |
| **计算复杂度** | 低(简单加法) | 中(向量计算 + 归一化) |
| **缓动函数** | ease-in-out (quadratic) | easeInOutCubic |
| **动画时长** | 由帧率和速度参数决定 | 固定2.5秒 |
| **适用场景** | 小尺度、多类型天体 | 大尺度、单一类型(恒星系统) |
---
## 5. 关键参数调优建议
### 5.1 太阳系模式
如果需要调整相机距离:
```typescript
// 在 CameraController.tsx 中修改 offset 值
if (focusTarget.type === 'planet') {
offset = 4; // 增大此值会让相机离行星更远
heightMultiplier = 1.5; // 增大此值会增加相机的高度
sideMultiplier = 1; // 增大此值会增加相机的侧向距离
}
```
**建议**
- 小天体卫星、小行星offset 2-5
- 中等天体行星offset 4-8
- 大天体木星、土星offset 8-15
- 探测器offset 3-6
### 5.2 银河系模式
如果需要调整相机距离:
```typescript
// 在 GalaxyScene.tsx 中修改 basePullBack 和系数
const basePullBack = 150; // 基础拉远距离单位AU × 100
const pullBackDistance = targetDistanceFromSun < 500
? basePullBack
: basePullBack + (targetDistanceFromSun - 500) * 0.08; // 0.08 是距离系数
```
**建议**
- 近距离恒星(< 500单位):basePullBack = 120-180
- 距离系数0.05-0.12(越大,远距离恒星拉得越远)
- 垂直偏移20-50增加俯视角度
### 5.3 动画速度调整
**太阳系模式**
```typescript
animationProgress.current += delta * 0.8; // 增大此值会加快动画
```
**银河系模式**
```typescript
const duration = 2500; // 增大此值会减慢动画(单位:毫秒)
```
---
## 6. 常见问题与解决方案
### Q1: 银河系模式聚焦后看不到目标恒星?
**原因**:相机距离目标太近,或者相机定位在目标和太阳之间。
**检查**
```typescript
// 确保使用的是加法,不是减法
const cameraX = x + dirX * pullBackDistance; // ✅ 正确
const cameraX = x - dirX * pullBackDistance; // ❌ 错误,会把相机放在中间
```
### Q2: 太阳系模式下相机离探测器太远?
**解决**
```typescript
// 减小探测器的 offset 值
if (focusTarget.type === 'probe') {
offset = 3; // 从 6 减小到 3
}
```
### Q3: 银河系模式下远距离恒星聚焦后太小?
**解决**
```typescript
// 减小距离系数,让远距离恒星的相机不要拉得太远
const pullBackDistance = targetDistanceFromSun < 500
? basePullBack
: basePullBack + (targetDistanceFromSun - 500) * 0.05; // 从0.08降到0.05
```
### Q4: 动画过渡不够平滑?
**解决**
```typescript
// 太阳系模式:减小动画速度
animationProgress.current += delta * 0.5; // 从0.8降到0.5
// 银河系模式:增加动画时长
const duration = 3500; // 从2500增加到3500ms
```
---
## 7. 版本历史
| 版本 | 日期 | 修改内容 |
|-----|------|---------|
| 1.0 | 2025-12-06 | 初始版本,记录太阳系模式和银河系模式的聚焦算法 |
---
## 8. 参考资料
- Three.js 文档: https://threejs.org/docs/
- React Three Fiber: https://docs.pmnd.rs/react-three-fiber/
- OrbitControls: https://threejs.org/docs/#examples/en/controls/OrbitControls
- Easing Functions: https://easings.net/
---
**文档维护**: Cosmo Development Team
**最后更新**: 2025-12-06