rtx: Add MaterialX (.mtlx) material support#348
Merged
Conversation
Registers the materialx subtype and the VISRTX_MATERIAL_MATERIALX extension. The MaterialX MDL support modules ride the MDL search path — resolved from the FetchContent source tree for in-tree builds, the installed libraries/mdl layout otherwise. Commit transcodes the document to MDL behind a change guard and exposes a materialNames property. TestMaterialXMaterial compiles with USE_MATERIALX so the advertised extension flag matches the device build.
…rface builtin Transcoder emits a clean-name -> generated-MDL-arg map so ANARI parameters keep their MaterialX names. Bundles standard_surface.mtlx and resolves the visrtx::standard_surface builtin against it.
materialx material token with a bare-constructor branch, a StandardSurface preset helper, and MaterialX/StandardSurface entries in the material menu.
The transcoder splices an <image> node in front of each texture-bindable constant input and pairs it in the paramMap (carrying origin path and type); ANARI samplers then route through the spliced nodes by pure arg binding, and a re-commit no longer reverts textured inputs. Only 2D image samplers (image2D / compressedImage2D) bind — a spliced MaterialX image lowers to an MDL texture_2d — checked locally at the bind site rather than on the Sampler base. Also fixes MDL search-path init: the search paths are now applied from initMDL (via syncMdlSearchPaths), not only from deviceCommitParameters. On the lazy-init path (app never commits the device) the bundled MaterialX support-module path was left unset, so the generated MDL failed to compile.
…a render gate When a document has no renderable element, instantiate its file-local surfaceshader nodedef. The file is read before stdlib import so auto-instantiation sees file-local nodedefs.
Transcoder takes a DocumentSource (file or inline) with snapshot provenance; the documentInline scheme survives when only source is re-set.
The implicit-input materialization (for inputs absent on the node instance, e.g. the bundled/auto-instantiated standard_surface) looked the input up with NodeDef::getInput, which only sees DIRECTLY declared inputs. standard_surface's default nodedef declares just base/base_color directly and inherits the other ~40 (emission_color, metalness, ...) from ND_standard_surface_surfaceshader_100, so getInput returned null for them, the splice was skipped, and binding a sampler to such an input failed with 'sampler not bound (image2D textured input required)'. base_color only worked by being one of the two direct inputs. Use getActiveInput, which resolves inputs through nodedef inheritance. Adds a transcoder test binding emission_color (verified to fail before the fix).
Classifies wired filename ports as texture-bindable rows, exempts them from retranscode topology, and exposes a textureInputs discovery property. Device-side sampler binding is not implemented yet.
A MaterialX standard_surface transcodes its emission to a coat-over-base EDF — df::unbounded_mix([edf_component(saturate(coat), directional_factor(diffuse)), edf_component(1-saturate(coat), diffuse)]) — so the ADR-0007 classifier rejected it (Directional kind + unknown intensity sign) and synthesized NO Geometry Light. The emitter then lit receivers only via the forward path, so its NEE pool was fainter/noisier than the equivalent physicallyBased or raw-MDL emitter. Four folds let the classifier see through the coat wrapper when coat is 0: - model math::saturate (nonnegative, in [0,1], zero iff the argument is zero); - prune a provably-zero-weight df::edf_component from a mix's kind union, so the view-dependent coat branch drops at coat=0 and only the diffuse base remains; - stop descending into a modeled EDF leaf's operands in the mix deep-scan — the diffuse_edf handle string is an Opaque node that otherwise poisoned the mix to Unknown (now matches the evalEdf leaf path); - fold a - 0 = a in combineSub, so the 1 - saturate(coat) layer weight resolves to the constant 1 and the intensity sign is ProvablyNonnegative. Pruning keys on the arg-block value, so a genuinely coated emitter (coat>0) keeps the view-dependent branch and stays unregistered (unfaithful), per the miss=variance / over-register=bias policy. Also classify the textured emitter: MaterialX's <image> node lowers to a materialx::stdlib_*::mx_image_* wrapper (a tex::lookup with an invalid-texture default fallback), NOT a bare tex::lookup intrinsic — so it carries no DS_INTRINSIC_TEX_LOOKUP semantic and the emission IR saw it as an opaque call. Recognize the mx_image_* family by name and treat it as a texture lookup whose texture is the mxp_file argument. The DB-name match is sound here (unlike EDF-leaf recognition, which stays semantics-only): a texture lookup only supplies a Pick-Power mean, so a misrecognized or unbound lookup folds to zero — variance, never bias.
Replace the compile-baked MaterialX paths with a runtime search chain: materialxSearchPaths device param -> MATERIALX_SEARCH_PATH -> mx::getDefaultDataSearchPath() -> compile-time last resort. The MDL implementation modules dir derives from the winning root. Delete the visrtx::standard_surface builtin alias and the shipped standard_surface.mtlx: applications author the instantiation inline (TSD preset does), the nodedef comes from the distribution.
jeffamstutz
approved these changes
Jul 18, 2026
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.
Adds a
materialxANARI material subtype to the VisRTX RTX device(
VISRTX_MATERIAL_MATERIALXextension). A.mtlxdocument — supplied as afile path or inline XML — is transcoded to MDL at commit time and rendered
through the existing MDL material path, so MaterialX materials get the full
MDL shading/emission pipeline with no new runtime backend.
The MaterialX standard library is resolved from a system-installed distribution at runtime,
not embedded or shipped (ADR 0008).
TSD gains a thin authoring layer (material token, a StandardSurface preset, a menu entry).
Requires
VISRTX_ENABLE_MDL_SUPPORT(MaterialX transcodes to MDL) and gated byVISRTX_ENABLE_MATERIALX_SUPPORT.