192 lines
18 KiB
Markdown
192 lines
18 KiB
Markdown
# Surface
|
||
|
||
## Image Empty 转透视网格
|
||
|
||
1. 在 3D View 选中仅包含一帧的 Image Empty,右键打开 `SnapForm` 子菜单并选择
|
||
`Convert to Surface`。该入口独立于
|
||
`Convert to Depth Plane`,当前不处理视频或序列帧。
|
||
模型列表包含三个 MoGe-2 Normal 模型和两个 DA3 模型,默认使用 MoGe-2 ViT-S Normal。DA3 的
|
||
Resolution Scale 与 Depth Plane 使用相同设计;MoGe-2 改为显示共享的 Resolution Level。
|
||
2. Server 先用未经抠图的原始 RGB 请求 DA3 的 `depth`、`conf` 与 `intrinsics`,随后仅在开启
|
||
`Remove Background` 时运行 BEN2 取得前景 Alpha;Alpha 经目标网格分辨率缩放与轻微模糊后,只要
|
||
大于 `0` 就把对应点视为前景;每个三角形的三个顶点必须全部为前景,任一顶点 Alpha 为 `0` 就在输出前删除该面;
|
||
关闭时不加载 BEN2。Depth 不做 smoothing,Server 反投影深度网格,建立源三角面,
|
||
计算 confidence percentile 与 3×3 相对深度不连续度,并写入
|
||
基于原图像素坐标的源 `UVMap`。这些步骤只准备修改器的输入网格,不执行 Relax、拓扑修复、平滑、
|
||
小岛清理或最终 UV 重投影。运行时显示的六个 `Preparing surface data` 步骤均指这段 Server 端源数据准备。
|
||
DA3 与 MoGe-2 最终都由 `server/geometry/surface.py` 构建同一份 Surface 数据;模型模块只提供各自的
|
||
深度、点、法线、Mask 与相机数据,不再自行拥有 Surface 网格输出实现。
|
||
3. Blender 把勾选 `Usage > Modifier` 的 `O DA3 Surface` 节点组直接作为 Geometry Nodes 修改器
|
||
添加到 Surface,不另设修改器名称。节点组内没有子节点组,依次完成:
|
||
- 修改器顶层依次显示 `Iterations`、`Resolution`、`Smooth`;`Iterations` 默认 `2`、最小 `0`,
|
||
默认展开的 `Culling` Panel 依次包含 `Depth Continuity` 与 `Confidence`。前者直接读取面域 3×3
|
||
相对深度差,并使用指数映射:
|
||
默认 `50%` 对应官方 `rtol=0.04`,数值越大,内部阈值越小,剔除的断层面越多;
|
||
- 每轮 Remesh Iteration 依次执行 Connected Merge、循环拓扑修复与一次 Smooth,使新拓扑在进入下一轮
|
||
Merge 前先稳定;拓扑修复删除非流形面、单面拓扑和小面积 mesh islands;
|
||
- 默认 `Resolution` 为原始有效三角边长中位数乘以 `sqrt(10) * 2`。`sqrt(10)` 来自原先 90% 面数缩减
|
||
的二维边距估计,再乘以 2 为迭代合并留出更平滑、稳定的默认尺度;节点内部 Connected Merge 的
|
||
Distance 为 `Resolution * 0.5`;
|
||
- Smooth 采用 `O Smooth` 的边界检测和模糊法线权重,固定开放边界与薄面,只保留一个
|
||
`Smooth` 程度系数;节点组不再额外执行 Flip Faces;
|
||
- 从修改前的原始表面以 nearest-surface 重新采样 `UVMap`,最终明确写为 Corner 域 `2D Vector`。
|
||
MoGe-2 使用独立的 `O MoGe2 Surface`。Server 由内置 NumPy 网格构建函数
|
||
生成有限深度范围内的紧凑三角网格,并保留模型 mask、逐点 depth 及相邻点相对深度差。Geometry Nodes
|
||
按官方 `mask & ~depth_map_edge(depth, rtol=0.04)` 的含义,先由 `Mask` 删除 mask 外的面,再由
|
||
`Depth Continuity` 按同一份 3×3 邻域相对深度差删除断层面。DA3 与 MoGe-2 使用相同的属性、
|
||
名称、指数滑块和 FACE 删除逻辑,不再额外删除孤立点。之后才执行拓扑修复、Relax、孤岛清理与 UV 重投影。
|
||
节点组不提供 Confidence 或 Remesh 开关;Remesh 仍是节点处理流程的一部分。
|
||
4. 节点组随插件放在 `O_SnapForm.blend`。Blender 4.5 首次 Append 后复用同一 datablock;
|
||
Blender 5.0 以 linked ID 载入后调用 Pack,后续 Surface 继续复用。插件不会为每个 Surface 新建节点组,
|
||
也不会在添加后重命名 Geometry Nodes 修改器。节点组只启用 `Usage > Modifier`,不 Mark as Asset;
|
||
修改器关闭 `show_group_selector`。`Fix Topology` 位于默认折叠的 `Options` Panel。
|
||
节点图按 Culling、Remesh、Topology Fix、Island Cleanup 与 UV 分区摆放;各区就近使用独立的
|
||
Group Input,并隐藏没有连线的接口,避免跨越整个节点图的输入连线。
|
||
`O DA3 Surface` 与 `O MoGe2 Surface` 的 `Iterations` 默认值均为 `2`、最小值为 `0`,
|
||
`Resolution` 默认值均为 `0.1`,所有输入 socket 的 Structure Type 均固定为 `Single`。
|
||
5. Surface Job 统一按输入准备、模型推理、可选 BEN2、构建 SurfaceMesh、写出产物五个阶段更新进度;单帧推理只显示模型与阶段名称,不追加 `1/1`,仅多帧时显示当前帧与总数;
|
||
Surface Builder 是不接收 Job Context 或 progress callback 的纯数据转换。
|
||
6. Server 在 job 根目录写出 `mesh.npz`。NPZ 保存 camera-space `vertices`、`faces`、`uv`、
|
||
`intrinsics`、`image_size` 与有效深度中位数 `reference_depth`。颜色层无论来自原图复制还是 BEN2,
|
||
Server 都统一写为 `color.0001.*`。Blender 读取网格数据后额外导入为 `.color`,材质不复用源
|
||
Empty 的 Image。颜色贴图会按当前场景的 View Transform 选择对应输入色彩空间:AgX、ACES 1.3、ACES 2.0、Filmic、Khronos PBR Neutral 分别使用 `AgX Base sRGB`、`ACES 1.3 sRGB`、`ACES 2.0 sRGB`、`Filmic sRGB`、`Khronos PBR Neutral sRGB`;无法匹配当前 View Transform 时回退 `sRGB`。 Server 不生成 `texture.png`,材质只在
|
||
Blender 端创建。
|
||
DA3 Surface 只保留 Geometry Nodes 后处理实际使用的 attributes:点域 `o_depth`、面域
|
||
`o_confidence` 与面域 `o_depth_discontinuity`。Remove Background 已在 Server
|
||
建面前直接删除背景三角面,不写入任何 background attribute。MoGe-2 的导入网格保留点域 `o_depth`,
|
||
以及面域 `o_mask`、`o_depth_discontinuity`,不伪造 confidence、stretch 或 background 属性,
|
||
也不写入 custom normal。MoGe-2 推理返回的 normal 直接编码为独立的 `normal.png`,Blender 将它
|
||
作为 Non-Color 贴图导入现有材质;normal 不进入 `mesh.npz`。
|
||
所有 MoGe-2 Image datablock 都以源图片名而非对象名为基名:depth、normal 与 mask 分别使用
|
||
`图片名.depth`、`图片名.normal`、`图片名.mask`,不附加 `.moge2`。
|
||
7. 网格以 `reference_depth` 截面匹配 Image Empty 的显示矩形:该截面的投影四角与 Empty 的可见
|
||
边界重合,主点同时匹配 Empty Offset。网格对象直接沿用源 Empty 的 `matrix_world`,因此位置、旋转、
|
||
缩放及父级关系产生的世界变换保持一致。更近的顶点沿 Empty 局部 `+Z` 方向凸出,三角面正面朝向
|
||
观察相机,启用 Backface Culling 时可从正面看到。
|
||
8. 转换对话框不再提供后处理参数,只保留模型与 Resolution Scale 设置、`Create Matching Camera`、
|
||
`Remove Background`、`Shadeless`,以及分割线下方的 `Keep Original`。背景移除默认关闭;开启后 Server
|
||
使用 BEN2,并在写出 Surface 前直接删除背景三角面,不再由 Geometry Nodes 的 Background 开关处理。
|
||
该预删除同时适用于 DA3 与 MoGe-2;因此 MoGe-2 转换后即使关闭 `Mask`,BEN2 已删除的背景也不会恢复。
|
||
Surface 材质的 Color Image Texture 始终使用 `.color`;Remove Background 只决定该层是否带 BEN2 Alpha,原 Empty 始终保持原图。颜色贴图会按当前场景的 View Transform 选择对应输入色彩空间:AgX、ACES 1.3、ACES 2.0、Filmic、Khronos PBR Neutral 分别使用 `AgX Base sRGB`、`ACES 1.3 sRGB`、`ACES 2.0 sRGB`、`Filmic sRGB`、`Khronos PBR Neutral sRGB`;无法匹配当前 View Transform 时回退 `sRGB`。
|
||
`Shadeless` 使用与 Depth Plane 相同的 `O Shadeless` 节点组,并继续通过 Alpha 与 Transparent BSDF 混合;普通材质的
|
||
Bump Scale 按 `abs(object.scale.z) × camera scale_z` 设置,并在材质组内乘以固定基准 `0.001`,
|
||
与透视表面实际深度尺度一致;它允许负数以反转凹凸方向。
|
||
相机默认关闭;开启后会在 Empty 局部 `+Z` 侧创建一台朝向局部 `-Z` 的透视相机,并成为生成 Surface
|
||
的子级,使二者移动时保持投影关系。关闭 `Keep Original` 时,网格成功创建后删除源 Empty。
|
||
表面、材质与相机会归入同一个 `Convert to Surface` Undo 步骤。
|
||
|
||
Process Res 的设计依据 DA3 官方实现:API 与 CLI 默认使用 `process_res=504` 和
|
||
`process_res_method="upper_bound_resize"`;输入预处理的 `PATCH_SIZE=14`,会把宽高分别调整到最接近的
|
||
`14` 倍数。DA3 论文以 `504×504` 为 base resolution,公开的训练尺寸长边为 `504`、`672`、`756` 或
|
||
`896`。官方 CLI 虽给出 `1024` 示例,但没有声明更高分辨率会提升质量,也没有规定 `1000` 为硬上限。
|
||
因此转换入口使用相对原图的 Resolution Scale,同时通过上下限避免 Process Res 过低或过高,并把训练范围
|
||
之外的值明确视为实验设置。参考
|
||
[DA3 API](https://github.com/ByteDance-Seed/Depth-Anything-3/blob/main/docs/API.md)、
|
||
[输入预处理实现](https://github.com/ByteDance-Seed/Depth-Anything-3/blob/main/src/depth_anything_3/utils/io/input_processor.py)
|
||
与[技术报告](https://depth-anything-3.github.io/assets/da3_tech_report_2025.pdf)。
|
||
|
||
所有任务使用统一的状态栏进度显示。变化中的阶段说明显示在右侧的进度条内部,取消按钮
|
||
位于进度条右侧。状态字段支持阶段、当前操作与预计剩余秒数,界面统一组合为紧凑文案并保证
|
||
进度单调递增。任务进程与 Server 请求在后台启动,按钮点击后立即进入进度状态。Image Depth 的阶段包括 Environment 启动、输入准备、模型加载、深度估算、缓存
|
||
写出和面片构建。进度更新会强制刷新状态栏与窗口,无需移动鼠标即可实时显示。安装中断后残留的
|
||
半成品 Environment 会在下次安装前自动清理,不会因为中断而无法再次安装。
|
||
|
||
导入过程精确保留 NPZ 内深度值之间的差值。例如深度 `2.0` 和 `5.5` 在
|
||
`Displacement Strength = -1` 时,沿局部 Z 轴相差 `3.5 Blender Unit`。它不会把数值压缩到
|
||
`0..1`,也不再提供视觉归一化模式。
|
||
|
||
面片尺寸以有效深度的中位数 `Zref` 作为参考截面。先用 `mini_npz` 中的相机内参计算原始
|
||
投影面积,再恢复源图纵横比:
|
||
|
||
```text
|
||
RawWidth = depth_width × Zref × abs(Displacement Strength) / fx
|
||
RawHeight = depth_height × Zref × abs(Displacement Strength) / fy
|
||
Area = RawWidth × RawHeight
|
||
Aspect = source_image_width / source_image_height
|
||
Width = sqrt(Area × Aspect)
|
||
Height = sqrt(Area / Aspect)
|
||
```
|
||
|
||
这样即使 DA3 估计出的 `fx/fy` 不一致,也不会把正方形原图拉成长方形。主点 `cx/cy` 仍用于
|
||
确定原始相机面片,随后对象 XY Transform 将它匹配到源 Image Empty。生成对象会记录
|
||
`snapform_reference_depth`、`snapform_camera_plane_width` 和
|
||
`snapform_camera_plane_height`,便于检查实际采用的
|
||
参考深度和尺寸。
|
||
|
||
材质始终导入深度 Image Texture,并连接到 Displacement 节点和 Material Output;材质 Surface
|
||
Displacement 设置为 `Displacement Only`,DA3 与 Original 都使用 `Midlevel = 0`、`Scale = 1`。MoGe-2 的位移只在专用 Geometry Nodes 中处理。
|
||
`o_material_displacement` 为 `False` 时,材质把 Height 乘以零,置换只在几何节点中生效;为 `True` 时,
|
||
几何节点关闭结构置换,材质 Height 恢复。色彩贴图的 Alpha 二值结果同时用于选择原始深度或有符号的
|
||
`Background Depth`,使两种置换路径保持一致;未移除背景的普通不透明图片因此全部视为前景。
|
||
视频与序列帧的颜色、深度与前景 Image Texture
|
||
通过 image_user 的 Frames / Offset / Start 把第 i 帧映射到场景 `Start+i` 帧,并启用 Auto Refresh。
|
||
颜色贴图到 Principled BSDF 之间使用独立的 Shader Node Group。DA3 Surface 使用 `O DA3 Surface Layer`,
|
||
MoGe-2 Surface 使用独立的 `O MoGe2 Surface Layer`;它额外接收模型输出的 `Normal` 贴图,使用
|
||
`Normal Map` 节点按 `Object Space` 解码后连接到 Bump 的 `Normal` 输入,使像素级模型法线与原图
|
||
Bump 合并,不依赖重拓扑后的顶点密度;`Normal Strength` 默认值为 `1.0`,只调节模型 normal map,
|
||
颜色贴图 Bump 仍由独立的 `Bump Scale` 控制;
|
||
Depth Plane 使用 `O DA3 Depth Plane Layer` 材质组,并把背景剔除、背景深度与材质置换 Attribute
|
||
读取全部收进组内。两者都接收 `Color`、`Alpha` 与 `Bump Scale`,只有 `O DA3 Depth Plane Layer`
|
||
额外接收 Float 类型的 `Depth`。
|
||
`O DA3 Surface Layer` 只输出 `Color`、`Roughness`、`Alpha` 与 `Normal`,以原图 Color 保留 Bump 效果,
|
||
不创建 Displacement 节点或输出;`O DA3 Depth Plane Layer` 另外输出 `Displacement`。外层材质只保留贴图、
|
||
材质组、Principled BSDF 与 Material Output,方便继续调整。
|
||
Depth Plane 的 Bump Height 直接使用材质组的 `Color` 输入,只受独立的 `Bump Scale` 影响,不使用深度贴图,
|
||
也不经过 `Displacement` 模式开关、`Depth Scale` 或 `Background Depth`;材质 Displacement 仍使用这些参数处理后的深度。
|
||
无论选择哪种置换方式,Layer 的 `Color` 都会连接 Principled BSDF 的 `Base Color`、
|
||
`Subsurface Radius` 与 `Emission Color`;Layer 的 `Normal` 同时连接 `Normal` 与 `Coat Normal`,
|
||
通过 `Bump.Height → Bump.Normal` 提供表面细节。Subsurface、Emission 与 Coat 的强度等其他输入
|
||
保持 Principled BSDF 默认值。Bump 的基础 Distance 为
|
||
`0.001`,会乘以面片 Transform 的统一缩放倍率。原图 Color 还会经过 `Float Curve` 连接到
|
||
Principled BSDF 的 `Roughness`,曲线两端输出分别为 `0.5` 和 `0.1`;材质 IOR 保持
|
||
Principled BSDF 默认值 `1.5`。
|
||
开启 `Shadeless` 后,色彩贴图 Color 接入 `O Shadeless`。该组沿用 Blender Images as Planes 的
|
||
Diffuse、Emission、Light Path 与射线判断连接;二值 Alpha 仍作为 Mix Shader 系数,在 Transparent BSDF
|
||
与 `O Shadeless` 之间混合后连接 Material Output。外层不再创建 Emission、Principled BSDF、Bump 或
|
||
Float Curve。开启 `Remove Background` 时色彩贴图使用 `.color`;关闭时使用 `.color`。
|
||
|
||
这里的“原始距离”是 DA3 模型输出的尺度。普通 Small、Base、Large 和 Giant 模型的深度不应
|
||
被解释为现实世界的米;需要现实尺度时应选择带 metric depth 的
|
||
`DA3 Nested Giant Large 1.1`。此外,当前结果仍是固定 XY 平面上的深度位移面,不是使用
|
||
相机内参逐像素反投影得到的完整 camera-space 网格;中位深度截面为 Geometry Nodes
|
||
深度面片提供正确的整体高宽尺度。
|
||
|
||
生成完成后会直接创建面片,不提供缓存重建入口或单独的导入参数。重新选择
|
||
`Convert to Depth Plane` 会按当前 Depth from 再次转换。
|
||
面片、深度 Image、颜色 Image、Material 与 Geometry Nodes 修改器会在同一个 Blender Undo 步骤中创建;转换完成后执行
|
||
`Ctrl+Z` 会完整撤销本次转换,不会越过转换去回退更早的 Image Empty 操作。
|
||
|
||
Image Empty 使用 Packed Image 时,SnapForm 会通过 Blender 写出临时 PNG 交给 DA3,
|
||
不会 Unpack 或修改源 Image;任务结束后自动删除临时文件。普通图片仍直接读取源文件。DA3 的原始
|
||
`results.npz` 保留在对应 job 文件夹根下,生成的浮点深度 Image 会 Pack 到
|
||
当前 `.blend`。Depth Image 与 Displace Texture 根据源 Image datablock 命名。例如 Empty Object
|
||
名为 `obj_img`、引用的 Image 名为 `img.png` 时,Depth Image 与 Texture 都命名为 `img.depth`;
|
||
生成的 Object、Mesh 与 Material 仍命名为 `obj_img`。Pack 使用 OpenEXR,因而不会把大于 `1.0`
|
||
的原始深度截断。已有同名 datablock 时由 Blender 自动追加 `.001` 等编号。
|
||
视频输入时 Blender 无法整体 Pack 序列帧,因此 `.depth` 与颜色序列改为引用 job 目录根下的
|
||
`depth.0001.exr`、`color.0001.jpg`(`.color`)或 `color.0001.png`,并在每帧保留原始
|
||
浮点深度;保存 `.blend` 后再次打开仍需对应 job 目录存在。
|
||
|
||
每个 Server Job 被接受时,Job Runtime 在 Runtime Storage Root 的 `jobs/` 下创建一个独立文件夹。文件夹名是 32 位 opaque Job ID,不包含任务类型或对象名称。执行逻辑从 `JobContext.directory` 获取路径,输出产物直接写入该目录。
|
||
|
||
Blender 为 Packed Image 或序列帧生成的输入副本先保存在系统临时目录,Server 读取完成后由 Blender 清理,不归档到 Job 目录。普通磁盘文件和视频仍直接读取原文件。Server 日志统一写入 `server.log`,进度只保存在 Server 内存中。Environment Bootstrap 使用 Storage Root 下的 `install.log` 与临时的 `install-status.json`,不创建 Job 目录。
|
||
|
||
正式几何输出由 `generate-depth` 与 `generate-surface` 负责。Debug 使用彼此独立的
|
||
`debug-da3`、`debug-moge2` 与 `debug-ben2`,只保存模型 API 的直接返回,不进入 Geometry 或
|
||
Surface 输出流程。
|
||
|
||
Job 目录中的典型正式产物如下:
|
||
|
||
```text
|
||
<opaque-job-id>/
|
||
├── results.npz、mesh.npz 或 normal.png
|
||
└── prediction.npz Debug 原始 API 返回
|
||
|
||
<opaque-job-id>/
|
||
├── color.0001.jpg/png… 正式工作流颜色层(原图复制或 BEN2 Alpha)
|
||
├── depth.0001.exr… AI 深度序列帧
|
||
└── normal.png MoGe-2 Surface 法线贴图
|
||
```
|
||
|
||
建议从 `DA3 Base` 开始。它使用 Apache 2.0 模型权重。
|