From 95968c0575fd741d4ab3df2eea8092963fe5b644 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 16 Jul 2026 16:46:10 +0800 Subject: [PATCH] docs(spec): Kerr orbiting planets design (Phase 3.4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Spec for 5-8 planets on Kerr circular orbits with strong-field Lense-Thirring nodal precession. Path A: CPU computes position each frame, uploads to the existing storage buffer; shader unchanged. Physics: closed-form Ω_φ (Bardeen 1972 eqn 2.16) and Ω_θ (Caltech Ph236 epicyclic vertical frequency), giving exact nodal precession Ω_φ - Ω_θ. χ=0 degenerates exactly to Schwarzschild (Ω_LT = 0, Newtonian Ω_φ). Explicitly rejects Mino-time analytic geodesics (Fujita-Hikida): circular orbits collapse the three-frequency decomposition to Ω_φ alone, elliptic functions have no WGSL/std backing, and it would break physics.rs's single-testable-mirror design. Closed-form Kerr frequencies are the right tool for circular orbits. Orbit radius bound to k·kerr_isco(χ), so planets track the disk inner edge as spin changes and span the strong-field region (k=2.5 → r ∈ [1.25, 7.5]). Random initial attitudes (ChaCha8Rng + UI seed) so the three orbital planes precess at different Ω_LT rates — the visible Kerr signature. Decomposes Planet into OrbitParams (immutable elements) + Planet (derived center), keeps SphereData GPU layout untouched. Adds time_scale UI knob because Ω_LT at r=8 needs ~25 min per revolution otherwise. --- ...2026-07-16-kerr-orbiting-planets-design.md | 404 ++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-16-kerr-orbiting-planets-design.md diff --git a/docs/superpowers/specs/2026-07-16-kerr-orbiting-planets-design.md b/docs/superpowers/specs/2026-07-16-kerr-orbiting-planets-design.md new file mode 100644 index 0000000..1d06763 --- /dev/null +++ b/docs/superpowers/specs/2026-07-16-kerr-orbiting-planets-design.md @@ -0,0 +1,404 @@ +# Kerr 轨道行星系统(Phase 3.4) + +**日期:** 2026-07-16 +**状态:** 设计稿,待实现 +**前置:** Phase 2(Kerr 光线积分)、Phase 3.1(体积盘) + +## 目标 + +让 5–8 颗行星沿 Kerr 时空的**圆轨道**绕黑洞旋转,轨道面因 Lense–Thirring 效应绕黑洞自旋轴进动。行星位置每帧由 CPU 用闭式解析公式计算,写入既有 storage buffer;渲染端 shader **不改动**。 + +这是 AGENTS.md 里档次 3 的方案:Kerr 圆轨 + 强场节点进动。路径 A(CPU 算位置 + 现有上传机制),shader 零改动。 + +## 非目标 + +- **不做完整类时测地线**(偏心、倾轨、plunging)。理由:圆轨道下径向频率 `Ω_r = 0`,Mino 时间的三频率分解退化为纯 `Ω_φ`;上 Mino 时间需要椭圆函数(WGSL 与 std 都没有),破坏 `physics.rs` 单一可测试镜像的设计原则。这不是"测地线可视化"项目。 +- **不改渲染端**。`planet_hit`、`SphereData`、storage buffer 布局全部不动。进动和轨道运动只在 CPU 端,最终位置照常上传。 +- **不做行星-行星引力相互作用**。测试粒子近似。 + +## 物理模型 + +全部闭式公式,无数值积分。自然单位 `Rs = 1`,故 `M = 0.5`,`a = χM = 0.5χ`。 + +### 轨道角速度 `Ω_φ` + +赤道 prograde 圆轨,[Bardeen 1972, eqn 2.16](https://physics.stackexchange.com/questions/502796/how-to-derive-the-angular-velocity-of-circular-orbits-in-kerr-geometry): + +``` +Ω_φ(r, χ) = 1 / (r^1.5 + a) // a = 0.5χ +``` + +`χ = 0` 时 `a = 0`,退化为牛顿开普勒 `Ω = 1/r^1.5`。 + +### 节点进动率 `Ω_LT` + +轨道面绕黑洞自旋轴(Y 轴)的进动率。精确强场形式: + +``` +Ω_LT(r, χ) = Ω_φ(r, χ) − Ω_θ(r, χ) +``` + +其中 `Ω_θ` 是 Kerr 赤道圆轨的垂直 epicyclic 频率,闭式表达([Caltech Ph236 lec27](http://www.tapir.caltech.edu/~chirata/ph236/2011-12/lec27.pdf);[Okazaki、Kato 等的 标准 epicyclic 频率结果](https://arxiv.org/pdf/1304.6936)): + +``` +Ω_θ² = Ω_φ² · (1 − 4a·Ω_φ/r + 3a²/r²) // a = 0.5χ +Ω_θ = Ω_φ · sqrt(1 − 4a·Ω_φ/r + 3a²/r²) +``` + +**退化验证:** `χ = 0` 时 `a = 0`,括号内 = 1,故 `Ω_θ = Ω_φ`,`Ω_LT = 0`——精确退化为"轨道面固定"(Schwarzschild 球对称),满足 AGENTS.md 的核心不变量。 + +**弱场极限交叉验证:** 大 `r` 时展开 `Ω_φ ≈ r^−1.5`,`Ω_θ ≈ Ω_φ(1 − 1.5·(2Ma/r³)/Ω_φ · ...)`,最终 `Ω_LT → 2Ma/r³ = χ/r³`(M=0.5)。此弱场极限用作测试断言,不用于渲染。 + +**实现:** `kerr_nodal_precession(r, chi)` 封装上述两式,返回 `Ω_φ - Ω_θ`。注意括号内可能因数值精度略负(极端 r/a 组合),`sqrt` 前用 `.max(0.0)` 钳位。 + +### 轨道半径 + +动态绑定 Kerr ISCO(`physics.rs:65` 已实现的 `kerr_isco`): + +``` +r = k · kerr_isco(χ) +``` + +`k` 是 UI 可调的倍数(默认 2.5)。ISCO 从 `χ=0` 的 3 缩到 `χ=1` 的 0.5,故默认 `k=2.5` 给 `r ∈ [1.25, 7.5]`,**横跨强场区**——这是选强场 `Ω_LT` 而非弱场近似的物理理由。 + +### χ=0 退化验证表 + +| 量 | χ = 0 | 含义 | +|---|---|---| +| `Ω_φ` | `1/r^1.5` | 牛顿开普勒 | +| `Ω_LT` | `0` | 无进动,轨道面固定 | +| `r` | `k · 3` | Schwarzschild ISCO = 6M = 3 Rs | + +## 组件与数据结构 + +### `OrbitParams`(轨道根数,启动时随机生成) + +不可变,除非 UI 改种子重生整个系统。 + +```rust +#[derive(Component, Clone, Copy)] +pub struct OrbitParams { + pub radius_factor: f32, // k, 乘到 kerr_isco(χ) 上得实际半径 + pub inclination: f32, // 轨道面法向与 Y 轴夹角 (rad) + pub longitude_of_node: f32, // 升交点经度 (rad), 决定初始进动相位 + pub phase: f32, // 轨道内初始相位 (rad) +} +``` + +### `Planet`(渲染状态,每帧重算 center) + +字段不变,但 `center` 从"静态坐标"变成"每帧由 orbit_system 派生"。 + +```rust +#[derive(Component, Clone, Copy)] +pub struct Planet { + pub center: Vec3, // ← 每帧重算 + pub radius: f32, + pub color: Vec3, + pub emissive: bool, +} +``` + +**为什么拆两个组件:** `OrbitParams` 是不可变根数,`Planet.center` 是派生量。分开后 `orbit_system` 只写 `Planet.center`、读 `OrbitParams`——职责清晰,且轨道力学逻辑可独立测试,不依赖渲染数据结构。符合 AGENTS.md 的 deep-module 原则。 + +### `SphereData`(GPU 布局)— 不变 + +`render/material.rs:122` 的 `SphereData { center, radius, color, emissive, _pad0..2 }` **不改**。进动和轨道运动全在 CPU 算完,只把最终 `center` 写进 buffer。这是路径 A 的核心好处。 + +## 轨道几何 + +给定 `OrbitParams` 和当前时间 `t`,计算行星世界空间位置。 + +### 1. 轨道面法向与基向量 + +从倾角 `i` 和升交点 `Ω`(用 `longitude_of_node`)解析构造轨道面内两个正交单位基: + +```rust +// 轨道面法向 (Y 轴为极轴的球坐标) +let n = Vec3::new( + i.sin() * Omega.cos(), + i.cos(), + i.sin() * Omega.sin(), +); +// 轨道面内基: u 沿升节点方向, v = n × u +let u = Vec3::new(-Omega.sin(), 0.0, Omega.cos()); +let v = n.cross(u); // 已单位化 (u, n 均单位且正交) +``` + +### 2. 进动 + +整个 `(u, v)` 基绕 Y 轴旋转 `Ω_LT · t`。用旋转矩阵作用: + +```rust +let prec_angle = Omega_LT * t; +let cos_p = prec_angle.cos(); +let sin_p = prec_angle.sin(); +// 绕 Y 轴: x' = x cos + z sin, z' = -x sin + z cos +let u_prec = Vec3::new( + u.x * cos_p + u.z * sin_p, + u.y, + -u.x * sin_p + u.z * cos_p, +); +let v_prec = Vec3::new( + v.x * cos_p + v.z * sin_p, + v.y, + -v.x * sin_p + v.z * cos_p, +); +``` + +### 3. 行星位置 + +```rust +let theta = phase + Omega_phi * t; +let r = radius_factor * kerr_isco(chi); +let center = r * (theta.cos() * u_prec + theta.sin() * v_prec); +``` + +**disk_tilt 的处理:** shader 的 `planet_hit`(`black_hole.wgsl:632`)会把世界空间球心用 `rot_x(center, -disk_tilt)` 转进盘局部空间。所以轨道平面定义在**世界空间**,倾斜交给 shader——轨道系统不读 `disk_tilt`。 + +## 随机生成 + +5–8 颗,全随机散布。用确定性 PRNG(`ChaCha8Rng` + UI 种子),改种子时整个系统可复现地重生。 + +```rust +pub fn spawn_planet_system( + mut commands: Commands, + params: Res, + seed: Res, +) { + // 先 despawn 现有 (Planet, OrbitParams) — 由系统签名 query 完成 + let mut rng = ChaCha8Rng::seed_from_u64(seed.0); + for _ in 0..params.planet_count_target { + let inclination = rng.gen_range(0.0..PI); + let longitude = rng.gen_range(0.0..TAU); + let phase = rng.gen_range(0.0..TAU); + let radius_factor = rng.gen_range(2.0..4.0); + commands.spawn(( + OrbitParams { radius_factor, inclination, longitude_of_node: longitude, phase }, + Planet { + center: Vec3::ZERO, // 首帧由 orbit_system 填 + radius: rng.gen_range(0.8..1.6), + color: random_planet_color(&mut rng), + emissive: false, + }, + )); + } +} +``` + +`ChaCha8Rng`(不是 `thread_rng()`)的理由: +1. **可复现**——UI 改种子时整个系统重生,同样种子给同样布局,方便调试和截图对比。 +2. **可测试**——CPU 测试能 seed 固定值断言生成的根数。 +3. **跨平台一致**——web 与 desktop 同种子给同布局。 + +依赖:`rand`(通常已是 Bevy 间接依赖)+ `rand_chacha`。若 `rand` 未在 `Cargo.toml` 直接声明,需加上。 + +## 系统调度 + +### 新增系统 + +```rust +// Update: 读 OrbitParams + time + spin, 写 Planet.center +fn orbit_system( + time: Res