09 - 地形与地表反射¶
本章介绍三种地形(平地、栅格高程、三角网格地形)以及常用的地表反射模型:朗伯反射、RPV 双向反射和水面。
平地¶
不给出高程时,地形是位于 z = 0 的平面,覆盖 scene.size 的范围:
import less
scene = less.Scene()
scene.size = 50.0
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.15))
栅格高程(DEM)¶
dem 可以是 GeoTIFF 文件路径,也可以是二维 NumPy 数组,单位为米。高程网格铺满 scene.size 的水平范围:数组第一行对应场景北边,第一列对应西边。导入时会减去最低高程,使地形最低点位于 z = 0。
下面用一个数组构造一座小山:
import numpy as np
x = np.linspace(0, 100, 101)
gx, gy = np.meshgrid(x, x)
hill = 15.0 * np.exp(-((gx - 50) ** 2 + (gy - 50) ** 2) / (2 * 20.0 ** 2))
scene = less.Scene()
scene.size = 100.0
scene.terrain = less.Terrain(dem=hill, property=less.Lambertian(reflectance=0.2))
scene.illumination = less.Illumination(
source=less.Sun(zenith=40, azimuth=135), atmosphere=less.NoAtmosphere())
image = scene.simulate(less.OpticalImager(
less.Orthographic(image_size=128), bands=[550], quality=32))
使用文件时写作 less.Terrain(dem="elevation.tif", property=...)。几点说明:
- 高程网格至少有两行两列,数值必须是有限的;导入前请填补或裁掉无数据区域;
subsample=N每隔 N 个采样点取一个点,用于降低大 DEM 的分辨率;- 每个网格单元被分成两个三角形,
height_at()在这些三角形上插值,与模拟中的地表完全一致。
print(scene.terrain.height_at(50.0, 50.0)) # 山顶
print(scene.terrain.height_at([10.0, 50.0], [10.0, 50.0])) # 也可以是数组
在 DEM 上放置物体时只需给出 (x, y),物体会自动放到该处的地面高度:
tree = less.Object("ash", mesh=less.asset_path("FREX.obj"))
tree.set_property("leaves", less.Prospect(cab=40))
tree.set_property("stem_branch", less.Lambertian(reflectance=0.1))
scene.add(tree, positions=[[30.0, 50.0], [50.0, 50.0], [70.0, 50.0]])
三角网格地形¶
复杂地形、陡崖或人工地表可以用 OBJ 网格描述:
scene = less.Scene()
scene.size = (100, 100)
scene.terrain = less.Terrain(
mesh=less.asset_path("tutorial_terrain.obj"),
property=less.Lambertian(reflectance=0.2),
)
scene.illumination = less.Illumination(
source=less.Sun(zenith=30), atmosphere=less.NoAtmosphere())
image = scene.simulate(less.OpticalImager(
less.Orthographic(image_size=128), bands=[550, 850], quality=32))
网格地形按 OBJ 的坐标约定读入(见第 5 章),保持原有尺寸、位置和高程,不会被拉伸到 scene.size,请确保网格位于要观测的范围内。scene.terrain.height_at(x, y) 返回该位置最上层地表的高度。
dem 和 mesh 只能二选一。修改地形时重新赋值 scene.terrain。起伏地形(DEM 或网格)不能与周期边界(scene.repetitive)一起使用。
朗伯反射¶
朗伯表面向各方向反射的辐亮度相同,是最常用的土壤和人工表面模型:
soil = less.Lambertian(reflectance=0.15)
soil = less.Lambertian(
wavelengths=[400, 500, 600, 700, 800, 900, 1000],
reflectance=[0.03, 0.05, 0.08, 0.10, 0.25, 0.30, 0.32],
)
给出 transmittance 时表示半透明薄片,给出 back_reflectance 时两面反射率不同,可用于简单叶片(更真实的叶片模型见第 10 章):
RPV 双向反射¶
RPV(Rahman–Pinty–Verstraete)模型描述反射随观测方向的变化,适合用一个参数化表面代表冠层或粗糙土壤:
| 参数 | 含义 |
|---|---|
rho0 |
反射的整体大小 |
k |
小于 1 时边缘方向反射更强(碗形),大于 1 时星下点更强(钟形) |
theta |
小于 0 时后向散射更强 |
rhoc |
热点强度 |
各参数都可以是光谱数组。第 21 章演示如何模拟和查看双向反射。
水面¶
WaterSurface 把地形当作平均水面,用 Cox–Munk 波面斜率分布描述风浪,计算菲涅耳镜面反射,适合模拟太阳耀斑和多角度水体反射:
water = less.WaterSurface(
roughness=less.CoxMunk(wind_speed=5.0, wind_azimuth=90.0),
refractive_index=1.34,
)
scene = less.Scene()
scene.size = 100.0
scene.terrain = less.Terrain(property=water)
scene.illumination = less.Illumination(
source=less.Sun(zenith=30, azimuth=180), atmosphere=less.NoAtmosphere())
brf = scene.simulate(less.BRFSensor(
bands=[443, 560, 665],
directions=[(0, 0), (30, 0), (30, 90), (30, 180)],
num_photons=200_000,
))
print(brf.brf) # 每行一个方向,每列一个波段
wind_speed 是 10 m 高度的风速,适用范围 1–14 m s⁻¹;wind_azimuth 从北顺时针计。
需要包含水体本身的贡献时,可以加入离水辐射。已知遥感反射率 Rrs(sr⁻¹)时:
water = less.WaterSurface(
roughness=less.CoxMunk(wind_speed=5.0),
water_leaving=less.PrescribedRrs(
wavelengths=[443, 560, 665], values=[0.0060, 0.0030, 0.0008]),
)
已知吸收系数和后向散射系数(m⁻¹)时:
water = less.WaterSurface(
roughness=less.CoxMunk(wind_speed=5.0),
water_leaving=less.DeepWaterIOP(
wavelengths=[443, 560, 665],
absorption=[0.025, 0.070, 0.430],
backscattering=[0.006, 0.004, 0.002]),
)
水面模型适用于光学深水,不追踪水下光路,也不生成起伏的波浪几何,因此不能用于浅水底部、水下目标或单个波峰的研究。它可用于光学成像、多角度反射率和三维辐射场。