很多人第一次接触光学设计软件时,都会有类似体验:输入镜片半径、厚度、玻璃材料、波长和视场,点一下按钮,软件就给出点列图、光线扇形图、波前图,或者一条看起来很权威的 MTF 曲线。曲线有坐标轴,有频率,有子午和弧矢方向,有中心和边缘视场。它当然有用,但也很容易让人误以为:软件输出本身就是理解。

computational-optics 这个项目最有价值的地方,正是反过来做了一件事:它不把 MTF 当成从软件里“吐出来”的结果,而是把这条曲线拆成一条可以追踪的计算链。镜片处方如何变成光线追迹?光线追迹如何给出光程?光程差如何写成波前误差?波前如何变成复数光瞳函数?光瞳函数怎样通过傅里叶计算得到 PSF?PSF 又怎样变成 OTF 和 MTF?这套链条一旦走通,光学软件的输出就不再像黑箱判决,而更像一个可以检查、可以复现、可以质疑的数值过程。

这个项目表面上是一本计算光学电子书,实际上更像一本“带验证代码的技术手稿”。它同时提供中英文版本,使用 mdBook 构建在线站点;正文包含公式、插图和 Python 示例;仓库中还有配套的 Python 包、测试代码和生成图像的脚本。也就是说,它不是只讲概念,也不是只堆代码,而是把文字说明、数学表达、图像结果和程序实现放在同一个项目里,让读者能看到每一步是怎样从前一步来的。

全书的章节安排很清楚。前几章从镜片处方开始,把光学系统写成可以计算的数据:曲率半径、厚度、材料、孔径、波长、视场。这一步看似朴素,却是后面所有计算的入口。镜头不是一张漂亮的剖面图,而是一组表格化、可解析、可传给程序的数据。接着,书中讨论如何表示一条光线:起点、方向、波长、有效性、光程长度。再往后,才进入光线与平面或球面的交点计算,以及用矢量形式的斯涅尔定律计算折射方向。

这样的写法有一个好处:它先把底层步骤讲清楚。光线是否归一化,曲面交点取哪个根,法线方向是否一致,介质折射率是否对应当前波长,这些细节在图形界面里通常不可见,但它们会直接决定后面的点列图、波前图和 MTF 是否可信。项目中的 pure_optics.py 就承担了这类基础计算:光线对象、平面和球面求交、折射、圆形光瞳采样、OPD 示例、光瞳函数、PSF、OTF、MTF 以及简单的评价函数。代码不长,但每个函数都指向书中的一个具体概念。

中段章节开始进入图像质量评价。单条光线只能说明程序开始工作了,一束光线才开始接近成像问题。点列图展示真实光线落在像面上的分布,近轴光学给出一阶参考,真实光线相对近轴光线的偏离则帮助读者理解像差。这里的重点是理解图为什么会长成那样。点列图不是装饰,光线扇形图也不是软件菜单里的一个选项,它们都是同一套几何计算的不同投影。

到 OPD 和波前误差之后,项目完成了从几何光学到傅里叶光学的过渡。几何光线告诉我们光线落在哪里,但衍射成像还关心相位。于是,光程差被写成以波长为单位的波前误差,再通过相位因子写入复数光瞳函数。光学计算不再只是“很多条线”在空间里折来折去,而是开始处理整个光瞳上的复振幅分布。随后,光瞳函数经过傅里叶变换得到焦平面复场,取模平方得到 PSF;PSF 再经过傅里叶变换得到 OTF,其幅值就是 MTF。这样,开头那条看似直接出现的 MTF 曲线,终于被还原成了一串具体计算。

项目对采样和归一化的强调也很重要。很多数值图像最危险的地方在于:错了也可能很好看。PSF 可以很平滑,MTF 可以从 1 开始下降,曲线可以显得很“科学”,但频率轴、零频归一化、填充倍数、采样间隔只要有一处处理错,结论就可能变形。书中专门安排一章讨论采样、归一化和那些会破坏 PSF、MTF 的小错误,这让它不只是一本讲公式的书,也是在教读者养成数值计算的检查习惯。

