refactor: incorporate Depth Scale into Depth Split criteria for consistent geometry handling
This commit is contained in:
@@ -54,7 +54,7 @@ flowchart TD
|
||||
|
||||
### 每个节点组承担自己的公共处理
|
||||
|
||||
三个资产复用同一 Cleanup 构建函数,在塑形前按原平面岛总面积清理;UI 值继续乘 0.01 转为面积。Smooth 与轴向输出在各自塑形后执行。仅深度 Cutout 提供 Split;比较式继续为 `abs(原始相邻面 Z 差) × Uniform Scale × Split > 原始面中心距离 × 2.5`,Depth Scale 只参与后续显示位移。
|
||||
三个资产复用同一 Cleanup 构建函数,在塑形前按原平面岛总面积清理;UI 值继续乘 0.01 转为面积。Smooth 与轴向输出在各自塑形后执行。仅深度 Cutout 提供 Split;比较式为 `相邻面采样距离差 × Reference Depth × Depth Scale × Split > 面中心距离 × 平均射线距离 × 2.5`,Depth Scale 既缩放显示位移也缩放切边判据。
|
||||
|
||||
### 法线策略在 Operator 层统一解析
|
||||
|
||||
|
||||
@@ -22,12 +22,12 @@ O Image Depth Cutout 的 Balloon 模式 SHALL 以投影正面减 o_balloon 估
|
||||
- **WHEN** 选择 Uniform 并设为正厚度
|
||||
- **THEN** 正背面保持原 Depth Surface 的沿内向轴等厚关系
|
||||
|
||||
### Requirement: Split 独立于显示深度幅度
|
||||
Split SHALL 使用原始相邻面采样 Z 差经 Uniform Scale 标定后与变形前跨度比较,Cleanup MUST 位于 Split 前;Depth Scale SHALL 仅控制投影呈现幅度。
|
||||
### Requirement: Split 使用投影后的深度落差
|
||||
Split SHALL 以原始相邻面采样距离差乘 Depth Scale 与变形前跨度比较,Cleanup MUST 位于 Split 前;Depth Scale SHALL 决定切边的可见落差,因此也决定切边是否存在。
|
||||
|
||||
#### Scenario: Depth Scale 不改变切边
|
||||
- **WHEN** 固定输入和 Split,将 Depth Scale 依次设为 0、0.25、1、2、4
|
||||
- **THEN** 零厚度输出的切边拓扑保持一致,深度位移随 Depth Scale 变化
|
||||
#### Scenario: Depth Scale 缩放切边
|
||||
- **WHEN** 固定输入和 Split,将 Depth Scale 从 0 增大到 1
|
||||
- **THEN** 零厚度输出在 Depth Scale 为 0 时没有切边,切边随 Depth Scale 增大出现,深度位移随之变化
|
||||
|
||||
#### Scenario: 保留分离产生的小片
|
||||
- **WHEN** 原平面岛面积通过 Cleanup,但 Split 后的部分小片低于该面积阈值
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-09-16
|
||||
42
openspec/changes/scale-depth-split-by-depth-scale/design.md
Normal file
42
openspec/changes/scale-depth-split-by-depth-scale/design.md
Normal file
@@ -0,0 +1,42 @@
|
||||
## Context
|
||||
|
||||
Depth Split 的判据在 `split_depth_surface`(`tools/nodes/common/depth_surface.py`),在边域求值,被 `O Image Depth Plane`、`O Image Depth Cutout`、`O Image Depth Panorama` 共用。当前式子是:
|
||||
|
||||
```
|
||||
|Δ射线距离| × Reference × Strength > 面中心距离 × 平均射线距离 × 2.5
|
||||
```
|
||||
|
||||
四个量各有分工:Δ 是共享边两个面各自的 `|相机 XYZ|` 之差,`Reference` 把无量纲的相对跳变换算回局部长度尺度,`面中心距离` 用采样密度归一化(Subdivide 变化不改变选择),`平均射线距离` 把绝对差归一化成相对跳变(深度图整体缩放不改变选择)。Depth Scale 不在其中。
|
||||
|
||||
投影后的深度落差是 `Δ × Depth Scale`:`Y = (Reference − 相机 Z × Uniform Scale) × Depth Scale`。所以判据里缺的正是这个因子。
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Depth Scale 为 0 时表面完全平整且不被切开、不删除条带面,输出与关闭 Split 相同。
|
||||
- 判据表达投影后的几何落差,切边随 Depth Scale 单调出现。
|
||||
- 默认 Depth Scale 为 1 时结果与当前实现完全一致。
|
||||
- 三个共用判据的资产行为一致。
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- 不改变相对跳变的归一化方式:平均射线距离、面中心距离、Reference 的作用保持不变。
|
||||
- 不新增 Split 参数、不引入自动阈值或滞回。
|
||||
- 不改变切边后顶点沿射线重新定位、条带面清理、边界平滑的既有规则。
|
||||
- 不改动 `O Image Relief Plane` 与全景的球面半径插值。
|
||||
|
||||
## Decisions
|
||||
|
||||
**只把 Depth Scale 乘进落差项,不动归一化因子。** 分子就是投影后 Y 方向的真实落差,分母承担的是采样密度与深度数据整体尺度的归一化,跟显示幅度无关;分母也跟着乘会让阈值失去可解释的单位。备选是把 Depth Scale 当开关(只有 0 才禁用 Split),那样阈值不连续,中间幅度没有意义;另一个备选是在投影后的几何上重新求值判据,需要重建面中心距离与射线距离,代价大而收益只是一个常数因子的精度。
|
||||
|
||||
**Depth Scale 作为 `split_depth_surface` 的显式参数,由三个调用方各自传入。** 三个资产共用同一判据,参数化后行为一致,不引入全局读取或隐式约定。全景的 Depth Scale 本来就参与「球半径到预测距离」的插值,乘进判据与它的显示语义一致。
|
||||
|
||||
**不加平滑或滞回。** Split 是硬阈值,滞回会让同一网格的拓扑依赖求值历史,与现有单次求值模型不符。低 Depth Scale 下拖参数导致的跳变按既定语义接受。
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- 拖动 Depth Scale 时会出现「突然全部切开」的跳变 → 接受。Split 本来就是阈值判断,Depth Scale 为 0 的纯净行为优先。
|
||||
- Depth Scale 开始影响拓扑:顶点数、边界平滑结果、壳壁都随之变化 → 接受,并在 Depth Split 的参数说明中写清它随 Depth Scale 缩放。
|
||||
- 已保存场景若 Depth Scale 小于 1,切边会比以前少 → 这是本次改动的目的;默认值 1 的场景不受影响。
|
||||
- 与 `reorganize-cutout-shapes` 中「Split 独立于显示深度幅度」的条目冲突 → 在同一变更里修订该条目。
|
||||
@@ -0,0 +1,29 @@
|
||||
## Why
|
||||
|
||||
Depth Split 现在的判据只由深度采样差和局部采样密度决定,Depth Scale 完全不参与。于是 Depth Scale 为 0(表面被压成完全平的一层)时仍然切开并删除条带面,平面上留下没有几何依据的缝隙。用户把 Depth Scale 理解为显示幅度,0 应当不产生任何切割。
|
||||
|
||||
## What Changes
|
||||
|
||||
- Depth Split 的比较式乘上 Depth Scale,判据从「源深度数据的断层」改为「投影后表面的几何落差」。
|
||||
- Depth Scale 为 0 时不切开、不删除条带面,也不改变拓扑;Depth Scale 增大时切边逐步出现。
|
||||
- 三个使用该判据的资产行为一致:`O Image Depth Plane`、`O Image Depth Cutout`、`O Image Depth Panorama`。
|
||||
- 默认 Depth Scale 为 1,默认行为与现在完全相同。**BREAKING**:Depth Scale 由纯显示参数变为影响拓扑的参数,保存了 Depth Scale 小于 1 的场景会看到切边减少、顶点数与壳壁结果随之改变。
|
||||
- 同步修订 `reorganize-cutout-shapes` 中「Split 独立于显示深度幅度」的过时条目。
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `depth-split-selection`: Depth Split 依据什么判定相邻面之间的断层,以及 Depth Scale 在其中的作用。
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
无。`openspec/specs` 尚无可修改的已归档规格。
|
||||
|
||||
## Impact
|
||||
|
||||
- `tools/nodes/common/depth_surface.py`:`split_depth_surface` 增加 Depth Scale 输入,比较式乘该因子。
|
||||
- `tools/nodes/groups/image_depth_plane.py`、`image_depth_cutout.py`、`image_depth_panorama.py`:传入各自的 Depth Scale。
|
||||
- `tests/tools/nodes/test_depth_surface_split.py`:反转「Depth Scale 不改变切边」的断言,补充 Depth Scale 0 不切开的覆盖。
|
||||
- `src/anyimage/assets/O_AnyImage.blend`:重新构建节点资产。
|
||||
- 不改动 `O Image Relief Plane`、Cutout 创建入口、材质与依赖。
|
||||
@@ -0,0 +1,23 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Depth Split 使用投影后的几何落差
|
||||
Depth Split 的判定 SHALL 以原始相邻面采样距离差乘 Depth Scale 作为落差量,与变形前的采样跨度比较;平均射线距离 SHALL 继续作为把绝对差归一化为相对跳变的因子,Depth Scale MUST NOT 参与该归一化。参数说明 MUST 说明切边随 Depth Scale 缩放。
|
||||
|
||||
#### Scenario: Depth Scale 为零
|
||||
- **WHEN** 固定深度图与 Depth Split,把 Depth Scale 设为 0
|
||||
- **THEN** 输出拓扑与 Split 为 0 时相同,没有切边,也没有条带面被删除
|
||||
|
||||
#### Scenario: Depth Scale 放大落差
|
||||
- **WHEN** 同一输入在 Depth Scale 从 0 增大到 1 之间求值
|
||||
- **THEN** 选中的切边只增不减,Depth Scale 为 1 时与当前判据结果一致
|
||||
|
||||
#### Scenario: 深度数据整体缩放
|
||||
- **WHEN** 同一深度图的采样数值整体乘以任意正数
|
||||
- **THEN** 选中切边完全相同
|
||||
|
||||
### Requirement: 三个深度资产共用同一判据
|
||||
`O Image Depth Plane`、`O Image Depth Cutout`、`O Image Depth Panorama` SHALL 使用同一个 Depth Split 判据并各自传入自己的 Depth Scale。
|
||||
|
||||
#### Scenario: 资产一致性
|
||||
- **WHEN** 三个资产分别求值,Depth Scale 设为零或设为 1
|
||||
- **THEN** 三者都表现为零不切开、1 与改动前一致
|
||||
16
openspec/changes/scale-depth-split-by-depth-scale/tasks.md
Normal file
16
openspec/changes/scale-depth-split-by-depth-scale/tasks.md
Normal file
@@ -0,0 +1,16 @@
|
||||
## 1. Split 判据
|
||||
|
||||
- [x] 1.1 `split_depth_surface` 增加 Depth Scale 参数,落差项乘该因子,并按新含义更新 docstring。
|
||||
- [x] 1.2 `O Image Depth Plane`、`O Image Depth Cutout`、`O Image Depth Panorama` 传入各自的 Depth Scale,并把 Depth Split 的参数说明改为随 Depth Scale 缩放。
|
||||
|
||||
## 2. 测试
|
||||
|
||||
- [x] 2.1 反转 `test_split_topology_uses_depth_before_display_scaling`:Depth Scale 为 0 时不切开且拓扑与关闭 Split 相同,为 1 时与现有结果一致。
|
||||
- [x] 2.2 更新 `test_split_mapping_and_uniform_scale` 中 Depth Scale 为 0 仍保持切边的断言。
|
||||
- [x] 2.3 为 `O Image Depth Cutout` 与 `O Image Depth Panorama` 补充 Depth Scale 为 0 不切开的覆盖。
|
||||
|
||||
## 3. 资产与规范
|
||||
|
||||
- [x] 3.1 运行 `uv run node-group build` 重建并验证 `O_AnyImage.blend`。
|
||||
- [x] 3.2 修订 `reorganize-cutout-shapes` 中 `depth-cutout-volume` 的 Split 条目,使其与投影后落差判据一致。
|
||||
- [x] 3.3 运行相关节点测试并确认通过。
|
||||
Binary file not shown.
@@ -85,7 +85,9 @@ def diagonal_surface(triangles=False):
|
||||
image.pixels.foreach_set(pixels.ravel())
|
||||
image.update()
|
||||
set_value("Depth Split", 0.5)
|
||||
set_value("Depth Scale", 0)
|
||||
# A shallow display keeps the cut nearly flat so smoothing cannot fold the
|
||||
# sawtooth slivers this fixture exists to exercise.
|
||||
set_value("Depth Scale", 0.05)
|
||||
return obj, set_value
|
||||
|
||||
|
||||
@@ -201,7 +203,6 @@ def test_smoothing_reduces_stairs_without_leaving_two_ring_band(triangles):
|
||||
before.append(np.linalg.norm(original[adjacent].mean(axis=0) - original[i]))
|
||||
after.append(np.linalg.norm(smoothed[adjacent].mean(axis=0) - smoothed[i]))
|
||||
assert sum(after) < 0.8 * sum(before)
|
||||
np.testing.assert_array_equal(smoothed[:, 1], original[:, 1])
|
||||
set_value("Boundary Smooth", 0)
|
||||
np.testing.assert_array_equal(evaluated(obj)[0], original)
|
||||
|
||||
@@ -393,4 +394,3 @@ def test_junction_band_keeps_the_sharpness_weight():
|
||||
creased_step = abs(creased[4][1] + 0.5)
|
||||
assert flat_step > 1e-3
|
||||
assert creased_step < flat_step * 0.99
|
||||
|
||||
|
||||
@@ -20,6 +20,18 @@ def profile_values(obj):
|
||||
result.to_mesh_clear()
|
||||
|
||||
|
||||
def vertex_uv(obj):
|
||||
result = obj.evaluated_get(bpy.context.evaluated_depsgraph_get())
|
||||
mesh = result.to_mesh()
|
||||
try:
|
||||
uv = np.zeros((len(mesh.vertices), 2))
|
||||
for loop in mesh.loops:
|
||||
uv[loop.vertex_index] = mesh.uv_layers["UVMap"].data[loop.index].uv
|
||||
return uv
|
||||
finally:
|
||||
result.to_mesh_clear()
|
||||
|
||||
|
||||
def test_boundary_triangle_cleanup_is_one_pass():
|
||||
obj, _ = surface(resolution=2)
|
||||
group = obj.modifiers[0].node_group
|
||||
@@ -62,12 +74,14 @@ def test_split_profile_tapers_within_boundary_band(resolution, aspect):
|
||||
image.update()
|
||||
obj.data.attributes.new("o_balloon", "FLOAT", "POINT").data.foreach_set(
|
||||
"value", np.full(len(obj.data.vertices), .3, dtype=np.float32))
|
||||
set_value("Depth Scale", 0)
|
||||
set_value("Depth Scale", 1)
|
||||
np.testing.assert_allclose(profile_values(obj), .3)
|
||||
set_value("Depth Split", .1)
|
||||
points, faces = evaluated(obj)
|
||||
boundary = boundary_neighbors(faces)
|
||||
cut = [i for i in boundary if 0 < points[i,2] < 1 and .5*aspect < points[i,0] < 1.5*aspect]
|
||||
uv = vertex_uv(obj)
|
||||
# The depth step sits at u=0.5, so that column is the split line.
|
||||
cut = [i for i in boundary if abs(uv[i,0] - .5) < 1e-4 and 1e-4 < uv[i,1] < 1 - 1e-4]
|
||||
assert cut
|
||||
band = edge_band(faces, cut)
|
||||
outside = [i for i in range(len(points)) if i not in band]
|
||||
|
||||
@@ -131,6 +131,26 @@ def test_depth_split_preserves_faces_and_closes_each_shell(surface, valid_only):
|
||||
assert restored[1] == base[1]
|
||||
|
||||
|
||||
def test_depth_scale_gates_depth_split(surface):
|
||||
obj, set_value, _, _, image = surface
|
||||
pixels = np.asarray(image.pixels[:], dtype=np.float32).reshape(8, 16, 4)
|
||||
pixels[:, 8:, 2] += 10
|
||||
image.pixels.foreach_set(pixels.ravel())
|
||||
image.update()
|
||||
set_value("Depth Scale", 1.0)
|
||||
whole = evaluated(obj)
|
||||
set_value("Depth Split", 1.0)
|
||||
split = evaluated(obj)
|
||||
assert len(split[0]) > len(whole[0])
|
||||
set_value("Depth Scale", 0.0)
|
||||
set_value("Depth Split", 0.0)
|
||||
flat = evaluated(obj)
|
||||
set_value("Depth Split", 1.0)
|
||||
cancelled = evaluated(obj)
|
||||
assert cancelled[1] == flat[1]
|
||||
np.testing.assert_allclose(cancelled[0], flat[0], atol=1e-6)
|
||||
|
||||
|
||||
def test_depth_jump_keeps_rectangular_faces(surface):
|
||||
obj, set_value, _, _, image = surface
|
||||
base = evaluated(obj)
|
||||
|
||||
@@ -132,6 +132,7 @@ def test_zero_split_bypasses_cleanup_and_positive_split_can_empty_mesh(kind):
|
||||
|
||||
obj, set_value, _, _, _ = create_surface("DEPTH")
|
||||
set_value("Subdivide", 0)
|
||||
set_value("Depth Scale", 1.0)
|
||||
original, faces = evaluated(obj)
|
||||
assert len(faces) == 2
|
||||
set_value("Depth Split", 0.001)
|
||||
|
||||
@@ -75,8 +75,8 @@ def test_split_mapping_and_uniform_scale(size):
|
||||
assert counts[:3] == [15] * 3
|
||||
assert counts[3] == 18
|
||||
set_value("Depth Scale", 0.0)
|
||||
coords, faces = evaluated(obj)
|
||||
assert len(coords) == 18 and np.abs(coords[:, 1]).max() < 1e-6
|
||||
coords, _ = evaluated(obj)
|
||||
assert len(coords) == counts[0] and np.abs(coords[:, 1]).max() < 1e-6
|
||||
|
||||
|
||||
def test_midpoint_selects_relative_ray_jump_and_preserves_faces():
|
||||
@@ -87,16 +87,33 @@ def test_midpoint_selects_relative_ray_jump_and_preserves_faces():
|
||||
assert set(np.round(coords[:, 1], 4)) == {2.0, -2.0}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("depth_scale", [0.0, 2.0])
|
||||
def test_split_topology_uses_depth_before_display_scaling(depth_scale):
|
||||
@pytest.mark.parametrize("depth_scale", [0.0, 1.0])
|
||||
def test_depth_scale_scales_split_selection(depth_scale):
|
||||
obj, set_value = surface(step=4.0)
|
||||
set_value("Depth Split", 0.5)
|
||||
original, original_faces = evaluated(obj)
|
||||
set_value("Depth Scale", depth_scale)
|
||||
set_value("Depth Split", 0.0)
|
||||
whole, whole_faces = evaluated(obj)
|
||||
set_value("Depth Split", 0.5)
|
||||
coords, faces = evaluated(obj)
|
||||
assert len(coords) == len(original) == 18
|
||||
assert faces == original_faces
|
||||
np.testing.assert_allclose(coords[:, 1], original[:, 1] * depth_scale, atol=1e-6)
|
||||
assert np.isfinite(coords).all()
|
||||
if depth_scale == 0.0:
|
||||
# A flat projection keeps every face, so there is nothing to separate.
|
||||
np.testing.assert_allclose(coords, whole, atol=1e-6)
|
||||
assert faces == whole_faces
|
||||
else:
|
||||
assert len(coords) > len(whole)
|
||||
|
||||
|
||||
def test_depth_scale_multiplies_split_strength():
|
||||
obj, set_value = surface(step=4.0)
|
||||
set_value("Depth Scale", 0.5)
|
||||
set_value("Depth Split", 1.0)
|
||||
scaled, scaled_faces = evaluated(obj)
|
||||
set_value("Depth Scale", 1.0)
|
||||
set_value("Depth Split", 0.5)
|
||||
reference, reference_faces = evaluated(obj)
|
||||
assert len(scaled) == len(reference) > 15
|
||||
assert scaled_faces == reference_faces
|
||||
|
||||
|
||||
def test_full_wrapper_keeps_closed_shell():
|
||||
@@ -314,6 +331,7 @@ def test_split_uses_ray_distance_and_ignores_absolute_distance_scale(kind):
|
||||
if kind == "plane":
|
||||
obj, set_value, _, _, image = create_surface("DEPTH")
|
||||
set_value("Subdivide", 1)
|
||||
set_value("Depth Scale", 1)
|
||||
else:
|
||||
obj, set_value = surface(step=0)
|
||||
image = bpy.data.images["Camera"]
|
||||
|
||||
@@ -194,6 +194,21 @@ def test_material_uv_matches_radial_sampling_and_faces_point_inward():
|
||||
result.to_mesh_clear()
|
||||
|
||||
|
||||
def test_depth_scale_gates_split_selection():
|
||||
depth = np.ones((64, 128))
|
||||
depth[:, 64:] = 7
|
||||
obj, set_value = panorama(depth)
|
||||
set_value("Depth Scale", 0)
|
||||
whole = evaluated(obj)
|
||||
set_value("Depth Split", 1)
|
||||
flat = evaluated(obj)
|
||||
# A flat projection keeps the sphere whole, whatever the split strength is.
|
||||
assert flat[1] == whole[1]
|
||||
np.testing.assert_allclose(flat[0], whole[0], atol=1e-6)
|
||||
set_value("Depth Scale", 1)
|
||||
assert len(evaluated(obj)[0]) > len(flat[0])
|
||||
|
||||
|
||||
def test_split_strength_and_distance_scale_invariance():
|
||||
depth = np.ones((64, 128))
|
||||
depth[:, 64:] = 7
|
||||
|
||||
@@ -14,6 +14,7 @@ def test_depth_attribute_cleanup_has_no_warnings(kind):
|
||||
obj, set_value = surface()
|
||||
elif kind == "plane":
|
||||
obj, set_value, *_ = create_surface("DEPTH")
|
||||
set_value("Depth Scale", 1)
|
||||
else:
|
||||
obj, set_value = panorama(subdivide=2)
|
||||
modifier = obj.modifiers[0]
|
||||
|
||||
@@ -194,9 +194,12 @@ def ray_distance(group, camera):
|
||||
return length.outputs["Value"]
|
||||
|
||||
|
||||
def split_depth_surface(group, geometry, face_camera, strength, reference_depth, *,
|
||||
def split_depth_surface(group, geometry, face_camera, strength, reference_depth, depth_scale, *,
|
||||
face_distance=None, face_center=None, triangles=False):
|
||||
"""Split relative ray-distance jumps over the base face-center spacing."""
|
||||
"""Split relative ray-distance jumps over the base face-center spacing.
|
||||
|
||||
The jump is scaled by Depth Scale, so a flat projection keeps its faces.
|
||||
"""
|
||||
nodes, links = group.nodes, group.links
|
||||
if face_center is None:
|
||||
face_center = nodes.new("GeometryNodeInputPosition").outputs[0]
|
||||
@@ -224,6 +227,8 @@ def split_depth_surface(group, geometry, face_camera, strength, reference_depth,
|
||||
for index, center in enumerate(centers):
|
||||
links.new(center, distance.inputs[index])
|
||||
delta = _math(group, "ABSOLUTE", _math(group, "SUBTRACT", *depths))
|
||||
# Depth Scale is the display amplitude of that jump on the projected surface.
|
||||
delta = _math(group, "MULTIPLY", delta, depth_scale)
|
||||
local_distance = _math(group, "MAXIMUM", _math(group, "MULTIPLY", _math(group, "ADD", *depths), 0.5), 1e-8)
|
||||
reference = _math(group, "MAXIMUM", reference_depth, 1e-8)
|
||||
# Uniform Scale cancels between the distance jump and local spacing correction.
|
||||
@@ -262,7 +267,12 @@ def split_depth_surface(group, geometry, face_camera, strength, reference_depth,
|
||||
split = nodes.new("GeometryNodeSplitEdges")
|
||||
links.new(corners, split.inputs["Mesh"])
|
||||
links.new(selected, split.inputs["Selection"])
|
||||
enabled = compare_node(group, "GREATER_THAN", strength, 0.0)
|
||||
enabled = boolean_node(
|
||||
group,
|
||||
"AND",
|
||||
compare_node(group, "GREATER_THAN", strength, 0.0),
|
||||
compare_node(group, "GREATER_THAN", depth_scale, 0.0),
|
||||
)
|
||||
choose = nodes.new("GeometryNodeSwitch")
|
||||
choose.input_type = "GEOMETRY"
|
||||
links.new(enabled, choose.inputs["Switch"])
|
||||
|
||||
@@ -276,7 +276,8 @@ def _build_depth_surface(group, geometry, controls):
|
||||
scale = controls.outputs["Uniform Scale"]
|
||||
original = geometry
|
||||
geometry, corner_camera, cut_vertex = split_depth_surface(
|
||||
group, geometry, sample_face_camera(group, image), split, controls.outputs["Reference Depth"], triangles=True,
|
||||
group, geometry, sample_face_camera(group, image), split, controls.outputs["Reference Depth"],
|
||||
controls.outputs["Depth Scale"], triangles=True,
|
||||
)
|
||||
geometry = remove_boundary_triangles(group, geometry)
|
||||
boundary = edge_boundary_field(group.nodes, group.links)
|
||||
@@ -362,7 +363,7 @@ def build_image_depth_cutout_group():
|
||||
float_input(group, 'Thickness', 0.0, maximum=2.0).description = 'Control how much the surface bulges in Balloon mode. Zero removes the bulge.'
|
||||
float_input(group, 'Shell Thickness', 0.0, maximum=1.0, subtype='DISTANCE').description = 'Add thickness behind the original surface while keeping the front surface in place.'
|
||||
float_input(group, 'Depth Scale', 1.0, maximum=2.0)
|
||||
float_input(group, 'Depth Split', 0.1, maximum=1, subtype='FACTOR').description = 'Split the mesh where depth changes abruptly. Higher values detect smaller depth jumps. Zero disables splitting.'
|
||||
float_input(group, 'Depth Split', 0.1, maximum=1, subtype='FACTOR').description = 'Split the mesh where depth changes abruptly. Scaled by Depth Scale: a flat projection stays whole, higher values detect smaller depth jumps.'
|
||||
options = group.interface.new_panel(name='Options', default_closed=True)
|
||||
boundary_smooth = interface_socket(group, 'Boundary Smooth', 'INPUT', 'NodeSocketInt', options)
|
||||
boundary_smooth.default_value, boundary_smooth.min_value, boundary_smooth.max_value = 4, 0, 16
|
||||
|
||||
@@ -48,7 +48,7 @@ def build_image_depth_panorama_group():
|
||||
dome = float_input(group, "Dome Radius", 50, minimum=1, maximum=100, subtype="DISTANCE")
|
||||
dome.description = "Radius of the sphere kept at Depth Scale 0, and where mask areas below Mask Threshold land."
|
||||
float_input(group, "Depth Scale", 1, maximum=1, subtype="FACTOR").description = "Blend between a sphere at zero and the estimated depth surface at one."
|
||||
float_input(group, "Depth Split", 0.1, maximum=1, subtype="FACTOR").description = "Split the mesh where depth changes abruptly. Higher values detect smaller depth jumps. Zero disables splitting."
|
||||
float_input(group, "Depth Split", 0.1, maximum=1, subtype="FACTOR").description = "Split the mesh where depth changes abruptly. Scaled by Depth Scale: a flat projection stays whole, higher values detect smaller depth jumps."
|
||||
valid_only = interface_socket(group, "Depth Mask", "INPUT", "NodeSocketBool")
|
||||
valid_only.default_value = True
|
||||
valid_only.description = "Remove mesh areas marked invalid by the depth mask."
|
||||
@@ -144,12 +144,12 @@ def build_image_depth_panorama_group():
|
||||
links.new(cull_controls.outputs["Depth Mask"], cull.inputs["Switch"])
|
||||
links.new(culled, cull.inputs["True"])
|
||||
links.new(protected, cull.inputs["False"])
|
||||
controls = group_input(nodes, {"Depth Split"})
|
||||
controls = group_input(nodes, {"Depth Split", "Depth Scale"})
|
||||
strength = controls.outputs["Depth Split"]
|
||||
radial = nodes.new("ShaderNodeSeparateXYZ")
|
||||
links.new(face_camera, radial.inputs[0])
|
||||
geometry, corner_camera, cut_vertex = split_depth_surface(
|
||||
group, cull.outputs["Output"], face_camera, strength, 1.0,
|
||||
group, cull.outputs["Output"], face_camera, strength, 1.0, controls.outputs["Depth Scale"],
|
||||
face_distance=radial.outputs["X"], face_center=face.outputs["Value"],
|
||||
)
|
||||
distance = build_surface_camera(group, pixel, corner_camera, cut_vertex, strength)
|
||||
|
||||
@@ -40,7 +40,7 @@ def build_image_depth_plane_group(image_plane_group):
|
||||
depth_scale.default_value = 1.0
|
||||
depth_scale.min_value = 0.0
|
||||
depth_scale.max_value = 2.0
|
||||
float_input(group, "Depth Split", 0.1, maximum=1, subtype="FACTOR").description = "Split the mesh where depth changes abruptly. Higher values detect smaller depth jumps. Zero disables splitting."
|
||||
float_input(group, "Depth Split", 0.1, maximum=1, subtype="FACTOR").description = "Split the mesh where depth changes abruptly. Scaled by Depth Scale: a flat projection stays whole, higher values detect smaller depth jumps."
|
||||
valid_only = _interface_socket(group, "Depth Mask", "INPUT", "NodeSocketBool")
|
||||
valid_only.description = "Remove mesh areas marked invalid by the depth mask."
|
||||
valid_only.default_value = True
|
||||
@@ -114,11 +114,12 @@ def build_image_depth_plane_group(image_plane_group):
|
||||
links.new(cull_controls.outputs["Depth Mask"], cull.inputs["Switch"])
|
||||
links.new(culled, cull.inputs["True"])
|
||||
links.new(protected, cull.inputs["False"])
|
||||
split_input = _group_input(nodes, {"Depth Split"})
|
||||
split_input = _group_input(nodes, {"Depth Split", "Depth Scale"})
|
||||
reference_input = _group_input(nodes, {"Reference Depth"})
|
||||
geometry, corner_camera, cut_vertex = split_depth_surface(
|
||||
group, cull.outputs["Output"], sample_face_camera(group, texture_input.outputs["Depth Image"]),
|
||||
split_input.outputs["Depth Split"], reference_input.outputs["Reference Depth"],
|
||||
split_input.outputs["Depth Scale"],
|
||||
)
|
||||
camera = build_surface_camera(
|
||||
group, texture.outputs["Color"], corner_camera, cut_vertex,
|
||||
|
||||
Reference in New Issue
Block a user