14 - 非均匀体数据场¶
VolumeField 描述密度和温度随位置变化的介质,例如烟羽、霾或火焰。数据可以来自 NumPy 数组,也可以从 OpenVDB 文件导入。
由数组创建¶
数组的维度顺序为 [z, y, x],数值位于体素中心。density_scale 是无量纲的密度倍数,与光谱吸收系数和散射系数相乘。bounds_min 和 bounds_max 给出数据在组件局部坐标中的范围(m):
import numpy as np
import less
z, y, x = np.meshgrid(np.linspace(0, 1, 16), np.linspace(-1, 1, 16),
np.linspace(-1, 1, 16), indexing="ij")
density = np.exp(-(x ** 2 + y ** 2) / 0.2) * (1 - z) # 中心浓、向上变淡
temperature = 300 + 500 * density # K
field = less.VolumeField(
density_scale=density.astype(np.float32),
bounds_min=(-10, -10, 0),
bounds_max=(10, 10, 20),
temperature_field=temperature.astype(np.float32),
wavelengths=[450, 12000],
absorption=[0.08, 0.25], # density_scale = 1 时的值(m⁻¹)
scattering=[0.22, 0.01],
g=[0.65, 0.2],
temperature=300, # 没有温度场时使用的温度
)
把它赋给一个封闭边界,边界与数据范围使用同一个局部坐标系。之后像普通物体一样放置:
scene = less.Scene()
scene.size = 100
scene.terrain = less.Terrain(property=less.Lambertian(reflectance=0.15))
scene.terrain.set_property(less.ThermalProperty(temperature=300, emissivity=0.95))
scene.illumination = less.Illumination(
source=less.Sun(zenith=30), atmosphere=less.NoAtmosphere())
smoke = less.Object("smoke")
smoke.add_component("domain", less.TurbidBoundary(less.asset_path("volume_field_domain.obj")))
smoke.set_property("domain", field)
scene.add(smoke, positions=[(50, 50, 0)])
密度和温度在体素之间三线性插值,介质中的衰减、Henyey–Greenstein 散射、多次散射和热发射都会计算在内。
可见光与热红外成像¶
projection = less.Perspective(image_size=64, fov=35,
position=(70, -20, 25), target=(50, 50, 8))
visible = scene.simulate(less.OpticalImager(projection, bands=[450, 550, 650], quality=32))
thermal = scene.simulate(less.ThermalImager(projection, bands=[3900, 10500], quality=32,
mode="property"))
visible.save("smoke_visible.tif")
thermal.save("smoke_thermal.tif")
含体数据场的场景可以用光学成像和热红外成像模拟。
导入 OpenVDB¶
在制作场景的 Python 环境中安装 OpenVDB 的 Python 绑定(openvdb 或 pyopenvdb),然后用 less.VolumeField.from_openvdb(path, ...) 读取:
| 参数 | 示例 |
|---|---|
density_grid、temperature_grid |
"density"、"temperature" |
density_scale |
0.4 |
temperature_scale、temperature_offset |
900.0、300.0 |
absorption、scattering、g |
0.25、0.35、0.6 |
- OpenVDB 网格的坐标变换决定数据的位置和尺度,要求各轴与坐标轴对齐;有旋转或剪切的网格需要先重采样;
- 温度按
开尔文 = 原值 × temperature_scale + temperature_offset换算;原数据已是开尔文时使用默认值(1 和 0); - 导入后数据保存在 LESS 场景中,之后的模拟和场景加载不再需要 OpenVDB。
max_voxels限制导入的体素总数,避免占满内存。
从 Blender 导出¶
- 烘焙烟火模拟;
- 保持物体变换不变,把流体域导出为 OBJ,作为
TurbidBoundary; - 把密度和温度网格导出为 VDB;
- 设置单位比例,使 Blender 中的 1 m 对应 LESS 中的 1 m;
- Mantaflow 的温度通常是相对值,根据环境温度和火焰温度设置
temperature_scale与temperature_offset。
材质的发光颜色不会被当作光源,热发射完全由温度场和吸收系数决定。
内置示例¶
python -m less.examples.volume_field --backend embree --image-size 64 --quality 16
python -m less.examples.forest_fire --backend optix --image-size 64 --quality 16 --output-dir forest-fire
森林火灾示例包括健康和烧焦的树木、地表、散射的烟和高温气体,输出 RGB、3.9 µm 中波红外和 10.5 µm 长波红外影像。