From cf2c82a991082d8e1773626a11bc15e62b6656f2 Mon Sep 17 00:00:00 2001 From: Kai Rohmer Date: Tue, 7 Feb 2023 19:15:01 +0100 Subject: [PATCH 1/2] Update MDL code generator to support MDL 1.7 - changed the sheen implementation to match OSL as close as possible - completed the mix, add, and multiply functions for BSDFs, EDFs, and VDFs - add MDL implementation for mix of surfaceshader, volumeshader, and displacementshader - fixed empty resource paths that showed MaterialX fileprefixes - handled relative resources, but encourage to use `flattenFilenames` on the document before generating MDL in order to have full application control on resource paths --- .../pbrlib/genmdl/pbrlib_genmdl_impl.mtlx | 11 +- .../stdlib/genmdl/stdlib_genmdl_impl.mtlx | 4 +- source/MaterialXGenMdl/MdlSyntax.cpp | 25 +++- source/MaterialXGenMdl/mdl/materialx/core.mdl | 6 +- .../MaterialXGenMdl/mdl/materialx/pbrlib.mdl | 115 +++++++++--------- .../MaterialXGenMdl/mdl/materialx/stdlib.mdl | 104 +++++++++++----- 6 files changed, 171 insertions(+), 94 deletions(-) diff --git a/libraries/pbrlib/genmdl/pbrlib_genmdl_impl.mtlx b/libraries/pbrlib/genmdl/pbrlib_genmdl_impl.mtlx index 9c46b17e83..a2893f8fdc 100644 --- a/libraries/pbrlib/genmdl/pbrlib_genmdl_impl.mtlx +++ b/libraries/pbrlib/genmdl/pbrlib_genmdl_impl.mtlx @@ -69,14 +69,17 @@ - - + + + - - + + + + diff --git a/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx b/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx index e2784d65c7..f860ae7226 100644 --- a/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx +++ b/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx @@ -611,7 +611,9 @@ - + + + diff --git a/source/MaterialXGenMdl/MdlSyntax.cpp b/source/MaterialXGenMdl/MdlSyntax.cpp index fcd1212a42..260eb3e38a 100644 --- a/source/MaterialXGenMdl/MdlSyntax.cpp +++ b/source/MaterialXGenMdl/MdlSyntax.cpp @@ -44,14 +44,35 @@ class MdlFilenameTypeSyntax : public ScalarTypeSyntax { return getDefaultValue(true); } + // handle the empty texture, the fileprefix is passed + // assuming it ends with a slash ... + if (outputValue.back() == '/') + { + return getDefaultValue(true); + } + // ... or the last segment does not have an extension suffix + size_t idx_s = outputValue.find_last_of('/'); + size_t idx_d = outputValue.find_last_of('.'); + if (idx_d == std::string::npos || (idx_s != std::string::npos && idx_s > idx_d)) + { + return getDefaultValue(true); + } + // prefix a slash in order to make MDL resource paths absolute i.e. to be found + // in the root of an MDL search path + // do not add the slash in case the path is explicitly relative string pathSeparator(""); FilePath path(outputValue); - if (!path.isAbsolute()) + size_t len = outputValue.size(); + if (!path.isAbsolute() && + !(len > 2 && outputValue[0] == '.' && outputValue[1] == '.' && outputValue[2] == '/') && + !(len > 1 && outputValue[0] == '.' && outputValue[1] == '/')) { pathSeparator = "/"; } - return getName() + "(\"" + pathSeparator + outputValue + "\", tex::gamma_linear)"; + + // MDL is using leading slashes as separator + return getName() + "(\"" + pathSeparator + path.asString(FilePath::FormatPosix) + "\", tex::gamma_linear)"; } }; diff --git a/source/MaterialXGenMdl/mdl/materialx/core.mdl b/source/MaterialXGenMdl/mdl/materialx/core.mdl index c146442fdf..ddcea572e4 100644 --- a/source/MaterialXGenMdl/mdl/materialx/core.mdl +++ b/source/MaterialXGenMdl/mdl/materialx/core.mdl @@ -22,12 +22,12 @@ // Document v1.37 REV2, January 19, 2020 // www.materialx.org // in -// NVIDIA Material Definition Language 1.6 +// NVIDIA Material Definition Language 1.7 // Language Specification -// Document version 1.6.1, December 16, 2019 +// Document version 1.7.2, January 17, 2022 // www.nvidia.com/mdl -mdl 1.6; +mdl 1.7; import ::math::*; import ::tex::*; diff --git a/source/MaterialXGenMdl/mdl/materialx/pbrlib.mdl b/source/MaterialXGenMdl/mdl/materialx/pbrlib.mdl index dcd66ed676..f8ce7ee17f 100644 --- a/source/MaterialXGenMdl/mdl/materialx/pbrlib.mdl +++ b/source/MaterialXGenMdl/mdl/materialx/pbrlib.mdl @@ -19,12 +19,12 @@ // Document v1.37 REV2, July 16, 2019 (Revised October 17, 2019) // see www.materialx.org // in -// NVIDIA Material Definition Language 1.6 +// NVIDIA Material Definition Language 1.7 // Language Specification -// Document version 1.6.1, December 16, 2019 +// Document version 1.7.2, January 17, 2022 // www.nvidia.com/mdl -mdl 1.6; +mdl 1.7; using .::core import *; import .::swizzle::*; @@ -313,33 +313,33 @@ export material mx_subsurface_bsdf( ) ); -// TODO: MDL's sheen BSDF has no possibility to configure the base BSDF, it is -// always a diffuse BSDF. Its color is configurable through the multiscatter -// tint and can be fed through the extra mxp_diffuse_tint input. A context -// analysis in the generator can map the color of the base to this input. +// To match with OSL, the sheen weight is scaled with average color as approximation of albedo. +// OSL uses the layer operator which mixes based on albedo. export material mx_sheen_bsdf( float mxp_weight = 1.0, color mxp_color = color(1.0), float mxp_roughness = 0.2, float3 mxp_normal = state::normal(), - material mxp_base = material() [[ anno::usage( "materialx:bsdf") ]], - color mxp_diffuse_color = color(1.0) // color of the base layer, MDL supports only diffuse + material mxp_base = material( + surface: material_surface( + scattering: df::diffuse_reflection_bsdf( + ))) [[ anno::usage( "materialx:bsdf") ]] ) [[ anno::usage( "materialx:bsdf") ]] = material( surface: material_surface( + // using the mix seems to fit OSL best, at least in the test cases scattering: df::weighted_layer( - weight: mxp_weight, + weight: math::average(mxp_color) * mxp_weight, layer: df::sheen_bsdf( roughness: mxp_roughness, tint: mxp_color, - multiscatter_tint: mxp_diffuse_color + multiscatter_tint: color(1.0), + multiscatter: mxp_base.surface.scattering ), base: mxp_base.surface.scattering, - normal: mxp_normal - ) - ), + normal: mxp_normal)), // we need to carry volume properties along for SSS ior: mxp_base.ior, volume: mxp_base.volume @@ -517,15 +517,20 @@ export material mx_thin_surface( ) ); -// TODO: emissive volumes not supported in MDL 1.6, EDF will be ignored here +// MDL 1.7, Volumes do support emssion, but not as EDF, just emission intensity. +// A uniform emission DF is the only practical DF here. export material mx_volume( material mxp_vdf = material() [[ anno::usage( "materialx:vdf") ]], - material mxp_edf = material() [[ anno::usage( "materialx:edf"), anno::unused() ]] + material mxp_edf = material() [[ anno::usage( "materialx:edf") ]] ) [[ anno::usage( "materialx:volumeshader") ]] = material( - volume: mxp_vdf.volume + volume: material_volume( + absorption_coefficient: mxp_vdf.volume.absorption_coefficient, + scattering_coefficient: mxp_vdf.volume.scattering_coefficient, + emission_intensity: mxp_edf.surface.emission.intensity + ) ); export material mx_light( @@ -607,26 +612,24 @@ export material mx_mix_bsdf( export material mx_mix_edf( material mxp_fg = material() [[ anno::usage( "materialx:edf") ]], - material mxp_bg = material() [[ anno::usage( "materialx:edf"), anno::unused() ]], - float mxp_mix = 0.0 [[ anno::unused() ]] + material mxp_bg = material() [[ anno::usage( "materialx:edf") ]], + float mxp_mix = 0.0 ) [[ anno::usage( "materialx:edf") ]] -= mxp_fg; -// TODO: mixing two EDFs fails with the df::clamped_mix since the weights are uniform in MDL -// = material( -// surface: material_surface( -// emission: material_emission( -// emission: df::clamped_mix( -// df::edf_component[]( -// df::edf_component( mxp_mix, mxp_fg.surface.emission.emission), -// df::edf_component( 1.0 - mxp_mix, mxp_bg.surface.emission.emission)) -// ), -// intensity: mxp_mix * mxp_fg.surface.emission.intensity + -// (1.0 - mxp_mix) * mxp_bg.surface.emission.intensity -// ) -// ) -// ); += material( + surface: material_surface( + emission: material_emission( + emission: df::clamped_mix( + df::edf_component[]( + df::edf_component( mxp_mix, mxp_fg.surface.emission.emission), + df::edf_component( 1.0 - mxp_mix, mxp_bg.surface.emission.emission)) + ), + intensity: mxp_mix * mxp_fg.surface.emission.intensity + + (1.0 - mxp_mix) * mxp_bg.surface.emission.intensity + ) + ) +); export material mx_mix_vdf( material mxp_fg = material() [[ anno::usage( "materialx:vdf") ]], @@ -661,23 +664,24 @@ export material mx_add_bsdf( ]] = material( surface: material_surface( - scattering: df::weighted_layer( - weight: 0.5, - layer: mxp_in1.surface.scattering, - base: mxp_in2.surface.scattering + scattering: df::unbounded_mix( + df::bsdf_component[]( + df::bsdf_component( 1.0, mxp_in1.surface.scattering), + df::bsdf_component( 1.0, mxp_in2.surface.scattering) + ) ) ), // we need to carry volume properties along for SSS volume: material_volume( - scattering: df::clamped_mix( + scattering: df::unbounded_mix( df::vdf_component[]( - df::vdf_component( 0.5, mxp_in1.volume.scattering), - df::vdf_component( 0.5, mxp_in2.volume.scattering)) + df::vdf_component( 1.0, mxp_in1.volume.scattering), + df::vdf_component( 1.0, mxp_in2.volume.scattering)) ), - absorption_coefficient: 0.5 * mxp_in1.volume.absorption_coefficient + - 0.5 * mxp_in2.volume.absorption_coefficient, - scattering_coefficient: 0.5 * mxp_in1.volume.scattering_coefficient + - 0.5 * mxp_in2.volume.scattering_coefficient + absorption_coefficient: mxp_in1.volume.absorption_coefficient + + mxp_in2.volume.absorption_coefficient, + scattering_coefficient: mxp_in1.volume.scattering_coefficient + + mxp_in2.volume.scattering_coefficient ) ); @@ -694,10 +698,10 @@ export material mx_add_edf( = material( surface: material_surface( emission: material_emission( - emission: df::clamped_mix( + emission: df::unbounded_mix( df::edf_component[]( - df::edf_component( 0.5, mxp_in1.surface.emission.emission), - df::edf_component( 0.5, mxp_in2.surface.emission.emission)) + df::edf_component( 1.0, mxp_in1.surface.emission.emission), + df::edf_component( 1.0, mxp_in2.surface.emission.emission)) ), intensity: mxp_in1.surface.emission.intensity + mxp_in2.surface.emission.intensity ) @@ -714,17 +718,18 @@ export material mx_add_vdf( anno::usage( "materialx:vdf") ]] = material( + // assuming mixing the IOR is the best we can do here ior: 0.5 * mxp_in1.ior + 0.5 * mxp_in2.ior, volume: material_volume( - scattering: df::clamped_mix( + scattering: df::unbounded_mix( df::vdf_component[]( - df::vdf_component( 0.5, mxp_in1.volume.scattering), - df::vdf_component( 0.5, mxp_in2.volume.scattering)) + df::vdf_component( 1.0, mxp_in1.volume.scattering), + df::vdf_component( 1.0, mxp_in2.volume.scattering)) ), - absorption_coefficient: 0.5 * mxp_in1.volume.absorption_coefficient + - 0.5 * mxp_in2.volume.absorption_coefficient, - scattering_coefficient: 0.5 * mxp_in1.volume.scattering_coefficient + - 0.5 * mxp_in2.volume.scattering_coefficient + absorption_coefficient: mxp_in1.volume.absorption_coefficient + + mxp_in2.volume.absorption_coefficient, + scattering_coefficient: mxp_in1.volume.scattering_coefficient + + mxp_in2.volume.scattering_coefficient ) ); diff --git a/source/MaterialXGenMdl/mdl/materialx/stdlib.mdl b/source/MaterialXGenMdl/mdl/materialx/stdlib.mdl index d33e4881dc..b73e881fcb 100644 --- a/source/MaterialXGenMdl/mdl/materialx/stdlib.mdl +++ b/source/MaterialXGenMdl/mdl/materialx/stdlib.mdl @@ -7,12 +7,12 @@ // Document v1.37 REV2, January 19, 2020 // www.materialx.org // in -// NVIDIA Material Definition Language 1.6 +// NVIDIA Material Definition Language 1.7 // Language Specification -// Document version 1.6.1, December 16, 2019 +// Document version 1.7.2, January 17, 2022 // www.nvidia.com/mdl -mdl 1.6; +mdl 1.7; using core import *; import swizzle::*; @@ -2781,7 +2781,7 @@ float mx_overlay(float mxp_fg, float mxp_bg) { return (mxp_fg < 0.5) ? (2 * mxp_fg * mxp_bg) : (1 - (1 - mxp_fg) * (1 - mxp_bg)); } -float2 mx_overlay(float2 mxp_fg, float2 mxp_bg) +float2 mx_overlay(float2 mxp_fg, float2 mxp_bg) [[ anno::unused() ]] { return float2( mx_overlay(mxp_fg.x, mxp_bg.x), @@ -2964,35 +2964,81 @@ export color4 mx_mix_color4_color4( return mk_color4(::math::lerp(mk_float4(mxp_bg), mk_float4(mxp_fg), mk_float4(mxp_mix))); } +// mix all parts of the material, bsdf, edf, and vdf, geometry export material mx_mix_surfaceshader( - material mxp_fg [[ anno::unused() ]], - material mxp_bg, - float mxp_mix = float(0.0) [[ anno::unused() ]] -) - [[ - anno::description("Node Group: compositing") - ]] -= mxp_bg; // TODO + material mxp_fg = material() [[ anno::usage( "materialx:surfaceshader") ]], + material mxp_bg = material() [[ anno::usage( "materialx:surfaceshader") ]], + float mxp_mix = 0.0 +) [[ + anno::description("Node Group: compositing"), + anno::usage( "materialx:surfaceshader") +]] += material( + surface: material_surface( + scattering: df::weighted_layer( + weight: mxp_mix, + layer: mxp_fg.surface.scattering, + base: mxp_bg.surface.scattering + ), + emission: material_emission( + emission: df::clamped_mix( + df::edf_component[]( + df::edf_component( mxp_mix, mxp_fg.surface.emission.emission), + df::edf_component( 1.0 - mxp_mix, mxp_bg.surface.emission.emission)) + ), + intensity: mxp_mix * mxp_fg.surface.emission.intensity + + (1.0 - mxp_mix) * mxp_bg.surface.emission.intensity + ) + ), -export material mx_mix_displacementshader( - material mxp_fg [[ anno::unused() ]], - material mxp_bg, - float mxp_mix = float(0.0) [[ anno::unused() ]] -) - [[ - anno::description("Node Group: compositing") - ]] -= mxp_bg; // TODO + // we need to carry volume properties along for SSS + ior: mxp_fg.ior, // NOTE: IOR is uniform, cannot mix here + volume: material_volume( + scattering: df::clamped_mix( + df::vdf_component[]( + df::vdf_component( mxp_mix, mxp_fg.volume.scattering), + df::vdf_component( 1.0 - mxp_mix, mxp_bg.volume.scattering)) + ), + absorption_coefficient: mxp_mix * mxp_fg.volume.absorption_coefficient + + (1.0 - mxp_mix) * mxp_bg.volume.absorption_coefficient, + scattering_coefficient: mxp_mix * mxp_fg.volume.scattering_coefficient + + (1.0 - mxp_mix) * mxp_bg.volume.scattering_coefficient + ), + geometry: material_geometry( + displacement: mxp_mix * mxp_fg.geometry.displacement + + (1.0 - mxp_mix) * mxp_bg.geometry.displacement, + cutout_opacity: mxp_mix * mxp_fg.geometry.cutout_opacity + + (1.0 - mxp_mix) * mxp_bg.geometry.cutout_opacity, + normal: mxp_mix * mxp_fg.geometry.normal + + (1.0 - mxp_mix) * mxp_bg.geometry.normal + ) +); export material mx_mix_volumeshader( - material mxp_fg [[ anno::unused() ]], - material mxp_bg, - float mxp_mix = float(0.0) [[ anno::unused() ]] -) - [[ - anno::description("Node Group: compositing") - ]] -= mxp_bg; // TODO + material mxp_fg = material() [[ anno::usage( "materialx:volumeshader") ]], + material mxp_bg = material() [[ anno::usage( "materialx:volumeshader") ]], + float mxp_mix = 0.0 +) [[ + anno::description("Node Group: compositing"), + anno::usage( "materialx:volumeshader") +]] += mx_mix_surfaceshader( + mxp_fg: mxp_fg, + mxp_bg: mxp_bg, + mxp_mix: mxp_mix); + +export material mx_mix_displacementshader( + material mxp_fg = material() [[ anno::usage( "materialx:displacementshader") ]], + material mxp_bg = material() [[ anno::usage( "materialx:displacementshader") ]], + float mxp_mix = 0.0 +) [[ + anno::description("Node Group: compositing"), + anno::usage( "materialx:displacementshader") +]] += mx_mix_surfaceshader( + mxp_fg: mxp_fg, + mxp_bg: mxp_bg, + mxp_mix: mxp_mix); export float mx_ifgreater_float( float mxp_value1 = float(1.0), From 574a921309916e2cca3ad50a70ba63df112800b0 Mon Sep 17 00:00:00 2001 From: Kai Rohmer Date: Thu, 9 Mar 2023 12:46:58 +0100 Subject: [PATCH 2/2] add vdf functions to testing white list --- source/MaterialXTest/MaterialXGenMdl/GenMdl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/MaterialXTest/MaterialXGenMdl/GenMdl.h b/source/MaterialXTest/MaterialXGenMdl/GenMdl.h index 5b635baa9c..32989afce9 100644 --- a/source/MaterialXTest/MaterialXGenMdl/GenMdl.h +++ b/source/MaterialXTest/MaterialXGenMdl/GenMdl.h @@ -72,7 +72,8 @@ class MdlShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester { "ambientocclusion", "arrayappend", "backfacing", "screen", "curveadjust", "displacementshader", "volumeshader", "IM_constant_", "IM_dot_", "IM_geomattrvalue", "IM_angle", - "geompropvalue", "surfacematerial", "volumematerial", "IM_absorption_vdf_", "IM_mix_vdf_", + "geompropvalue", "surfacematerial", "volumematerial", + "IM_absorption_vdf_", "IM_mix_vdf_", "IM_add_vdf_", "IM_multiply_vdf", "IM_measured_edf_", "IM_blackbody_", "IM_conical_edf_", "IM_displacement_", "IM_thin_surface_", "IM_volume_", "IM_light_" };