【3D-AICG 系列-14】Trellis 2 的 Texturing Pipeline 保留单层薄壳,而 Textured GLB 会变成双层

系列文章目录


文章目录

  • 系列文章目录
  • [为什么 Texturing Pipeline 保留单层薄壳,而 to_glb 会变成双层?](#为什么 Texturing Pipeline 保留单层薄壳,而 to_glb 会变成双层?)
    • 背景
    • 一、两条路径的完整流程对比
      • [1.1 Image-to-3D Pipeline → to_glb](#1.1 Image-to-3D Pipeline → to_glb)
      • [1.2 Texturing Pipeline](#1.2 Texturing Pipeline)
    • 二、双层的根源:`remesh_narrow_band_dc`
      • [2.1 算法原理](#2.1 算法原理)
      • [2.2 为什么 UDF 必然产生双层](#2.2 为什么 UDF 必然产生双层)
      • [2.3 作者为什么接受双层](#2.3 作者为什么接受双层)
    • [三、Texturing Pipeline 为什么不需要 remesh](#三、Texturing Pipeline 为什么不需要 remesh)
      • [3.1 输入假设不同](#3.1 输入假设不同)
      • [3.2 纹理烘焙方式的差异](#3.2 纹理烘焙方式的差异)
      • [3.3 材质设置的佐证](#3.3 材质设置的佐证)
    • 四、对服装仿真的影响
    • 五、总结

为什么 Texturing Pipeline 保留单层薄壳,而 to_glb 会变成双层?

深入分析 TRELLIS2 两条纹理化路径的架构差异与工程取舍

背景

TRELLIS2 提供了两条将 3D 形状"穿上纹理"的路径:

  1. Image-to-3D Pipelinetrellis2_image_to_3d.py):从图像直接生成带纹理的 mesh,最终通过 o_voxel.postprocess.to_glb() 导出 GLB。
  2. Texturing Pipelinetrellis2_texturing.py):接受一个已有的 mesh + 图像,只生成纹理并贴回原 mesh。

对于服装等单层 open surface,两条路径的输出存在根本性差异:

路径 几何变化 输出层数 可仿真性
to_glb (remesh=True) 完全重建拓扑 双层闭合薄壳 不可仿真
Texturing Pipeline 零改变 保持原始单层 可仿真

本文将从代码层面解释这个差异的根源。


一、两条路径的完整流程对比

1.1 Image-to-3D Pipeline → to_glb

复制代码
[图像]
  │
  ▼
image_cond_model → 图像特征
  │
  ▼
sample_sparse_structure → 稀疏体素坐标 (coords)
  │
  ▼
sample_shape_slat → shape latent (SparseTensor)
  │
  ▼
sample_tex_slat → texture latent (SparseTensor)
  │
  ▼
decode_latent:
  ├── decode_shape_slat → Mesh (vertices, faces) + subs
  ├── decode_tex_slat   → PBR 体素 (attr_volume)
  ├── fill_holes()
  └── 打包成 MeshWithVoxel (几何 + 体素属性,纹理还是"散装"的)
  │
  ▼
o_voxel.postprocess.to_glb():          ← 在这里分叉
  ├── remesh=True (带纹理 GLB)
  │     remesh_narrow_band_dc → 窄带 DC 重建 → ★ 双层 ★
  │     UV 展开 → 纹理烘焙 → GLB
  │
  └── remesh=False (白模 GLB)
        simplify → cleanup → unify_face_orientations
        UV 展开 → 纹理烘焙 → GLB (保持单层)

关键:decode_latent 的输出是 MeshWithVoxel------几何体 + 体素空间里的 PBR 属性。纹理尚未"贴到面上",只是和体素坐标绑定。要变成可渲染的 GLB,必须 经过 UV 展开 + 纹理烘焙,而这正是 to_glb() 的职责。

1.2 Texturing Pipeline

复制代码
[已有 mesh (trimesh)] + [图像]
  │                        │
  ▼                        ▼
preprocess_mesh         preprocess_image
(归一化 + Y/Z 交换)     (去背景 + 裁剪)
  │                        │
  ▼                        ▼
encode_shape_slat       get_cond
(FDG 体素化 → encoder)  (图像特征)
  │                        │
  └──────┬─────────────────┘
         ▼
  sample_tex_slat → texture latent
         │
         ▼
  decode_tex_slat → PBR 体素
         │
         ▼
  postprocess_mesh:
    ├── 在 ★原始 mesh★ 上做 UV 展开 (cumesh.uv_unwrap)
    ├── nvdiffrast 在 UV 空间光栅化
    ├── grid_sample_3d 从 PBR 体素采样颜色
    ├── cv2.inpaint 修补 UV 缝隙
    └── 组装 trimesh + PBRMaterial → GLB

关键:postprocess_mesh 直接在输入 mesh 的原始顶点/面上操作,没有任何拓扑重建步骤。


二、双层的根源:remesh_narrow_band_dc

2.1 算法原理

to_glbremesh=True 时调用的核心函数是 cumesh.remeshing.remesh_narrow_band_dc,它做的事情是:

  1. 计算 UDF(无符号距离场):对输入 mesh 表面,计算空间中每个点到最近表面的距离(始终 ≥ 0)。
  2. 偏移等值面 :设 eps = band × scale / resolution,提取 UDF = eps 的等值面。
  3. 稀疏体素 + Dual Contouring:在表面附近建立窄带体素网格,用 DC 算法从体素场中提取 mesh。
  4. 投影回原面project_back=0.9,把新顶点拉回原始表面附近。

2.2 为什么 UDF 必然产生双层

这是数学上的必然:

  • UDF 不区分内外:对 open surface(如一件衣服),表面两侧的距离都是正的。

  • UDF = eps 等值面 :在原始表面的内侧和外侧eps 处各形成一层,包裹成一个闭合的薄壳。

    复制代码
           eps    原始表面    eps
            ↓       ↓        ↓
    ─────── ╔═══════╗ ═══════╗ ──────
    外侧等值面 ║  薄壳  ║ 内侧等值面
    ─────── ╚═══════╝ ═══════╝ ──────

project_back 又把两层都压回原面附近 → 两层几乎重合,间距极小(实测 median_thickness ≈ 0.0007)。

2.3 作者为什么接受双层

to_glb 的设计目标是渲染/展示,而非仿真:

  • DC remesh 后的拓扑均匀规整,UV 展开质量高。
  • 双层闭合 mesh 不管从哪个角度看都有面朝向观察者,渲染结果完整。
  • 因此材质设置为 doubleSided=False(不需要双面渲染,闭合壳自己就够了)。

三、Texturing Pipeline 为什么不需要 remesh

3.1 输入假设不同

Image-to-3D Texturing Pipeline
输入 无(从噪声生成) 已有 mesh(如 PLY/OBJ)
生成的几何 FDG decoder 输出,拓扑可能不规则 用户提供,拓扑通常已经合理
是否需要修拓扑 是(DC remesh)

Texturing Pipeline 的前提是:你已经有一个拓扑质量可接受的 mesh。它不负责修拓扑,只负责"上色"。

3.2 纹理烘焙方式的差异

虽然两条路径最终都是"从体素空间采样 PBR 属性到 UV 纹理",但实现方式截然不同:

to_glb 的方式postprocess.py):

python 复制代码
# 用 remesh 后的新 mesh 做 UV 展开
out_vertices, out_faces, out_uvs, out_vmaps = mesh.uv_unwrap(...)

# 在 UV 空间光栅化(用新 mesh 的顶点/面)
rast, _ = dr.rasterize(ctx, uv_coords, out_faces, ...)

# 通过 BVH 映射回原始高分辨率 mesh 来采样属性
_, face_id, uvw = bvh.unsigned_distance(valid_pos, return_uvw=True)

它需要 BVH 做"新 mesh → 原始 mesh"的映射,因为几何已经被 remesh 替换了。

Texturing Pipeline 的方式trellis2_texturing.py):

python 复制代码
# 直接在原始 mesh 上做 UV 展开
_cumesh.init(vertices_torch, faces_torch)
vertices_torch, faces_torch, uvs_torch, vmap = _cumesh.uv_unwrap(return_vmaps=True)

# 在 UV 空间光栅化(用原始 mesh 的顶点/面)
rast, _ = dr.rasterize(ctx, uvs_torch, faces_torch, ...)

# 直接用原始顶点位置去体素空间采样
pos = dr.interpolate(vertices_torch.unsqueeze(0), rast, faces_torch)[0][0]
attrs[mask] = grid_sample_3d(pbr_voxel.feats, pbr_voxel.coords, ...,
                              grid=((pos[mask] + 0.5) * resolution).reshape(1, -1, 3))

由于几何没变,UV 空间里光栅化出来的每个像素直接对应原始 mesh 上的 3D 位置,可以直接去体素空间采样------不需要 BVH 做中间映射,也不需要 remesh

3.3 材质设置的佐证

python 复制代码
# to_glb (postprocess.py:303)
doubleSided = True if not remesh else False
#   remesh=True  → doubleSided=False (双层闭合,不需要双面)
#   remesh=False → doubleSided=True  (单层,需要双面渲染)

# Texturing Pipeline (trellis2_texturing.py:355)
doubleSided = True  # 始终双面,因为输入可能是单层 open surface

两处代码的 doubleSided 设置直接反映了开发者对输出几何的预期。


四、对服装仿真的影响

对于 CLO3D、Style3D、Marvelous Designer 等布料仿真软件:

要求 to_glb (remesh=True) Texturing Pipeline
单层 open surface ✗ 双层闭合 ✓ 保持输入
法向一致性 ✗ 内外层法向相反 取决于输入
可定义缝合线 ✗ 无边界边 ✓ 有边界边
碰撞检测 ✗ 自碰撞爆炸 ✓ 正常
质量/弯曲 ✗ 翻倍 ✓ 正确
纹理质量 高(规整拓扑 UV) 取决于输入拓扑

结论:如果目标是生成可仿真的带纹理单层服装 mesh,应该:

  1. 用 Image-to-3D Pipeline 生成几何(remesh=False 路径保留单层)
  2. 再用 Texturing Pipeline 在单层 mesh 上贴纹理

而不是直接用 to_glb(remesh=True) 一步到位。


五、总结

复制代码
                    ┌─────────────────────────────────┐
                    │      纹理体素 (PBR Voxels)       │
                    │   在 3D 空间中存储颜色/材质属性    │
                    └──────────┬──────────────────┬────┘
                               │                  │
                    ┌──────────▼──────┐  ┌────────▼─────────┐
                    │   to_glb 路径    │  │ Texturing 路径    │
                    │                 │  │                   │
                    │  remesh (DC)    │  │  直接 UV 展开      │
                    │  → 双层闭合     │  │  → 保持原始几何    │
                    │  → 规整拓扑     │  │  → 原始拓扑        │
                    │  → BVH 映射     │  │  → 直接采样        │
                    │  → 高质量 UV    │  │  → UV 质量取决输入  │
                    │                 │  │                   │
                    │  doubleSided:   │  │  doubleSided:     │
                    │  False          │  │  True             │
                    └─────────────────┘  └───────────────────┘
                    适合:渲染/展示        适合:仿真/动画

双层不是 bug,而是窄带 UDF + Dual Contouring 对 open surface 的固有行为------这是 to_glb 为获得规整拓扑和高质量 UV 展开所付出的代价。Texturing Pipeline 通过"不碰几何、只贴纹理"的设计,完全规避了这个问题,代价是要求输入 mesh 自身的拓扑质量足够好。

两条路径不是"好与坏"的关系,而是服务于不同的下游场景: 还是

相关推荐
Solitary-walk2 小时前
前缀和思想
数据结构·c++·算法
言無咎2 小时前
垂直AI落地实践:财务机器人如何破解代账行业效率与合规难题
人工智能·rpa·财务机器人
大傻^2 小时前
智能体(Agent)深度解析:从概念到落地的全栈技术指南
人工智能·agent·智能体
智驱力人工智能2 小时前
机场鸟类活动智能监测 守护航空安全的精准工程实践 飞鸟检测 机场鸟击预防AI预警系统方案 机场停机坪鸟类干扰实时监测机场航站楼鸟击预警
人工智能·opencv·算法·安全·yolo·目标检测·边缘计算
量子物理学2 小时前
三、C#高级进阶语法——特性(Attribute)
java·算法·c#
咖啡星人k2 小时前
MonkeyCode:重新定义AI编程新时代
人工智能
才兄说2 小时前
机器人任务怎么确认?现场演示预置流程
人工智能·机器人
刘恒1234567892 小时前
Windows 电脑文件夹手动分类指南
java·windows·python·电脑·php
方璧2 小时前
【第十一篇】AI外脑能解决什么问题
人工智能