Files
GR-raytracing/video_rendering_plan.md
T

13 KiB
Raw Blame History

从单张渲染推进到视频的实现计划

目标与边界

把当前已验证的 Minkowski / 解析 Schwarzschild 单张点源渲染器扩展为视频渲染器,同时把顶层执行路径改造成可承受未来 nmesh 四维数据 out-of-core 读取的结构。

视频的物理单位是一个 frame 在相机 coordinate time t_camera 的 observer event:位置、四速度和空间 tetrad。每个 frame 的 image-plane samples 都从该 event 向过去追踪 null geodesic。不能用“上一帧的图像平移/变形”替代追踪,也不能把星表变为 RGB sky texture。

本计划的第一项可见产物是带零填充序号的 PNG 序列;视频编码只在 PNG 序列通过测试后作为独立的可选步骤加入。OpenCV 和 FFmpeg 都不进入 observer、geodesic、frame 或 spacetime 的物理接口。

目标架构

observer-track input
       |
       v
Movie: frame times + per-frame image meshes
       |
       v       (one refinement generation at a time)
SampleRequest -> RayPool (SoA, inactive / active / terminated)
                         |
                         v
          time slabs: latest coordinate time -> earliest
          activate rays when slab reaches t_camera
          advance all active rays to the slab's left boundary
                         |
                         v
              endpoints installed into the owning frame mesh
                         |
                         v
        refine if needed; otherwise inverse lens map + catalog + HDR
                         |
                         v
             numbered PNG frames -> optional FFmpeg video command

一个 refinement pass 必须完成一次从最新到最早的 slab sweep 后,才能由 endpoint 判断是否再加样本。下一 pass 只追踪新样本并重新进行 sweep;绝不能在已经释放早期 slab 后临时创建 ray 再回到相机时间。解析 Minkowski / Schwarzschild backend 可以把 load_slab 实现为无 I/O 的逻辑 slab,因此能先验证同一 scheduler,而不为解析时空保留逐帧 special path。

Phase A:先建立输入与输出边界,保持单帧兼容

  1. 新增 observer_track.h/.c,将目前的 ObserverState 提升为可插值的 ObserverTrack:每个 sample 保存 t_coordinate、tau、x[3] 和完整 tetrad。提供加载、严格单调时间校验、区间查找、插值和销毁函数。原有固定 observer 构造函数保留,作为只含一个 sample 的兼容快捷方式。
  2. 第一版轨迹输入采用人可审阅的 21 列 CSV,字段固定为 t,tau,x,y,z,e0t,e0x,...,e3z;不在 renderer 内从“速度曲线”临时猜 tetrad。loader 做完整性、有限数与严格单调时间检查;tetrad 的 metric 正交归一由轨迹生成器和 backend-aware regression 验证。另提供生成器工具/模式为测试写出 CSV。这样 NR 生产轨迹、解析测试轨迹和以后离线 trajectory generator 使用完全相同的 renderer 输入。
  3. 加入 movie.h/.c,定义 MovieSettings、MovieFrame(frame_id、t_camera、插值得到的 observer state、mesh、HDR image)与 Movie。用 start-time、duration、fps 生成 frame 时间表;先拒绝轨迹覆盖范围外的时间。
  4. 将 render_frame() 中与单帧无关的工作拆成“初始化 mesh / 安装 endpoint / splat HDR / 写出”四个明确步骤。保持现有单帧命令和 PPM/PNG 行为可用;movie 模式只是调用同一套 frame rendering 与 optics。
  5. 新增 output.h/.c,从 optics 移出文件命名和写图逻辑。PNG 仍复用当前 libpng 路径,movie PNG 命名固定为 <output-dir>/<prefix>_%06u.png,例如 output/imgs/minkowski_accel_000042.png。在创建前验证目录、prefix 与帧号范围,写入失败应指出 frame id 和路径。

验收:原有 make test 和两种单帧 backend 的输出保持不变;新测试验证 CSV 拒绝缺列、非有限数与非单调时间,frame 时间表精确包含首帧且不越界,PNG 序号无冲突。此阶段不要求 FFmpeg,也不改变 ray tracing 的调度方式。

