Files
AnyImage/openspec/changes/refine-cutout-symmetry-seam/design.md
nan c62ec2cd48 Refactor depth surface handling and unify depth sampling rules
- Removed the `always_vertex` parameter from `build_surface_camera` to streamline depth sampling for cutouts and ensure consistent behavior across depth plane and panorama.
- Updated depth sampling logic to ensure that all free boundary points, including the original frame of the depth plane, use neighboring face depth instead of their own texture sampling.
- Adjusted the depth mask deletion process to operate on faces rather than points, eliminating the need for additional cleanup of dangling edges.
- Ensured that the cutout rules are consistently applied across all relevant nodes, removing exceptions and simplifying the logic.
- Updated tests to verify the new behavior, ensuring that cutout points follow the depth of adjacent faces and that the original frame maintains its depth sampling.
- Refactored geometry handling in multiple depth-related nodes to improve clarity and maintainability.
2026-09-16 16:30:56 +08:00

6.3 KiB
Raw Blame History

Context

tools/nodes/groups/image_cutout_symmetry.py 当前先对齐方向、按 Offset 与 Scale 成形,再用 _delete_positive_faces 删除越过对称面的几何,最后镜像、补侧壁并合并。

两处现状造成中缝杂乱:

  • _delete_positive_faces 把 POINT 域的越界布尔求值到 FACE 域再取 > 0,等价于「任一顶点越界即删除整个面」。保留区整体退让,退让量随局部深度梯度逐处变化,切口不落在对称面上。
  • build 里的补面边界取「只有一个相邻面的边」,没有排除切口那条。直壁模式下每条切口边生成宽度等于两倍退让量的侧壁,宽度逐边不同且平面着色;_walls 的平滑只按 0.5 / 0 比例写入新增环的对称轴坐标,并用 (.5, 0, .5) 掩码只移动面内分量,因此无法消除宽度差。

同一节点曾用布尔原型验证过正确构成:C:/Users/icrdr/AppData/Local/Temp/anyimage-car-symmetry/close_projected.py 的补面选择为「边界边且位于界内一侧」,切口依靠合并自焊,验证 open_edges = 0、nonmanifold_edges = 0。本轮用面删除加边缘归零在无布尔的条件下复现该构成。

约束:不引入布尔或裁切算子;不使用 Capture Attribute;切边平滑只改面内分量,对称轴坐标保持为零。

Goals / Non-Goals

Goals: 切口严格落在对称面上并由镜像合并自焊,零厚度与非零厚度输入走同一条折叠路径;补面只覆盖外轮廓;切口环由独立参数按两圈渐变平滑;节点资产名不变。

Non-Goals: 精确裁切、布尔实现、细分加密、外轮廓侧壁的形态重设计、既有 .blend 内嵌节点的自动迁移。

Decisions

开口薄片:折叠过界部分

薄片的做法是把配平到对称面上的工作量压到最小:只删除越过对称面的顶点所对应的折叠结果,不预先删除与之相连的面。

顶点 处理
越过对称面 保留,对称轴坐标归零
界内 保留,不改位置
归零后整面落在对称面上 该面删除

跨界面的顶点被拉回平面而不是被删除,因此保留的面比整面删除多(同一 33×33 薄片样例:3356 面对 3044 面),界内顶点完全不动,形变只发生在跨界面上;切口边缘写死为 0,镜像后两侧严格重合,合并即焊成接缝。

统一折叠,并清理内部贴平边

闭合壳体(非零厚度输入)同样折叠,折叠会在壳体边缘留下「内部贴平边」:一条落在对称面上的边仍邻接两个面,镜像后这条边会变成四面共边。这类边不是有效切口,而是折叠后两层表面叠在一起形成的非法退化,因此统一清理掉贴平边邻接的面,让切口在此重新成为边界边,再与镜像焊接。这样不按输入是否闭合分支,零厚度与非零厚度共用一条路径;测试壳体上非流形边从 4–6 条降为 0。

清理用 InputMeshEdgeVertices 判断边两端都在对称面上,用 InputMeshEdgeNeighbors 判断边仍邻接两个面,再经 CornersOfFace / EdgesOfCorner / FieldAtIndex 把边的非法性聚合到面并删除。布尔裁切精度最高,但既有对照测量为 5–7 倍耗时且依赖输入流形,不采用。

实现只依赖逐点与逐边求值。这里不能用「把 POINT 域布尔求值到 FACE 域再判断」的写法:函数节点链在不同域上按该元素自身的位置求值,FACE 域得到的是面中心是否越界,而不是任一顶点越界,部分越界的面会既没被删除也进不了切口环。判断面是否整体贴平面时改为「面中心贴平面且面法线沿对称轴」,避免把斜跨对称面的面一起删掉。

补面只覆盖不落在对称面上的边界边

边界判据保持「只有一个相邻面」,再与「顶点越出对称面容差」求交,判断按顶点而非边的平均位置。落在对称面上的切口环在合并后已是共享边,再生成侧壁只会得到退化面或三面共享边。

切口环与衔接带两个平滑

Seam Smooth 与 Fill Smooth 都只移动面内分量、锁定对称轴。

Seam Smooth 作用于折叠后的切口环,减少切边的面内量化折线,按两圈渐变衰减,并按 pinned-geometry-smoothing 的 Pin Boundary 与 Pin Sharp 处理。Fill Smooth 作用于 front 的衔接带,即界外轮廓与对称面上的切缝共同围出的边界带,按四圈渐变向内松弛,随后从平滑后的轮廓构建侧壁,因此侧壁自然跟随轮廓;衔接处不是网格边界,只保留 Pin Sharp。补面的细分始终存在,不再以 Fill Smooth > 0 为开关;Fill Smooth = 0 只表示不平滑。

两个平滑都在镜像之前完成,两半保持一致,且都与 Fill Sides 无关:关闭补面时 Fill Smooth 没有轮廓可作用,Seam Smooth 照常作用。

折叠与清理属于几何正确性,与 Fill Sides 无关

Fill Sides 关闭时跳过侧壁与侧壁平滑,折叠、贴平面片清理与切边平滑照常执行,否则两半会互相穿插。

Risks / Trade-offs

  • 跨界面被拉向对称面,界内顶点不移动 → 用样例记录越界量分布,确认参考深度切得较深时形变可接受。
  • 切口环的面内折线仍是网格分辨率级近似 → 由 Seam Smooth 与上游 Boundary Smooth 共同改善,测试明确其为近似边界。
  • 壳体折叠后的非法贴平边由清理移除,缝隙在有 Fill Sides 时由侧壁补上 → 测试断言无越过对称面的顶点、无四面共边、切口自焊。
  • 参考深度把整片几何置于界外时输出为空 → 沿用现有空结果处理与测试。
  • 源码与资产不同步 → 修改后运行 uv run node-group build,源码、测试与 .blend 一并更新。

Migration Plan

直接替换删除与补面逻辑,不保留旧路径或开关;重新构建节点资产;不重写用户工程中的旧节点组。新增 Seam Smooth 的默认值为 2,与既有默认观感一致;用户工程中的旧节点实例沿用保存时的接口。回滚时恢复本次构建函数与测试并重建资产。

Open Questions

无阻塞决策。若试作后切边形变在参考深度切入较深时仍显眼,再把归零目标改为平行于对称面的一个偏移面作为后续选项。