feat: Implement Depth Surface Stretch Limit for improved edge culling
- Added a new "Stretch Limit" parameter to the Depth Surface node, allowing for dynamic edge culling based on the ratio of original to projected edge lengths. - Updated the geometry processing to capture original edge lengths before projection and calculate current lengths after projection. - Implemented logic to delete edges and faces that exceed the stretch limit, ensuring cleaner topology. - Enhanced tests to validate the new functionality, including scenarios for edge retention and deletion based on stretch limits. - Updated documentation and node assets to reflect the new parameter and its implications on geometry processing.
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-03
|
||||
@@ -0,0 +1,79 @@
|
||||
## Context
|
||||
|
||||
Depth Surface 当前从 Vector Depth EXR 的 RGB 采样 Camera XYZ,在 `Set Position` 中把 Cutout 基础网格投影为透视表面,再生成 Thickness 并执行 Smooth。相邻像素位于不同深度层时,投影会把原本相近的顶点拉开,形成跨越前后景的长边和长三角面。外层 `O Image Cutout` 的 Cleanup 只按基础网格的 Mesh Island 面积执行,无法识别仍连接在主体上的拉伸边。
|
||||
|
||||
旧实现曾依赖预计算的深度连续性 Field;当前 Vector Depth 已保存完整 Camera XYZ,因此可以直接在 Geometry Nodes 中比较同一条边投影前后的长度,不再增加 Server Artifact 或占用 Alpha 通道。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- 直接按 Depth Surface 实际投影结果识别过度拉伸的边,并删除其关联面。
|
||||
- 让判据随 `Depth Scale` 实时变化,并对不同对象尺寸使用相同的无量纲比例。
|
||||
- 用默认 `0` 明确关闭剔面,保证新建结果的默认拓扑不变。
|
||||
- 删除剔面后不再属于任何面的松散 Edge 与 Point。
|
||||
- 保持节点主链从左到右,接口只暴露与用户结果相关的一个控制量。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 不恢复 Depth EXR 中的旧 Depth Continuity 数据,也不修改 RGB 或 Validity Alpha 契约。
|
||||
- 不用 Blur Attribute、曲率或模型 Validity 参与拉伸判断。
|
||||
- 不为 Surface、Balloon 或 Depth Balloon 增加相同控制。
|
||||
- 不新增自动 Remesh、补洞或拓扑重建。
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 在 Point Domain 捕获投影前边长 Field
|
||||
|
||||
`O Image Depth Surface` 在修改顶点位置前使用 Edge Vertices 的两个 Position 计算边长,并用 Capture Attribute 以 `POINT` Domain 保存原始值。同一个未捕获的 Distance Field 直接连接 Divide,在完整 XYZ Set Position 后的 Delete Geometry 上下文中重新求值得到当前边长。
|
||||
|
||||
该接法与 Blender Geometry Nodes 对 Edge Vertices 距离 Field 的求值方式一致;捕获发生在 `Set Position` 之前,因此后续位置变化不会重算原始值。与在 Blender Python 中预写属性相比,节点内捕获还能让结果跟随基础几何和 Modifier 求值。
|
||||
|
||||
### 2. Stretch Limit 使用剩余比例阈值
|
||||
|
||||
拉伸判断等价于:
|
||||
|
||||
```text
|
||||
remaining ratio = original edge length / current edge length
|
||||
delete = remaining ratio < Stretch Limit
|
||||
```
|
||||
|
||||
接口名为 **Stretch Limit**,类型为 `0–1` Factor,默认值 `0`,放在 Depth Surface 的 Options Panel。原始与当前 Edge 距离 Field 都适配到 Point Domain;值越大,清理越严格。所有正常值都不小于 `0`,因此默认值 `0` 自然关闭剔面,不需要额外 Boolean 分支。
|
||||
|
||||
不采用 Blur XYZ 的残差,因为它同时响应正常曲率、开放边界和 Mesh Detail。
|
||||
|
||||
### 3. 在投影后、Thickness 前删除 Point
|
||||
|
||||
节点以 `POINT / ALL` 执行 Delete Geometry。Edge 长度 Field 会适配到 Point Domain,删除超限位置及其相邻 Edge 和 Face,使深度断层成为开放边界。剔除发生在 Thickness 之前,因此后续加厚可以围绕最终表面边界生成侧壁;Smooth 只处理已经清理的表面。
|
||||
|
||||
拉伸判据直接读取 Set Position 后的几何,所以 `Depth Scale = 0` 时边长回到基础网格,不产生拉伸剔面;调整 Depth Scale 或 Stretch Limit 会实时更新拓扑。现有外层 Mesh Island Cleanup 仍在 Shape 分支前执行,不改变其绝对面积语义。
|
||||
|
||||
### 4. 按拓扑归属清理松散元素
|
||||
|
||||
第一次删除后,使用 Edge Neighbors 的 `Face Count == 0` 选择并删除纯线 Edge。随后使用 Vertex Neighbors 的 `Face Count == 0` 删除不再属于任何 Face 的 Point。两步都位于 Stretch Culling 后、Thickness 前。
|
||||
|
||||
只清理 Face Count 为零的元素,不删除开放边界上仍属于一个 Face 的 Edge,也不运行补洞或 Remesh。
|
||||
|
||||
### 5. 通过外层菜单组暴露 Depth Surface 专属输入
|
||||
|
||||
`O Image Depth Surface` 新增 `Stretch Limit` 输入;`O Image Cutout` 增加同名输入并只连接到 Depth Surface 子组。沿用当前 Menu Switch 的未使用输入隐藏行为,使其他三个 Shape 不显示或消费该参数。创建对象时依赖节点接口默认值 `0`,不增加 Operator Property 或 Server Job 参数。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- [阈值接近 `1` 时可能受浮点误差影响] → 默认关闭,并用明显跨过阈值的合成深度断层覆盖求值测试。
|
||||
- [删除一条长边会同时删除它两侧的 Face,形成比单面判断更宽的开口] → 这是断层边界的预期行为,并以合成三角网格求值测试锁定范围。
|
||||
- [极端阈值可能把表面切成多个较大岛] → 保留用户对 Stretch Limit 和现有 Cleanup 的独立控制,不自动 Remesh 或删除有面的岛。
|
||||
- [Delete Geometry 后可能残留纯线或孤立点] → 顺序执行 Edge Face Count 与 Point Face Count 清理,并验证最终网格所有 Edge 和 Point 都属于至少一个 Face。
|
||||
- [节点资产源码与 `.blend` 不一致] → 同步构建脚本、验证脚本、资产和真实 Blender 求值测试。
|
||||
|
||||
## Migration Plan
|
||||
|
||||
1. 为接口、比例判断、关闭语义和松散拓扑增加失败测试。
|
||||
2. 更新 Cutout 节点资产构建脚本并重建、验证 `O_AnyImage.blend`。
|
||||
3. 更新用户与内部文档,运行 Cutout 定向测试和全量测试。
|
||||
|
||||
回退时整体恢复节点构建脚本和节点资产即可;默认值为 `0`,无需迁移已有对象数据或 Depth Artifact。
|
||||
|
||||
## Open Questions
|
||||
|
||||
无。
|
||||
@@ -0,0 +1,28 @@
|
||||
## Why
|
||||
|
||||
Depth Surface 将基础 Cutout 网格投影到相机空间 XYZ 后,深度断层会把相邻顶点拉成跨越前后景的长面。当前 Cleanup 只在 Shape 变形前删除小型 Mesh Island,无法识别这些仍连接在主体上的拉伸面,也无法清理剔面后留下的单线拓扑。
|
||||
|
||||
## What Changes
|
||||
|
||||
- 为 Depth Surface 增加 **Stretch Limit**,根据深度投影前后的边长比例剔除过度拉伸的 Edge 及其关联 Face。
|
||||
- **Stretch Limit** 默认值为 `0`,表示关闭拉伸剔面;正值越大,清理越严格。
|
||||
- 在 Depth Surface 投影前按 Point Domain 捕获原始边长,投影 Set Position 后直接重新计算边长,使判据跟随实际 `Depth Scale` 结果且不依赖 Depth EXR 的额外通道。
|
||||
- 以 Point Domain 删除超限位置及其相邻拓扑,再删除 `Face Count == 0` 的松散 Edge 和 Point,最后执行 Depth Surface 后续 Thickness 与 Smooth;现有 Mesh Island Cleanup 语义保持不变。
|
||||
- 更新 Cutout 节点资产、验证、Blender 求值测试及用户与内部文档。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `depth-surface-stretch-culling`: 规定 Depth Surface 的边长比例判据、关闭语义、松散拓扑清理及节点处理顺序。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。
|
||||
|
||||
## Impact
|
||||
|
||||
- 影响 `tools/node_assets/build_cutout.py` 中 `O Image Depth Surface` 与外层 `O Image Cutout` 的接口和节点主链。
|
||||
- 影响 `src/anyimage/assets/O_AnyImage.blend`、节点资产验证及 `tests/test_cutout_objects.py` 的真实 Blender 求值覆盖。
|
||||
- 需要同步 Cutout 用户说明、内部 Operator 文档与节点资产文档。
|
||||
- 不修改 Server Job、模型推理、Depth Metadata 或 Vector Depth EXR 的 RGB/Validity Alpha 协议,不新增依赖。
|
||||
@@ -0,0 +1,53 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Depth Surface exposes a stretch multiplier limit
|
||||
系统 SHALL 仅为 Depth Surface 提供 **Stretch Limit** Factor 输入,范围为 `0–1`、默认值为 `0`,并将它放入 Options Panel。系统 SHALL 以原始边长除以投影后边长得到剩余比例;`0` SHALL 关闭拉伸剔面,正值越大 SHALL 越严格地清理拉伸面。
|
||||
|
||||
#### Scenario: Default value preserves the complete surface
|
||||
- **WHEN** 用户创建 Depth Surface 且保持 Stretch Limit 为 `0`
|
||||
- **THEN** 系统 SHALL 不因边长比例删除任何 Edge 或 Face
|
||||
|
||||
#### Scenario: Other Cutout shapes do not consume the limit
|
||||
- **WHEN** 当前 Shape 为 Surface、Balloon 或 Depth Balloon
|
||||
- **THEN** Stretch Limit SHALL 不显示为该 Shape 的有效控制,也 SHALL 不改变其几何结果
|
||||
|
||||
### Requirement: Stretch is measured on matching edges around depth projection
|
||||
系统 SHALL 在 Depth Surface 的 `Set Position` 之前以 Point Domain 捕获 Edge Vertices Distance Field 的原始值,并将同一个未捕获 Field 直接用于 Set Position 后的当前距离。系统 SHALL 在 `原始距离 / 当前距离 < Stretch Limit` 时以 Point Domain 删除对应位置及其相邻 Edge 和 Face。
|
||||
|
||||
#### Scenario: Point remains within the threshold
|
||||
- **WHEN** 一个 Point 上适配后的边长比例不低于 Stretch Limit
|
||||
- **THEN** 系统 SHALL 保留该 Point 及其相邻拓扑
|
||||
|
||||
#### Scenario: Point exceeds the threshold
|
||||
- **WHEN** 一个 Point 上适配后的边长比例低于 Stretch Limit
|
||||
- **THEN** 系统 SHALL 删除该 Point 及相邻 Edge 和 Face
|
||||
|
||||
#### Scenario: Depth Scale changes the evaluated stretch
|
||||
- **WHEN** 用户调整 Depth Scale,使同一条 Edge 的投影后长度跨过当前 Stretch Limit
|
||||
- **THEN** Geometry Nodes SHALL 实时更新该 Edge 及关联 Face 的保留状态
|
||||
|
||||
#### Scenario: Lower threshold permits more stretch
|
||||
- **WHEN** 同一个 Point 的剩余比例不低于 Stretch Limit
|
||||
- **THEN** 系统 SHALL 保留该 Point 及其相邻拓扑
|
||||
|
||||
### Requirement: Stretch culling removes loose topology
|
||||
系统 SHALL 在删除拉伸 Point 后删除 `Edge Neighbors.Face Count == 0` 的 Edge,并随后删除 `Vertex Neighbors.Face Count == 0` 的 Point。该清理 SHALL 位于 Depth Surface Thickness 与 Smooth 之前,并 SHALL 保留仍属于至少一个 Face 的开放边界。
|
||||
|
||||
#### Scenario: Face deletion leaves a single line
|
||||
- **WHEN** 拉伸剔面后有 Edge 不再属于任何 Face
|
||||
- **THEN** 系统 SHALL 删除该 Edge,最终 Depth Surface 不包含纯线拓扑
|
||||
|
||||
#### Scenario: Edge deletion leaves isolated points
|
||||
- **WHEN** 松散 Edge 清理后有 Point 不再属于任何 Face
|
||||
- **THEN** 系统 SHALL 删除该 Point,最终 Depth Surface 不包含孤立点
|
||||
|
||||
#### Scenario: Culling creates an open surface boundary
|
||||
- **WHEN** 一条 Edge 在拉伸剔面后仍属于一个保留的 Face
|
||||
- **THEN** 系统 SHALL 保留该 Edge,使后续 Thickness 能围绕最终开放边界构建侧壁
|
||||
|
||||
### Requirement: Stretch culling preserves Depth Artifact semantics
|
||||
系统 SHALL 只使用 Geometry Nodes 中投影前后的几何边长完成拉伸判断,并 SHALL 保持 Vector Depth EXR 的 RGB Camera XYZ、Validity Alpha、Depth Metadata 和 Server Job 协议不变。
|
||||
|
||||
#### Scenario: Depth Surface evaluates stretch data
|
||||
- **WHEN** Depth Surface 使用生成的 Vector Depth EXR 求值
|
||||
- **THEN** 系统 SHALL 从 RGB Camera XYZ 形成投影并比较几何边长,且 SHALL NOT 要求额外 Depth Continuity 通道或贴图
|
||||
@@ -0,0 +1,24 @@
|
||||
## 1. 锁定节点接口与求值行为
|
||||
|
||||
- [x] 1.1 扩展 Cutout 节点资产结构测试,规定 `Stretch Limit` 仅连接 `O Image Depth Surface`、位于 Options、Factor 范围为 `0–1` 且默认值为 `0`,并运行对应 `tests/test_cutout_objects.py` 用例
|
||||
- [x] 1.2 增加真实 Blender 求值测试,覆盖 Stretch Limit 为 `0` 时保留完整表面、正阈值分别保留阈值内 Edge 与删除超限 Edge,并验证调整 Depth Scale 会实时改变拉伸判断
|
||||
- [x] 1.3 增加剔面后拓扑测试,验证 `Face Count == 0` 的 Edge 与 Point 被删除,而仍属于一个 Face 的开放边界保留
|
||||
|
||||
## 2. 构建 Depth Surface 拉伸剔面
|
||||
|
||||
- [x] 2.1 在 `O Image Depth Surface` 的 Set Position 前以 Point Domain 捕获 Edge Vertices Distance 原值,并在 Set Position 后以同一个 Field 直接求当前值
|
||||
- [x] 2.2 比较 `原始距离 / 当前距离 < Stretch Limit`,以 `POINT / ALL` 删除超限位置及相邻拓扑;默认 `0` 自然关闭剔面
|
||||
- [x] 2.3 在 Stretch Culling 后按 Edge Neighbors Face Count 删除纯线 Edge,再按 Vertex Neighbors Face Count 删除无 Face Point,并将清理后的几何交给 Thickness 与 Smooth
|
||||
- [x] 2.4 在外层 `O Image Cutout` 暴露并只向 Depth Surface 分支连接 `Stretch Limit`,保持其他 Shape、现有 Mesh Island Cleanup 和 Depth 数据输入不变
|
||||
|
||||
## 3. 节点资产、文档与验证
|
||||
|
||||
- [x] 3.1 更新 `tools/node_assets/validate_cutout.py`,构建并验证 `src/anyimage/assets/O_AnyImage.blend` 的接口、节点顺序、无重叠与几何求值
|
||||
- [x] 3.2 更新 Cutout 用户说明、`docs/internals/operators/cutout-tool.md` 与 `docs/internals/node-assets.md`,说明 Stretch Limit 的零值关闭、剩余比例语义和松散拓扑清理
|
||||
- [x] 3.3 运行 Cutout 定向测试、`uv run build_node` 与 `uv run pytest`,确认 Vector Depth RGB/Validity Alpha、其他三个 Shape 和现有 Cleanup 没有回归
|
||||
|
||||
## 4. 按最终节点接法修正
|
||||
|
||||
- [x] 4.1 移除 Projected Position Capture 与 Sample Index,以同一个 Edge Vertices Distance Field 分别捕获原值并在完整 XYZ Set Position 后直接求当前值
|
||||
- [x] 4.2 将拉伸 Delete Geometry 改为 Point Domain,并保留后续纯线 Edge 与孤立 Point 清理
|
||||
- [x] 4.3 同步结构与真实求值测试、节点资产和文档,运行 OpenSpec、节点构建及全量测试
|
||||
Reference in New Issue
Block a user