Phase B:用平直时空 accelerated observer 验证 video / tetrad / Doppler

  1. 为 Minkowski 增加“从静止开始、沿相机 forward 轴恒定 proper acceleration”的离线轨迹生成器,且把生成的 samples 写成上述通用 CSV。取初始 event t=tau=0, x=0,加速度方向为 forward;以 proper time 参数化:

    [ \eta=a\tau,\quad t=a^{-1}\sinh\eta,\quad x_\parallel=a^{-1}(\cosh\eta-1), ]

    e_(0) 与 forward tetrad 为相同 rapidity eta 的 Lorentz boost,其余两根空间轴不变。用 t 作为 frame / ray / future slab 的共同时间轴,tau 只作轨迹元数据。首个固定 regression 使用 coordinate-time 0..2、30 fps(含两端共 61 frame)以及 a = 1.52(c=1);末帧速度约为 0.95c,足以产生清晰的 aberration、headlight 与 Doppler 效应。CLI/配置仍可覆盖这些基准值。

  2. movie 的首个可运行实现可先在每 frame 调用当前 geodesic_trace_past,以尽快验证轨迹和输出链;但它只能作为解析 backend 的过渡测试模式。紧接着在同一 phase 完成 Phase C 的 RayPool/slab scheduler,并删除 movie 路径中的逐帧 trace loop。单帧入口仍可直接 trace,便于 debug。

  3. 对每个 frame 保存可机器检查的诊断:中心方向的 g、前向与后向指定 catalog stars 的 g、它们的 image position / flux,以及 observer rapidity。诊断是 CSV/测试数据,不叠加到图像。

  4. 回归物理:a=0 时所有 frame 与静止 Minkowski 基准一致,且 g=1;加速后前半球出现 aberration/headlight 集中,前向星蓝移、后向星红移,结果与解析 Doppler factor 逐 sample 比较。再检查 tetrad 的 Minkowski 正交归一和 e_(0) 与轨迹有限差分速度的一致性。

验收:短序列(低分辨率、几十帧)能快速写出 PNG;首尾帧和诊断共同证明从静止到加速的连续变化,没有镜头翻转、时间倒退或每帧亮度无物理原因的跳变。

Phase C:以真正的 time-slab scheduler 替换 movie 逐帧追踪

  1. 扩展 spacetime.h 为 slab-oriented 接口:source 打开/重置、查询可用时间范围、从 t_hi 装载前一个 MetricSlab、释放 slab、在 slab 内 eval / classify。解析 backend 的 slab 是轻量边界对象;nmesh 才在此处进行顺序 I/O。顶层 movie 不得检查 backend 类型。
  2. 新增 ray.h/.c 的 SoA RayPool。字段至少包含 coordinate time、位置、Pi_i、log(alpha p^0)、step size、frame_id、mesh vertex/sample id 与状态;capacity 可增长但不逐 ray 分配。所有 endpoint 写回使用稳定的 (frame_id, vertex_id) 关联。
  3. 将 geodesic 拆为“从 observer 初始化 ray”“在指定 slab 内推进到左边界或终止”“提取 endpoint”。保留现有 geodesic_trace_past 作为单帧/测试 convenience wrapper,以免破坏已有 regression。
  4. 每个 generation 的调度严格为:收集所有 frame 尚未追踪的 mesh vertices -> 建 RayPool -> 从 movie 最大 t_camera 倒扫 -> 在相应 slab 激活 rays -> OpenMP bulk evolve active rays -> compact terminated rays -> 释放 slab -> 安装结果。所有后端可变 evaluator scratch 放在每线程 workspace;不使用 per-ray task 或 mutex。
  5. 在现有固定 coarse mesh 上先完成以上路径,保持当前“image vertices -> source triangles -> catalog inverse interpolation -> PSF”的数据流和共享边界 ownership。adaptive refinement 留作下一 phase;不能为视频退化成逐 pixel sky lookup。

验收:对同一 Minkowski 与解析 Schwarzschild observer track,slab scheduler 与旧逐帧 convenience path 的 endpoint status、n_infinity、log g 及完成图像在容差内一致;增加 instrumentation 证明一个 generation 的每个 slab 只 load/free 一次,而不是每 frame 一次。Minkowski / Schwarzschild 不应出现额外磁盘 I/O。

Phase D:补全多 pass adaptive mesh 与 out-of-core 准备

  1. 给 FrameLensMesh 增加稳定 vertex ID、请求队列、triangle level/flags 和“缺失 endpoint”状态。每 pass 只为需要的 edge midpoint/center 产生 SampleRequest;安装结果后再判定 mapping interpolation error、orientation consistency 与局部 Jacobian 奇异性。
  2. 以迭代 queue(可按 frame/root tile 并行、线程本地 request buffer 后 sort/deduplicate)替代递归 task。每一个 pass 完整执行 Phase C sweep;只有所有 frame 都无新请求才进行 catalog splat。
  3. 在 movie 生命周期中及时释放已完成的 RayPool、临时 request 和单帧 HDR;保留最终 mesh/endpoints,或在渲染 PNG 后按明确策略释放,避免视频时无界增长。
  4. 为 nmesh 预留并实现 source-side slab overlap / temporal ghost-slice 契约、可配置 memory budget、slab coverage 日志和线程本地 MetricWorkspace ownership。此 phase 不重采样为 Cartesian grid,也不假定相邻时间 slice 的 AMR tree 相同。

