2、几何光学基础:光线模型、反射定律与折射定律、斯涅尔定律、全反射条件

各位同学,咱们今天聊几何光学。说白了,就是研究光怎么走直线、怎么拐弯、怎么被挡住的一套理论。我刚开始做光学仿真那会儿,觉得这东西太简单了,不就是画几条线嘛。结果真上手做复杂光路系统时,才发现基础不牢,后面全是坑。

好,咱们一步步来。

2.1 光线模型:把光想象成一根线

几何光学里,我们不考虑光的波动性,也不管它是什么电磁波。我们只做一件事——把光抽象成一条带箭头的直线。这条线代表能量传播的方向。

核心假设:在均匀介质中,光沿直线传播。遇到不同介质的界面时,才会发生反射或折射。

这个模型虽然粗糙,但在绝大多数工程场景下够用了。我做过一个激光准直系统仿真,用光线模型算出来的光斑位置,和实测结果误差不到 0.1mm。所以别小看这几条线。

光线模型有几个关键要素:

  • 光线方向:用单位向量表示,指向能量传播方向
  • 光线起点:光源位置或前一个界面的出射点
  • 光线颜色/波长:决定折射率,影响后续行为
  • 光线强度:在纯几何模型中常忽略,但做能量分析时要用

我的习惯:写仿真代码时,我会把光线定义成一个类,包含起点坐标、方向向量、波长三个属性。方向向量一定要归一化,不然后面算交点时会出各种奇怪的问题。

2.2 反射定律:入射角等于反射角

这个大家初中就学过。但做仿真时,我建议你用向量形式来理解,而不是只记角度。

反射定律的向量表达式:

r = i - 2 * (i · n) * n

其中:

  • r:反射光线方向向量
  • i:入射光线方向向量(指向界面)
  • n:界面法向量(指向入射介质一侧)
  • ·:点积运算

为什么用向量?因为写代码时,你不可能每次去算角度再转回来。直接用向量运算,一行代码搞定。

我曾经踩过的坑:法向量方向搞反了。反射光线直接射到介质内部去了,仿真结果完全不对。记住:法向量要指向入射光线所在的那一侧。

2.3 折射定律与斯涅尔定律

光从一种介质进入另一种介质时,传播方向会改变。这就是折射。定量描述这个现象的,就是斯涅尔定律。

公式很简单:

n₁ * sin(θ₁) = n₂ * sin(θ₂)

其中:

  • n₁、n₂:两种介质的折射率
  • θ₁:入射角(光线与法线的夹角)
  • θ₂:折射角

折射率是啥?说白了就是光在真空中的速度除以在介质中的速度。空气的折射率约等于 1.0003,一般工程计算直接取 1。水的折射率约 1.33,玻璃在 1.5 左右。

介质 折射率(约值) 常见应用场景
真空 1.0000 理想参考基准
空气 1.0003 大多数地面光学系统
水 1.3330 水下成像、海洋光学
普通玻璃 1.5000 透镜、棱镜
蓝宝石 1.7700 高硬度窗口、LED衬底
金刚石 2.4200 特殊光学窗口

折射定律的向量形式稍微复杂一点,但写代码时更实用:

t = (n₁/n₂) * i + [ (n₁/n₂) * cos(θ₁) - sqrt(1 - (n₁/n₂)² * (1 - cos²(θ₁))) ] * n

其中 cos(θ₁) = - (i · n)。注意这里 i 指向界面,n 指向入射介质。

我建议:写仿真代码时,先判断根号里的值是否大于 0。如果小于 0,说明发生了全反射,这时候就别算折射了。

2.4 全反射条件

光从光密介质射向光疏介质时(比如从玻璃射向空气),如果入射角大于某个临界值,光就不会进入第二种介质,而是全部反射回去。这就是全反射。

临界角公式:

θ_c = arcsin(n₂ / n₁)

当 n₁ > n₂ 时,这个公式才有意义。如果 n₁ ≤ n₂,永远不会发生全反射。

举个例子:光从玻璃(n=1.5)射向空气(n=1.0),临界角约为 41.8°。入射角超过这个值,光就出不去了。

工程应用:光纤通信就是靠全反射。光在光纤芯层里不断全反射,才能传输几公里甚至上百公里。我做光纤耦合仿真时,全反射条件判断错了,结果光全部泄漏出去,仿真结果完全不能用。

全反射的判断流程:

  1. 判断是否从光密到光疏(n₁ > n₂)
  2. 计算临界角 θ_c = arcsin(n₂ / n₁)
  3. 比较入射角 θ₁ 与 θ_c
  4. 若 θ₁ ≥ θ_c,发生全反射,只计算反射
  5. 若 θ₁ < θ_c,正常计算反射和折射

2.5 知识体系总览

下面这张图把本章的核心逻辑串起来了。你写仿真代码时,按这个流程走,基本不会漏掉关键步骤。

几何光学基础 · 知识体系 光线模型 反射定律 入射角 = 反射角 向量形式:r = i - 2(i·n)n 折射定律 斯涅尔定律:n₁sinθ₁ = n₂sinθ₂ 向量形式:含根号判断 全反射条件 临界角:θ_c = arcsin(n₂/n₁) 应用:光纤通信、波导 n₁ > n₂ 时触发

2.6 仿真代码示例

最后,给你一段 Python 代码。这段代码实现了光线与平面界面的交互,包含反射、折射和全反射判断。我自己的仿真工具包里,核心逻辑和这个差不多。

import numpy as np

def trace_ray(origin, direction, normal, n1, n2):
    """
    光线与平面界面交互
    origin: 光线起点 (3,)
    direction: 光线方向 (3,),已归一化
    normal: 界面法向量 (3,),指向入射介质
    n1: 入射介质折射率
    n2: 出射介质折射率
    返回: (反射方向, 折射方向, 是否全反射)
    """
    # 确保方向向量归一化
    direction = direction / np.linalg.norm(direction)
    normal = normal / np.linalg.norm(normal)
    
    # 计算入射角余弦
    cos_theta1 = -np.dot(direction, normal)
    
    # 反射方向
    reflect_dir = direction - 2 * cos_theta1 * normal
    
    # 判断是否全反射
    ratio = n1 / n2
    sin_theta1_sq = 1 - cos_theta1**2
    discriminant = 1 - ratio**2 * sin_theta1_sq
    
    if discriminant < 0:
        # 全反射
        return reflect_dir, None, True
    
    # 折射方向
    cos_theta2 = np.sqrt(discriminant)
    refract_dir = ratio * direction + (ratio * cos_theta1 - cos_theta2) * normal
    
    return reflect_dir, refract_dir, False

# 示例:光从玻璃射向空气
origin = np.array([0.0, 0.0, 0.0])
direction = np.array([0.0, -1.0, 0.0])  # 垂直入射
normal = np.array([0.0, 1.0, 0.0])      # 法线向上
n1, n2 = 1.5, 1.0

r_dir, t_dir, is_total = trace_ray(origin, direction, normal, n1, n2)
print(f"反射方向: {r_dir}")
print(f"折射方向: {t_dir}")
print(f"是否全反射: {is_total}")

我建议:跑这段代码时,试试把入射方向改成斜入射,比如 direction = [0.707, -0.707, 0],看看折射角的变化。再试试入射角大于 41.8° 的情况,你会看到全反射被触发了。