AcademySoftwareFoundation / MaterialX

MaterialX is an open standard for the exchange of rich material and look-development content across applications and renderers.
http://www.materialx.org/
Apache License 2.0
1.84k stars 350 forks source link

Reading texture path from primvar #878

Open flord opened 2 years ago

flord commented 2 years ago

I am currently working on packaging USD assets for public release and I though it would be a good idea to use the MaterialX shaders instead of the Usd Preview Surface. I'm using Houdini 19 Solaris to assemble the asset and I'm testing with Karma and Arnold. I came accross an issue that prevents me from using MaterialX entirely. In our USD pipeline, we create a USD layer for the texture department. This layer adds primvars to the assets, and these primvars contain the path to the textures. Later, when the shading department creates a material, they simply read these primvars and use them to drive the texture path parameter of the textures nodes. This workflow works well with Karma and Arnold when using their respective shaders as well as when using the USD shaders. I realized that we can not drive the filename parameter of the MtlX Image and MtlX Tiled Image nodes with a MtlX USD Primvar Reader node. I saw in the documentation that MaterialX supports filename substitution in those nodes, and that it supports a \<geometry token> to use

"The value of a specified token declared in a \<geominfo> element for the current geometry."

I tried different things but could not use this token to read a primvar on the geometry I'm using.

Here is why we want to use this workflow. By having the texture paths in a seperate layer than the shading, it allows us to completely decouple the two pipeline steps. When there are new versions of texture, no one need to update the shading since it reads directly from the texture layer. It also allows us to create texture variants independently from shading variants. Setting the texture variantSet only changes the paths read by the shaders. We can also share the same material on multiple meshes but each one has a different texture.

Just to be clear, I'm not necessarily after a solution to drive the path of a texture node from a shader, but mostly after a solution to use primvars to drive texture paths.

Thanks!

Francois Lord RodeoFX

kwokcb commented 2 years ago

Hi Francois,

The current logic to add in "default" geominfo resolving is here When you create a string resolver you can optionally pass over a geomInfo name to use.

Based on the spec the geomInfo tokens are specified at that level. e.g. from spec doc:

<geominfo name="gi1" geom="/a/g1">
  <token name="txtid" type="string" value="Lengine"/>
</geominfo>

and then reference that token's value in a filename:

<image name="cc1" type="color3">
<input name="file" type="filename" value="txt/color/asset.color.<txtid>.tif"/>
</image>

The current MaterialX USD definitions have this type of basic implementation (string here):

<nodegraph name="IMP_UsdPrimvarReader_string" nodedef="ND_UsdPrimvarReader_string">
    <geompropvalue name="primvar" type="string">
        <input name="geomprop" type="string" interfacename="varname" />
    </geompropvalue>
    <output name="out" type="string" nodename="primvar" />
  </nodegraph>

I think you want to create a geominfo association like this:

 <geominfo name="usegeominfo1" geom="/">
   <token name="txtid" type="string" value="Lengine"/> 
   <geomprop name="usdgeomprop1" type="string" value="Lengine">
</geominfo>

<UsdPrimvarReader name="usdprimvar" type="string">
    <input name="varname" value="usdgeomprop1"/>
</UsdPrimvarReader>

This is "a" way to associate the node with a geomprop which is part of a geominfo which has tokens :) This is just by looking at code, so others should probably comment on this as token support is proposed to change a bit for 1.39.

One thing I noted @jstone-lucasfilm, @dbsmythe is that UDIMs are parsed using the geomprop as the values to use for UDIM substitution which is inconsistent with using a token. e.g. a udim specification looks like this, where the token is explicitly hardcoded as <UDIM>:

  <geominfo name="testudimset" geom="/">
    <geomprop name="udimset" type="stringarray" value="1001, 1002" />
  </geominfo>

I would have expected this instead:

