Blender AI Wiki / English Поиск

USD / MATERIALX / OPENUSD PIPELINE

OpenUSD pipeline for Blender → USD → Houdini/Maya/Katana → Render

📦 USD 10 блоков кода обновлено 2026-10-09

OpenUSD pipeline for Blender → USD → Houdini/Maya/Katana → Render


🎯 USD IN BLENDER

Blender USD Support (4.0+)

Blender USD Features:
├── Export: Full scene, selection, collections
├── Import: Full scene, specific prims, variants
├── Materials: Principled BSDF → MaterialX / UsdPreviewSurface
├── Animation: Full animation, shape keys, constraints
├── Cameras: Full camera data, DOF, stereo
├── Lights: All light types, IES profiles
├── Volumes: VDB, OpenVDB, custom volumes
├── Instancing: Native USD instancing, point instancer
├── Variants: Variant sets, variant selection
├── Composition: References, payloads, sublayers
├── Custom Data: Custom properties, custom attributes
└── Hydra: Viewport rendering via Hydra delegates

⚙️ USD EXPORT

Basic Export

import bpy
from pathlib import Path

def export_usd(
    filepath: str,
    selection_only: bool = False,
    export_materials: bool = True,
    export_uvmaps: bool = True,
    export_animation: bool = True,
    export_frame_range: bool = True,
    export_frame_step: int = 1,
    export_uvmaps: bool = True,
    export_normals: bool = True,
    export_colors: bool = True,
    export_animation: bool = True,
    export_skinning: bool = True,
    export_uvmaps: bool = True,
    export_display_color: bool = True,
    export_instancing: bool = True,
    export_visibility: bool = True,
    export_custom_properties: bool = True,
    export_custom_attributes: bool = True,
    export_materials: bool = True,
    export_uvmaps: bool = True,
    export_animation: bool = True,
    export_frame_range: bool = True,
    export_frame_step: int = 1,
    # MaterialX
    material_x: bool = True,
    shading_mode: str = 'USE_MATERIAL_X',  # 'USE_MATERIAL_X', 'USE_PREVIEW', 'USE_LOOKDEV'
    # Purpose
    purpose: str = 'default',  # 'default', 'render', 'proxy', 'guide'
    # Instancing
    export_instancing: bool = True,
    # Visibility
    export_visibility: bool = True,
    # Custom Properties
    export_custom_properties: bool = True,
    export_custom_attributes: bool = True,
):
    """Export USD with full options"""
    
    Path(filepath).parent.mkdir(parents=True, exist_ok=True)
    
    bpy.ops.wm.usd_export(
        filepath=filepath,
        selection_only=selection_only,
        export_materials=export_materials,
        export_uvmaps=export_uvmaps,
        export_animation=export_animation,
        export_frame_range=export_frame_range,
        export_frame_step=export_frame_step,
        export_uvmaps=export_uvmaps,
        export_normals=export_normals,
        export_colors=export_colors,
        export_animation=export_animation,
        export_skinning=True,
        export_uvmaps=True,
        export_display_color=True,
        export_instancing=export_instancing,
        export_visibility=export_visibility,
        export_custom_properties=export_custom_properties,
        export_custom_attributes=export_custom_attributes,
        export_materials=export_materials,
        export_uvmaps=export_uvmaps,
        export_animation=export_animation,
        export_frame_range=export_frame_range,
        export_frame_step=export_frame_step,
        # MaterialX
        material_x=material_x,
        shading_mode=shading_mode,
        # Purpose
        purpose=purpose,
        export_instancing=export_instancing,
        export_visibility=export_visibility,
        export_custom_properties=export_custom_properties,
        export_custom_attributes=export_custom_attributes,
    )

# Usage
export_usd("/project/assets/hero/hero_v001.usda", selection_only=True)

Export with Variants

def export_usd_with_variants(filepath, variant_sets=None):
    """
    variant_sets = {
        "modeling": ["high", "low", "proxy"],
        "material": ["clean", "damaged", "rusty"],
        "lod": ["LOD0", "LOD1", "LOD2", "LOD3"]
    }
    """
    # Create variant sets in USD
    # This requires USD Python API (pxr)
    from pxr import Usd, UsdGeom, Sdf
    
    stage = Usd.Stage.CreateNew(filepath)
    
    # Define variant sets
    if variant_sets:
        for set_name, variants in variant_sets.items():
            variant_set = stage.GetRootPrim().GetVariantSets().AddVariantSet(set_name)
            for variant in variants:
                variant_set.AddVariant(variant)
                variant_set.SetVariantSelection(variant)
                with variant_set.GetVariantEditContext():
                    # Add variant-specific content
                    pass
    
    stage.GetRootLayer().Save()