后半部分把这些内容放进一个更完整的设计例子:库克三片式镜头。这个镜头结构不复杂,三片透镜已经足够展示球差、彗差、像散、场曲、畸变和色差等真实光学行为,又不会像现代复杂镜头那样让教学重点被结构数量淹没。项目中的适配器代码围绕固定版本的 Optiland 构建库克三片式镜头,运行布局、点列图、光线扇形图、波前、PSF、MTF,并执行一个有边界的优化过程。优化变量包括若干曲率半径和后焦距,评价函数使用主波长点列 RMS 半径的平方。这个例子说明了一个常被忽略的事实:优化器不会理解设计者的意图,它只会按照你写下的评价函数行动。

这也是本项目与普通教程不同的地方。很多教程会告诉你“点击优化”,然后展示优化前后图像;这个项目更愿意让你看到评价函数是什么、变量是什么、边界是什么、结果是否真的让数值目标下降。它不把优化神秘化,也不把软件输出贬低为无用结果。它的态度更稳:软件很有用,但你要知道软件算的是什么;图像很有用,但你要能追溯它从哪里来;优化很有用,但你要明白优化器只是沿着你给它的目标函数走。

可微光学章节则把话题推进到更现代的方向。传统优化常常把光学系统当成一个可调用的函数:给一组参数,得到一组评价值,然后优化器尝试降低评价值。可微光学关心的是,能否让整个计算过程对参数求导,让梯度更直接地参与设计。项目在这里没有夸大它的范围,而是用确定性的标量损失和解析、有限差分梯度的对照,解释为什么“可微”值得关注。它没有强迫读者立刻进入大型自动微分框架,而是先把梯度这件事讲清楚。

从工程角度看,computational-optics 的成熟之处在于它有明确的版本边界和验证意识。配套环境固定 Python 版本范围和 Optiland 版本,正文中的 Python 代码块会被解析检查,基础光学函数有测试,章节插图可以重新生成,关键图像和数值结果有验证记录。上传的阶段包中还包含事实检查报告、样式修订报告、验证结果和章节清单。这些文件不一定是普通读者最先阅读的内容,但它们说明作者在尽量避免“文字说得漂亮,代码无法复现”的问题。

当然,这个项目也不是生产级镜头设计软件。它更像一套面向理解的计算路径:让读者能从镜片处方一路走到 MTF,并理解每一步的假设。真实工程中还会涉及更复杂的材料数据、制造公差、装调误差、杂散光、镀膜、探测器采样、矢量衍射、偏振、热效应和系统级约束。项目没有把教学例子包装成万能工具,这反而让它更可信。

我认为它特别适合三类读者。第一类是已经用过 Zemax、Code V、Optiland 或其他光学软件,但总觉得输出图像像黑箱的人。第二类是学过几何光学或傅里叶光学,却缺少代码实践的人。第三类是做计算成像、相机、显微、AR/VR 或机器视觉相关工作,希望理解镜头评价指标来源的软件工程师。它不会替代经典教材,也不会替代专业软件手册,但它能补上两者之间经常缺失的一段:把公式写成程序,把程序输出读回物理意义。

这本书真正想训练的不是“会不会点出一条 MTF 曲线”,而是“看到这条曲线时,能不能在脑中反向追踪它的来路”。它让读者知道,MTF 的背后有处方,有材料和波长,有光线追迹,有光程,有波前,有光瞳函数,有傅里叶变换,也有采样和归一化。理解了这些,软件输出就不再只是一个结论,而是一个可以拆开检查的计算结果。

这正是 computational-optics 的价值:它把计算光学从软件按钮后面拉出来,放回到公式、代码、图像和验证之间。对于想真正理解光学软件输出的人来说,这样的项目比单纯的功能演示更耐读,也更值得反复查阅。