Skip to content

fix(examples): redesign custom-normals-shade jerry can and make the three shadings read - #271

Merged
TMHSDigital merged 1 commit into
mainfrom
fix/hero-custom-normals-shade
Sep 26, 2026
Merged

TMHSDigital merged 1 commit into
mainfrom
fix/hero-custom-normals-shade

Conversation

@TMHSDigital

Copy link
Copy Markdown
Owner

What and why

The gallery review judged the custom-normals-shade hero weak. It showed three near-identical olive jerry cans whose shading differences disappeared at card size, and a crude X of floater prisms was the only detail. PR #257 demoted the can from the asset reference set as least-designed. This PR redesigns both the asset and the scene.

Asset. The jerry can is rebuilt as a pressed-steel prop with 13 named parts and 3 materials:

  • Shell: four raised rounded-triangle panels on each face with 45-deg walls; the X is the channel left between them.
  • Weld-seam bead round the side band.
  • Triple handle on three welded feet.
  • Spout with a red seal, domed cap, cam lever, hinge and locking pin.
  • Olive paint chipped to steel on exposed edges, driven by a wear point attribute on coplanar support rings. The rings add no shading break.

Facet angles are chosen for the contract: curves step at 22.5 deg or less, creases break at 45 or 90 deg, and nothing sits near the 30-deg threshold.

Scene. Three fresh builds of the can, one per shading treatment:

  • Flat: the side-band corners, cap and handle break into facets.
  • Smooth-everywhere (the shade-smooth-and-forget AI habit): the flat fields smear into gradients and the pressed panels go pillowy.
  • By-angle (the contract): crisp creases and smooth rounds. Its sharp_edge attribute is read back from the mesh and traced in thin cyan lines, so the render draws the contract's own output.

A low strip light makes the highlight shapes diverge. There is no floor text. The render path now calls gallery_framing (exit 10) and gallery_asset_quality (exit 11); before this PR it called neither.

Check changes (no tolerance changed)

  • by-angle now covers Shell, Handle and Neck. The Rib part no longer exists. Result: 1304 of 4738 edges, an exact set match against the independent dihedral recompute.
  • normal-welds on the new shell: sharp-split angle error 1.898e-03 rad (tol 5e-3), smooth deviation 0.0. This works because every curved fan that meets a sharp edge either steps at 10 deg or less (panel corners) or is perpendicular to the adjacent face (side band against the face).
  • custom-normals round-trip: the request pattern changed from 2*pi*i/n swept over loop index to "face normal tilted 20 deg toward the corner bisector". On the new 8196-loop shell the old sweep measured 1.38e-02. That value comes from Blender's lnor encoder, not int16 storage: requests in one fan closer than 0.81 deg are merged (LNOR_SPACE_TRIGO_THRESHOLD). I also tried a fixed 144-direction cycle; it hit the encoder's second rule, which snaps an in-plane angle under 0.81 deg to zero. That gave 13 of 4524 corners at 9.1e-03, with stored beta = 0, verified from the custom_normal INT16_2D data. The bisector pattern stays clear of both rules by construction and reads back at 3.904e-05 (tol 2e-4, unchanged). Straight runs are now split every 0.08 so the face fill has no sliver corners.

Gates (5.2.1 LTS)

  • Framing: fill x=0.797 y=0.750 (band 0.70–0.90). Margins: left 0.113, right 0.091, bottom 0.128, top 0.111 (min 0.02).

  • Asset quality (by-angle can): no default names; materials n=3, dominant OlivePaint at 0.69; edge90 0.086; compactness 28.9 (informational). aq_ok.

  • Contact sheet: docs/gallery/contact-sheets/custom-normals-shade-contact-sheet.webp

    tile luma stage warmth sat
    custom-normals-shade (candidate) 0.2807 0.2272 +0.0135 0.1854
    armature-bend 0.2787 0.2132 -0.4279 0.1436
    damped-track-aim 0.1511 0.1448 +0.2772 0.2305
    bmesh-gear 0.2358 0.1772 +0.3027 0.2495

    Verdicts:

    • Stage darkness: pass. Luma 0.281 against a calibration mean of 0.222, and within 0.002 of armature-bend.
    • Wedge warmth: marginal on the metric, pass by eye. The warm pool is visible on the back wall behind the right can. The metric samples the brightest tenth of the lower half, which here is olive paint and the cyan edge trace, so it reads near-neutral. armature-bend is a calibration member at -0.43 for the same reason.
    • Subject fill: pass (0.80).
    • Saturation: pass (0.185, within the calibration range 0.14–0.25).
    • Thumbnail legibility: pass. At 427x240 the three cans read as faceted, glossy-smeared, and crisp with a traced outline.
  • Asset sheet: docs/gallery/asset-sheets/custom-normals-shade.webp, against collision-hull-proxy, socket-attach-points, vertex-color-ao, wheelbarrow and apothecary-shelf. Verdict: the can is not the least-designed object in the lineup. Pressed panels, seam, triple handle, spout hardware and edge wear put it above the primitive-built hydrant. It sits below the drone and the shelf.

  • measure_hero_drift.py --only custom-normals-shade: mean_abs 0.00475, verdict matches.