# Usage
export_usd_with_variants("/assets/hero/hero.usda", {
    "modeling": ["high", "low", "proxy"],
    "material": ["clean", "dirty", "wet"],
    "lod": ["LOD0", "LOD1", "LOD2", "LOD3"]
})

🎨 MATERIALX IN BLENDER

MaterialX Export

def export_materialx(material_name, filepath):
    """Export Blender material as MaterialX"""
    mat = bpy.data.materials[material_name]
    
    if not mat.use_nodes:
        raise ValueError("Material must use nodes")
    
    # Ensure MaterialX output
    mat.node_tree.nodes['Material Output'].target = 'MATERIALX'
    
    # Export via USD with MaterialX
    bpy.ops.wm.usd_export(
        filepath=f"{filepath}.usda",
        material_x=True,
        shading_mode='USE_MATERIAL_X',
        selection_only=True,
    )

# MaterialX Standard Surface
def create_materialx_standard_surface(mat_name):
    """Create MaterialX-compatible standard surface"""
    mat = bpy.data.materials.new(name=mat_name)
    mat.use_nodes = True
    nodes = mat.node_tree.nodes
    links = mat.node_tree.links
    nodes.clear()
    
    # Standard Surface (MaterialX compatible)
    standard = nodes.new('ShaderNodeBsdfPrincipled')
    standard.location = (0, 0)
    
    # MaterialX outputs
    output = nodes.new('ShaderNodeOutputMaterial')
    output.location = (400, 0)
    output.target = 'MATERIALX'
    
    # Connect
    mat.node_tree.links.new(standard.outputs['BSDF'], output.inputs['Surface'])
    
    # MaterialX specific: Use standard surface parameters
    # base_color, specular_color, specular_roughness, metallic, etc.
    
    return mat

MaterialX Node Reference

# MaterialX Nodes in Blender (4.2+)
# -----------------------------------
# Standard Surface: mx_standard_surface
# Normal Map: mx_normal_map
# Displacement: mx_displacement
# Image: mx_image
# Color Correct: mx_color_correct
# Mix: mx_mix
# Switch: mx_switch
# Condition: mx_condition
# Transform: mx_transform
# Position: mx_position
# Normal: mx_normal
# Geometric Properties: mx_geometric_properties

# Example: MaterialX Network
def create_materialx_network():
    mat = bpy.data.materials.new("MX_Standard")
    mat.use_nodes = True
    nodes = mat.node_tree.nodes
    links = mat.node_tree.links
    nodes.clear()
    
    # MaterialX Standard Surface
    standard = nodes.new('ShaderNodeBsdfPrincipled')
    standard.location = (0, 0)
    # Note: Blender's Principled maps to MX standard_surface
    
    # Image Texture
    tex = nodes.new('ShaderNodeTexImage')
    tex.location = (-400, 0)
    
    # Color Correct
    cc = nodes.new('ShaderNodeColorBalance')
    cc.location = (-200, 0)
    cc.correction_method = 'LIFT_GAMMA_GAIN'
    
    # Output
    output = nodes.new('ShaderNodeOutputMaterial')
    output.location = (200, 0)
    output.target = 'MATERIALX'
    
    # Links
    links.new(tex.outputs['Color'], cc.inputs['Color'])
    links.new(cc.outputs['Color'], standard.inputs['Base Color'])
    links.new(standard.outputs['BSDF'], output.inputs['Surface'])
    
    return mat

🏗️ USD PIPELINE

Asset Publishing

# usd_pipeline.py
"""
USD Pipeline: Blender → USD → Houdini/Maya/Katana → Render
"""

