Skip to content

feat(paint): an inflated material whose shading follows the silhouette - #399

Merged
LeadcodeDev merged 1 commit into
mainfrom
feat/inflated-material
Sep 27, 2026
Merged

LeadcodeDev merged 1 commit into
mainfrom
feat/inflated-material

Conversation

@LeadcodeDev

Copy link
Copy Markdown
Owner

Closes #385. Refs #388.

The gap it closes

The three presets from #357 compute their highlight on the node's box, then clip it. On a five-branch star cut out of a rectangle, that is one band of light across the box — the shape reads as a flat varnished plane, not a volume.

inflated derives the shading from the silhouette: the clipped path is rasterised and blurred, and the gradient of that blurred mask is the surface normal, lit by the scene's own light. Each branch has its own edge, so each gets its own highlight and hollow.

"style": {
  "clip-path": { "kind": "polygon", "points": [ … a star … ] },
  "material": { "preset": "inflated", "bevel": 24, "softness": 0.6 }
}

Rendered side by side — glossy, inflated at bevel: 26, softness: 0.6, inflated at bevel: 8, softness: 0.1 — the first is one flat gradient across the bounding box and the other two have relief on every branch.

An artefact I had to fix, not ship

The first version banded visibly: with a large bevel the mask's gradient is shallow, and sampling adjacent pixels quantises it into concentric steps on 8-bit alpha. The gradient stencil now widens with bevel — the same fact that caused the artefact is what fixes it: measure a shallow slope over a longer baseline.

Verification

Three tests. The decisive one counts separate bright runs along a scanline cutting two branches:

across a row cutting two branches, an inflated star must show more separate
bright runs than a glossy one, which lights the bounding box as a single band:
glossy=0, inflated=0

The other two assert equality and pass both ways on purpose — nothing paints outside the silhouette, and intensity: 0 is byte-identical to declaring no material.

Three things the rule file leads with

  • It costs pixels. The other presets are three Skia gradients; this one rasterises, blurs and walks a mask every frame. It is for stars, pills and icons — not a full-screen backdrop.
  • bevel must stay small relative to the shape. It is both the blur width and the scale the slope is measured at, so a large bevel on a small shape drowns the silhouette before a normal can be taken from it. At most a quarter of the shape's smallest dimension.
  • It ignores highlight and edge. No specular return at all — its light is entirely geometric. Next to a glossy node that is two lighting models in one frame, which may be intended but is not a tuning variant.

Also in here

dot_map_terminates_on_a_zero_dot_spacing guards against a runaway loop with a 10-second wall-clock budget, and blew it four separate times today under parallel build load while painting perfectly well. It is 120 seconds now. The guard answers "does this terminate", and 10 versus 120 seconds makes no difference to that question while making every difference to whether the suite can be trusted during a chantier.

Two ffmpeg-dependent tests have the same shape of problem from a different cause; they are not touched here.

cargo fmt --all --check and cargo clippy --workspace --all-targets -- -D warnings clean.

Written comment-free, per the codebase-wide rule from #345.

The three presets from #357 compute their highlight on the node's **box** and
then clip it. On a star cut out of a rectangle that gives one band of light
across the box, not a relief per branch — the shape stays a flat varnished
plane, which is what #385 measured against a reference.

`inflated` derives the shading from the silhouette instead. The clipped path is
rasterised and blurred, and the **gradient of that blurred mask is the surface
normal**, lit by the scene's own `light` from #357. Each branch has its own
edge, so each gets its own highlight and its own hollow. `bevel` sets how far
in the rounding reaches, `softness` the profile from a hard chamfer to a
cushion.

The first version banded visibly: with a large `bevel` the mask's gradient is
shallow, and sampling adjacent pixels quantises it into concentric steps on
8-bit alpha. The gradient stencil now widens with `bevel`, which is the same
reason the artefact existed — measure a shallow slope over a longer baseline.

Three tests. The decisive one counts separate bright runs along a row cutting
two branches, and fails without the relief with `glossy=0, inflated=0`. The
other two assert equality and pass both ways on purpose: nothing paints outside
the silhouette, and `intensity: 0` is byte-identical to no material at all.

Also here, because it obstructed this work four times:
`dot_map_terminates_on_a_zero_dot_spacing` guards against a runaway loop with a
10-second wall-clock budget, and exceeded it under parallel build load while
painting perfectly well. The budget is 120 seconds now. The guard answers
"does this terminate", and 10 versus 120 seconds makes no difference to that
question while making every difference to whether the suite is trustworthy
during a chantier.

Closes #385
@LeadcodeDev LeadcodeDev added the enhancement New feature or request label Sep 27, 2026
@LeadcodeDev LeadcodeDev self-assigned this Sep 27, 2026
@LeadcodeDev
LeadcodeDev merged commit 1172d87 into main Sep 27, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

material: an "inflated" preset whose shading follows the clipped silhouette

1 participant