Source code for axe_usd.usd.material_builders.arnold

"""Arnold shader network builder."""

from typing import Optional

from pxr import Sdf, UsdShade

from .base import (
    ARNOLD_DISPLACEMENT_BUMP,
    MaterialBuildContext,
    RENDERER_ARNOLD,
    _connect_nodegraph_output,
    _iter_textures,
)
from .arnold_defaults import initialize_standard_surface
from ..material_model import apply_texture_format_override

ARNOLD_INPUTS = {
    "basecolor": "base_color",
    "emission": "emission_color",
    "metalness": "base_metalness",
    "roughness": "specular_roughness",
    "normal": "normal",
    "opacity": "opacity",
    "displacement": "height",
}


[docs] class ArnoldBuilder: def __init__(self, context: MaterialBuildContext) -> None: self._context = context
[docs] def build(self, collect_path: str) -> UsdShade.NodeGraph: stage = self._context.stage override = self._context.texture_format_overrides.for_renderer(RENDERER_ARNOLD) nodegraph_path = f"{collect_path}/ArnoldNodeGraph" nodegraph = UsdShade.NodeGraph.Define(stage, nodegraph_path) shader_path = f"{nodegraph_path}/arnold_standard_surface1" shader = UsdShade.Shader.Define(stage, shader_path) shader.CreateIdAttr("arnold:standard_surface") _connect_nodegraph_output( nodegraph, "surface", Sdf.ValueTypeNames.Token, shader, "surface" ) initialize_standard_surface(shader) self._wire_textures(nodegraph, nodegraph_path, shader, override) if self._context.is_transmissive: self._enable_transmission(shader) return nodegraph
def _initialize_image_shader(self, image_path: str) -> UsdShade.Shader: image_shader = UsdShade.Shader.Define(self._context.stage, image_path) image_shader.CreateIdAttr("arnold:image") image_shader.CreateInput("color_space", Sdf.ValueTypeNames.String).Set("auto") image_shader.CreateInput("filename", Sdf.ValueTypeNames.Asset) image_shader.CreateInput("filter", Sdf.ValueTypeNames.String).Set( "smart_bicubic" ) image_shader.CreateInput( "ignore_missing_textures", Sdf.ValueTypeNames.Bool ).Set(False) image_shader.CreateInput("mipmap_bias", Sdf.ValueTypeNames.Int).Set(0) image_shader.CreateInput( "missing_texture_color", Sdf.ValueTypeNames.Float4 ).Set((0, 0, 0, 0)) image_shader.CreateInput("multiply", Sdf.ValueTypeNames.Float3).Set((1, 1, 1)) image_shader.CreateInput("offset", Sdf.ValueTypeNames.Float3).Set((0, 0, 0)) image_shader.CreateInput("sflip", Sdf.ValueTypeNames.Bool).Set(False) image_shader.CreateInput("single_channel", Sdf.ValueTypeNames.Bool).Set(False) image_shader.CreateInput("soffset", Sdf.ValueTypeNames.Float).Set(0) image_shader.CreateInput("sscale", Sdf.ValueTypeNames.Float).Set(1) image_shader.CreateInput("start_channel", Sdf.ValueTypeNames.Int).Set(0) image_shader.CreateInput("swap_st", Sdf.ValueTypeNames.Bool).Set(False) image_shader.CreateInput("swrap", Sdf.ValueTypeNames.String).Set("periodic") image_shader.CreateInput("tflip", Sdf.ValueTypeNames.Bool).Set(False) image_shader.CreateInput("toffset", Sdf.ValueTypeNames.Float).Set(0) image_shader.CreateInput("tscale", Sdf.ValueTypeNames.Float).Set(1) image_shader.CreateInput("twrap", Sdf.ValueTypeNames.String).Set("periodic") image_shader.CreateInput("uvcoords", Sdf.ValueTypeNames.Float2).Set((0, 0)) image_shader.CreateInput("uvset", Sdf.ValueTypeNames.String).Set("") return image_shader def _initialize_color_correct_shader( self, color_correct_path: str ) -> UsdShade.Shader: shader = UsdShade.Shader.Define(self._context.stage, color_correct_path) shader.CreateIdAttr("arnold:color_correct") shader.CreateInput("add", Sdf.ValueTypeNames.Float3).Set((0, 0, 0)) shader.CreateInput("contrast", Sdf.ValueTypeNames.Float).Set(1) shader.CreateInput("exposure", Sdf.ValueTypeNames.Float).Set(0) shader.CreateInput("gamma", Sdf.ValueTypeNames.Float).Set(1) shader.CreateInput("hue_shift", Sdf.ValueTypeNames.Float).Set(0) return shader def _initialize_range_shader(self, range_path: str) -> UsdShade.Shader: shader = UsdShade.Shader.Define(self._context.stage, range_path) shader.CreateIdAttr("arnold:range") shader.CreateInput("bias", Sdf.ValueTypeNames.Float).Set(0.5) shader.CreateInput("contrast", Sdf.ValueTypeNames.Float).Set(1) shader.CreateInput("contrast_pivot", Sdf.ValueTypeNames.Float).Set(0.5) shader.CreateInput("gain", Sdf.ValueTypeNames.Float).Set(0.5) shader.CreateInput("input_min", Sdf.ValueTypeNames.Float).Set(0) shader.CreateInput("input_max", Sdf.ValueTypeNames.Float).Set(1) shader.CreateInput("output_min", Sdf.ValueTypeNames.Float).Set(0) shader.CreateInput("output_max", Sdf.ValueTypeNames.Float).Set(1) shader.CreateInput("smoothstep", Sdf.ValueTypeNames.Bool).Set(False) return shader def _initialize_normal_map_shader(self, normal_map_path: str) -> UsdShade.Shader: shader = UsdShade.Shader.Define(self._context.stage, normal_map_path) shader.CreateIdAttr("arnold:normal_map") shader.CreateInput("color_to_signed", Sdf.ValueTypeNames.Bool).Set(True) shader.CreateInput("input", Sdf.ValueTypeNames.Float3).Set((0, 0, 0)) shader.CreateInput("invert_x", Sdf.ValueTypeNames.Bool).Set(False) shader.CreateInput("invert_y", Sdf.ValueTypeNames.Bool).Set(False) shader.CreateInput("invert_z", Sdf.ValueTypeNames.Bool).Set(False) shader.CreateInput("normal", Sdf.ValueTypeNames.Float3).Set((0, 0, 0)) shader.CreateInput("order", Sdf.ValueTypeNames.String).Set("XYZ") shader.CreateInput("strength", Sdf.ValueTypeNames.Float).Set(1) shader.CreateInput("tangent", Sdf.ValueTypeNames.Float3).Set((0, 0, 0)) shader.CreateInput("tangent_space", Sdf.ValueTypeNames.Bool).Set(True) return shader def _initialize_bump2d_shader(self, bump2d_path: str) -> UsdShade.Shader: shader = UsdShade.Shader.Define(self._context.stage, bump2d_path) shader.CreateIdAttr("arnold:bump2d") shader.CreateInput("bump_height", Sdf.ValueTypeNames.Float).Set(1) shader.CreateInput("bump_map", Sdf.ValueTypeNames.Float).Set(0) shader.CreateInput("normal", Sdf.ValueTypeNames.Float3).Set((0, 0, 0)) return shader def _initialize_displacement_shader( self, displacement_path: str ) -> UsdShade.Shader: shader = UsdShade.Shader.Define(self._context.stage, displacement_path) shader.CreateIdAttr("arnold:displacement") return shader def _enable_transmission(self, shader: UsdShade.Shader) -> None: shader.GetInput("transmission").Set(0.9) shader.GetInput("thin_walled").Set(True) def _wire_textures( self, nodegraph: UsdShade.NodeGraph, collect_path: str, std_surf_shader: UsdShade.Shader, override: Optional[str], ) -> None: bump2d_path = f"{collect_path}/arnold_Bump2d" bump2d_shader = None for slot, input_name, path in _iter_textures( self._context, ARNOLD_INPUTS, "arnold" ): tex_filepath = apply_texture_format_override(path, override) texture_prim_path = f"{collect_path}/arnold_{slot}Texture" texture_shader = self._initialize_image_shader(texture_prim_path) texture_shader.GetInput("filename").Set(tex_filepath) if slot == "basecolor": color_correct_path = f"{collect_path}/arnold_{slot}ColorCorrect" color_correct_shader = self._initialize_color_correct_shader( color_correct_path ) color_correct_shader.CreateInput( "input", Sdf.ValueTypeNames.Float4 ).ConnectToSource( texture_shader.ConnectableAPI(), "rgba", ) std_surf_shader.CreateInput( input_name, Sdf.ValueTypeNames.Float3 ).ConnectToSource( color_correct_shader.ConnectableAPI(), "rgb", ) elif slot == "emission": color_correct_path = f"{collect_path}/arnold_{slot}ColorCorrect" color_correct_shader = self._initialize_color_correct_shader( color_correct_path ) color_correct_shader.CreateInput( "input", Sdf.ValueTypeNames.Float4 ).ConnectToSource( texture_shader.ConnectableAPI(), "rgba", ) std_surf_shader.CreateInput( input_name, Sdf.ValueTypeNames.Float3 ).ConnectToSource( color_correct_shader.ConnectableAPI(), "rgb", ) emission_input = std_surf_shader.GetInput("emission") if emission_input: emission_input.Set(1) elif slot == "metalness": if self._context.is_transmissive: continue range_path = f"{collect_path}/arnold_{slot}Range" range_shader = self._initialize_range_shader(range_path) range_shader.CreateInput( "input", Sdf.ValueTypeNames.Float4 ).ConnectToSource( texture_shader.ConnectableAPI(), "rgba", ) std_surf_shader.CreateInput( input_name, Sdf.ValueTypeNames.Float ).ConnectToSource( range_shader.ConnectableAPI(), "r", ) elif slot == "roughness": range_path = f"{collect_path}/arnold_{slot}Range" range_shader = self._initialize_range_shader(range_path) range_shader.CreateInput( "input", Sdf.ValueTypeNames.Float4 ).ConnectToSource( texture_shader.ConnectableAPI(), "rgba", ) std_surf_shader.CreateInput( input_name, Sdf.ValueTypeNames.Float ).ConnectToSource( range_shader.ConnectableAPI(), "r", ) elif slot == "opacity": range_path = f"{collect_path}/arnold_{slot}Range" range_shader = self._initialize_range_shader(range_path) range_shader.CreateInput( "input", Sdf.ValueTypeNames.Float4 ).ConnectToSource( texture_shader.ConnectableAPI(), "rgba", ) std_surf_shader.CreateInput( input_name, Sdf.ValueTypeNames.Float3 ).ConnectToSource( range_shader.ConnectableAPI(), "rgb", ) elif slot == "displacement": range_path = f"{collect_path}/arnold_{slot}Range" range_shader = self._initialize_range_shader(range_path) range_shader.CreateInput( "input", Sdf.ValueTypeNames.Float4 ).ConnectToSource( texture_shader.ConnectableAPI(), "rgba", ) if self._context.arnold_displacement_mode == ARNOLD_DISPLACEMENT_BUMP: if not bump2d_shader: bump2d_shader = self._initialize_bump2d_shader(bump2d_path) bump_map_input = bump2d_shader.GetInput("bump_map") if not bump_map_input: bump_map_input = bump2d_shader.CreateInput( "bump_map", Sdf.ValueTypeNames.Float ) bump_map_input.ConnectToSource(range_shader.ConnectableAPI(), "r") else: displacement_path = f"{collect_path}/arnold_Displacement" displacement_shader = self._initialize_displacement_shader( displacement_path ) displacement_shader.CreateInput( "height", Sdf.ValueTypeNames.Float ).ConnectToSource( range_shader.ConnectableAPI(), "r", ) _connect_nodegraph_output( nodegraph, "displacement", Sdf.ValueTypeNames.Token, displacement_shader, "out", ) elif slot == "normal": normal_map_path = f"{collect_path}/arnold_NormalMap" normal_map_shader = self._initialize_normal_map_shader(normal_map_path) normal_map_shader.CreateInput( "input", Sdf.ValueTypeNames.Float3 ).ConnectToSource( texture_shader.ConnectableAPI(), "vector", ) if not bump2d_shader: bump2d_shader = self._initialize_bump2d_shader(bump2d_path) normal_input = bump2d_shader.GetInput("normal") if not normal_input: normal_input = bump2d_shader.CreateInput( "normal", Sdf.ValueTypeNames.Float3 ) normal_input.ConnectToSource( normal_map_shader.ConnectableAPI(), "vector" ) if bump2d_shader: std_surf_shader.CreateInput( "normal", Sdf.ValueTypeNames.Float3 ).ConnectToSource( bump2d_shader.ConnectableAPI(), "vector", )