Skip to main content

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