` <geominfo name="testudimset" geom="/">
    <token name="udimset" type="stringarray" value="1001, 1002" />
  </geominfo>
spiffmon commented 2 years ago

@flord , I was actually shocked that a UsdPrimvarReader_string feeding a UsdUvTexture's inputs:file was working for you, as this was not a workflow we wanted to support (complications for GPU renderers, and general efficiency), and specify that inputs:file is not connectable (connectability = "interfaceOnly"), though it is not as salient in the specification as it could be, and it is currently up to renderers and GUIs to enforce it, and it sounds like Arnold does not?

Rather, we want to fulfill MaterialX's filename substitutions semantics in USD/Hydra, in a way that works for CPU and GPU renderers efficiently, and on which we can track changes properly for incremental updates. We have some ideas on how we might go about that, but it's a good sized project that is not currently on our 6 month roadmap.

That's not terribly helpful to your immediate goals, I know, but at least it maybe can help us align on a direction to pursue? @bernardkwok , I am not sure whether UsdMtlx handles \<geominfo> elements, as we consider primvars to be part of the geometry, and it sounds like @flord is constructing his assets as USD, so specifying the actual primvars in the native USD way may be sufficient? Getting Hydra to pay attention to them is the issue, here.

rafalSFX commented 2 years ago

For the future version of MaterialX, we were considering to allow connecting a uniform string inputs (eg, of an image texture node, equivalent to USD UV Texture) to a uniform string outputs (eg, of a geompropvalue node, equivalent to USD Primvar Reader).

This was influenced (inspired) by the USD preview shader prims (in v2.0 of the proposal).

Note, the uniform requirement of the Primvar Reader shader implies that only uniform primvars are supported. This is very much equivalent to substitution, but fits entirely in the USD framework of primvars on geometry and primvar readers in the shader network.

flord commented 2 years ago

@spiffmon Arnold does support it in the UsdPrimvarReader shader but only since I asked them to fix it. ;)

ahemberger commented 2 years ago

Hi there;

I was pointed to this thread by a staffmember of sidefx, to whom I was asking for help trying to do the exact thing described in this thread (namely: using a uniform string primvar to drive a texture path parameter on a materialX shader). I too am working in Houdini Solaris, currently 19.5.

The use case I am trying to address includes things like procedurally scattering rocks and trees within a set environment, as well as some procedural construction of geometry using Houdini. It's not practical to create separate shaders for each bit of geometry that might need a different set of textures, so being able to assign one shader and have the geometry drive the required texture paths is much simpler from a scene management perspective. I imagine destruction workflows might have a similar requirement (requiring a separate shader setup for each fragment of a shattered building would be quite complicated to manage).

I'd love to transition my stuff to materialX, to take advantage of Karma XPU, and am trying to understand the current state of the art. I'm curious where this issue stands now, and how other folks might be addressing this kind of work with materialX currently?

Thanks!

spiffmon commented 2 years ago

Hi @ahemberger , renderer-independent support for primvar substitution still does not exist in Hydra, which means you can only use renderer-specific affordances (RenderMan also has its own syntax for primvar substitutions in texture filenames... which will only work for .tex textures).

ahemberger commented 2 years ago

Hey @spiffmon, thanks for this sir!

Let me try to say that back to you, to make sure I understand: the feature I'm asking about (being able to store a constant string primvar on an object, e.g. "basecolor", and then be able to pass the value of that primvar to a MaterialX shader parameter) is contingent on Hydra (side question: is that a thing that's currently on a roadmap somewhere?).

In lieu of this, you're saying that specific renderers may opt to have a specific syntax they look for when parsing MaterialX paramaters. So, for example, "allenh's hypothetical renderer" may allow me to set a MaterialX Shader Path to "<[texture]>", in which case I could store a primvar "<[texture]>" on my geometry and this could possibly work?

I'm trying to understand what question to take back to ask SideFX, as I'm ultimately interested today in rendering in Karma. I understand that you're saying that a different solution may exist if I want to use Renderman.

And, rereading your response: to be clear, I am trying to do this: constant string basecolor "/path/tp/my/basecolor.exr"

Then read the string primvar "basecolor" in my MaterialX shader network. This string value is one I need to only set per-prim in USD...not as point or vertex data.

Thanks!

spiffmon commented 2 years ago

Hi @ahemberger!    @rafalSFX raised this again this morning in the usd-materialx monthly working group, and I think we are all aligned on where we want to go, which is pretty much as you describe.  We want to support the MaterialX-style primvar substitution, always, which would be @/path/to/my/<basecolor>.exr@, and render-delegates would be required to convert that to their own syntax is their texture manager requires something else, or allow Hydra to perform the substitution, pre-render, resulting in material-splitting.

Alas we don't have a date yet for when we'd start the Hydra work, but renderers that do perform their own substitutions at texture-lookup time should, I believe, be able to implement this already.

spiffmon commented 2 years ago

... sorry, there is still potentially a missing piece even for "primvar-substituting renderers", which is that if Hydra is configured to be parsimonious about which primvars it consumes and presents to the renderer, there's not yet a way for the dependency implied by the above assetPath to be recognized.

flord commented 1 year ago

@spiffmon What about the <UDIM> token? SHould it be inside the primvar value or next to the .exr part? e.g.: @/path/to/my/<basecolor>.<UDIM>.exr@

spiffmon commented 1 year ago

Great issue to raise, @flord ! Unless people think it is vitally necessary (and there is no discussion of it in the MaterialX spec), it would be ideal for simplicity and efficiency if the substitutions do not need to be iterative. I.e.:

... so exactly as you propose in your comment: @/path/to/my/<basecolor>.<UDIM>.exr@

Is that reasonable? @jstone-lucasfilm , is this consistent with ILM's support for MaterialX?

jstone-lucasfilm commented 1 year ago

@spiffmon I believe that's correct, and string substitutions in MaterialX were never intended to be applied recursively. I'm CC'ing @dbsmythe to make sure my recollection is correct, and this may be an area where additional clarification in the specification would be beneficial.