Function: reconstructFinalNormal()
reconstructFinalNormal(
offsetNode):any
Turns a trained tangent-space (dx, dy) offset into the mesh's final
view-space normal. The network only predicts the 2-component offset (see
NTCFormat.js's 'tanh'-activated normal/clearcoatNormal
channels) - z is reconstructed here as the positive root
sqrt(1 - dx*dx - dy*dy), matching the always-positive-hemisphere
tangent-space z that MaterialX's own normalNode produces (the same
assumption the old full-vector encoding relied on: this z was always ~1,
never actually trained as an independent degree of freedom).
This is NOT the standard three.js normalMap(texture, scale) convention,
where the returned vector is left in tangent space and the base
NodeMaterial.setupNormal() pipeline transforms it downstream using
whatever mesh it's applied to - setupNormal() actually uses normalNode
verbatim as the final normal, no further transform. MaterialX's own
<normalmap> conversion (see examples/jsm/loaders/materialx/
MaterialXNodeLibrary.js's mx_normalmap + MaterialXSurfaceMappings.js's
transformNormalToView(...) call) does the tangent/bitangent/normal blend
inside the graph itself, using whatever mesh that graph gets built
against - which is why baking that node's output on our flat training
quad and reapplying it verbatim to the torus knot was wrong for any real
(non-constant) bump detail: the baked value is relative to the quad's own
orientation, not a portable tangent-space vector, and setupNormal() never
re-blends it per-mesh. This function replicates that same blend here, but
against tangentWorld/bitangentWorld/normalWorld as resolved for whatever
mesh THIS material is actually applied to (the torus knot) - these are
dynamic per-mesh TSL accessors, so this "just works" the same way
MaterialX's own conversion does when applied directly to a mesh.
Shared (via OUTPUT_TYPES.normal.reconstruct below) by both the normal
and clearcoatNormal channel descriptors in NTCFormat.js -
moved here, out of NTCNodeMaterial.js, so it's reusable by any
channel descriptor (built-in or caller-supplied) that declares itself
type: 'normal', not just those two hardcoded call sites. Still
re-exported from NTCNodeMaterial.js for backward compatibility
with existing imports/tests.
Parameters
offsetNode
any
Returns
any