cosmo/CAMERA_FOCUS_ALGORITHMS.md

393 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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