usdMtlx does not consistently preserve MaterialX node names
dgovil opened this issue · comments
Description of Issue
When I convert a .mtlx
document to USD, the resulting graph does not preserve the name.
Take this example here and note that the UsdPreviewSurface
node has a specified name SR_brass1
.
Also note that the tileimage
nodes both have a name image_roughness
and image_color
.
<?xml version="1.0"?>
<materialx version="1.38" colorspace="lin_rec709">
<nodegraph name="NG_brass1" fileprefix="../../../Images/">
<tiledimage name="image_color" type="color3">
<input name="file" type="filename" value="brass_color.jpg" colorspace="srgb_texture" />
<input name="uvtiling" type="vector2" value="1.0, 1.0" />
</tiledimage>
<tiledimage name="image_roughness" type="float">
<input name="file" type="filename" value="brass_roughness.jpg" />
<input name="uvtiling" type="vector2" value="1.0, 1.0" />
</tiledimage>
<output name="out_color" type="color3" nodename="image_color" />
<output name="out_roughness" type="float" nodename="image_roughness" />
</nodegraph>
<UsdPreviewSurface name="SR_brass1" type="surfaceshader">
<input name="diffuseColor" type="color3" nodegraph="NG_brass1" output="out_color" />
<input name="metallic" type="float" value="1" />
<input name="roughness" type="float" nodegraph="NG_brass1" output="out_roughness" />
</UsdPreviewSurface>
<surfacematerial name="USD_Tiled_Brass" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="SR_brass1" />
</surfacematerial>
</materialx>
When converted to USD, I get the following hierarchy (from usdtree)
/
`--MaterialX [def]
|--Materials [def]
| `--USD_Tiled_Brass [def Material]
| |--ND_UsdPreviewSurface_surfaceshader [def Shader]
| `--NG_brass1 [def NodeGraph]
| |--image_color [def Shader]
| `--image_roughness [def Shader]
|--Shaders [def]
| `--ND_UsdPreviewSurface_surfaceshader [def Shader]
`--NodeGraphs [def]
`--NG_brass1 [def NodeGraph]
|--image_color [def Shader]
`--image_roughness [def Shader]
Notice that the two tiled image nodes have preserved their names from the mtlx document. However the USDPreviewSurface has eschewed its name and instead picked the node definition as the name instead.
This seems inconsisent and makes it difficult to predict the output graph when looking at the MaterialX document.
Steps to Reproduce
- Convert any sample
.mtlx
file to USD using usdcat
System Information (OS, Hardware)
Tested with USD 24.3 and 24.5 on macOS.
Hi @dgovil , we don't recall the reason for this... closest recollection is that we had some possibly confusion about the need/identity for NodeGraphs for mtlx Materials. In any case, we agree it's unfortunate, and are open to a PR that addresses it, maybe with a FF arg to preserve the old behavior for existing documents... though it feels like we're putting/contemplating alot of switches on this plugin.
Thanks for the response. I agree that it would be good to avoid putting too many switches in.
I was thinking perhaps a good solution is to use an env variable instead for this and give it a deprecation period?
E.g Something like PXR_USDMTLX_LEGACY_SHADER_NAME
to opt into the current naming, and when set it emits a warning like "This option will be removed in USD (current year+1). Please convert your mtlx files with usdcat if you need to rely on this naming."
That way we don't bake in the switch into any files and give people a time boxed off ramp at the same time.
I think now's a good time to do it while use of mixed USD+MTLX files outside of studios is going to be low.
If that sounds reasonable, I can put up a PR accordingly.
Filed as internal issue #USD-9713
If that flies with the #wg-usd-materialx community, it sounds good to me. Looks like you've already begun discussing and that @JGamache-autodesk may add thoughts here, also?
I was wondering what would happen if there were multiple standard surface nodes in the document. Looks like they all resolve to a single /MaterialX/Shaders/ND_UsdPreviewSurface_surfaceshader
that gets referenced in all materials, and all references get to be named ND_UsdPreviewSurface_surfaceshader
because they are in the namespace of the material. Unsure if this helps since it looks like those nodes only get an info:id
and a outputs:surface
attribute (which does not necessarily match the name of the output referenced in the shader registry).
Looks like the mindset for the current importer is very material-centric. The name of the surfacematerial node is all that matters. The way the loader transfers all input parameters to the UsdMaterial is also strange. The UsdShade node, via its SdrShader reference knows how to display itself in an inspector. Once all those attributes are transferred to the material we lose information like the uimin
and uimax
of the attributes unless the inspector makes a special case for UsdMaterial and peeks under the connections to find what shader node is referenced.
We can either start adding a lot of attributes on the loader, or environment variables to switch behaviors as we rework the loader, but I think an option to have multiple loaders for a .mtlx
file, along with a way to register and select which loader to use could be interesting if we want to preserve the initial behavior while exploring new ways to ingest MaterialX.
@JGamache-autodesk , I wanted to respond to this:
The way the loader transfers all input parameters to the UsdMaterial is also strange. The UsdShade node, via its SdrShader reference knows how to display itself in an inspector. Once all those attributes are transferred to the material we lose information like the uimin and uimax of the attributes unless the inspector makes a special case for UsdMaterial and peeks under the connections to find what shader node is referenced.
Constructing a "public material interface" on the Material prim is a crucial part of the UsdShade OM. As discussed with @dgovil in either a slack thread or the forum, it provides artists the ability to centralize and customize the "expected to be tweaked" interface. And it is vital for "instanced materials" to be useful at all, because there, the only prim available for editing is the Material prim itself.
That said, we do indeed have the awkwardness of access to the auxiliary data for each input that we provide for users to interact with. We do the majority of our editing in an application that is built on the Presto framework, which reads in the USD data directly, but presents it ona "Presto stage", and Presto stages provide cached "back pointers" for all relationships and connections, and Presto's Inspector editor has a fair amount of custom code - for Material prims it does just what you describe: it hops across the connection to find the Sdr-configured Shader property and configure the presentation of the Material property accordingly.
That is not a very satisfying answer for many reasons, I know, especially because UsdAttribute/UsdRelationship provide no such back pointers (as they are incredibly expensive to compute and cache for the whole stage). However, I think we'd be open to adding (especially via PR) a UsdShade or UsdShadeMaterial utility that would do all the necessary work for you for a Material at a time and return a map of attribute-name to SdrShaderProperty (or a dictionary of its metadata). In fact, I think we used to provide a similar utility that was needed by the Katana ingest of UsdShade, but we moved it the katana plugins at some point (and it wasn't quite what we're talking about here, but close).
Need to think more about your other points...
Hi @klucknav , I was wondering if you could shed some light on this comment that I believe is attributed to you
// Choose the name of the shader. In MaterialX this is just
// mtlxShaderNode->getName() and has no meaning other than to uniquely
// identify the shader. In USD to support materialinherit we must
// ensure that shaders have the same name if one should compose over
// the other. MaterialX composes over if a shader node refers to the
// same nodedef so in USD we use the nodedef's name. This name isn't
// ideal since it's just an arbitrary unique name; the nodedef's
// node name is more meaningful.
I'm not sure I fully understand why this is needed for material inherit or what that entails?
Ah, a coworker pointed me to https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/documents/Specification/MaterialX.Specification.md#material-inheritance which I assume is what was trying to be supported.
I don't see a good heuristic to support this, which is also why I assume you went with just picking the most generic name for the prim in the hopes of supporting this.
I'll put up a PR that adds a PXR_MTLX_SUPPORT_MATERIAL_INHERITS
env var to choose between the node defs name and the shader nodes name.
Personally, I think it's more valuable to provide an intuitive conversion to the majority of use cases, and I don't believe material inheritance is used all that much? I couldn't find any example shaders in the MaterialX repo (though nodedefs themselves use it).