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.87k stars 352 forks source link

Graph Editor: `Implicit cast from "vec3" to "vec2"` - using texcoord both as vector3 and vector2 #1464

Closed marwie closed 1 year ago

marwie commented 1 year ago

This seems to be related to texcoord 0 being used for the normalmap as well as the basecolor - I'm still investigating this and will try to reproduce it with a fresh material but for the sake of documentation I'll open an issue here now šŸ˜…

image

<?xml version="1.0"?>
<materialx name="" version="1.38" >
    <nodegraph name="needle_shadergraph" >
        <!-- MainTex -->
        <input name="_MainTex" type="filename" value="Tile_Albedo.jpg" />
        <dot name="dot__MainTex" type="filename" >
            <input name="in" type="filename" interfacename="_MainTex" />
        </dot>
        <texcoord name="uv" type="vector3" >
            <input name="index" type="integer" value="0" />
        </texcoord>
        <convert name="convert_to_vector2" type="vector2" >
            <input name="in" type="vector3" nodename="uv" />
        </convert>
        <image name="tex" type="color4" >
            <input name="file" type="filename" nodename="dot__MainTex" />
            <input name="texcoord" type="vector2" nodename="convert_to_vector2" />
        </image>
        <convert name="convert_to_color3" type="color3" >
            <input name="in" type="color4" nodename="tex" />
        </convert>
        <output name="out" type="color3" nodename="convert_to_color3" />
        <!-- Normal -->
        <input name="_Normal" type="filename" value="Tile_Normal.jpg" />
        <dot name="dot__Normal" type="filename" >
            <input name="in" type="filename" interfacename="_Normal" />
        </dot>
        <texcoord name="uv_2" type="vector2" >
            <input name="index" type="integer" value="0" />
        </texcoord>
        <image name="tex_1" type="color4" >
            <input name="file" type="filename" nodename="dot__Normal" />
            <input name="texcoord" type="vector2" nodename="uv_2" />
        </image>
        <swizzle name="swizzle_0_tex_1" type="vector3" >
            <input name="in" type="color4" nodename="tex_1" />
            <input name="channels" type="string" value="rgb" />
        </swizzle>
        <output name="out_1" type="vector3" nodename="normalmap" />
        <normalmap name="normalmap" type="vector3" >
            <input name="in" type="vector3" nodename="swizzle_0_tex_1" />
        </normalmap>
        <constant name="specular_roughness" type="float" >
            <input name="value" type="float" value="0.0" />
        </constant>
        <output name="out_2" type="float" nodename="invert" />
        <invert name="invert" type="float" >
            <input name="in" type="float" nodename="specular_roughness" />
        </invert>
    </nodegraph>
    <standard_surface name="needle_standard_surface" type="surfaceshader" >
        <!-- BaseColor -->
        <input name="base_color" type="color3" nodegraph="needle_shadergraph" output="out" />
        <!-- NormalTS -->
        <input name="normal" type="vector3" nodegraph="needle_shadergraph" output="out_1" />
        <!-- Smoothness -->
        <input name="specular_roughness" type="float" nodegraph="needle_shadergraph" output="out_2" />
    </standard_surface>
    <surfacematerial name="Default" type="material" >
        <input name="surfaceshader" type="surfaceshader" nodename="needle_standard_surface" />
    </surfacematerial>
