Skip to content

网格变形

如果我们想根据少量约束来对一个三角网格进行变形,可以使用网格变形(mesh deformation)算法。Open3D 实现了 [SorkineAndAlexa2007] 提出的”尽可能刚性”(as-rigid-as-possible, ARAP)方法,它优化以下能量函数:

∑i∑j∈N(i)wij∣∣(pi′−pj′)−Ri(pi−pj)∣∣2 ,\sum_i \sum_{j \in \mathcal{N}(i)} w_{ij} || (\mathbf{p}'_i - \mathbf{p}'_j) - \mathbf{R}_i (\mathbf{p}_i - \mathbf{p}_j)||^2 \,,

其中 Ri\mathbf{R}_i 是我们需要优化的旋转矩阵,pi\mathbf{p}_i 和 pi′\mathbf{p}'_i 分别是优化前后的顶点位置;N(i)\mathcal{N}(i) 是顶点 ii 的邻居集合;权重 wijw_{ij} 为余切权重(cotangent weights)。

Open3D 在 deform_as_rigid_as_possible 中实现了该方法。该方法的第一个参数是一组 constraint_ids,指向三角网格中的顶点;第二个参数 constraint_pos(原文如此)定义这些顶点在优化后应处于的位置。优化过程是一个迭代格式,因此我们还可以通过 max_iter 来设定迭代次数。

armadillo = o3d.data.ArmadilloMesh()
mesh = o3d.io.read_triangle_mesh(armadillo.path)
vertices = np.asarray(mesh.vertices)
static_ids = [idx for idx in np.where(vertices[:, 1] < -30)[0]]
static_pos = []
for id in static_ids:
static_pos.append(vertices[id])
handle_ids = [2490]
handle_pos = [vertices[2490] + np.array((-40, -40, -40))]
constraint_ids = o3d.utility.IntVector(static_ids + handle_ids)
constraint_pos = o3d.utility.Vector3dVector(static_pos + handle_pos)
with o3d.utility.VerbosityContextManager(
o3d.utility.VerbosityLevel.Debug) as cm:
mesh_prime = mesh.deform_as_rigid_as_possible(constraint_ids,
constraint_pos,
max_iter=50)

在 Debug 日志级别下,求解器会输出建矩阵与每轮迭代的进度(迭代日志重复输出 [DeformAsRigidAsPossible],此处省略):

[Open3D DEBUG] [DeformAsRigidAsPossible] setting up S'
[Open3D DEBUG] [DeformAsRigidAsPossible] done setting up S'
[Open3D DEBUG] [DeformAsRigidAsPossible] setting up system matrix L
[Open3D DEBUG] [DeformAsRigidAsPossible] done setting up system matrix L
[Open3D DEBUG] [DeformAsRigidAsPossible] setting up sparse solver
[Open3D DEBUG] [DeformAsRigidAsPossible] done setting up sparse solver
[Open3D DEBUG] [DeformAsRigidAsPossible] ...(省略重复的迭代日志)...
print('Original Mesh')
R = mesh.get_rotation_matrix_from_xyz((0, np.pi, 0))
o3d.visualization.draw_geometries([mesh.rotate(R, center=mesh.get_center())])
print('Deformed Mesh')
mesh_prime.compute_vertex_normals()
o3d.visualization.draw_geometries(
[mesh_prime.rotate(R, center=mesh_prime.get_center())])

tutorial_geometry_mesh_deformation_3_1.png tutorial_geometry_mesh_deformation_3_3.png

输出:

Original Mesh
Deformed Mesh

Open3D 还实现了 ARAP 目标的平滑版本,定义为:

∑i∑j∈N(i)wij∣∣(pi′−pj′)−Ri(pi−pj)∣∣2+αA∣∣Ri−Rj∣∣2 ,\sum_i \sum_{j \in \mathcal{N}(i)} w_{ij} || (\mathbf{p}'_i - \mathbf{p}'_j) - \mathbf{R}_i (\mathbf{p}_i - \mathbf{p}_j)||^2 + \alpha A ||\mathbf{R}_i - \mathbf{R}_j||^2\,,

该版本对相邻旋转矩阵之间的偏离进行惩罚。其中 α\alpha 是正则化项的权衡参数,AA 是表面积。

在 deform_as_rigid_as_possible 中,通过 energy 参数传入 Smoothed 即可使用该平滑目标。