今天和大家分享的是
如何创建用户自定义散射模型
概述
这篇文章介绍了如何编写DLL来定义用户自定义表面散射函数。本文中给出了一个Gaussian X-Y散射分布的示例。
介绍
光线在表面上的散射会对系统的成像质量产生显著的负面影响。然而在一些系统中,我们又需要光线发生散射,例如如果我们需要一个均匀角分布的光源,我们可以通过让一束光穿过一个朗伯散射漫射器来实现。OpticStudio中内置的表面散射分布模型包括朗伯 (Lambertian)、高斯 (Gaussian)、ABg三种模型。有关这些模型的详细信息请参考用户手册“Non-sequential Component”一章。
在实际情况中,一些物体的表面散射属性无法由OpticStudio内置的散射模型进行描述。这种情况下我们可以使用用户自定义散射模型。它是通过建立一个windows下的动态链接库 (Dynamic Link Library, DLL) 来实现的,有关如何构建DLL请参考知识库文章“How to Compile a User-Defined Surface”,请您在开始编写自己的DLL之前先阅读并理解这篇文章。
OpticStudio在安装时自带了几个自定义散射模型的示例。C代码和相应的DLL文件位于Zemax > Objects > DLL > SurfacesScatter 文件夹中。每段代码的大部分内容是相似的,这包括用于连接DLL和OpticStudio的模板程序。每个程序之间的区别在于散射光线的定义方式不同。这篇文章讲述了我们如何自定义散射分布。
散射概率分布
任何散射光线的方向余弦都是由蒙特卡洛方法生成的。参数RN1和RN2是0和1之间以均匀概率分布生成的随机数。这两个数用于定义散射光线的两个方向余弦。第三个方向余弦使散射光线的方向向量的模为1。
接下来的问题是如何将方向余弦与随机数联系在一起。这个关系是由散射分布的积分决定的。假设散射模型是根据概率进行定义的,例如 P(θ,φ) 用来描述散射光线向角度为dθ (以θ为中心)和dφ(以φ为中心)方向的角微分区域内发射的概率:
如上图所示,θ为散射光线与表面法向量n的夹角,φ为散射光线与表面之间的夹角。则极坐标系下的积分公式为:
然后将这个不确定积分(仍是θ和φ的函数)根据全积分散射 (TIS) 进行归一化。其中TIS也是根据与上式相同的公式进行计算的,但有固定的积分限,例如:积分计算θ和φ的所有区域(例如在θ中的0到π/2以及φ中的0到2π)。归一化以后的积分 (IN=I/TIS)是θ和φ的函数,(考虑不确定积分中的常数)其值被限制在0到1之间。
对于 P(θ,φ) 中θ和φ相互独立的情况,例如:P(θ,φ)=F(θ)G(φ)-IN,该式可写为:
散射光线的角度是通过设置两个随机数计算得到的,其关系如下:
如果P(θ,φ) 中的θ和φ不是相互独立的,则需有一步中间过程,但基本原理是相同的。首先计算Iθ(θ,φ)对φ的积分:
通过对φ进行0到2π的积分以使该式对φ归一化,即等于Iθ(θ)。通过对Iθ(θ)积分分别计算得到I和TIS:
散射光线与表面法向量的夹角是由第一个随机数决定的IN反推出来的:
散射光线与表面投影的夹角则由第二个随机数决定的Iθ(θ,φ) 反推出来的:
当θ和φ不相互独立时,积分的顺序就不那么重要了。无论是先对θ积分还是φ积分,对于相同的随机数RN1和RN2来说,其计算结果是相同的:
因此,我们可以根据最方便的方式积分得到概率分布。
散射BSDF分布
如果散射模型是由BSDF函数 (Bi-Directional Scatter Distribution) 决定的,则积分式中则需要添加一个额外的参数cosθ:
BSDF是通过落在表面上的辐照度来定义的,因此需要计算投影面积,上式中cos参数便来源于此。否则,散射角θ和φ的定义式与上一节相同。
这里让我们来看一个简单的示例---朗伯散射光源。对于这样一个光源来说,BSDF在所有角度上均为1/π。I可以写为:
总积分散射的积分限对于θ为0到π/2,对于φ为0到2π:
因此I为归一化的值。由于在本例中θ和φ相互独立,我们可以直接从随机数中计算出散射角:
散射光线s的轨迹在各个方向上的投影为:
其中n为表面法向量,p和q分别为表面上与法向量垂直的坐标轴。对于已知 (n,p,q)来说,散射光线在 (x,y,z)空间上的轨迹可以由下式得到:
实例:Gaussian_XY模型
本例中,分布在笛卡尔坐标系下表示,因此积分式会稍作调整:
其中Erf为误差函数。由于P是独立的,因此我们可以直接基于随机数生成散射方向:
但不幸的是,误差函数及其反函数在c语言中并未内置。但是在本例中,我们可以使用下式作为代替方案:
上式是基于Box-Muller方法生成高斯分布的随机数,更多细节请参考《Numerical Recipes in C》一书中的第7章第2节。
该示例源代码(Gaussian_XY.c)位于Zemax根目录下DLL > SurfaceScatter 文件夹中。这段代码以Windows中的APIENTRY UserScatterDefinition为开始。代码的第一部分包括变量定义,随机数生成其初始化并且DLL向OpticStudio报告一条光线产生散射并且给出光线能量(相对于输入光中可产生散射部分的能量):
接下来定义与表面法向量n垂直的坐标轴p和q,以及反射光线所在的p-n平面:
在读取用户输入的σp和σq后,示例代码使用两个随机数生成散射光线的方向:
随机数产生的散射方向是基于Box-Muller公式中反射光线的方向的。之后,在p-q平面中的散射方向向量通过这些随机数以及在这个平面中已知的反射光线的方向进行计算。这里进行了一次核验以保证散射向量投影到平面上的模长不大于单位向量的模长。最终通过计算散射光线在表面法向量方向上的投影得到其在(x,y,z)坐标系下的实际轨迹。当这一计算过程完成后,程序再次核验散射光线与法向量夹角没有超过90度:
如果不满足这一条件,则整个计算过程返回至代码初始阶段,重新生成两个随机数;否则对于给定的输入光线的散射计算在这里就计算完毕了。
在附件链接下载的示例文件 (Gaussian_XY.zmx) 对这段DLL进行了演示。在该系统中,沿直线传播的输入光线入射到具有散射属性的矩形面上,发射光线发生漫反射。其中矩形面的漫反射设置参数如下图所示:
在本例中,对于输入值σp=σq=0.5的情况下,辐射强度分布如下图所示:
小结
在OpticStudio中内置有多种定义表面散射的散射模型。然而,在一些情况下我们需要定义的散射分布不包括在其中。对于这种情况,我们可以使用Windows DLL创建用户自定义散射模型来模拟我们想要的散射属性。概率分布或BSDF中用来生成散射分布的数据是通过蒙特卡洛算法生成的,该算法只简单的要求分布是可积的。