水波衍射互动课件——惠更斯原理
在水波干涉互动课件的基础上,本课件模拟的是水波衍射,要实现的是沪科版选择性必修一第二章 第四节 机械波的干涉和衍射中的图 3–30 和图 3–31。如下图所示。
做出的成品如下所示:
核心代码解读
物理原理
本程序的物理理论基础是惠更斯原理,内容如下:
《大学物理学 第 4 版 力学 热学》(张三慧)第 210 页:惠更斯在研究波动现象时,于1690年提出:介质中任一波阵面上的各点,都可以看做是发射子波的波源,其后任一时刻,这些子波的包迹就是新的波阵面。这就是惠更斯原理。这里所说的“波阵面”是指波传播时最前面那个波面,也叫“波前”。
《普通物理学 第 8 版 下册》(程守洙)第 63 页:惠更斯(C.Huygens)于 1678 年提出了关于波传播的几何法则:在波的传播过程中,波阵面(波前)上的每一点都可看作是发射子波的波源,在其后的任一时刻,这些子波的包迹就成为新波阵面。这就是惠更斯原理(Huygens' principle)。……
应该指出,惠更斯原理并没有说明各个子波在传播中对某一点振动的相位和振幅究竟有多少贡献,不能给出沿不同方向传播的波的强度分布,后来菲涅耳对惠更斯原理做了补充,这将在本书光学部分介绍。
当波在传播过程中遇到障碍物时,其传播方向绕过障碍物发生偏折的现象,称为波的衍射(diffraction of wave)。如图 11–20 所示(注意:为配合课件中缝放置的方式,将原图旋转了90°),平面波通过一狭缝后会偏离原直线前进。这一现象可用惠更斯原理来解释。当波阵面到达狭缝时,狭缝处各点成为子波源,它们发射的子波的包迹在边缘处不再是平面,从而使传播方向偏离原方向而向外延展,进入狭缝两侧的阴影区域。
程序实现
绘制的单缝的情况
核心思路是:若缝宽为 100 像素,则在单缝所在的水平线上,从缝的左侧到右侧选取 100 个采样点作为次波波源,对每个次波源 S1、S2、S3、……、S99、S100 计算:
- 从主波源 S0 到次波源的距离 r1;(下图 S0 是圆波,各次波的 r1 不同;而平面波 r1 都相同,为孔隙到屏幕下边界的竖直距离,计算量稍微小一点)
- 从次波源到当前像素点 P 的距离 r2;
- 总距离
totalDistance= r1 + r2 即波程 r,代入行波公式 y = Asin(ωt − \(\dfrac{{2\pi }}{\lambda }\)r) 求出位移,对圆波的情况,还乘以一个衰减系数attenuation,让波的振幅随半径的增大减少一点; - 循环计算所有 100 个次波在当前像素对应的位移并相加,对应波的叠加;
- 最后求平均并乘以 2. 0 调整振幅。
求平均的原因是:代码中采样了 100 个次波源点,每个次波源都贡献一个完整的波。如果不平均,叠加 100 个波会导致振幅过大(理论上可达 100 倍)。实际上,物理上缝隙的每个微元贡献的波应该是连续分布的,而不是 100 个离散点。除以采样点数相当于将离散求和近似为连续积分。
乘以 2.0 的原因是:衍射区域的波振幅通常比直接传播区域小,乘以 2.0 可以增强衍射图案的可见度让用户更清楚地观察到衍射现象。严格来说,次波源的振幅应该与入射波振幅成正比,这个 2.0 是一个经验系数,通过视觉调试确定,使衍射后的波形既不会太强(失真)也不会太弱(看不清)。
本程序计算量庞大,因此放弃了 Canvas2D 的实现,只使用 WebGL。通过单缝的情况对应的像素着色器代码如下:
主要代码如下:
// 下方主波(波源直接传播)
if (pixelPos.y < barrierY) {
if (isCircularWave) {
// 圆波:从波源传播,考虑衰减
if (waveDist <= maxDistance && waveDist >= 0.0) {
float attenuation = 1.0 / (1.0 + waveDist * 0.005);
displacement = sin(omega * u_time - k * waveDist) * attenuation;
waveReached = true;
}
} else {
// 平面波:从屏幕底部向上传播
if (waveDist <= maxDistance) {
displacement = sin(omega * u_time - k * waveDist);
waveReached = true;
}
}
} else {
// 上方区域
if (isSlitMode) {
// 单缝模式
if (isCircularWave) {
// 圆波:惠更斯原理,只有缝隙内的点作为次级波源
const int maxSamples = 200;
float slitWidth = barrierRight - barrierLeft;
int actualSamples = int(slitWidth);
float step = slitWidth / float(actualSamples);
float count = 0.0;
for (int i = 0; i < maxSamples; i++) {
if (i >= actualSamples) break;
float sx = barrierLeft + float(i) * step + step * 0.5;
vec2 d1 = vec2(sx, barrierY) - u_sourcePosition;
float r1 = length(d1);
vec2 d2 = pixelPos - vec2(sx, barrierY);
float r2 = length(d2);
float totalDistance = r1 + r2;
if (totalDistance <= maxDistance) {
float attenuation = 1.0 / (1.0 + totalDistance * 0.005);
displacement += sin(omega * u_time - k * totalDistance) * attenuation;
count += 1.0;
waveReached = true;
}
}
if (count > 0.0) {
displacement = displacement / count * 2.0;
}
} else {
// 平面波穿过孔隙:惠更斯原理,孔隙内每个点作为次波源
const int maxSamples = 200;
float slitWidth = barrierRight - barrierLeft;
int actualSamples = int(slitWidth);
float step = slitWidth / float(actualSamples);
float count = 0.0;
// 平面波到达屏障的距离(从屏幕底部到屏障)
float distToBarrier = barrierY;
for (int i = 0; i < maxSamples; i++) {
if (i >= actualSamples) break;
float sx = barrierLeft + float(i) * step + step * 0.5;
// 从孔隙点到像素点的距离
float distFromSlit = length(pixelPos - vec2(sx, barrierY));
float totalDistance = distToBarrier + distFromSlit;
if (totalDistance <= maxDistance) {
displacement += sin(omega * u_time - k * totalDistance);
count += 1.0;
waveReached = true;
}
}
if (count > 0.0) {
displacement = displacement / count * 2.0;
}
}
}
在程序中还可以拖动点波源左右移动,你可以看到穿过单缝的波线左右摇摆,类似于一束光穿过小孔时的情境。这其实就是水波干涉互动课件中提到的相控阵原理,只不过这个例子中是由于主波源距离次波源的距离不同产生了相位差,而雷达中直接设置各次波源的相位差。在介绍雷达的文章中,尝尝会出现主瓣、副(旁)瓣这两个单词,本质上就是衍射的能量分布。例如几百个横向分布的次波源彼此相位差都是0,则垂直平分线是每个次波源相互叠加的振动加强位置,能量可占到 90% 左右。
障碍物的情况
思路是先通过“直线传播”求出“阴影区域”,然后将障碍物左右两侧的水平线看成缝隙,在缝隙上取采样点,计算“阴影区域”内的次波叠加。
但是失败了!阴影区域有明显的边界,与真实情况不符。
究其原因可能是:只计算了障碍物左右两侧的水平线上的次波源,并没有考虑水平线之上的点,但若考虑计算量又太大。
既然用惠更斯原理这种“运动学”方法模拟效果不佳,下一个版本要用波动的“动力学”版本了。
完整代码
发布时间:2026/3/15 上午10:16:29 阅读次数:41
