BlenderUSDHydraAddon icon indicating copy to clipboard operation
BlenderUSDHydraAddon copied to clipboard

MaterialX materials are not instanced

Open pablode opened this issue 3 years ago • 4 comments

Tested with latest master (1.0.77) and Blender 3.0.1.

To reproduce:

  1. create two cubes and one material with MaterialX graph assigned
  2. assign the material to both cubes
  3. export USD to file
  4. usdedit file or convert to USDA
  5. see that the material has been emitted two times instead of only once

This can lead to very long compile times and slow rendering.

See USDA of the exported file
#usda 1.0
(
    doc = """Generated from Composed Stage of root layer c:\\Users\\pablode\\AppData\\Local\\Temp\\hdusd\\4784\\tmpls_3rcd2.usda
"""
    metersPerUnit = 1
    upAxis = "Z"
)

def Xform "Camera"
{
    matrix4d xformOp:transform = ( (0.6859206557273865, 0.7276763319969177, 0, 0), (-0.32401347160339355, 0.305420845746994, 0.8953956365585327, 0), (0.6515582203865051, -0.6141703724861145, 0.44527140259742737, 0), (7.358891487121582, -6.925790786743164, 4.958309173583984, 1) )
    uniform token[] xformOpOrder = ["xformOp:transform"]

    def Camera "Camera"
    {
        float2 clippingRange = (0.1, 100)
        float focalLength = 50
        float horizontalAperture = 36
        float horizontalApertureOffset = 0
        token projection = "perspective"
        float verticalAperture = 20.25
        float verticalApertureOffset = 0
    }
}

def Xform "Cube"
{
    matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (3.391047239303589, 0, 0, 1) )
    uniform token[] xformOpOrder = ["xformOp:transform"]

    def Mesh "Cube"
    {
        uniform bool doubleSided = 1
        int[] faceVertexCounts = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
        int[] faceVertexIndices = [0, 1, 3, 0, 3, 2, 2, 3, 7, 2, 7, 6, 6, 7, 5, 6, 5, 4, 4, 5, 1, 4, 1, 0, 2, 6, 4, 2, 4, 0, 7, 3, 1, 7, 1, 5]
        rel material:binding = </Cube/Material/Materials/USD_Default>
        normal3f[] normals = [(-1, -0, 0), (-1, -0, 0), (-1, -0, 0), (-1, -0, 0), (-1, -0, 0), (-1, -0, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (1, -0, 0), (1, -0, 0), (1, -0, 0), (1, -0, 0), (1, -0, 0), (1, -0, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1), (0, -0, 1), (0, -0, 1), (0, -0, 1), (0, -0, 1), (0, -0, 1), (0, -0, 1)] (
            interpolation = "faceVarying"
        )
        point3f[] points = [(-1, -1, -1), (-1, -1, 1), (-1, 1, -1), (-1, 1, 1), (1, -1, -1), (1, -1, 1), (1, 1, -1), (1, 1, 1)]
        texCoord2f[] primvars:st = [(0.375, 0), (0.625, 0), (0.625, 0.25), (0.375, 0.25), (0.375, 0.25), (0.625, 0.25), (0.625, 0.5), (0.375, 0.5), (0.375, 0.5), (0.625, 0.5), (0.625, 0.75), (0.375, 0.75), (0.375, 0.75), (0.625, 0.75), (0.625, 1), (0.375, 1), (0.125, 0.5), (0.375, 0.5), (0.375, 0.75), (0.125, 0.75), (0.625, 0.5), (0.875, 0.5), (0.875, 0.75), (0.625, 0.75)] (
            interpolation = "faceVarying"
        )
        int[] primvars:st:indices = [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23]
        uniform token subdivisionScheme = "none"
    }

    def "Material"
    {
        def "Materials"
        {
            def Material "USD_Default"
            {
                float inputs:clearcoat = 0
                float inputs:clearcoatRoughness = 0.01
                color3f inputs:diffuseColor = (0.18, 0.18, 0.18)
                float inputs:displacement = 0
                color3f inputs:emissiveColor = (0, 0, 0)
                float inputs:ior = 1.5
                float inputs:metallic = 0
                float3 inputs:normal = (0, 0, 1)
                float inputs:occlusion = 1
                float inputs:opacity = 1
                float inputs:opacityThreshold = 0
                float inputs:roughness = 0.5
                color3f inputs:specularColor = (0, 0, 0)
                int inputs:useSpecularWorkflow = 0
                token outputs:mtlx:surface.connect = </Cube/Material/Materials/USD_Default/ND_UsdPreviewSurface_surfaceshader.outputs:surface>

                def Shader "ND_UsdPreviewSurface_surfaceshader"
                {
                    uniform token info:id = "ND_UsdPreviewSurface_surfaceshader"
                    float inputs:clearcoat.connect = </Cube/Material/Materials/USD_Default.inputs:clearcoat>
                    float inputs:clearcoatRoughness.connect = </Cube/Material/Materials/USD_Default.inputs:clearcoatRoughness>
                    color3f inputs:diffuseColor.connect = </Cube/Material/Materials/USD_Default.inputs:diffuseColor>
                    float inputs:displacement.connect = </Cube/Material/Materials/USD_Default.inputs:displacement>
                    color3f inputs:emissiveColor.connect = </Cube/Material/Materials/USD_Default.inputs:emissiveColor>
                    float inputs:ior.connect = </Cube/Material/Materials/USD_Default.inputs:ior>
                    float inputs:metallic.connect = </Cube/Material/Materials/USD_Default.inputs:metallic>
                    float3 inputs:normal.connect = </Cube/Material/Materials/USD_Default.inputs:normal>
                    float inputs:occlusion.connect = </Cube/Material/Materials/USD_Default.inputs:occlusion>
                    float inputs:opacity.connect = </Cube/Material/Materials/USD_Default.inputs:opacity>
                    float inputs:opacityThreshold.connect = </Cube/Material/Materials/USD_Default.inputs:opacityThreshold>
                    float inputs:roughness.connect = </Cube/Material/Materials/USD_Default.inputs:roughness>
                    color3f inputs:specularColor.connect = </Cube/Material/Materials/USD_Default.inputs:specularColor>
                    int inputs:useSpecularWorkflow.connect = </Cube/Material/Materials/USD_Default.inputs:useSpecularWorkflow>
                    token outputs:surface
                }
            }
        }

        def "Shaders"
        {
            def Shader "ND_UsdPreviewSurface_surfaceshader"
            {
                uniform token info:id = "ND_UsdPreviewSurface_surfaceshader"
                token outputs:surface
            }
        }
    }
}