验收:构造可控 Schwarzschild strong-lensing case,确认 refinement 只增加必要 samples、多个 image patches 正确产生多像、shared edge 上的星不会重复 splat。记录每 pass 的 ray 数、峰值 active ray 数、slab load 数、peak metric memory 与 wall time,作为未来 nmesh 性能基线。

Phase E:动态 Schwarzschild 视频验证

  1. 首先使用解析 Schwarzschild Kerr--Schild backend 和离线 observer track,不改变 metric 为静态这一事实。首个回归轨迹人为规定为:从静止于 r=30M 出发,平滑加速径向向内,到 r=20M 附近平滑制动为静止,再按时间反演的同一路径返回 r=30M;径向局部速度上限取 0.5c(相对每个 event 的 static Schwarzschild tetrad)。轨迹生成器必须同时积分/构造位置和正交 tetrad,而非只对当前 observer_inward_speed 逐帧套 boost。
  2. 这条路线远离 horizon,优先测试运动频移、shadow / lensing 的连续形变和回程可逆性;CSV metadata 标注 prescribed_radial_turnaround、速度上限与 proper acceleration。renderer 不对其物理来源作假设。逼近 horizon 的路线留到该基准稳定后再加。
  3. 对每帧测试 shadow 轮廓、Einstein ring / 多像位置、capture fraction、g map 与 catalog star temperature shift 的连续性。与同一时刻旧单帧结果比对;再以静止轨迹确认它退化为现有 Schwarzschild baseline。

验收:前进与后退均显示相对静止 observer 的引力/运动频移和相应 image distortion,且不会因 frame 边界、slab 边界或 tetrad 插值出现闪烁。这里只要求定性图像和定量 ray regressions;具体径向轨迹参数、ODE step、refinement 阈值仍由收敛试验决定。

Phase F:输出视频与运行界面

  1. 保持 PNG sequence 为默认调试/测试产物:任何失败都能定位到具体 frame,也便于逐帧回归与检查 tone mapping。CLI 采用 --frames-dir、--frames-prefix、--start-time、--duration、--fps、--observer-track;单帧 --output 继续存在且语义不变。
  2. 在 PNG 序列稳定后,添加 make encode 或单独的 documented FFmpeg invocation,把序列编码为 H.264/H.265/ProRes 等明确选择的容器。编码读取已经 tone-mapped 的 PNG;HDR/EXR 与最终色彩管理是后续独立决策,不悄悄混入首次视频实现。
  3. 如需“直接输出视频”,实现一个只属于 output 的 FFmpeg pipe/encoder backend,并以同一 RGB frame bytes 与帧率接口接收图像。它必须与 PNG 逐帧输出并列、可独立关闭;第一次实现优先用 FFmpeg CLI/library,不强制引入 OpenCV。只有当 OpenCV 未来提供明确的读取/验证价值时才加入,避免让其成为核心依赖。

验收:同一短 PNG 序列可被 FFmpeg 无丢帧编码;解码后帧数、分辨率、帧率与原序列一致。PNG 写出失败或编码器不可用不能影响 tracing 结果的正确性,错误信息应给出可重跑的 PNG 序列位置。

建议的提交与测试顺序

  1. observer track + Movie frame schedule + numbered PNG, 不改变 ray scheduler。
  2. Minkowski accelerated-track generator + analytic Doppler regressions。
  3. slab interface + RayPool + aggregate movie tracing,先固定 mesh。
  4. multi-pass adaptive refinement。
  5. Schwarzschild moving observer regressions。
  6. FFmpeg encoding backend / documentation。
  7. nmesh 4D slab reader,用数值 Schwarzschild 对解析结果作 regression,最后才接 FOZ4c BBH。

每一步均应保留前一步的 Minkowski 和 Schwarzschild regression;新参数(轨迹采样密度、slab 内存预算、ODE step、refinement 阈值、PNG/视频 tone map)由小型 prototype 或收敛测试选择,而非在本计划中固定。

已确认的首批基准

  1. observer track 采用上述 21 列完整 CSV;之后若 NR trajectory 有既定格式,再由独立转换器生成该 canonical CSV。
  2. Minkowski 采用两秒、从静止持续加速到约 0.95c 的基准序列,验证前灯效应与前向蓝移。
  3. Schwarzschild 首先采用 30M -> 20M -> 30M、局部径向速度上限 0.5c 的人为平滑进退轨迹;它是 renderer input 的测试数据,不应被误认为自由落体解。