Modly UV展开器工作原理:Box Mapping六面投影到Atlas打包的完整流程
【免费下载链接】modlyDesktop app to generate 3D models from images or prompt using local AI — runs entirely on your GPU项目地址: https://gitcode.com/GitHub_Trending/mo/modly
Modly 是一款本地 AI 3D 模型生成桌面应用,支持从图片或提示词在本地 GPU 上生成 3D 模型。其中UV 展开(UV Unwrap)是让模型"穿衣服"的关键一步:把三维网格表面展开成二维平面,得到一组 (U, V) 坐标,后续的纹理烘焙器再依据这组坐标把颜色"贴"回去。本文带你完整读懂 Modly 内置的 uv_unwrapper 展开器:从 Box Mapping 六面立方体投影,到重叠检测,再到 3×2 Atlas 图集打包的全流程。
UV 展开是什么?为什么需要 Atlas
可以把 UV 展开想象成把地球仪表面摊平到纸上:
- Box Mapping(盒式映射):不用复杂的切割缝算法,而是把模型投到包围盒的 6 个面上,像"拍立体照片"一样,每个面得到一张二维贴图切片;
- Atlas(图集):6 个切片不能重叠摆放,Modly 把它们排成3 列 × 2 行的网格,最终拼成一张 0~1 范围内的完整 UV 图。
这样纹理烘焙器(texture_baker)只需一张贴图就能覆盖整个模型。
完整流程总览:5 步流水线
整个展开逻辑集中在 Unwrapper 类的forward方法中(unwrap.py#L615-L669),依次执行:
| 步骤 | 方法 | 作用 |
|---|---|---|
| 1 | _align_mesh_with_main_axis | PCA 对齐模型主轴 |
| 2 | _box_assign_vertex_to_cube_face | 六面投影,生成每面 UV |
| 3 | _rotate_uv_slices_consistent_space | 统一 6 个切片的朝向 |
| 4 | _assign_faces_uv_to_atlas_index | BVH 检测重叠,分配图集位置 |
| 5 | _distribute_individual_uvs_in_atlas | 缩放、平移,打包进 3×2 网格 |
第 1 步:PCA 对齐模型主轴
模型生成后姿态可能是斜的,直接投影会造成六个面"各占一角"、空间浪费。_align_mesh_with_main_axis(unwrap.py#L537-L613)用**主成分分析(PCA)**找出模型的两个主方向,再叉乘出第三个轴,构建旋转矩阵把模型摆正——让它尽量贴合坐标轴,这样六面投影的利用率最高。
第 2 步:Box Mapping 六面投影(核心)
_box_assign_vertex_to_cube_face(unwrap.py#L14-L121)是整个展开器的心脏,分四小步:
- 归一化:把顶点坐标从包围盒范围映射到
[-1, 1](unwrap.py#L41-L43); - 定面:对每个三角形,取三个顶点法线的平均值作为面法线,然后与 6 个轴向
(±1,0,0)、(0,±1,0)、(0,0,±1)做点积,取最大者决定该三角形属于立方体哪一面(unwrap.py#L58-L77); - 投影:面向 +X/−X 的三角形取 (y, −z) 作为 (u, v);面向 ±Y 取 (x, −z);面向 ±Z 取 (x, ±y)——即"丢弃投影方向那个坐标,保留另外两个",这就是正交投影的盒式映射;
- 归一化到 [0,1]:按该面所有三角形的最大轴向分量统一缩放,保证六个切片纹理密度一致。
此时每个三角形都有了二维 UV,且被标记了"属于第几个面"(0~5)。
第 3 步:统一六个切片的朝向
六个面各自投影后,"上北下南"并不统一:+X 面朝上的方向和 +Z 面朝上的方向在三维空间中可能差 90°。若不一致,烘焙出来的贴图会出现"花纹方向错乱"。
_rotate_uv_slices_consistent_space(unwrap.py#L279-L354)的做法很巧妙:
- 先用
_calculate_tangents(unwrap.py#L211-L277)从位置与 UV 差分算出每个顶点的切线向量; - 再计算一个"期望切线"(绕 Y 轴的切向,即模型的自然"水平方向");
- 对每个面的平均切线求夹角,得到该切片应旋转的角度,用 2D 旋转矩阵把整个切片旋转对齐,最后重新归一化回 [0,1]。
第 4 步:BVH 重叠检测,分配 Atlas 槽位
六面投影有个天然缺陷:斜朝向的三角形会投到不相邻的面上,和别的三角形在 2D 上重叠——重叠部分贴图信息就会被覆盖丢失。
Modly 的解决方案在 C++ 扩展 unwrapper.cpp 中:
- 为六个面各自建BVH 包围盒树(bvh.h),并行检测三角形相交(unwrapper.cpp#L46-L88);
- 发现重叠后,按三角形质心沿轴向排序,靠前的三角形保留原位,靠后的被"驱逐",移入图集的备用区域。
因此图集槽位编号(atlas index)分三档:
0~5:六个主面切片(3×2 网格);6~11:第一轮重叠三角形(缩小一半塞进第二层 3×2 小网格);12+:残余随机重叠(放进底部 1/3 区域的长条里)。
第 5 步:3×2 网格打包成最终 Atlas
槽位分配好之后,_find_slice_offset_and_scale(unwrap.py#L149-L209)查表计算每片在 0~1 大图集中的偏移与缩放:
- 主切片:
offset = 1/3 × 网格坐标,div = 3,即经典3 列 2 行布局; - 重叠切片:
offset基于1/6的更小网格,div = 6,以一半分辨率挤进主切片旁边; - 更多残余重叠:挤进底部 1/3 横条(unwrap.py#L200-L207)。
随后_handle_slice_uvs与_handle_remaining_uvs(unwrap.py#L356-L475)分别把两类切片归一化填满各自的小格子并加island_padding内边距(防止相邻切片纹理采样串色),最后_distribute_individual_uvs_in_atlas做uv / div + offset一步完成平移缩放。收尾的_get_unique_face_uv(unwrap.py#L517-L535)用torch.unique去重,输出最终顶点 UV 及顶点索引映射。
在实际应用中如何生效
展开器的产出(UV 坐标 + 顶点索引)会交给纹理烘焙器把 AI 生成的外观信息烘成贴图像素。在生成界面里,"Generate Texture"开关就是这一整条 UV 展开 + 烘焙管线的入口(GenerationOptions.tsx#L445),可自由设置 64~2048 的贴图分辨率。
小结
Modly UV 展开器用**"简单投影 + 冲突重排"**的思路,换掉了传统 UV 展开中复杂的切割缝求解:
- PCA 摆正模型,提高投影利用率;
- Box Mapping 六面正交投影,代码量小、速度快、无撕裂;
- 切线对齐保证六片朝向一致;
- BVH 加速重叠检测,冲突三角形降级到备用区;
- 3×2 网格 + 内边距完成 Atlas 打包。
代价是斜向表面存在重叠损失(靠 6/12 备用区缓解),但对 AI 生成模型的纹理烘焙场景,这种以速度优先的取舍非常务实——整套流程源码见 api/uv_unwrapper/,配合 api/texture_baker/ 即可完成从模型到贴图的全自动管线。
【免费下载链接】modlyDesktop app to generate 3D models from images or prompt using local AI — runs entirely on your GPU项目地址: https://gitcode.com/GitHub_Trending/mo/modly
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考