texcmp: normalize normal-map packing before diffing; angle-based verdicts - #5
Merged
Conversation
…icts A texture stored as a plain RGB normal map and the same texture in Unity's AG-swizzle (DXT5nm convention: R pinned to 255, X in alpha, B copying G) render identically through UnpackNormalmapRGorAG, but raw-decode as blue vs pink — texcmp diffed the raw channels and reported every pixel different (ppm=1e6, "visible") for render-identical bundles. Observed live on stylizedWood_v01_normal.png pairs where upstream v49 swizzled a texture our per-entity usage classification stored plain. When either side of a matched texture shows the AG signature (R>=250 on 99% of pixels, varying alpha, B~G), both sides are re-expressed as reconstructed normals (X, Y, sqrt(1-X^2-Y^2)) before the pixel diff, the packing per side is recorded (normalPacking field + note), and imperceptibility uses the perceptual quantity for normals: pixels tilted more than 5 degrees must cover at most 200ppm of the image (mirrors the render amnesty shape). maxNormalAngleDeg / normalAngleOverPpm are emitted per texture. Verified against live ab-cdn v49 bundles: - packing-mismatch pair (same content): ppm 1e6 "visible" -> imperceptible (137ppm of pixels over 5deg, max 16.5deg — cross-encoder block noise) - both-AG pair (same content): ppm 19k "visible" -> imperceptible (max angle 1.89deg, 0ppm over) - genuinely different normal maps: stays visible (633ppm over 5deg) - plain color textures: untouched code path, verdicts unchanged Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the false "visible" verdicts on normal-map texture pairs, e.g. live-jit pair m7db93618.
The bug
A tangent-space normal map can be stored two ways that render identically through URP's
UnpackNormalmapRGorAG(X = R×A):R=X, G=Y, B=Z— raw-decodes blueR=255, G=Y, B≈G, A=X— raw-decodes pinkUpstream v49 swizzles normal maps; abgen swizzles per-entity-usage classification (a copy of
stylizedWood_v01_normal.pngdeployed at a path nothing references gets stored plain). texcmp diffed the raw decoded channels, so a packing difference reported every pixel different (ppm=1e6, maxd=206) for bundles whose stored normal vectors are identical channel-for-channel.The fix
When either side of a matched texture shows the AG signature (R≥250 on ≥99% of pixels, varying alpha, B≈G), both sides are re-expressed as reconstructed normals
(X, Y, √(1−X²−Y²))before the pixel diff, and the packing per side is recorded (normalPackingfield +normal-map:note). Imperceptibility for these pairs uses the perceptual quantity for normals — worst-case tilt over area: pixels tilted >5° must cover ≤200ppm of the image (mirrors the existing render-amnesty shape "Δ>8 ≤200ppm").maxNormalAngleDeg/normalAngleOverPpmare emitted per texture. Non-normal textures take the exact same code path as before.classify.pyneeds no changes — texture labels map from texcmp'sclassdirectly.Verification (against live ab-cdn v49 bundles + local abgen output)
m7db93618(the report)mc9326fc3)Clippy clean.
🤖 Generated with Claude Code