Files
AnyImage/docs/surface.md

18 KiB
Raw Blame History

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、 输入预处理实现 与技术报告。

所有任务使用统一的状态栏进度显示。变化中的阶段说明显示在右侧的进度条内部,取消按钮 位于进度条右侧。状态字段支持阶段、当前操作与预计剩余秒数,界面统一组合为紧凑文案并保证 进度单调递增。任务进程与 Server 请求在后台启动,按钮点击后立即进入进度状态。Image Depth 的阶段包括 Environment 启动、输入准备、模型加载、深度估算、缓存 写出和面片构建。进度更新会强制刷新状态栏与窗口,无需移动鼠标即可实时显示。安装中断后残留的 半成品 Environment 会在下次安装前自动清理,不会因为中断而无法再次安装。

导入过程精确保留 NPZ 内深度值之间的差值。例如深度 2.0 和 5.5 在 Displacement Strength = -1 时,沿局部 Z 轴相差 3.5 Blender Unit。它不会把数值压缩到 0..1,也不再提供视觉归一化模式。

面片尺寸以有效深度的中位数 Zref 作为参考截面。先用 mini_npz 中的相机内参计算原始 投影面积,再恢复源图纵横比:

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 目录中的典型正式产物如下:

<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 模型权重。