10 - BRDF 模拟¶
本章介绍如何使用 LESS 进行二向反射率 (BRF/BRDF) 模拟,分析地表的方向反射特性。
什么是 BRDF?¶
双向反射分布函数 (BRDF) 描述了地表对不同入射方向和不同观测方向的反射特性。在遥感中,通常使用 二向反射率因子 (BRF) 来表征——它是特定几何条件下的地表反射率与理想朗伯体的比值。
BRDF 的变化受三维植被结构影响:
- 热点效应 (Hot Spot):观测方向与太阳方向重合时,反射率显著增大(因为此时看不到阴影)
- 碗形效应 (Bowl Shape):植被冠层通常在大观测天顶角时反射率更高
- 前向/后向散射:叶片的散射特性导致前向和后向不对称
BRFSensor —— 多方向一次出结果¶
BRFSensor 是 BRF 模拟的首选传感器:
- 传一组方向,单次 simulate 全部跑完(前向光子追踪,所有方向共享同一批光子,效率高)
- 不出图像,只出每个方向上场景平均的 BRF 值
- 不依赖
scene.size的相机布点,对大角度观测也无几何边界问题
import less
sensor = less.BRFSensor(
bands=[650, 850],
directions=[(0, 0), (30, 180), (60, 0)], # (zenith°, azimuth°) 列表
num_photons=50_000_000, # 默认 5e7;噪声大就加
)
返回的是 BRFProduct:
| 属性 | 形状 | 说明 |
|---|---|---|
.brf |
[n_dirs, n_bands] |
每个方向、每个波段的 BRF |
.directions |
list | 输入的 (z, a) 列表 |
.wavelengths |
[n_bands] |
波段中心波长 (nm) |
多角度 BRF 模拟¶
构建一个观测方向列表(主平面 / 全半球都行),一次 simulate() 就能拿到完整 BRDF 曲面。
import less
import numpy as np
# ── 构建场景 ──────────────────────────────────────────────────
scene = less.Scene()
scene.size = 10.0
scene.repetitive = False # 有限场景(默认);周期平铺仅限平坦均质地形
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.15))
scene.illumination = less.Illumination(source=less.Sun(zenith=30, azimuth=180), atmosphere=less.NoAtmosphere())
maize = less.Object("maize", mesh=less.examples.asset_path("maize.obj"))
maize.set_property(less.Prospect(cab=45, car=10, cw=0.012, cm=0.006, N=1.55))
rng = np.random.RandomState(42)
positions = less.place.grid(scene, spacing=0.75, jitter=0.04)
n = len(positions)
scene.add(maize, positions=positions,
scales=rng.uniform(0.9, 1.1, n),
rotations=rng.uniform(0, 360, n))
# 太阳方位角=180°(南),所以主平面是南北向
# - 后向散射:方位角 180°(同侧)
# - 前向散射:方位角 0° / 360°(对侧)
view_zeniths = np.arange(0, 65, 5)
directions = ([(z, 180) for z in view_zeniths] # backward
+ [(z, 0) for z in view_zeniths[1:]]) # forward (skip z=0 dup)
# ── 单次 simulate 拿到所有方向 ───────────────────────────────
result = scene.simulate(less.BRFSensor(
bands=[650, 850],
directions=directions,
num_photons=50_000_000,
))
print("主平面 BRF (SZA=30°, SAA=180°)")
print(f"{'VZA':>6s} {'AZ':>4s} {'Red(650)':>10s} {'NIR(850)':>10s}")
for (z, a), (r, n) in zip(result.directions, result.brf):
signed_vza = z if a == 180 else -z # 主平面常用习惯:负=前向
mark = " ← hot spot" if a == 180 and abs(z - 30) < 3 else ""
print(f"{signed_vza:>6d} {a:>4d} {r:>10.4f} {n:>10.4f}{mark}")
# 极坐标 / 主平面曲线一行搞定
result.show(plane="principal", style="line") # matplotlib 弹图
result.save("plot_brf.csv") # 也能导出 CSV / JSON / NPY
配套脚本:
scripts/10_brdf.py
代码解读¶
方向列表的两种常用模式¶
# 主平面(VZA 扫描)
directions = [(z, 180) for z in range(0, 65, 5)] \
+ [(z, 0) for z in range(5, 65, 5)]
# 全半球(VZA × VAA 网格)—— 用于极坐标图
import itertools
directions = list(itertools.product(
range(0, 75, 10), # zeniths
range(0, 360, 30), # azimuths
))
主平面 (Principal Plane)¶
主平面是包含太阳方向和法线方向的平面。在这个平面内:
- 后向散射方向:观测者位于太阳同侧(观测方位角 = 太阳方位角)
- 前向散射方向:观测者位于太阳对侧(观测方位角 = 太阳方位角 + 180°)
- 热点:观测天顶角 = 太阳天顶角,且在后向散射方向
场景重复的重要性¶
周期 BRF 基准通常使用 scene.repetitive=True 表示无限平铺的平坦均质样地。本教程使用有限场景,因此结果包含地块边缘效应;比较观测或其他模型时应保持相同的边界条件。
BRFSensor vs OpticalImager¶
| 想要什么 | 用谁 |
|---|---|
| 一组方向上的 BRF 标量值(典型 BRF / BRDF 曲线) | BRFSensor |
| 某一方向的高分辨率二维图像(关心空间纹理 / 阴影) | OpticalImager + Orthographic(view_zenith=…) |
BRFSensor 通过前向光子追踪,所有方向共享同一批光子,跑 N 个方向 ≈ 单方向耗时;OpticalImager 每方向都得重跑一次。
RPV BRDF 模型¶
除了通过三维场景模拟 BRF,LESS 也支持直接使用参数化 BRDF 模型。RPV 模型可以用于地面 BRDF:
参数含义:
| 参数 | 含义 | 范围 |
|---|---|---|
rho0 |
反射率量级 | 0-1 |
k |
形状参数:<1 碗形(大角度更亮),>1 钟形(垂直更亮) | 0-2 |
theta |
散射不对称:<0 后向散射增强,>0 前向散射增强 | -1 ~ 1 |
自定义可视化¶
result.show() 默认出主平面线图。要画半球极坐标图(VZA × VAA 网格),需要自己组织数据:
import matplotlib.pyplot as plt
import numpy as np
# directions = [(z, a), ...],假设是规则网格
zeniths = sorted({z for z, _ in result.directions})
azimuths = sorted({a for _, a in result.directions})
brf_grid = np.zeros((len(azimuths), len(zeniths)))
for (z, a), val in zip(result.directions, result.brf[:, 0]): # 第 0 个波段
brf_grid[azimuths.index(a), zeniths.index(z)] = val
fig, ax = plt.subplots(subplot_kw={'projection': 'polar'}, figsize=(7, 7))
ax.set_theta_zero_location('N'); ax.set_theta_direction(-1)
theta = np.deg2rad(azimuths)
r = zeniths
ax.contourf(theta, r, brf_grid.T, levels=20, cmap='RdYlGn')
ax.set_title(f"BRF @ {result.wavelengths[0]:.0f} nm")
plt.savefig("brf_polar.png", dpi=150, bbox_inches='tight')
应用场景¶
| 应用 | 说明 |
|---|---|
| 反照率估算 | 积分 BRF 获得半球反照率 |
| 多角度遥感 | 模拟 MISR、POLDER 等多角度传感器 |
| BRDF 校正 | 评估 BRDF 参数化模型精度 |
| 结构参数反演 | 利用 BRF 各向异性反演冠层结构 |
下一步¶
相关 API¶
less.BRFSensor、less.Scene.simulate()less.RPV、less.Prospect、less.Lambertianless.place.grid()、less.place.poisson_disk()less.BRFProduct.save()、less.BRFProduct.plot_principal_plane()