网格处理#
你加载到 Genesis World 的每个网格都要承担两项不同的任务,而这两项任务对网格的要求恰好相反。视觉网格要看起来正确,因此会保留美术作者制作的每一个三角形。碰撞网格则要供给物理求解器,而求解器在几何体简单、水密(watertight)且为凸形时速度最快、也最稳定。来自扫描仪或美术工具的原始网格通常一条都不满足。
为了弥合这一差距,Genesis 会在你加载刚体实体时自动处理它的碰撞几何体(水密化、减面并进行凸分解),同时保持视觉网格不变。本页说明这条流水线做了什么、控制它的选项有哪些,以及什么时候该调整各个选项。
文中提到的两个可运行示例是代码的权威依据:
examples/rigid/nonconvex_mesh.py:加载网格,并观察物体静置在其上。examples/rigid/convex_decomposition.py:分解后网格的碰撞行为。
加载网格#
非关节网格通过 gs.morphs.Mesh 加载,它接受 .obj、.stl、.glb 和 .gltf 文件(morph 的基础知识见 Hello, Genesis World,Y 轴朝上与 Z 轴朝上的处理见 约定):
import genesis as gs
tank = scene.add_entity(
gs.morphs.Mesh(
file="meshes/tank.obj",
scale=5.0,
fixed=True,
),
vis_mode="collision", # render the processed collision mesh instead of the visual mesh
)
vis_mode 这个快捷选项可用来检查处理结果。它会渲染 "visual"(默认)或 "collision" 几何体,让你准确看到求解器眼中的形状。
Note
下文描述的处理只在该 morph 变成 RigidEntity 时生效。可形变实体(FEM、MPM 以及其他基于粒子的材质)不会对网格做凸化;参见 超越刚体。
碰撞处理流水线#
当网格成为刚体实体时,Genesis 分三个阶段准备它的碰撞几何体:
水密化: 封闭缝隙、移除非流形结构,使网格围成一个定义明确的体积。由
watertighten控制(0–8 的整数,默认 5)。减面: 把三角形数量降到目标值,使窄相碰撞的开销保持低廉。
凸化: 用一个或多个凸包替换网格。凸形具有精确的支撑函数,而这正是碰撞算法所查询的对象。
对于刚体实体,减面和凸化默认开启;可以分别用 decimate=False 或 convexify=False 单独关闭其中一项。
减面#
面数过高的碰撞网格会拖慢碰撞检测,还可能损害数值稳定性。减面会把三角形折叠到目标数量:
gs.morphs.Mesh(
file="high_poly.obj",
decimate=True,
decimate_face_num=500, # target triangle count
decimate_aggressiveness=2, # 0 (lossless) to 8 (target at all costs)
)
decimate: 当convexify为True时默认True,否则默认为False。非凸的碰撞网格通常会被保留,正是因为它的表面细节值得保留。decimate_face_num: 目标三角形数量,默认 500。请保持在 100 以上;低于该值会丢失过多几何信息,结果不可靠。decimate_aggressiveness: 简化器为达到目标所付出的力度,取 0–8 的整数(默认 2)。取 0 时无损,取 2 时保留全部特征,取 5 时可能明显改变形状,取 8 时不计保真度、强行达到目标。
如果你加载面数超过 5000 的网格却没有减面,Genesis 会给出警告,并建议设置 decimate=True。
凸分解#
碰撞求解器把每个碰撞形状都当作凸形。对杯子或盒子来说,单个凸包就够了,但凹形物体(碗、罐体外壳、带手柄的工具)如果被一个凸包包裹,就会失去内部的空腔。凸分解则利用 CoACD 库,把网格切分成一组凸块,由它们共同逼近原始形状。
gs.morphs.Mesh(
file="concave.obj",
convexify=True,
)
对刚体实体,convexify 默认为 True,对可形变实体则默认为 False。不过 Genesis 并不总是执行完整的分解:它会先比较原始网格与其单个凸包的体积,如果差异足够小,就保留开销更低的凸包。这一判断由两个阈值控制:
decompose_object_error_threshold: 相对体积误差低于该值时,普通刚体物体跳过分解,直接使用其凸包。默认 0.15(15%)。设为0.0可强制分解,设为float("inf")可禁用分解。decompose_robot_error_threshold: 多关节机器人使用的同一阈值。默认float("inf"),因此除非你调低它,机器人连杆都会被凸化成单个凸包。
Note
较早的 decompose_nonconvex 参数已废弃。请改用 convexify 配合两个 decompose_*_error_threshold 选项。
调优 CoACD#
当确实执行完整分解时,可以传入 gs.options.CoacdOptions 来控制质量与凸包数量之间的权衡:
gs.morphs.Mesh(
file="concave.obj",
convexify=True,
coacd_options=gs.options.CoacdOptions(
threshold=0.05, # lower = tighter fit, more hulls
max_convex_hull=-1, # -1 = no cap on the number of hulls
resolution=1000, # sampling resolution of the decomposition
preprocess_mode="auto",
),
)
实际使用中最关键的几个选项:
选项 |
默认值 |
作用 |
|---|---|---|
|
|
凹度容差。减半大约会使凸包数量翻倍。 |
|
|
凸包数量上限; |
|
|
分解过程中使用的采样分辨率。 |
|
|
流形预处理: |
完整的参数集合见 CoACD 文档。
用于可形变仿真的网格#
可形变求解器需要体网格,而不是表面网格。当网格用于驱动 FEM 实体时,Genesis 会先做一遍各向同性重网格化,使边长均匀化,让四面体化保持良态,然后再对网格做四面体化(用四面体填充内部)。这一过程是自动完成的:
entity = scene.add_entity(
morph=gs.morphs.Mesh(file="model.obj"),
material=gs.materials.FEM.Elastic(E=1e5, nu=0.4), # E in Pa, nu dimensionless
)
可形变仿真中材质方面的内容,参见 软体机器人。
缓存#
网格处理开销很大,因此 Genesis 会把每个结果缓存到磁盘上,缓存键是输入几何体与生成它的选项的 SHA-256 哈希。一旦改动相关选项,键就会变化,Genesis 会绕过过期条目并重新处理网格。之后用相同输入加载时,会直接从缓存读取。
结果 |
扩展名 |
生成方式 |
|---|---|---|
凸分解 |
|
凸化 |
四面体网格 |
|
FEM 四面体化 |
有符号距离场 |
|
基于 SDF 的碰撞 |
重网格化表面 |
|
为四面体化而做的重网格化 |
粒子采样 |
|
粒子采样 |
另请参阅#
Hello, Genesis World:morph、文件格式与加载实体。
约定:坐标系,以及 Y 轴朝上与 Z 轴朝上的网格处理。
超越刚体:可形变材质与基于粒子的材质。