跳转至

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 章):

thin_leaf = less.Lambertian(reflectance=0.10, back_reflectance=0.14, transmittance=0.05)

RPV 双向反射

RPV(Rahman–Pinty–Verstraete)模型描述反射随观测方向的变化,适合用一个参数化表面代表冠层或粗糙土壤:

surface = less.RPV(rho0=0.05, k=0.7, theta=-0.1, rhoc=0.05)
参数 含义
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]),
)

水面模型适用于光学深水,不追踪水下光路,也不生成起伏的波浪几何,因此不能用于浅水底部、水下目标或单个波峰的研究。它可用于光学成像、多角度反射率和三维辐射场。

下一步