feat(cutout): implement boundary padding for cutout textures

- Introduced a new `Boundary Padding` preference in the Cutout Tool with a default of 2 px and a configurable range of 1-4 px.
- Updated the Cutout client to establish inward mapping from the final Mask, sampling in a fixed direction and processing independent material RGBA and Depth.
- Enhanced the handling of RGBA and Depth during cutout operations, ensuring proper mapping and preservation of source data.
- Added tests to validate the new boundary padding functionality and its integration with existing cutout processes.

feat(geometry): remove overall mesh smoothing from geometry nodes

- Removed the `Smooth` input from several geometry node groups to eliminate unnecessary overall mesh smoothing.
- Retained `Smooth Weight` for depth nodes while removing it from Cutout and Relief Plane nodes.
- Ensured that boundary, normal, and fill smoothing behaviors remain intact and consistent.
- Updated tests and documentation to reflect the changes in node interfaces and behaviors.

feat(normals): scale tangent normals with depth

- Reused the existing `o_normal_reduction` attribute to adjust Tangent Normal strength based on the geometry's `Depth Scale`.
- Ensured that Tangent Normal strength is consistent with the geometry depth, preserving full strength for materials that do not provide the attribute.
- Updated tests to verify the correct behavior of normal strength adjustments in both Tangent and Object Space.
This commit is contained in:
2026-09-15 00:40:24 +08:00
parent ab282e4fdf
commit 591e62085a
93 changed files with 1915 additions and 2851 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-14

View File

@@ -0,0 +1,54 @@
## Context
`O Image Layer` currently reads `o_normal_reduction` and calculates Tangent and Object Normal Map strengths from `1 - attribute`. Its absence evaluates as zero, preserving full strength. `O Image Relief Plane` changes surface displacement with `Depth Scale` but supplies no matching material attribute, so its normal map remains visually over-strong at lower values.
The current Depth Plane and Depth Cutout conversion paths request Object Space normals. The panorama material is shadeless. Relief Plane is the only current Depth Scale geometry path that supplies a reduction attribute, and the shared material already applies that attribute in both normal spaces.
## Goals / Non-Goals
**Goals:**
- Reuse the existing `o_normal_reduction` attribute contract for Tangent Normal strength.
- Keep Tangent Normal strength numerically equal to the geometry's current `Depth Scale` on Relief Plane.
- Preserve full strength for material users and geometry that do not provide the attribute.
- Rebuild and verify the node asset and its rendered behavior.
**Non-Goals:**
- Alter Object Space normal-color transformation.
- Change Depth Scale ranges, geometry projection, normal-map generation, or user-facing sockets.
- Add a new normal-scale attribute or alter the existing material attribute lookup.
## Decisions
### Reuse the reduction attribute
`O Image Layer` already calculates the normal multiplier as `1 - o_normal_reduction`. Relief Plane therefore writes `1 - Depth Scale` to its final geometry. This produces a normal multiplier equal to `Depth Scale`, including zero and values greater than one, without changing the established shader contract.
Alternative: introduce a second scale attribute and alter the shader to use it. This duplicates an existing contract and would require migration of current geometry producers without improving the resulting calculation.
### Preserve the existing missing-attribute fallback
An absent float attribute evaluates as zero, and the existing `1 - o_normal_reduction` expression therefore evaluates to one. No material-node change is required to preserve ordinary image-material strength.
Alternative: add an existence switch in the material. It would replicate behavior that the current expression already provides.
### Apply the reduction in both normal spaces
`O Image Layer` already supplies the same `Normal Scale × (1 - o_normal_reduction)` strength field to both Tangent and Object Normal Map nodes. Relief Plane will use that established shared behavior, so both normal spaces follow its displayed geometry depth.
Alternative: split the material paths and limit the factor to Tangent Space. This would introduce an inconsistent interpretation of the existing shared attribute and require a separate Object Space policy.
## Risks / Trade-offs
- [Relief geometry writes the reduction on an inappropriate domain] → Store the field where the material evaluates it and add rendered POINT/FACE-domain coverage.
- [Missing attributes disable unrelated normal maps] → Test the explicit missing-attribute fallback on both Tangent and Object paths.
- [A later Tangent Depth Scale group omits the contract] → Treat `o_normal_reduction = 1 - Depth Scale` as the required output attribute whenever a Tangent-normal geometry group exposes `Depth Scale`.
- [Object Space visual response differs from Tangent Space] → Render and compare both paths at zero, fractional, unit, and extrapolated scales.
## Migration Plan
1. Update relevant Tangent-normal geometry builders to write `1 - Depth Scale` as `o_normal_reduction`.
2. Extend rendered node tests with Relief Plane depth-scale coverage.
3. Run focused node tests, then `uv run node-group build` to regenerate and verify `src/anyimage/assets/O_AnyImage.blend`.
4. Roll back by restoring the previous asset and source together.