def Xform "Cube_001"
{
    matrix4d xformOp:transform = ( (1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (-4.016847610473633, 0, 0, 1) )
    uniform token[] xformOpOrder = ["xformOp:transform"]

    def Mesh "Cube_001"
    {
        uniform bool doubleSided = 1
        int[] faceVertexCounts = [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
        int[] faceVertexIndices = [0, 1, 3, 0, 3, 2, 2, 3, 7, 2, 7, 6, 6, 7, 5, 6, 5, 4, 4, 5, 1, 4, 1, 0, 2, 6, 4, 2, 4, 0, 7, 3, 1, 7, 1, 5]
        rel material:binding = </Cube_001/Material/Materials/USD_Default>
        normal3f[] normals = [(-1, -0, 0), (-1, -0, 0), (-1, -0, 0), (-1, -0, 0), (-1, -0, 0), (-1, -0, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (0, 1, 0), (1, -0, 0), (1, -0, 0), (1, -0, 0), (1, -0, 0), (1, -0, 0), (1, -0, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (0, -1, 0), (0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1), (0, 0, -1), (0, -0, 1), (0, -0, 1), (0, -0, 1), (0, -0, 1), (0, -0, 1), (0, -0, 1)] (
            interpolation = "faceVarying"
        )
        point3f[] points = [(-1, -1, -1), (-1, -1, 1), (-1, 1, -1), (-1, 1, 1), (1, -1, -1), (1, -1, 1), (1, 1, -1), (1, 1, 1)]
        texCoord2f[] primvars:st = [(0.375, 0), (0.625, 0), (0.625, 0.25), (0.375, 0.25), (0.375, 0.25), (0.625, 0.25), (0.625, 0.5), (0.375, 0.5), (0.375, 0.5), (0.625, 0.5), (0.625, 0.75), (0.375, 0.75), (0.375, 0.75), (0.625, 0.75), (0.625, 1), (0.375, 1), (0.125, 0.5), (0.375, 0.5), (0.375, 0.75), (0.125, 0.75), (0.625, 0.5), (0.875, 0.5), (0.875, 0.75), (0.625, 0.75)] (
            interpolation = "faceVarying"
        )
        int[] primvars:st:indices = [0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23]
        uniform token subdivisionScheme = "none"
    }

    def "Material"
    {
        def "Materials"
        {
            def Material "USD_Default"
            {
                float inputs:clearcoat = 0
                float inputs:clearcoatRoughness = 0.01
                color3f inputs:diffuseColor = (0.18, 0.18, 0.18)
                float inputs:displacement = 0
                color3f inputs:emissiveColor = (0, 0, 0)
                float inputs:ior = 1.5
                float inputs:metallic = 0
                float3 inputs:normal = (0, 0, 1)
                float inputs:occlusion = 1
                float inputs:opacity = 1
                float inputs:opacityThreshold = 0
                float inputs:roughness = 0.5
                color3f inputs:specularColor = (0, 0, 0)
                int inputs:useSpecularWorkflow = 0
                token outputs:mtlx:surface.connect = </Cube_001/Material/Materials/USD_Default/ND_UsdPreviewSurface_surfaceshader.outputs:surface>

                def Shader "ND_UsdPreviewSurface_surfaceshader"
                {
                    uniform token info:id = "ND_UsdPreviewSurface_surfaceshader"
                    float inputs:clearcoat.connect = </Cube_001/Material/Materials/USD_Default.inputs:clearcoat>
                    float inputs:clearcoatRoughness.connect = </Cube_001/Material/Materials/USD_Default.inputs:clearcoatRoughness>
                    color3f inputs:diffuseColor.connect = </Cube_001/Material/Materials/USD_Default.inputs:diffuseColor>
                    float inputs:displacement.connect = </Cube_001/Material/Materials/USD_Default.inputs:displacement>
                    color3f inputs:emissiveColor.connect = </Cube_001/Material/Materials/USD_Default.inputs:emissiveColor>
                    float inputs:ior.connect = </Cube_001/Material/Materials/USD_Default.inputs:ior>
                    float inputs:metallic.connect = </Cube_001/Material/Materials/USD_Default.inputs:metallic>
                    float3 inputs:normal.connect = </Cube_001/Material/Materials/USD_Default.inputs:normal>
                    float inputs:occlusion.connect = </Cube_001/Material/Materials/USD_Default.inputs:occlusion>
                    float inputs:opacity.connect = </Cube_001/Material/Materials/USD_Default.inputs:opacity>
                    float inputs:opacityThreshold.connect = </Cube_001/Material/Materials/USD_Default.inputs:opacityThreshold>
                    float inputs:roughness.connect = </Cube_001/Material/Materials/USD_Default.inputs:roughness>
                    color3f inputs:specularColor.connect = </Cube_001/Material/Materials/USD_Default.inputs:specularColor>
                    int inputs:useSpecularWorkflow.connect = </Cube_001/Material/Materials/USD_Default.inputs:useSpecularWorkflow>
                    token outputs:surface
                }
            }
        }

        def "Shaders"
        {
            def Shader "ND_UsdPreviewSurface_surfaceshader"
            {
                uniform token info:id = "ND_UsdPreviewSurface_surfaceshader"
                token outputs:surface
            }
        }
    }
}

def "World"
{
    def DomeLight "World"
    {
        color3f inputs:color = (0.05087609, 0.05087609, 0.05087609)
        float inputs:intensity = 1
        custom float inputs:transparency = 1
        float xformOp:rotateX = 180
        float xformOp:rotateX:orientToStageUpAxis = 90
        float xformOp:rotateY = -90
        uniform token[] xformOpOrder = ["xformOp:rotateX:orientToStageUpAxis", "xformOp:rotateX", "xformOp:rotateY"]
    }
}

pablode avatar Mar 05 '22 16:03 pablode

Hi.

Thanks for report. Now this is expected behavior - materials are created exclusively for every object.

DagerD avatar Mar 09 '22 10:03 DagerD

Why? This disallows serious work based on exported scenes.

pablode avatar May 09 '22 21:05 pablode

Hello, initially we have separate USD prim with all materials. But we had significant reasons to change it to the way: object has its own material. One of the reason is USD node tree where we have different nodes including Merge, Filter, Instancing, Root. Using separate global USD prim for materials produces problems of using such nodes.

bnagirniak avatar May 12 '22 09:05 bnagirniak

Thanks for the answer, this definitely makes sense!

But I think that instancing is possible even without a root-level USD prim. I've crafted an example here:
#usda 1.0
(
    defaultPrim = "Geom"
    metersPerUnit = 1
    upAxis = "Y"
)

def Scope "Geom"
{
    def Xform "node1"
    {
        def Mesh "mesh"
        {
            def Material "someMaterial"
            {
                token outputs:surface.connect = </Geom/node1/mesh/someMaterial/node.outputs:surface>

                def Shader "node"
                {
                    uniform token info:id = "UsdPreviewSurface"
                    float3 inputs:diffuseColor = (1.0, 0.0, 0.0)
                    token outputs:surface
                }
            }

            int[] faceVertexCounts = [3, 3]
            int[] faceVertexIndices = [1, 0, 3, 1, 3, 2]
            normal3f[] normals = [(0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)] (interpolation = "vertex")
            point3f[] points = [(0.5, -0.5, 0), (-0.5, -0.5, 0), (-0.5, 0.5, 0), (0.5, 0.5, 0)]
            uniform token subdivisionScheme = "none"

            rel material:binding:preview = </Geom/node1/mesh/someMaterial>
        }
    }

    def Xform "node2"
    {
        def Mesh "mesh"
        {
            over "someMaterial" (
                references = </Geom/node1/mesh/someMaterial>
            )
            {
            }

            int[] faceVertexCounts = [3, 3]
            int[] faceVertexIndices = [1, 0, 3, 1, 3, 2]
            normal3f[] normals = [(0, 0, 1), (0, 0, 1), (0, 0, 1), (0, 0, 1)] (interpolation = "vertex")
            point3f[] points = [(0.5, -0.5, 1), (-0.5, -0.5, 1), (-0.5, 0.5, 1), (0.5, 0.5, 1)]
            uniform token subdivisionScheme = "none"

            rel material:binding:preview = </Geom/node2/mesh/someMaterial>
        }
    }
}

EDIT: after tinkering with this a bit more I realized that true material instancing with the instanceable metadatum, unlike the use of inheritance I outlined, does require a root level prim..

pablode avatar May 18 '22 18:05 pablode