这一页,给那些追问每个数字从何而来的人。
原理深度解读
主页上你看到的每一格像素,颜色都不是凭空调出来的。它们的生成全部沿着同一条物理链条走到底:从实测太阳光谱出发,光线进入地球大气,折射先把它弯向月面的某个落点;沿这条已经弯曲的路径,瑞利散射、臭氧吸收和背景气溶胶一层层削减光谱,最后出来的光谱经过 CIE 色匹配函数换算成颜色坐标。整条管线里没有贴图、没有手工调色参数、也没有经验修正项。这一页梳理管线设计中的六项关键技术决策,和三组来自不同研究团队的文献做了逐项对账,汇总了模型卡里的关键数字(每一项都标注了衡量口径),最后附上每张正式画面可复现的命令。
三条贯穿始终的原则
第一条,把物理计算和屏幕显示拆成两个独立的环节。光线追踪这一步产出的全部是线性辐射量,这个环节不做任何迎合视觉效果的加工。屏幕上的最终观感,交给曝光和 tone map 来决定,这两个处理步骤我们也如实标注为显示层的人为选择,不混进物理层。整个项目里代价最高的一个教训恰好出在这条分界线上:早期的渲染为了让暗部可见,拉高了曝光,结果把原本窄细的青带撑成了覆盖月盘近三分之一宽度的缓变带。我们对着那条宽阔的青带怀疑了很久物理模型是不是哪里错了,翻来覆去地查,最后才发现问题不在物理,是显示层在制造幻觉。现在的默认曝光改成了以月盘最亮处为锚点定标,亮暗的分界因此得到保留,青带的宽度回到了物理上应有的窄细。
第二条,物理模型不由视觉效果牵着走。蓝调有点偏、带宽显得太宽、青色饱和度不够。画面上每出现一次看着不对劲的时刻,摆在我们面前的选择都是回到度量方法、显示参数或者文献口径上找原因,而不是往模型里塞一个新机制去硬凑效果。后来从卫星实测数据那里收到了逐条回馈:带内红蓝比的量级、色带所在的月面位置、淡青色的整体观感,三项全部和实测对上了。
第三条,能用光线追踪求解的,就不写解析公式。聚焦因子也好,弦长权重也好,有效路径长度也好,这些东西本质上都是光线追踪自然跑出来的结果;一旦把它们封装成解析公式去算,偏差就会悄悄渗进去。拿本影中心的亮度来说,纯解析方法算出来是 −7.7 档,改用光线撒点、折射也换回真实追踪之后,一路修正到了 −13.5 档。中间差出来的每一档曝光值,都对应着某个解析近似在真实物理面前欠下的债。后面六项决策里的四项,都在还这笔债。
六个关键技术决策
一、折射不查公式表,逐条光线 RK4 积分
常规的解析折射公式会把偏转角度写成光线擦边高度的指数函数,按这个公式算,贴着地表掠过的光线大约偏转 70′。但我们对每一条光线逐个做 RK4 数值积分追踪之后发现,这 70′ 在真实大气里根本不存在。道理在于:瞄准高度设在 0 到 1 公里区间的那些光线,弯着弯着,它的切点就压到了地面以下,撞在地球表面上消失了,根本不曾到达月球。真正能够擦着地面穿过去的光线,瞄准高度大约在 1.7 公里,对应的偏转量是 63.5′。中高空的情况也类似,真实偏转比解析公式给的更大:在 12 公里高度,一个是 21′,另一个只有 15.7′。光线弯向本影中心的程度比公式预期的更狠。折射几何是整个模型的骨架,骨架上一旦出现系统性的偏移,颜色的月面位置和暗端的亮度就会同时出错,所以在这一关上我们没有省。
二、消光沿弯曲路径积分,并且用真实切点高度
光线一旦发生折射,穿过大气的实际路程就比直线更长,沿途的气体柱密度自然不能按直线距离去估算,必须沿着弯曲的真实路径逐段累加。这一关我们踩过一个因为口径理解不一致造成的坑:管线曾经把光线的瞄准高度(impact parameter)当作切点高度,直接输给了消光积分。但折射会把真实切点压到更低的位置。比如一条瞄准高度 2.7 公里的光线,它的真实切点只有 1.1 公里,那里的大气密度高出很多。用错了高度,消光量出现了系统性的低估。修正之后本影中心往下暗了 0.68 档。这个教训和第三条原则根出同源:即便是在一条整体上避开了解析近似的管线里,只要混进来一个解析量,它照样会咬人。
三、聚焦与散焦不写公式,从落点密度涌现
折射光线进入本影后,有的地方会汇聚,有的地方会散开。早期的解析管线用一个聚焦因子公式来处理这个效应,但那个公式在本影中心会发散,数值趋向无穷大,还得人为加一个正则化下限去压住它,压完之后又在不该亮的地方制造出移动亮斑。正确的处理方式其实就是回到光线追踪最初始的含义:随机撒出几百万条光线,看它们的落点集中在月面上的哪些区域,每个区域收到的光线数量,天然就等于那个区域的亮度。聚焦和散焦不需要任何公式,自己就涌现出来了。数字记录完整地保留了这条演化路径:解析弦长权重算出本影中心 −7.7 档;撒了光线但落点仍沿用解析折射,得到 −11 档;折射切换成真实追踪之后,来到 −13.1 档(口径:分子大气,photopic,0 档 = 未食满月)。最具判决力的诊断是这个:单条光线经过本影中心,单程透射衰减只有 −7.9 档;但落点分箱统计出的面亮度却是 −13.1 档。中间差出来的 5.2 档,全部来自几何散焦效应:擦着地球表面穿过来的最深层的折射光,摊薄到了整个月面上。这件事,没有任何解析公式曾把它写出来过。
四、半影直射光走同一套撒线
月亮从本影中移出时,太阳会从地球边缘逐渐露出来,直射光随之一点点回归。早期的管线只追踪经过折射的光线,结果光度曲线在本影边界之外出现了一段虚假的跳变:看起来像亮度突然跃升,实际上是直射光的那部分贡献完全没有计入。修补没有走加平滑处理去掩盖跳变的路子,而是把撒线的瞄准范围向外扩展到大气层外 5500 公里的高度。大气之外的空间里,光线不走弯路、不做衰减,半影区直射光的渐亮过程就从同一套撒线机制中自然地浮现出来,光度曲线平滑地爬升到 73′ 处的满月亮度。这里还有一个工程层面的教训:光线覆盖范围不够造成的虚假亮度下降,在这个项目里以不同面目反复出现了三次,分别是视频末帧的移动阴影、光度曲线末端的下弯、以及上面所说的直射光不足。现在覆盖范围的计算方式改成了从消费端所需的最大角距加上太阳半径反推,这个规则已经写进了检查清单,不会再漏。
五、分层抽样:同心环事件
撒线范围扩大之后,一颗暗雷也跟着埋了进去。折射壳层在整个撒线平面里只占大约 1.1% 的面积,也就是说 400 万条光线里,能走折射路径的只有几万条。这几万条再分摊到本影的一百多个径向环上,每个环分到的样本数掉到了百量级。这么低的样本量带来的统计噪声,经过查询表(LUT)插值之后进一步放大,最终在月盘画面上呈现出肉眼可以辨别的同心环纹路,光度曲线也同步出现了锯齿状的抖动。这个问题是用户在审图时用肉眼发现的。起初我们把它归类为蒙特卡洛方法固有的随机噪声,打算诚实地接受。重新排查之后改判了:这是采样分配不均匀导致的,并非不可避免,可以通过改进采样策略来消除。修正方法用的是标准的无偏方差缩减思路:折射壳层和直射壳层各按自己的面积来归一化光通量,然后把光线配额对半分给两个壳层。修正后,本影内部相邻环之间的亮度跳变中位数从 0.158 档降到了 0.085 档。这件事的教训是,均匀撒线在面对面积悬殊的分布时,会让关键的小区域陷入统计饥饿。分层抽样是物理优先的管线从一开始就该上缴的税。
六、两组分背景气溶胶与太阳临边昏暗
最后补进模型的两块真实物理,参数全部取自气候学领域的观测值,没有一个是靠调参调出来的。背景气溶胶分成了两个组分:第一组是对流层海洋背景(AOD550 = 0.07,指数廓线,Ångström 指数 0.7);第二组是平流层的硫酸盐细粒子(sAOD550 = 0.005,廓线以对流层顶为锚点按指数尾衰减,Ångström 指数 2.0)。平流层这条廓线不是随意画的,它要同时满足三条来自观测的硬约束:20 公里和 25 公里两处高度的消光系数,以及整层的柱总量,三者必须一起对上。太阳临边昏暗用了 Allen V 波段的二次律,按均值做了归一化。代价的记录也是坦率的:平流层气溶胶在短波端抬高了散射,加上临边昏暗使太阳盘面中心变亮,两股力量合在一起,把青带的红蓝比从纯分子大气情况下的 0.715 冲淡到了 0.814(raw sRGB 口径)。青带因此变淡了,这是客观物理的结果,渲染端没有回头去做任何补饱和的操作。
与文献的三组对账
几何:Mallama 折射映射表
我们把解析折射映射(从光线的擦边高度到月面角距的换算)与 Mallama 2021/2022 表格中的对应数值做了逐行核对,中高空的全部区间吻合度都在 2% 以内。这一项对上的意义是地基性的:只有折射映射站住了,后面讨论颜色的径向位置才有立足点。
暗端:两个 clear-sky 模型差出几档
我们的模型给出的本影中心亮度是 −15.1 档(默认物理,photopic),纯分子大气(关掉全部气溶胶和临边昏暗)的理论上限是 −13.5 档。Mallama 的 clear-sky 经验消光模型给出的结果则是:全盘积分 −18.7 档,盘面分辨 V 波段 −20.5 档,这两个数字与正常月食的实际观测是吻合的。两组同样挂着 clear-sky 标签的模型,输出差了 3.6 到 5.4 档,差距如此之大,说明它们虽然用了同一个词,衡量的口径并不一样。关键原因在于擦边光的斜程路径相当于 40 到 60 倍标准大气质量,消光参数上的任何微小标定差异,走过这条极长的路径之后都会被放大几十倍。我们用敏感性实验把分歧的来源定位到了切点高度 8 公里以下的深层光:这部分光线只影响暗端亮度,不影响色带的颜色和 ribbon 的表现。目前最可能的解释是这片高度区间里还存在某些没有被建模的消光来源,这是我们眼下最大的开放项。结论是诚实的:我们的暗端数值代表的是最晴朗条件下的亮度上限,真实深食的亮度会比它更暗。
颜色:Shu 2024 卫星窄带口径与 GOES-16
红蓝比这个词,在翻阅不同文献的时候需要特别小心,它在不同出处里至少对应着三种互相不能直接换算的口径。第一种是渲染所用的线性 sRGB 通道比;第二种是太阳光谱归一化后的透射比;第三种是卫星波段辐亮度直接相除,中间还要乘上月面反照率比 1.35,系统不确定度在正负 15% 之间。把所有这些口径摆齐之后,对账结果可以分成两半来看。对上的一半:宽波段的蓝化趋势是稳健的,带内红蓝比和 GOES-16 实测数据一样落在 0.8 到 1.0 的淡青色量级里。需要注意的是两边口径并不相同,所以这不构成严格意义上的定量吻合。没对上的一半,牵涉到 Shu 2024 用高分四号卫星测到的一个特征:R/B 小于 1 的边界 ribbon,它的径向全宽在 120 到 190 公里之间。我们的一维模型复现不出这个结构:臭氧造成的单层凹陷一旦经过 32′ 太阳盘面的卷积,再加上本影内部红光的侧向流入,最终抹平到了噪声量级。敏感性矩阵的分析已经排除了气溶胶参数和深层红光这两个嫌疑,剩下的候选因素按证据强度排列如下:切点高度 8 到 19 公里区间内可能存在未被建模的红端消光,对流层顶附近的薄卷云是一个候选但尚未进入模型;卫星传感器的实际光谱响应曲线;月面反照率带来的系统项;以及二维边界测量与一维径向平均之间天然存在的口径差。我们始终没有为了复现 ribbon 去调整过任何一个模型参数,开放项全部如实留档。
关键数字(全部带口径)
这些数字全部摘自仓库中的 docs/MODEL_CARD.md,由 band_profile.py 加上 --model-card 参数自动生成。生成条件:400 万条光线乘以 2000 个太阳子采样点,固定了随机种子以保证可重复性;默认物理组合为瑞利散射加臭氧吸收加两组分背景气溶胶加太阳临边昏暗。物理代码出现任何修改之后,模型卡都会重新运行生成,确保数字始终与当前代码一致。
| 量 | 数值 | 口径 |
|---|---|---|
| 本影中心亮度 | −15.1 档 | photopic,0 档 = 未食满月,默认物理;独立管线交叉检验 −15.3 档,差异在中心三环的 Poisson 统计 |
| 本影中心亮度(分子上限) | −13.5 档 | 同上,但关掉气溶胶与临边昏暗:纯瑞利加臭氧的理论亮度上限 |
| 带最蓝处红蓝比 | 0.814(41.0′ 处) | raw sRGB 线性通道比,含实测太阳谱、无任何归一化,即渲染实际呈现的量;分子大气对照为 0.715(40.3′ 处) |
| 亮度悬崖宽度 | 20.4′(46.7′ 到 67.1′) | photopic 面亮度从满月的 10% 升到 90%,即整个半影爬升段 |
| 本影边缘最陡坡度 | 1.8 档每角分 | 35′ 到 50′ 窗口,3 bin 平滑 |
| 中心 B / V / R 消光 | 16.6 / 11.8 / 9.2 mag | 各波段带内归一(该带满月 = 0 mag),含约 4.6 mag 几何稀释;I 波段缺水汽与氧气吸收带,绝对值不引用 |
| 对照锚点 | −18.7 到 −20.5 档 | Mallama clear-sky 经验模型,分别为全盘积分与盘面分辨 V 波段;与本模型的差距是开放项 |
每张正式图的复现命令
在仓库根目录下执行,须先运行 source .venv/bin/activate 激活虚拟环境。完成一次完整的光线追踪大约耗时几秒量级,视频渲染则需要分钟量级。
# 主页 hero:写实月食月盘(真光线追踪 LUT,默认 d=40')
python src/render_textured.py
# 六步 ablation 序列(增强展示版 preview + 16bit 线性 TIFF)
python src/render_ablation.py
# 光度剖面曲线(固定 16M 光线分层抽样,页面版本即此输出)
python scripts/render_photometric_profile.py
# 对偶视角四联画视频(SDR + HDR)
python src/build_video.py
# 亮度悬崖两种曝光对比
python src/diag_brightness_cliff.py
# 模型卡(页面与上表所有数字的出处)
python src/band_profile.py --model-card --n-rays 4000000
# 物理不变量与渲染冒烟测试
python -m pytest src/tests/ -v
点源与圆盘的对比图由 src/brute_ray_trace.py 的圆盘光线追踪功能及其点源退化模式渲染生成。轨迹合成的艺术图出自一次性脚本,构图参数记录在 docs/working.md 中。
踩坑全记录在哪里
这一页展示的是管线的最终形态,但走到这一步之前,路上走过的弯路比结果本身有意思得多。举两个例子。第一个:我们曾经为了修正一个根本不存在的 bug,发明了一条有错的折射公式。这条公式把青带推到了一个直觉上看起来更对的位置,带着一种问题已解决的自信,它在代码里存活了整整一天。直到我们和文献逐行对账时才发现它是错的,当场删除。一个错误的修正,比错误本身要危险得多。第二个:有一段时间,CIELAB 色相角显示青带偏蓝程度很高,我们差点因为这个信号去改动物理模型。最后查出来,CIELAB 这个色彩度量本身把温和的偏蓝放大了,物理模型没有问题。从那以后我们形成了一个原则:色相对不上时,先怀疑度量工具,再怀疑物理模型。
全部坑的根因和解法在仓库的 docs/PHYSICS_AND_PITFALLS.md,逐日的工作记录与每次勘误在 docs/working.md。文献出处:Mallama 2021/2022(arXiv:2112.08966)、Shu et al. 2024(Remote Sensing 16(22):4181)、Serdyuchenko et al. 2014 臭氧截面、AFGL 大气廓线、SAO2010 太阳谱。