class USDPipeline:
    def __init__(self, project_root: str):
        self.project_root = Path(project_root)
        self.usd_dir = self.project_root / "usd"
        self.usd_dir.mkdir(exist_ok=True)
    
    def publish_asset(self, asset_name: str, version: str, asset_type: str):
        """Publish asset as USD with versioning"""
        # Versioned path: /assets/props/crate/v003/crate_v003.usda
        version_dir = self.usd_dir / "assets" / asset_type / asset_name / f"v{version}"
        version_dir.mkdir(parents=True, exist_ok=True)
        
        usd_path = version_dir / f"{asset_name}_v{version}.usda"
        
        # Export current selection
        self.export_usd_with_materialx(str(version_dir / f"{asset_name}_v{version}.usda"))
        
        # Create symlink to latest
        latest_link = self.usd_dir / "assets" / asset_type / asset_name / "latest.usda"
        if latest_link.exists():
            latest_link.unlink()
        latest_link.symlink_to(usd_path.relative_to(latest_link.parent))
        
        return str(usd_path)
    
    def assemble_shot(self, shot_name: str, layout_data: list):
        """Assemble shot from published assets"""
        shot_dir = self.usd_dir / "shots" / shot_name
        shot_dir.mkdir(parents=True, exist_ok=True)
        
        shot_usd = shot_dir / f"{shot_name}.usda"
        self.assemble_shot(shot_name, layout_data, str(shot_usd))
        
        return str(shot_usd)
    
    def assemble_shot(self, shot_name: str, layout_data: list, output_path: str):
        """Assemble shot from published assets"""
        import pxr.Usd as Usd
        import pxr.UsdGeom as UsdGeom
        import pxr.Sdf as Sdf
        
        stage = Usd.Stage.CreateNew(output_path)
        stage.SetDefaultPrim(stage.DefinePrim(f"/{shot_name}", "Scope"))
        stage.SetDefaultPrim(stage.GetPrimAtPath(f"/{shot_name}"))
        
        # Add assets as references
        for i, asset in enumerate(layout_data):
            prim_path = f"/{shot_name}/assets/{asset['name']}_{i:03d}"
            prim = stage.DefinePrim(prim_path, "Xform")
            prim.GetReferences().AddReference(asset['usd_path'])
            
            # Set transform
            import pxr.UsdGeom as UsdGeom
            xform = UsdGeom.Xformable(prim)
            xform.AddTranslateOp().Set(asset.get('location', (0,0,0)))
            xform.AddRotateXYZOp().Set(asset.get('rotation', (0,0,0)))
            xform.AddScaleOp().Set(asset.get('scale', (1,1,1)))
        
        stage.GetRootLayer().Save()
        print(f"USD scene assembled: {output_path}")

# Usage
pipeline = USDPipeline("/project")
pipeline.publish_asset("hero", "003", "characters")
pipeline.assemble_shot("sh0010", [
    {"name": "hero", "usd_path": "/project/usd/assets/characters/hero/v003/hero_v003.usda", 
     "location": (0, 0, 0), "rotation": (0, 0, 0), "scale": (1, 1, 1)},
    {"name": "environment", "usd_path": "/project/usd/assets/environments/city/v001/city_v001.usda",
     "location": (0, 0, 0), "rotation": (0, 0, 0), "scale": (1, 1, 1)},
])

🎨 MATERIALX WORKFLOWS

MaterialX in Blender → Houdini

# Blender → MaterialX → Houdini
"""
1. Blender: Create MaterialX-compatible shader
2. Export USD with material_x=True
3. Houdini: USD Import → MaterialX Shader Builder
4. Houdini: Assign to geometry
"""

# Houdini Python (for reference)
"""
# In Houdini Python Shell:
import hou

# Load USD
node = hou.node('/stage').createNode('usdimport', 'import_asset')
node.parm('filepath1').set('/assets/hero/hero_v003.usda')

# MaterialX will be imported as MaterialX Shader
# Can edit in MaterialX Shader Builder
"""

MaterialX → Arnold / RenderMan / Karma

# MaterialX → Arnold
"""
Arnold 7+ supports MaterialX natively:
1. Export .mtlx from Blender
2. Arnold: MaterialX Shader → Load .mtlx
4. Assign to geometry
"""

# MaterialX → RenderMan
"""
RenderMan 25+:
1. PxrMaterialX node
5. Load .mtlx file
6. Connect to PxrSurface
"""

# MaterialX → Karma (Houdini)
"""
Karma (Houdini's render engine):
1. Native MaterialX support
2. MaterialX Shader Builder
3. Live editing in viewport
"""

🎬 USD SCENE ASSEMBLY

Shot Assembly

