You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在体素网格中可微表示多面体?求一致多面体体素化可微算法

可微多面体生成与体素化实现方案

一、一致性多面体生成:凸包+可微优化

要保证同一输入点集输出完全一致的多面体,凸包是最可靠的选择——点集的凸包是唯一的,且是能包含所有点的最大体积凸多面体,天然满足“一致性+最大体积”的要求。但传统凸包算法是离散的,不可微,所以得用可微优化的方式实现:

  • 核心思路:把凸包的几何约束转化为可微损失函数,用梯度下降求解
    1. 约束项:确保所有输入点都在多面体内部/表面——计算每个输入点到多面体各面的有符号距离(面方程ax+by+cz+d=0,内部点满足ax+by+cz+d>0),如果点在外部,就产生损失
    2. 目标项:最大化多面体体积——凸多面体的体积可以用顶点叉积的求和公式计算,这个表达式完全可微
    3. 一致性保证:固定优化的初始种子(比如用输入点的哈希值作为随机种子),确保同一输入每次优化的起点相同,最终收敛到同一个多面体

二、可微体素化:基于有符号距离场(SDF)

要把多面体转成体素网格,同时支持插值和可微,别用传统的射线法(不可微),用SDF是最优解:

  1. 坐标映射:先把多面体的顶点坐标映射到体素网格的坐标系(比如把输入点归一化到体素网格的[0, W-1]×[0, H-1]×[0, D-1]范围)
  2. 可微内部/外部判断:
    • 对每个体素的中心坐标,计算到多面体的有符号距离(内部为正,外部为负)
    • 用sigmoid函数把距离值映射到0.001~0.99:value = 0.99 * sigmoid(k*d) + 0.001*(1-sigmoid(k*d)),k是控制陡峭程度的参数(值越大,体素内部/外部的分界越锐利)
    • 插值天然支持:因为体素中心是连续坐标,直接代入SDF计算即可,不需要额外做离散插值
  3. 多面体支持:如果有多个多面体,就计算每个多面体的SDF,取所有SDF的最大值(只要体素在任意一个多面体内部,就标记为内部),再代入上面的sigmoid转换

三、关键可微模块的细节

  • 可微体积计算:凸多面体体积公式1/6 * |Σ(v_i · (v_j × v_k))|,其中v_i,v_j,v_k是每个面的三个顶点(从原点出发的向量),这个公式对顶点参数完全可微
  • 可微SDF计算:凸多面体的SDF是点到所有面的有符号距离的最小值,但硬最小值不可微,所以用softmin替代:softmin(x1,...,xn) = -ε * log(Σexp(-xi/ε)),ε是平滑参数,既近似最小值,又能传递梯度

四、PyTorch伪代码实现

import torch

def face_signed_dist(points, face_planes):
    # face_planes: (num_faces, 4),每个面的参数(a,b,c,d),内部点满足ax+by+cz+d>0
    a, b, c, d = face_planes.unbind(dim=-1)
    return a*points[...,0] + b*points[...,1] + c*points[...,2] + d

def softmin(x, eps=1e-3):
    # 平滑最小值,替代硬min保证可微
    return -eps * torch.log(torch.exp(-x/eps).sum(dim=-1))

def poly_sdf(points, face_planes):
    # 计算点到多面体的有符号距离(内部为正)
    face_dists = face_signed_dist(points, face_planes)
    return softmin(face_dists, eps=1e-3)

def poly_volume(vertices, faces):
    # 计算凸多面体体积,vertices: (num_verts,3), faces: (num_faces,3)(顶点索引)
    face_verts = vertices[faces]  # (num_faces, 3, 3)
    cross_prod = torch.cross(face_verts[:,1], face_verts[:,2], dim=-1)
    dot_prod = torch.sum(face_verts[:,0] * cross_prod, dim=-1)
    return torch.abs(dot_prod.sum()) / 6.0

def generate_consistent_poly(input_points, num_faces=12):
    # 固定种子保证同一输入输出一致
    seed = hash(tuple(input_points.tolist())) % 2**32
    torch.manual_seed(seed)
    
    # 初始化面参数(从输入点中选初始面)
    init_faces = torch.randint(0, len(input_points), (num_faces, 3))
    face_planes = torch.randn(num_faces, 4, requires_grad=True)
    
    optimizer = torch.optim.Adam([face_planes], lr=1e-3)
    for _ in range(1000):
        optimizer.zero_grad()
        # 约束所有输入点在多面体内部
        point_dists = poly_sdf(input_points, face_planes)
        constraint_loss = torch.mean(torch.relu(-point_dists))  # 外部点产生损失
        # 最大化体积
        vol = poly_volume(input_points, init_faces)
        # 总损失:约束优先,再最大化体积
        loss = 100 * constraint_loss - vol
        loss.backward()
        optimizer.step()
    return face_planes

def voxelize_multiple_polys(poly_face_planes, voxel_shape=(32,32,32)):
    W, H, D = voxel_shape
    # 生成体素中心坐标网格
    x = torch.linspace(0, W-1, W)
    y = torch.linspace(0, H-1, H)
    z = torch.linspace(0, D-1, D)
    xx, yy, zz = torch.meshgrid(x, y, z, indexing='ij')
    voxel_centers = torch.stack([xx, yy, zz], dim=-1).reshape(-1, 3)
    
    # 计算所有多面体的SDF,取最大值(只要在任意多面体内部就算内部)
    sdf_list = []
    for planes in poly_face_planes:
        sdf_list.append(poly_sdf(voxel_centers, planes))
    total_sdf = torch.stack(sdf_list, dim=-1).max(dim=-1)[0]
    
    # 映射到0.001-0.99的范围
    steepness = 10.0  # 控制分界锐利程度
    sig = torch.sigmoid(steepness * total_sdf)
    voxel_vals = 0.99 * sig + 0.001 * (1 - sig)
    return voxel_vals.reshape(W, H, D)

内容的提问来源于stack exchange,提问作者Jakub Mitura

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 05:55:04