- Implemented surface generation functionality in `surface.py`, including geometry estimation and texture writing. - Introduced media input handling in `input.py`, supporting image and video frame extraction. - Added model management for geometry adapters in `models/__init__.py`. - Created BEN2 and DA3 model inference logic in `ben2.py` and `da3.py`, respectively. - Developed ONNX model inference utilities for BEN2 and DA3 in `onnx_ben2.py` and `onnx_da3.py`. - Added support for MoGe-2 model inference in `moge2.py` with necessary ONNX utilities. - Implemented output handling for depth and surface data in `depth.py` and `surface.py`. - Created debug output utilities for DA3 and MoGe-2 predictions in `debug_da3.py` and `debug_moge2.py`. - Enhanced ONNX runtime session management in `onnx_runtime.py`. - Updated `__init__.py` files to include new modules and functions for better organization.
17 KiB
Surface
Image Empty 转透视网格
- 在 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。 - Server 先用未经抠图的原始 RGB 请求 DA3 的
depth、conf与intrinsics,随后仅在开启Remove Background时运行 BEN2 取得前景 Alpha;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 网格输出实现。 - Blender 把勾选
Usage > Modifier的O DA3 Surface节点组直接作为 Geometry Nodes 修改器 添加到 Surface,不另设修改器名称。节点组内没有子节点组,依次完成:- 修改器顶层依次显示
Iterations、Resolution、Smooth;Iterations默认2、最小0, 默认展开的CullingPanel 依次包含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 仍是节点处理流程的一部分。
- 修改器顶层依次显示
- 节点组随插件放在
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位于默认折叠的OptionsPanel。 节点图按 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。 - Surface Job 统一按输入准备、模型推理、可选 BEN2、构建 SurfaceMesh、写出产物五个阶段更新进度; Surface Builder 是不接收 Job Context 或 progress callback 的纯数据转换。
- Server 在 job 根目录写出
mesh.npz与texture.png。NPZ 保存 camera-spacevertices、faces、uv、intrinsics、image_size与有效深度中位数reference_depth。Blender 读取这些 数据后直接创建 Surface;材质默认使用原图,开启 Remove Background 时改用.foreground,不需要 Open3D 或 OpenCV 进入 Blender Python 环境。 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。 - 网格以
reference_depth截面匹配 Image Empty 的显示矩形:该截面的投影四角与 Empty 的可见 边界重合,主点同时匹配 Empty Offset。网格对象直接沿用源 Empty 的matrix_world,因此位置、旋转、 缩放及父级关系产生的世界变换保持一致。更近的顶点沿 Empty 局部+Z方向凸出,三角面正面朝向 观察相机,启用 Backface Culling 时可从正面看到。 - 转换对话框不再提供后处理参数,只保留模型与 Resolution Scale 设置、
Create Matching Camera、Remove Background、Unshaded,以及分割线下方的Keep Original。背景移除默认关闭;开启后 Server 使用 BEN2,并在写出 Surface 前直接删除背景三角面,不再由 Geometry Nodes 的 Background 开关处理。 该预删除同时适用于 DA3 与 MoGe-2;因此 MoGe-2 转换后即使关闭Mask,BEN2 已删除的背景也不会恢复。 开启 Remove Background 时,Surface 材质的 Color Image Texture 使用打包后的.foreground,原 Empty 仍保持原图。Unshaded使用与 Depth Plane 相同的 Emission/Transparent 材质路径;普通材质的 Bump Scale 按abs(object.scale.z) × camera scale_z设置,并在材质组内乘以固定基准0.001, 与透视表面实际深度尺度一致;它允许负数以反转凹凸方向。 相机默认关闭;开启后会在 Empty 局部+Z侧创建一台朝向局部-Z的透视相机,并成为生成 Surface 的子级,使二者移动时保持投影关系。关闭Keep Original时,网格成功创建后删除源 Empty。 表面、材质与相机会归入同一个Convert to SurfaceUndo 步骤。
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。
开启 Unshaded 后,色彩贴图 Color 连接 Emission,二值 Alpha 作为 Mix Shader 系数,在
Transparent BSDF 与 Emission 之间混合后连接 Material Output;不创建 Principled BSDF、Bump 或
Float Curve。开启 Remove Background 时色彩贴图使用 BEN2 输出;关闭时使用原图。
这里的“原始距离”是 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、frame.0001.jpg(.frame)或 foreground.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、texture.png 或 normal.png
└── prediction.npz Debug 原始 API 返回
<opaque-job-id>/
├── foreground.png 单图结果
├── foreground.0001.png… 视频/序列帧结果
├── frame.0001.jpg… 视频/序列帧的源帧副本
└── depth.0001.exr… AI 深度序列帧
建议从 DA3 Base 开始。它使用 Apache 2.0 模型权重。