View File

@@ -0,0 +1,26 @@
## Why
Tangent-space normal maps describe the full-depth surface. When a geometry node reduces its `Depth Scale`, its shape becomes flatter but the material normal remains at full strength, making surface detail appear too strong.
## What Changes
- Reuse the existing `o_normal_reduction` geometry attribute as the material-to-geometry contract for tangent-space normal strength.
- Make tangent-normal depth geometry write `1 - Depth Scale` to that attribute, so the material normal strength follows the displayed geometric depth.
- Extend the existing `O Image Layer` behavior without changing its attribute lookup or fallback semantics.
- Make Object Space normal strength follow the same geometry reduction contract.
- Reuse the existing `o_normal_reduction` contract for additional Tangent-normal depth geometry.
## Capabilities
### New Capabilities
- `depth-scaled-tangent-normals`: Keep tangent-space material normal strength consistent with geometry depth scaling.
### Modified Capabilities
None.
## Impact
- Tangent-normal geometry-group builders, especially Relief Plane.
- Normal-scale node tests; saved node assets must be rebuilt with `uv run node-group build`.
- No server inference format, user-facing modifier socket, or dependency changes.

View File

@@ -0,0 +1,27 @@
## ADDED Requirements
### Requirement: Normal strength follows geometry depth scale
When a Geometry Nodes group both exposes `Depth Scale` and is rendered with a normal map, it SHALL store `1 - Depth Scale` as the `o_normal_reduction` float attribute on its final geometry. `O Image Layer` SHALL calculate Tangent and Object Normal Map Strength as its `Normal Scale` input multiplied by `1 - o_normal_reduction`.
#### Scenario: Reduced relief depth reduces Tangent normal strength
- **WHEN** an O Image Relief Plane uses a Tangent normal map and `Depth Scale` is set to 0.5
- **THEN** the Tangent Normal Map Strength is one half of the value produced at `Depth Scale` 1 with the same `Normal Scale` input
#### Scenario: Zero relief depth disables Tangent normal detail
- **WHEN** an O Image Relief Plane uses a Tangent normal map and `Depth Scale` is set to 0
- **THEN** the Tangent Normal Map Strength is zero
#### Scenario: Depth extrapolation is preserved
- **WHEN** an eligible Tangent-normal geometry group uses `Depth Scale` greater than 1
- **THEN** `o_normal_reduction` is negative by the corresponding amount and the Tangent Normal Map Strength retains the extrapolated value without clamping
### Requirement: Reduction attribute retains its material fallback
`O Image Layer` SHALL use a factor of 1 for its Tangent Normal Map Strength when `o_normal_reduction` is absent. The existing `o_normal_reduction` material contract SHALL remain unchanged.
#### Scenario: Ordinary image material has no normal-scale attribute
- **WHEN** an object using O Image Layer has no `o_normal_reduction` attribute
- **THEN** its Tangent Normal Map Strength equals the `Normal Scale` input
#### Scenario: Object Space normal strength follows the reduction
- **WHEN** O Image Layer is configured for an Object Space normal map and its geometry has any `o_normal_reduction` value
- **THEN** the Object Space Normal Map Strength equals the `Normal Scale` input multiplied by `1 - o_normal_reduction`

View File

@@ -0,0 +1,11 @@
## 1. Reuse the geometry-to-material contract
- [x] 1.1 Update O Image Relief Plane to store `1 - Depth Scale` as `o_normal_reduction` on final geometry.
- [x] 1.2 Confirm O Image Layer's existing `1 - o_normal_reduction` Tangent and Object-strength paths and its absent-attribute fallback need no code change.
- [x] 1.3 Confirm existing O Image Cutout and O Image Depth Cutout attribute uses retain their current geometry and material behavior.
## 2. Verification and assets
- [x] 2.1 Update definition and rendered node tests for absent, zero, fractional, and greater-than-one `o_normal_reduction` values in both normal spaces.
- [x] 2.2 Run the relevant node tests and fix regressions.
- [x] 2.3 Run `uv run node-group build` and verify the regenerated `O_AnyImage.blend` asset and inventory tests.