cosmo/CAMERA_FOCUS_ALGORITHMS.md

12 KiB
Raw Permalink Blame History

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 算法原理

银河系模式采用向量方向聚焦策略,相机沿着"太阳→目标恒星"的方向向量定位,确保:

  1. 目标恒星始终在屏幕正前方
  2. 相机在目标的远端(远离太阳的一侧)
  3. 距离根据目标的远近动态调整

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. 参考资料


文档维护: Cosmo Development Team 最后更新: 2025-12-06