</materialx>
Textures ![Tile_Albedo](https://github.com/AcademySoftwareFoundation/MaterialX/assets/5083203/17b6f8f0-9b64-468c-aa53-05ff758d97a2) ![Tile_Normal](https://github.com/AcademySoftwareFoundation/MaterialX/assets/5083203/8f66cff8-8416-4bea-9eef-f6708e6d8108)
marwie commented 1 year ago

The material compiles when I remove one of the texcoord usages (both nodes use channel 0 but one is vector2 and one is vector3 )

image

marwie commented 1 year ago

Here is a minimal material that reproduces the issue above

image

<?xml version="1.0"?>
<materialx version="1.38" colorspace="lin_rec709">
  <standard_surface name="SR_marble1" type="surfaceshader" xpos="6.159420" ypos="-0.568965">
    <input name="base" type="float" value="1" />
    <input name="base_color" type="color3" nodename="image_color3" />
    <input name="specular_roughness" type="float" value="0.1" />
    <input name="subsurface" type="float" value="0.4" />
    <input name="subsurface_color" type="color3" nodename="image_color4" />
  </standard_surface>
  <surfacematerial name="Marble_3D" type="material" xpos="8.695652" ypos="0.000000">
    <input name="surfaceshader" type="surfaceshader" nodename="SR_marble1" />
  </surfacematerial>
  <image name="image_color3" type="color3" xpos="3.528986" ypos="-1.241379">
    <input name="file" type="filename" value="Tile_Albedo.jpg" />
    <input name="texcoord" type="vector2" nodename="texcoord_vector2" />
  </image>
  <texcoord name="texcoord_vector2" type="vector2" xpos="1.666667" ypos="-1.413793" />
  <texcoord name="texcoord_vector3" type="vector3" xpos="0.275362" ypos="1.077586" />
  <image name="image_color4" type="color3" xpos="3.485507" ypos="1.905172">
    <input name="file" type="filename" value="Tile_Albedo.jpg" />
    <input name="texcoord" type="vector2" nodename="swizzle_vector3_vector2" />
  </image>
  <swizzle name="swizzle_vector3_vector2" type="vector2" xpos="1.637681" ypos="1.767241">
    <input name="in" type="vector3" nodename="texcoord_vector3" />
    <input name="channels" type="string" value="xy" />
  </swizzle>
</materialx>
kwokcb commented 1 year ago

This is the generated GLSL code snippet:


void main()
{
  in VertexData
  {
      vec3 normalWorld;
      vec3 tangentWorld;
      vec2 texcoord_0;
      vec3 positionWorld;
  } vd;

    vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
    vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
    vec2 texcoord_vector2_out = vd.texcoord_0;
    vec3 texcoord_vector3_out = vd.texcoord_0;
    vec3 image_color3_out = vec3(0.0);

Seem we are declaring one stream input (vec2) and then skipping adding in the second (vec3) and then reusing the same stream route. Hence there is a vec2 to vec3 cast.

Adding @niklasharrysson, for thoughts on this.

I'm guessing that a type check is not being performed when determingin whether to create a geomstry stream ShaderNode?

BTW @marwie, The MaterialX Viewer has more diagnostic functionality as it allows you to do things like dump out the GLSL code (G key) if case you find this useful.

kwokcb commented 1 year ago

As suggested offline to avoid this declare unique stream inputs which are type vec3 as a workaround for now.

madmann91 commented 1 year ago

What happens here is that VariableBlock::add is called twice, once by each texcoord node, to create the necessary stage variables. The first call obviously wins, and depending on which texture node is first, texcoord_0 gets either a vec3 or vec2 type. Then, each node proceeds to generating code via TexCoordNodeGlsl::emitFunctionCall, which assigns the output of the node to texcoord_0, resulting in an error for the node that has the wrong type. It might make sense to either:

I'd be happy to work on this as a first issue for the dev days. If you think that makes sense, please let me know what you think about either solution.

kwokcb commented 1 year ago

Hi @madmann91,

Thanks a lot for taking a look at this!

I think the choices are:

  1. As you suggest, always keep vec3 and add code to extract a vec2 from vec3 if the lookup is vec2. (Basically you want to avoid explicit casts again). I like the idea to widen only when necessary.

  2. Keep both a vec2 and a vec3. This means no code generator changes are required but there needs to be a way to avoid 2 streams having the same name. The published naming convention indicates a renderer can bind to a stream with name of the form: <stream_type>_<stream_number>. All I can think of is to add an additional qualifier to get something like texcoord_0:2, texcoord_0:3. Binding code needs to be updated.

I'm leaning towards 1 since it does not affect integrators.

In this case vec3->vec2 conversion can be done by:

1a. inserting additional shader code on lookups, 1b. replace/insert a vec3-to-vec2 conversion ShaderNode instead of a texcoord ShaderNode . ShaderNodes only exist for code generation and generators do insert additional nodes as necessary including geometry nodes.

Option 1b allows you to not worry about each shading language's syntax and I think it should be pretty robust. There may be a 1c. but this is all that comes to mind currently :)

If this is okay to work on, it would great if you look at this for dev days. Of course there will be folks around to help out in this area :)