力场#
力场(force field)在每个子步为实体的粒子施加随空间变化的加速度。可用它让流体受风推动、把液体搅成旋涡、添加阻力或驱动湍流,而无需自己逐粒子编写作用力。
尽管名为力场,它实际上是一个加速度场:贡献的是以 m/s² 为单位的加速度,而不是以牛顿为单位的力。这里加速度才是具有物理意义的量,因为力没有空间密度的概念,而场在空间中每一点都返回一个值。Genesis World 按 vel += acc * substep_dt 把加速度积分到每个受影响粒子的速度中。
力场只作用于由 PBD 和 SPH 求解器推进的基于粒子的实体(流体、布料及其他粒子材质)。它们不影响刚体、MPM 或 FEM 实体。若要对刚体施加世界力,请改用其实体 API。
Note
该特性目前还没有随附的可运行示例,因此下面的片段仅作说明,不过其中每个符号和参数都来自实际 API(gs.force_fields)和 力场 API。
最小示例#
向场景添加一个场,激活它,构建,然后步进。下例中,恒定的侧风把一股下落的 SPH 液体吹向一侧:
import genesis as gs
gs.init(backend=gs.cpu)
scene = gs.Scene(
sim_options=gs.options.SimOptions(dt=4e-3, substeps=10),
sph_options=gs.options.SPHOptions(
lower_bound=(-0.5, -0.5, 0.0),
upper_bound=(0.5, 0.5, 1.0),
particle_size=0.01,
),
show_viewer=True,
)
scene.add_entity(gs.morphs.Plane())
liquid = scene.add_entity(
material=gs.materials.SPH.Liquid(),
morph=gs.morphs.Box(pos=(0.0, 0.0, 0.65), size=(0.4, 0.4, 0.4)),
)
# Instantiate, then register with the scene *before* building.
wind = gs.force_fields.Constant(direction=(1, 0, 0), strength=5.0) # 5 m/s2 along +X
scene.add_force_field(wind)
scene.build()
wind.activate() # fields start inactive; turn it on to apply acceleration
for i in range(1000):
scene.step()
生命周期#
力场经历四个阶段。顺序很重要:在 build() 之后添加,或忘记激活,是最容易犯的两个错误。
实例化:从
gs.force_fields.*创建场并传入其参数:wind = gs.force_fields.Constant(direction=(1, 0, 0), strength=5.0)
注册:用
scene.add_force_field(field)将其注册到场景,该方法返回同一个场。这必须在scene.build()之前完成:已注册场的数量会被烘焙进编译后的求解器内核,因此之后添加的场永远不会被求值。激活:用
field.activate()激活。场在创建时处于非激活状态(field.active为False),在激活前贡献的加速度为零。构建后(包括仿真过程中)可随时切换activate()和deactivate(),并读取field.active查看当前状态。步进:推进场景。每个子步中,求解器会把激活场的加速度加到每个受影响的粒子上。
可用的场类型#
所有场都是 gs.force_fields.ForceField 的子类,返回以 m/s² 为单位的加速度。除非另有说明,正的 strength 会放大效果,向量遵循右手系、Z 轴向上的坐标系。
场 |
效果 |
关键参数 |
|---|---|---|
|
处处均匀的加速度,类似稳定的重力或风。 |
|
|
恒定加速度,但仅限无限圆柱内部;外部为零。 |
|
|
按 |
|
|
与速度反向的加速度,由线性项和二次项组合。 |
|
|
每个粒子每一步独立随机加速度,各轴在 |
|
|
绕轴旋转粒子,含切向和径向分量,并随距离衰减。 |
|
|
从三维 Perlin 噪声采样的平滑变化加速度。 |
|
|
你自己编写的加速度函数,参数为位置、速度、时间和粒子索引。 |
|
对于 Custom,func 是一个 Quadrants 内核函数(用 @qd.func 包装的 Python 可调用对象),签名为 f(pos, vel, t, i) -> vec3,返回以 m/s² 为单位的加速度:
import quadrants as qd
@qd.func
def swirl(pos, vel, t, i):
return qd.Vector([-pos[1], pos[0], 0.0], dt=gs.qd_float) # m/s2
scene.add_force_field(gs.force_fields.Custom(swirl))
Turbulence 由 gs.force_fields.PerlinNoiseField 构建,后者是一个独立的三维 Perlin 噪声生成器(参数为 wrap_size、frequency、seed、seed_offset)。Turbulence 覆盖了常见情形,因此只有需要相干噪声的 Custom 场才需要自行构建生成器。
注意事项与常见陷阱#
它是加速度,不是力。 数值单位为 m/s²,与粒子质量无关,因此重粒子和轻粒子每一步获得的速度增量相同。若你要从力的角度思考,请自行乘以质量。
只有 PBD 和 SPH 粒子受影响。 请添加基于粒子的实体(SPH 液体、PBD 布料或流体),场才有作用对象。
场会叠加。 求解器对所有激活场求和,因此
Constant重力偏移、Drag项和Turbulence阵风会合并为一个加速度。激活可以随意切换。 因为激活只是翻转运行时标志,而不是重新编译内核,所以仿真中开关场不产生额外开销,也无需重新构建。