在[上一篇文章](https://yage.ai/gaia-allsky.html)里，我们利用电脑模拟渲染出了以假乱真的星空图片。这种模拟给了我们很多实拍所没有的超能力，比如星空漫游、时间加速、模拟不同光污染和视觉敏感度下的银河与星空。在这篇文章里，我们用同样的思路来探索一下月全食时候的绿松石带。

熟悉天文的同学对这个应该不陌生。在月全食的时候，月面不是全黑，而是呈现非常暗的古铜色。但是当月亮逐渐变亮、复圆的时候，如果仔细拍摄地球本影的边缘，会发现它不是红色也不是白色，而是一层青蓝色，就好像绿松石的颜色一样，所以叫绿松石带。

如果你去搜索为什么有这样一条青绿色的窄带，往往会得到和臭氧层相关的答案：这个环带内的光主要来自经臭氧层折射的阳光，臭氧会强烈地吸收黄橙光，因此造就了绿松石带。但如果我们沿着这条思路仔细思考的话会发现有蛮多对不上的地方。比如为什么绿松石带这么窄？经臭氧层折射的光，角径应该和太阳在一个量级，也就是 32 角分左右，文献里却只有两角分左右？同时，如果我们真的去看臭氧的吸收光谱的话，会发现它对红橙光的吸收是蓝紫光的将近 100 倍，出来的颜色应该是饱和的蓝绿色。但是实际上绿松石带非常难拍，蓝绿的颜色非常淡。

这些问题让我觉得里面有很多机理我并没有理解透彻。就好像我们在上篇文章中，每一个翻过的车都指向了我们对一些司空见惯的现象其实并没有理解。比如为什么亮星看起来更大？为什么数值模拟显示我们应该在九级光污染的地区也能看见银河，但实际上看不见？这些模拟与现实的矛盾逼着我们去发现了新的知识。

对于绿松石带也是一样。在这篇文章里我们也会一步步用最简单的数值模拟：光的折射、散射、吸收、直线传播来真的把绿松石带的颜色和位置计算出来，并且和实际的观测与文献进行对比。这也是一个不断翻车的旅程，而每一次翻车都会教会我们新的知识。

## 从一个白圆盘开始

所以我们就回到起点。月亮是一个灰白的圆盘，上面有些月海的暗斑（这些纹理可以在 NASA 官网下载到）。太阳是点光源，光打在月亮上反射出来，于是我们得到了一轮满月。这没什么稀奇的。

<div style="text-align:center; margin: 2em 0;">
  <img src="https://yage.ai/images/turquoise_ablation_step_1.jpg" alt="图一：起点——有月面纹理的灰白圆盘，还没有任何大气物理" style="max-width:60%; height:auto;" />
  <p style="color:#888; font-size:0.9em; margin-top:0.5em;">图一：起点——有月面纹理的灰白圆盘，还没有任何大气物理</p>
</div>

现在月食发生了：地球挡在太阳和月亮之间。如果地球只是一块不透光的石头，月亮上被挡住的那一半就是黑色，没挡住的一半是正常月光白，中间一条硬边。这就是图二。（图像经过了简单亮度后期来在普通显示器上正常显示，下同）

<div style="text-align:center; margin: 2em 0;">
  <img src="https://yage.ai/images/turquoise_ablation_step_2.jpg" alt="图二：加入几何遮挡——一半纯黑一半纯白，硬边，没有大气" style="max-width:60%; height:auto;" />
  <p style="color:#888; font-size:0.9em; margin-top:0.5em;">图二：加入几何遮挡——一半纯黑一半纯白，硬边，没有大气</p>
</div>

但月食时月亮并不是全黑的。这是因为地球有大气。阳光擦过地球边缘的大气时会折射弯进来，照亮那些本该全黑的区域。这束光在大气里是擦着边缘掠过去的，所以穿透的大气厚度是垂直方向的几十倍。走这么长的路，光会被严重吸收和散射。

这里我们先把最基础的散射加进来：瑞利散射，就是让天空变蓝的那个机制。瑞利散射的强度和波长的四次方成正比，所以蓝光被散射得最厉害。当阳光斜穿几十倍厚的大气时，蓝光几乎被散尽，只剩红光穿透。于是暗部亮了起来，而且变成了古铜红色。血月出来了。

<div style="text-align:center; margin: 2em 0;">
  <img src="https://yage.ai/images/turquoise_ablation_step_3.jpg" alt="图三：加入瑞利散射——本影不再全黑，血月红出现" style="max-width:60%; height:auto;" />
  <p style="color:#888; font-size:0.9em; margin-top:0.5em;">图三：加入瑞利散射——本影不再全黑，血月红出现</p>
</div>

到这里还一切顺利，我们看到了熟悉的血月。但下一步就出问题了。现在，我们加入臭氧的吸收。臭氧有一个吸收带叫 Chappuis 带，波长大概在 500 到 700 纳米，正好吃掉橙红光。我们加入了这个吸收带，青绿色真的出来了，而且和文献/直觉预期的一样，是一条很浓的青带（图四）。红蓝比到了 0.53，青得发亮。

<div style="text-align:center; margin: 2em 0;">
  <img src="https://yage.ai/images/turquoise_ablation_step_4.jpg" alt="图四：加入臭氧 Chappuis 吸收——青带出现了，但比真实浓得多、宽得多" style="max-width:60%; height:auto;" />
  <p style="color:#888; font-size:0.9em; margin-top:0.5em;">图四：加入臭氧 Chappuis 吸收——青带出现了，但比真实浓得多、宽得多</p>
</div>

问题是，这条带太浓了。真实月食照片里的绿松石带是淡淡的、窄窄的一丝，需要HDR+大幅后期才能看出来。这么明显的蓝色条带一眼假，肯定有问题。然而，我们的计算在物理上好像没漏什么，散射有了、折射有了、臭氧吸收有了，但出来的结果就是和真实观测对不上。

## 近似藏在哪里

我在这里卡了很久。反复检查代码，检查数据，都没找到错。直到我发现问题不在物理上，而出在一个我根本没意识到它是个近似的近似上：我们把太阳当成了点光源。

真实太阳是一个有 32 角分角径的圆盘，不是无限远的点。当我们把太阳当点源算的时候，每个月面位置只被一条光线照亮，对应一个确定的擦边高度。擦边高度落在臭氧层附近时，出来的就是鲜艳的蓝色。但真实情况是，太阳圆盘上不同位置的点发出的光，擦过地球大气的位置是不一样的，照到同一个月面位置的光来自一束不同擦边高度的光线，有的来自臭氧层，但更多的不是。

这种情况下，学术界通用的做法是，先用点源近似推公式，最后再加一个几何补偿。比如2022年 [Mallama](https://arxiv.org/abs/2112.08966) 关于月食建模的综述，用的是"for a point source of light"的假设，用点源假设推导整套折射公式，然后最后加了个几何补偿比如模糊处理。但我跟着论文的方法适配到我们的场景里，结果怎么都不对（本影亮度不符合观测）。

直到最后我终于放弃了，又回到了上一篇文章以力破巧的思路，不做公式推导，不做几何补偿，而是针对太阳的所有点做多重积分。取很多很多点，模拟发出来的很多光线。然后对每条光线做追踪，计算折射散射吸收，终于看到了真实的亮度和颜色。这个多重积分一做，立刻把那条浓青带稀释了：原本点源算出来的最蓝处红蓝比 0.53，加入太阳圆盘之后变成了 0.71。青色变淡了，带变宽变软了，位置也往内移了。这才是真实月食看到的样子。

<div style="text-align:center; margin: 2em 0;">
  <img src="https://yage.ai/images/turquoise_ablation_step_5.jpg" alt="图五：加入太阳圆盘——浓青被糊成浅青软边，接近真实观测" style="max-width:60%; height:auto;" />
  <p style="color:#888; font-size:0.9em; margin-top:0.5em;">图五：加入太阳圆盘——浓青被糊成浅青软边，接近真实观测</p>
</div>

这其实引出了一个更大的问题。天体物理这类科学计算，几十年来形成了一种工作方式，我称之为“近似的艺术”。一个现象解起来太难，就做一阶展开只看前两项。某处发散了，就加一个修正项。这个修正项又带来别的偏差，再加一层修正。每加一层近似都需要深刻的物理直觉和踩坑经验才能保证又简单又快又对。这套做法在超算机时按小时计费、一个博士的时间比算力还贵的年代，是非常合理的策略。会做近似在当年是真本事。

但近似有一个隐藏代价：每个近似都引入一个人为参数，这些人为参数会互相打架。一个近似在某处发散，加一个补丁去补；这个补丁在另一个地方带来其他偏差，再加一层。到最后误差来自物理还是来自某几个近似的相互作用，就分不清了。我们在月食这个项目里踩的就是这个坑。我们用了四五个相关的近似，每一个都是文献里有出处、看起来合理的近似。但叠在一起，本影中心的亮度算出来比真实偏亮了 6 档——也就是 250 倍。这种偏差很难定位是哪一层近似造成的，因为它们纠缠在一起了。

## 和 AI 搏斗

在AI时代之前，我就会止步于此了。因为我又不擅长物理，又不擅长计算。没有这样的物理直觉。实际上在一年前，我也试过用 AI 来做这样的事情，也是因为这个原因没有做出来：太多物理上的决策要做，每一个决策点我都得去理解前人为什么这么近似、这个近似在什么条件下成立、去掉它会发生什么。AI 会给我一段代码，但我连这段代码里的近似藏在哪里都看不出来。

但今年我突然意识到一个问题：反正现在代码都是 AI 写的，为什么我们还要做这些近似呢？为什么就不能从最原始的光线追踪出发，老老实实把太阳的每个像素、大气的每层高度、光谱的每个波长，到本影中心的每个距离都做一轮循环？我们就老老实实发很多光线出去，让它自己折射、吸收、散射、汇聚，不做任何高级的近似，就死算。这样一方面底层的物理原理非常清晰简单，犯错的概率小很多；另一方面，把这些东西写出来并且做性能优化，比如转成 GPU 上跑的代码，是 AI 非常擅长的东西。CPU 上跑一次，GPU 上跑一次，两边结果一样，就说明它肯定是正确的，也很容易测试验证。

那为什么我还犯了很多错呢？主要是因为 AI 物理太好了，它在做了文献调研之后，经常条件反射地跟随前人的脚步，用物理直觉去近似处理，结果又把我带坑里了。所以我大多数的时间其实是在跟 AI 搏斗，一层一层发现这里又藏了一个物理近似，那里又藏了一个物理近似，最终把它改成最简单最暴力的版本。

具体到这个项目，我们的最后一步是把一个近似 focusing 因子整个删掉。focusing 是指擦过地球边缘的光线被大气折射弯进来之后会在本影里汇聚，使本影中心比边缘亮。文献里的做法是用一个解析公式 `1/r` 来近似这个汇聚效果，再硬设一个 r_floor 下限去补它在中心的无穷大发散。我们一开始也用了这个公式。但问题是，focusing 本来就是光线追踪的自然产物：你撒出足够多的光线，让它们各自折射落点，落点的密度自然就是亮度，不需要任何公式。我们用了公式就是在用人为的闭式解去代替统计涌现，于是本影中心偏亮了。删掉 focusing 公式，改成纯撒光线落点分箱，本影中心从 −7.7 档直接暗到了 −13.1 档——和真实月食深食的 −14 到 −19 档已经在一个数量级上了。剩下的差距是真实大气里的气溶胶和云，不是数值问题。

<div style="text-align:center; margin: 2em 0;">
  <img src="https://yage.ai/images/turquoise_ablation_step_6.jpg" alt="图六：真·正向光线追踪——focusing/落点/亮度全部从撒线涌现，零解析处方" style="max-width:60%; height:auto;" />
  <p style="color:#888; font-size:0.9em; margin-top:0.5em;">图六：真·光线追踪 RTX ON ——focusing/落点/亮度全部从撒线涌现，零解析处方</p>
</div>

## 两个反直觉问题的答案

算到这里，回头来看一开始那两个问题。

为什么绿松石带是一条窄带而不是一大片？答案在亮度上，不在颜色上。颜色（红蓝比）沿月盘其实是平滑渐变，跨了十几角分，没有突变。但最蓝的那一段恰好也是整条曲线上最暗的一段——亮度只有月盘最亮处的 4.5%。人眼看不见。它旁边紧贴着的是出本影的正常月光区，亮了整整 250 倍，白色的强烈月光又把青蓝色完全淹没了。我们看到的绿松石带不能太暗，不能太亮，还要够蓝，在视觉上就被压成了一道细带。

<div style="text-align:center; margin: 2em 0;">
  <img src="https://yage.ai/images/turquoise_moon_brightness_cliff.png" alt="图七：亮度悬崖——最蓝处同时是最暗处，被旁边趋白区盖过，只剩一道细带" />
  <p style="color:#888; font-size:0.9em; margin-top:0.5em;">图七：亮度悬崖——最蓝处同时是最暗处，被旁边趋白区盖过，只剩一道细带</p>
</div>

为什么全食最深的时候反而没有绿松石带？因为深食时月亮在本影正中心，照亮它的阳光擦边高度极低，瑞利散射把蓝光散尽只剩红，血月最红最暗。青色要出现在擦边高度升到平流层的那一段——那里瑞利散射退居次要，臭氧的 Chappuis 吸收开始主导，把橙红吸收留下青绿光。但平流层对应的是本影边缘，不是中心。所以绿松石带只在月亮往本影边缘移动、亮度悬崖开始出现的时候才看得见。全食最深的中心位置，整张月盘都在瑞利的红区里，臭氧的青根本没有投射过去。

## 暴力为什么现在可行

讲到这里，我想退一步说一个更一般的事情。

过去做这类科学计算，近似是美德。超算机时按小时计费，写程序的博士们技能点在物理直觉上而不在软件工程上，用物理近似换取更少更快的计算是理性的。但这个成本结构在 AI 时代倒挂了。AI 既懂物理又懂编程，推折射公式、写 GPU kernel 都是几分钟的事。同时个人电脑就能撒几百万条光线，运行时间也不再是瓶颈。所以这时候"近似的艺术"就从美德变成了阻碍。暴力的第一性原理方法现在又快又对，而且没有人为参数互相掩盖的问题，反而效果最好。

这不是说前人做错了。他们做的是在当时的约束下最优的选择。Mallama 2022 那篇综述的点源假设、闭式折射公式、1/r focusing，每一项都是算力预算下的合理近似。甚至 García Muñoz 和 DLR 的 2025 年工作，在算力已经不稀缺的年代仍然选半解析，是为了物理可解释性或者实时渲染。只是我们要警惕，近似的惯性不会自动消失。当约束解除之后，不近似是一种非常可行且假设更少的替代方案。

另外我想澄清一点，我们想推动的不是暴力计算万能论。人力有时而穷，如果坚持原教旨主义，不做任何近似，很快我们仍然会撞上算力这堵墙。我想强调的是，这不是一个非黑即白的问题。在AI能力强、电脑算力强的2026年，我们在做科学计算的时候，不妨多考虑一些跳过近似的技术途径。

## 月球人看到的日全食长什么样

和上一篇文章一样，模拟链路跑通之后，它可以做很多实拍做不到的事情，比如可以改参数去探索。

一方面我们可以算出来地球上看到的月球，但因为我们是完全暴力模拟的，换个积分的维度，我们自然也可以看到月球人的视角。因此我们做了一个视频，展示从月球看地球的样子。注意月食的时候，站在月亮上的人看到的是地球完全挡住太阳，也就是地球上的日全食。地球的黑夜面上一圈折射光点亮的大气环，颜色从内圈红到外圈青再到白，正好是日落剖面展开成一圈。随着月亮移出本影，太阳从地球边缘探出来，大气环一侧越来越亮，最后变成钻石环。这整段画面就是前面那套辐射传输的同一个物理，只是换个视角而已。

<div style="text-align:center; margin: 2em 0;">
  <video controls playsinline preload="none" poster="https://grapeot.github.io/turquoise_band/assets/eclipse_video_poster.jpg" style="max-width:60%; height:auto; background:#000;">
    <source src="https://yage.ai/share/eclipse/moon_eclipse_sdr_h265.mp4" type="video/mp4" />
  </video>
  <p style="color:#888; font-size:0.9em; margin-top:0.5em;">月食对偶视角视频：左月球（随月亮移出本影）| 中地球全景 | 右大气环特写</p>
</div>

我们也做了光度曲线，把月面从本影中心到满月的亮度变化画出来。本影中心暗到 −15 档，然后一路缓升到本影边缘的亮度悬崖，再平滑爬升到满月的 0 档。这条曲线本身就是一张诊断图：如果你发现某个地方有跃变，那一定是物理上缺了一块。我们中间就发现出本影的地方有个假跃变，追下去是半影区的直射光没建模——太阳从地球边缘探头照过来的那部分光。老实把它 ray trace 进去，跃变就消失了。

<div style="text-align:center; margin: 2em 0;">
  <img src="https://yage.ai/images/turquoise_photometric_profile.png" alt="图八：光度曲线——本影中心 −15 档到满月 0 档，亮度悬崖在 41 角分处" style="max-width:70%; height:auto;" />
  <p style="color:#888; font-size:0.9em; margin-top:0.5em;">图八：光度曲线——本影中心 −15 档到满月 0 档，亮度悬崖在 41 角分处</p>
</div>

所有这些结果、代码、渲染脚本，我都放在了[项目主页](https://grapeot.github.io/turquoise_band/)上，也欢迎大家自己玩玩。

## 最后

我们一路翻了很多次车，但每次翻车都纠正了一个不准确的理解。点源近似让我们误以为绿松石带很浓，focusing 公式让本影中心偏亮 250 倍。每一次"看着差不多"的诱惑，正解都是回到物理去查漏了什么。中间有好几次，我看到算出来的青色太浓，本影太亮，很想调一下臭氧浓度或者加个气溶胶让它暗起来。但每次都忍住了。最后算出来的那条淡青窄带，和 GOES-16 卫星的实测、Shu 2024 的遥感数据逐条对上的时候，我觉得忍住是对的。

<script async data-uid="49b08a3e73" src="https://yage-ai.kit.com/49b08a3e73/index.js"></script>