Check runs (tests/smoke/run_example.py)

binary reported version result
E:\Blender-Developer-Tools\.scratch\blender-4.5.11-windows-x64\blender.exe Blender 4.5.11 LTS PASS (legacy op CANCELLED + untouched, as asserted)
E:\Blender-Developer-Tools\.scratch\blender-5.1.2-windows-x64\blender.exe Blender 5.1.2 PASS (legacy op FINISHED + NODES)
E:\Blender-Developer-Tools\.scratch\blender-5.2.1-windows-x64\blender.exe Blender 5.2.1 LTS PASS

The --output render path also passes both gates on 4.5.11. The hero was rendered on 5.2.1 through scripts/render_hero.py.

Falsifiers (5.2.1, each probe restored after)

  • --mismatch-angle (mark sharp at 20 deg, audit at 30): exit 5, Shell has 48 extra edges out of 1144.
  • Face smooth flags cleared on the shell before the weld audit: exit 6, smooth-edge loops split by 3.902e-01.
  • TOL_NORMAL = 1e-6: exit 7, measured 3.904e-05.
  • All parts on one material: exit 11, materials 1<2 on a 13-part hero.
  • Camera pulled in to (0.6, -6.4, 2.5): exit 10, fill 0.978 and right margin 0.000.

Proven by live run

  • All check results, versions, gate numbers, falsifier exits and drift numbers above.
  • The encoder merge and snap measurements (1.38e-02 and 9.1e-03), and stored beta = 0 on the snapped corners.

Established by inspection only

  • The mechanism behind those measurements: that LNOR_SPACE_TRIGO_THRESHOLD in Blender's lnor-space encoder causes the merge and snap. This comes from reading Blender source behavior; the measurements above are consistent with it.
  • The asset-sheet and contact-sheet verdicts are visual judgments.

🤖 Generated with Claude Code

…hree shadings read

The gallery review judged the hero weak: three near-identical olive cans
whose shading differences vanished at card size, and a crude X of floater
prisms as the only detail. PR #257 demoted the can from the asset
reference set as least-designed.

The can is rebuilt as a pressed-steel prop. The shell has four raised
rounded-triangle panels on each face with 45-deg walls, and the X is the
channel left between them. There is also a weld-seam bead, a triple
handle on welded feet, and a spout with red seal, domed cap, cam lever,
hinge and locking pin. Olive paint chips to steel on exposed edges,
driven by a `wear` point attribute on coplanar support rings. The facet
angles are chosen for the contract: curves step at 22.5 deg or less,
creases break at 45 or 90 deg, and nothing sits near 30 deg.

The render shows three fresh builds, flat, smooth-everywhere and by-angle.
The by-angle can's `sharp_edge` set is read back from the mesh and traced
in thin cyan lines. A low strip light makes the highlights diverge. The
render path now runs the gallery_framing (exit 10) and
gallery_asset_quality (exit 11) gates it previously skipped.

Checks are unchanged in tolerance. by-angle now covers Shell, Handle and
Neck (1304 of 4738 edges, an exact match). The custom-normal round-trip
request pattern moved from an index sweep to a 20-deg tilt toward each
corner's bisector. On the new 8k-loop shell the old sweep measured
Blender's lnor-encoder fan merge (1.38e-02), and a fixed 144-direction
cycle hit its in-plane angle snap (9.1e-03). Both come from the
0.81-deg LNOR_SPACE_TRIGO_THRESHOLD, not from storage precision. The
new pattern stays clear of both and reads back at 3.904e-05 (tol 2e-4).
Long straight runs are split so the face fill has no slivers.

Signed-off-by: TMHSDigital <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@github-actions github-actions Bot added examples Runnable smoke-gated examples under examples/ documentation Improvements or additions to documentation labels Sep 26, 2026
@TMHSDigital
TMHSDigital merged commit 07794ae into main Sep 26, 2026
11 checks passed
@TMHSDigital
TMHSDigital deleted the fix/hero-custom-normals-shade branch September 26, 2026 23:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation examples Runnable smoke-gated examples under examples/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant