12 KiB
12 KiB
Cosmo 相机聚焦算法文档
本文档详细说明了Cosmo项目中两种视图模式下的相机聚焦算法实现。
目录
1. 概述
Cosmo项目包含两种视图模式,每种模式都有独特的相机聚焦算法:
- 太阳系模式:用于观察太阳系内的天体(行星、卫星、探测器等)
- 银河系模式:用于观察恒星际空间中的恒星系统和系外行星
两种模式的聚焦算法设计理念不同,以适应各自的尺度和用户体验需求。
2. 太阳系模式(Solar System Mode)
2.1 实现位置
文件: /frontend/src/components/CameraController.tsx
组件: CameraController
2.2 算法原理
太阳系模式采用固定偏移量的聚焦策略,相机位置相对于目标天体有固定的空间偏移。
2.3 核心算法
// 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 动画实现
使用帧动画进行平滑过渡:
// 使用 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 算法原理
银河系模式采用向量方向聚焦策略,相机沿着"太阳→目标恒星"的方向向量定位,确保:
- 目标恒星始终在屏幕正前方
- 相机在目标的远端(远离太阳的一侧)
- 距离根据目标的远近动态调整
3.3 核心算法
// 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 动画实现
// 使用 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(渲染缩放)
坐标转换:
// 数据库中的坐标(秒差距)
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 太阳系模式
如果需要调整相机距离:
// 在 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 银河系模式
如果需要调整相机距离:
// 在 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 动画速度调整
太阳系模式:
animationProgress.current += delta * 0.8; // 增大此值会加快动画
银河系模式:
const duration = 2500; // 增大此值会减慢动画(单位:毫秒)
6. 常见问题与解决方案
Q1: 银河系模式聚焦后看不到目标恒星?
原因:相机距离目标太近,或者相机定位在目标和太阳之间。
检查:
// 确保使用的是加法,不是减法
const cameraX = x + dirX * pullBackDistance; // ✅ 正确
const cameraX = x - dirX * pullBackDistance; // ❌ 错误,会把相机放在中间
Q2: 太阳系模式下相机离探测器太远?
解决:
// 减小探测器的 offset 值
if (focusTarget.type === 'probe') {
offset = 3; // 从 6 减小到 3
}
Q3: 银河系模式下远距离恒星聚焦后太小?
解决:
// 减小距离系数,让远距离恒星的相机不要拉得太远
const pullBackDistance = targetDistanceFromSun < 500
? basePullBack
: basePullBack + (targetDistanceFromSun - 500) * 0.05; // 从0.08降到0.05
Q4: 动画过渡不够平滑?
解决:
// 太阳系模式:减小动画速度
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