def assemble_shot_usd(shot_name, layout_data, output_path):
    """Assemble shot from asset references"""
    from pxr import Usd, UsdGeom, Sdf, Gf
    
    stage = Usd.Stage.CreateNew(output_path)
    stage.SetDefaultPrim(stage.DefinePrim(f"/{shot_name}", "Scope"))
    stage.SetDefaultPrim(stage.GetPrimAtPath(f"/{shot_name}"))
    
    # Add assets as references
    for i, asset in enumerate(layout_data):
        prim_path = f"/{shot_name}/assets/{asset['name']}_{i:03d}"
        prim = stage.DefinePrim(prim_path, "Xform")
        prim.GetReferences().AddReference(asset['usd_path'])
        
        # Set transform
        xform = UsdGeom.Xformable(prim)
        xform.AddTranslateOp().Set(Gf.Vec3d(*asset.get('location', (0,0,0))))
        xform.AddRotateXYZOp().Set(Gf.Vec3f(*asset.get('rotation', (0,0,0))))
        xform.AddScaleOp().Set(Gf.Vec3f(*asset.get('scale', (1,1,1))))
    
    # Add lights
    for light_data in layout_data.get('lights', []):
        light_path = f"/{shot_name}/lights/{light_data['name']}"
        light_prim = stage.DefinePrim(light_path, "RectLight")
        light_prim.GetAttribute("intensity").Set(light_data['intensity'])
        light_prim.GetAttribute("color").Set(Gf.Vec3f(*light_data['color']))
        light_prim.GetAttribute("width").Set(light_data.get('width', 1.0))
        light_prim.GetAttribute("height").Set(light_data.get('height', 1.0))
    
    # Add camera
    cam_data = layout_data.get('camera', {})
    if cam_data:
        cam_path = f"/{shot_name}/camera"
        cam_prim = stage.DefinePrim(cam_path, "Camera")
        cam_prim.GetAttribute("focalLength").Set(cam_data.get('focal_length', 50))
        cam_prim.GetAttribute("horizontalAperture").Set(cam_data.get('aperture', 36))
        cam_prim.GetAttribute('focusDistance').Set(cam_data.get('focus_distance', 10))
        cam_prim.GetAttribute('fStop').Set(cam_data.get('fstop', 2.8))
    
    # Save
    stage.GetRootLayer().Save()
    print(f"Shot USD assembled: {output_path}")

# Usage
assemble_shot_usd("sh0010", {
    "assets": [
        {"name": "hero", "usd_path": "/assets/hero/hero_v003.usda", 
         "location": (0, 0, 0), "rotation": (0, 0, 0), "scale": (1, 1, 1)},
        {"name": "environment", "usd_path": "/assets/env/city_v001.usda",
         "location": (0, 0, 0), "rotation": (0, 0, 0), "scale": (1, 1, 1)},
    ],
    "lights": [
        {"name": "key_light", "intensity": 5000, "color": (1.0, 0.95, 0.9), "width": 2, "height": 2},
        {"name": "fill_light", "intensity": 1500, "color": (0.9, 0.95, 1.0), "width": 4, "height": 4},
    ],
    "camera": {"focal_length": 35, "aperture": 36, "focus_distance": 5, "fstop": 2.8}
}, "/project/shots/sh0010/sh0010.usda")

🔧 USD PYTHON API (PXR)

Common Operations

from pxr import Usd, UsdGeom, UsdShade, Sdf, Gf, Vt

# Open Stage
stage = Usd.Stage.Open("/path/to/scene.usda")

# Traverse
for prim in stage.Traverse():
    print(prim.GetPath(), prim.GetTypeName())

# Find Prims
prim = stage.GetPrimAtPath("/path/to/prim")

# Attributes
attr = prim.GetAttribute("attr_name")
value = attr.Get()
attr.Set(new_value)

# Relationships
rel = prim.GetRelationship("rel_name")
targets = rel.GetTargets()
rel.SetTargets([Sdf.Path("/new/target")])

# Variants
variant_set = prim.GetVariantSets().AddVariantSet("modeling")
variant_set.AddVariant("high")
variant_set.SetVariantSelection("high")
with variant_set.GetVariantEditContext():
    # Edit high-res variant
    pass

# Composition
stage.GetRootLayer().subLayerPaths.append("./layer.usda")

# Save
stage.GetRootLayer().Save()

[!NOTE]

  • USD Python API: если ссылка не работает, используйте поиск на openusd.org → Documentation → API
  • Kitware Hydra репозиторий может быть в нижнем регистре: github.com/kitware/hydra

🌍 MULTILANG

Language File
English multilang/en/usd/materialx.md
Russian multilang/ru/usd/materialx.md

Связанные материалы

Подобраны по тегам и категории.