网格处理#

你加载到 Genesis World 的每个网格都要承担两项不同的任务,而这两项任务对网格的要求恰好相反。视觉网格要看起来正确,因此会保留美术作者制作的每一个三角形。碰撞网格则要供给物理求解器,而求解器在几何体简单、水密(watertight)且为凸形时速度最快、也最稳定。来自扫描仪或美术工具的原始网格通常一条都不满足。

为了弥合这一差距,Genesis 会在你加载刚体实体时自动处理它的碰撞几何体(水密化、减面并进行凸分解),同时保持视觉网格不变。本页说明这条流水线做了什么、控制它的选项有哪些,以及什么时候该调整各个选项。

文中提到的两个可运行示例是代码的权威依据:

加载网格#

非关节网格通过 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",
    ),
)

实际使用中最关键的几个选项:

选项

默认值

作用

threshold

0.1

凹度容差。减半大约会使凸包数量翻倍。

max_convex_hull

-1

凸包数量上限;-1 表示不设上限。

resolution

1000

分解过程中使用的采样分辨率。

preprocess_mode

"auto"

流形预处理:"auto"、"on" 或 "off"。

完整的参数集合见 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 会绕过过期条目并重新处理网格。之后用相同输入加载时,会直接从缓存读取。

结果

扩展名

生成方式

凸分解

.cvx

凸化

四面体网格

.tet

FEM 四面体化

有符号距离场

.gsd

基于 SDF 的碰撞

重网格化表面

.rm

为四面体化而做的重网格化

粒子采样

.ptc

粒子采样

另请参阅#

  • Hello, Genesis World:morph、文件格式与加载实体。

  • 约定:坐标系,以及 Y 轴朝上与 Z 轴朝上的网格处理。

  • 超越刚体:可形变材质与基于粒子的材质。