06 - Terrain and Optical Properties¶
This chapter introduces LESS's various optical property models and how to set appropriate properties for different scene elements.
Overview of optical properties¶
LESS provides a variety of optical property models suitable for different surface types:
| Property type | Applicable objects | Features |
|---|---|---|
Lambertian |
Soil, artificial surface | Uniform reflection in all directions, the simplest |
Prospect |
Green leaves | Spectrum calculated from leaf biochemical parameters |
RPV |
Any non-Lambertian surface | Parametric BRDF model |
WaterSurface |
Deep ocean and lakes | Cox–Munk wind-roughened reflection with optional water-leaving radiance |
SpectrumDB / SpectrumLibrary |
Various surfaces | Load measured spectra from built-in database |
Lambertian - Lambertian¶
The Lambertian body is the simplest optical model: incident light is reflected uniformly in all directions.
Scalar reflectance¶
import less
# Constant reflectivity (same for all wavelengths)
soil = less.Lambertian(reflectance=0.15)
Spectral reflectance¶
import numpy as np
# Specify wavelength and corresponding reflectance
wavelengths = [400, 500, 600, 700, 800, 900, 1000]
reflectance = [0.03, 0.05, 0.08, 0.10, 0.25, 0.30, 0.32]
soil = less.Lambertian(
reflectance=reflectance,
wavelengths=wavelengths,
)
When the simulated band is not in the given wavelength list, LESS automatically performs linear interpolation.
Transmission properties¶
For translucent materials such as blades, Lambertian also supports transmittance:
# Simple green leaves (not recommended, Prospect is more accurate)
simple_leaf = less.Lambertian(
reflectance=0.1,
transmittance=0.05,
)
Prospect —— Blade optical model¶
Prospect is the most widely used blade optical model in remote sensing. It calculates the complete reflectance and transmittance spectrum in the range 400-2500 nm based on the biochemical parameters of the leaves (chlorophyll, moisture, etc.).
Parameter description¶
| Parameters | Meaning | Typical range | Units |
|---|---|---|---|
cab |
Chlorophyll a+b content | 10-80 | μg/cm² |
car |
Carotenoid content | 5-20 | μg/cm² |
canth |
Anthocyanin content | 0-40 | μg/cm² |
cbrown |
Brown pigment | 0-1 | Dimensionless |
cw |
Equivalent water thickness | 0.005-0.03 | cm |
cm |
Dry matter content | 0.003-0.015 | g/cm² |
N |
Blade structural parameters | 1.0-3.0 | Dimensionless |
Different blade states¶
# healthy green leaves
green_leaf = less.Prospect(
cab=45, car=10, canth=0, cbrown=0,
cw=0.012, cm=0.008, N=1.5,
)
# Senescent yellow leaves
yellow_leaf = less.Prospect(
cab=5, car=15, canth=0, cbrown=0.3,
cw=0.008, cm=0.010, N=2.0,
)
# Autumn red leaves (high anthocyanins)
red_leaf = less.Prospect(
cab=3, car=5, canth=30, cbrown=0.15,
cw=0.010, cm=0.009, N=1.8,
)
Physical meaning¶
The core idea of the Prospect model is to treat the blade as a multi-layer parallel plate structure:
- Chlorophyll (cab): absorbs red and blue light, reflects green light → determines the "green" degree of the leaves
- Carotenoids (car): absorb blue light → protect chlorophyll
- Anthocyanin (canth): Absorbs green light → produces red-purple color
- Moisture (cw): Affects the absorption from near infrared to shortwave infrared
- Structural parameters (N): The number of air cavities inside the blade → affects near-infrared scattering
RPV - BRDF model¶
The RPV (Rahman-Pinty-Verstraete) model describes the directional reflection properties of non-Lambertian surfaces:
| Parameters | Meaning |
|---|---|
rho0 |
Total reflectance magnitude |
k |
Bowl/bell parameters (<1 bowl, >1 bell) |
theta |
Forward/Backscatter (<0 Backscatter enhancement, hot spot effect) |
The RPV model is described in more detail in Chapter 10 .
WaterSurface — optically deep water¶
WaterSurface treats the terrain boundary as the mean water level and represents unresolved wind waves with a Cox–Munk slope distribution. It computes unpolarized Fresnel reflection, masking, sky reflection, and object reflections for sun-glint and multi-angle water observations. wind_speed is the 10 m wind speed; the currently validated range is 1–14 m/s.
water = less.WaterSurface(
roughness=less.CoxMunk(
wind_speed=5.0,
wind_azimuth=90.0, # clockwise from north, degrees
),
refractive_index=1.34,
)
scene.terrain = less.Terrain(property=water)
The water-leaving term is optional. Supply a measured, glint-removed above-water remote-sensing reflectance Rrs in sr⁻¹ with PrescribedRrs:
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],
),
)
When absorption a and backscattering bb are available in m⁻¹, the deep-water IOP model can derive the water-leaving term:
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],
),
)
Omit water_leaving for a surface-only simulation. OpticalImager and BRFSensor are supported; buildings, vessels, vegetation, and other objects in the same scene may use spatial textures. The wind state, refractive index, IOPs, and prescribed Rrs of WaterSurface itself are currently uniform across the water surface, and statistical vegetation boundaries are not coupled to it. The model is intended for optically deep water: it does not trace underwater light paths or create a wave-displaced geometric mesh. It can therefore represent directional reflection from wind-wave statistics, but not wave-profile occlusion, shallow bottoms, or underwater targets. IrradianceMapSensor and RadiationFieldSensor do not currently support scenes containing water surfaces and report an explicit error.
Multi-angle reflectance is simulated directly:
brf = scene.simulate(less.BRFSensor(
bands=[443, 560, 665],
directions=[(0, 0), (30, 0), (30, 90), (30, 180)],
num_photons=1_000_000,
))
SpectrumLibrary - built-in spectral library¶
LESS has built-in a variety of measured spectral data, covering common natural and artificial surfaces:
lib = less.SpectrumLibrary()
# List all available spectra
names = lib.list_spectra()
for name in names[:10]:
print(name)
# View data for a spectrum
data = lib.get("birch_leaf_green")
print(f"Wavelength: {data['wavelength'][:5]} ... nm")
print(f"Reflectivity: {data['reflectance'][:5]} ...")
# Convert directly to Property object
birch_leaf = lib.as_property("birch_leaf_green")
Set properties for objects¶
Single component object¶
If the OBJ file has only one component (or you want all components to use the same properties), you can leave the component name unspecified:
maize = less.Object("maize", mesh=less.asset_path("maize.obj"))
maize.set_property(less.Prospect(cab=40, car=8, cw=0.012, cm=0.006, N=1.5))
Multi-component objects¶
FREX.obj (European Ash) has two components: leaves and stem_branch. Different properties can be set for them respectively:
tree = less.Object("ash", mesh=less.asset_path("FREX.obj"))
# View component list
print(tree.components) # ['leaves', 'stem_branch']
# Blade: Prospect model
tree.set_property("leaves", less.Prospect(cab=40, car=10, cw=0.012, cm=0.008, N=1.5))
# Trunk/Branches: Low Reflectivity Lambertian
tree.set_property("stem_branch", less.Lambertian(reflectance=0.08))
Complete Example: Corn + Soil Scenario¶
import less
import numpy as np
scene = less.Scene()
scene.size = 10.0
# Soil: Dark Lambertian
scene.terrain = less.Terrain(
property=less.Lambertian(reflectance=0.12)
)
# illumination
scene.illumination = less.Illumination(source=less.Sun(zenith=30, azimuth=150), atmosphere=less.NoAtmosphere())
# corn
maize = less.Object("maize", mesh=less.asset_path("maize.obj"))
maize.set_property(less.Prospect(cab=45, car=10, cw=0.012, cm=0.006, N=1.55))
# Place 9 corn plants in the scene (3×3 grid)
xs = np.linspace(2, 8, 3)
ys = np.linspace(2, 8, 3)
gx, gy = np.meshgrid(xs, ys)
positions = np.column_stack([gx.ravel(), gy.ravel(), np.zeros(9)])
scene.add(maize, positions=positions)
sensor = less.OpticalImager(
less.Orthographic(image_size=512),
bands=[650, 550, 450, 842], # R, G, B, NIR
quality=128,
)
image = scene.simulate(sensor)
image.save("06_maize_grid.png")
# Access NIR band data
data = image.data
nir = data[:, :, 3] # Band 4 = 842 nm (NIR)
red = data[:, :, 0] # Band 1 = 650 nm (Red)
# Calculate NDVI
ndvi = (nir - red) / (nir + red + 1e-10)
print(f"NDVI range: {ndvi.min():.2f} ~ {ndvi.max():.2f}")
Supporting script:
scripts/06_terrain_properties.py
The effect of Prospect parameters on spectrum¶
Understanding how each Prospect parameter affects the spectrum will help you set simulation parameters correctly:
| Band range | Main influencing factors |
|---|---|
| 400-500 nm (blue light) | cab, car (strong absorption) |
| 500-600 nm (green light) | cab (absorption weakened, "green peak") |
| 600-700 nm (red light) | cab (red edge absorption), canth (anthocyanin absorption) |
| 700-750 nm (red edge) | cab, N (red edge position and slope) |
| 750-1300 nm (near infrared) | N (mesophyll scattering), cab No effect |
| 1300-2500 nm (short wave infrared) | cw (moisture absorption band), cm (dry matter) |
Chlorophyll
cabaffects almost exclusively the 400-750 nm range. In the near-infrared band, the high reflectivity of leaves is mainly determined by the mesophyll structure (N).
Next step¶
Related API¶
less.Lambertian、less.Prospect、less.RPVless.SpectrumLibraryless.Scene、less.Terrain、less.Objectless.Orthographic、less.OpticalImager