From a9e06a4fd5ca0863e112cf31087c34f9199e3dd5 Mon Sep 17 00:00:00 2001 From: Jonathan Stone Date: Sat, 27 May 2023 15:00:15 -0700 Subject: [PATCH 01/18] Improve robustness of unit tests - Remove dependencies of MaterialXTest on the current working directory, allowing it to be launched from any location. - Use getDefaultDataSearchPath to access search paths in MaterialXTest, aligning its behavior with that of other applications. - Merge the concepts of library and source search paths in render testing. --- source/MaterialXRenderOsl/OslRenderer.cpp | 9 +++++ source/MaterialXTest/Main.cpp | 16 +------- .../MaterialXTest/MaterialXCore/Document.cpp | 23 +++++------ .../MaterialXTest/MaterialXCore/Material.cpp | 7 +--- source/MaterialXTest/MaterialXCore/Node.cpp | 30 +++++++-------- .../MaterialXTest/MaterialXCore/Traversal.cpp | 9 +---- source/MaterialXTest/MaterialXCore/Unit.cpp | 28 ++++++++------ source/MaterialXTest/MaterialXFormat/File.cpp | 29 ++++++-------- .../MaterialXTest/MaterialXFormat/XmlIo.cpp | 13 ++++--- .../MaterialXGenGlsl/GenGlsl.cpp | 25 +++++------- .../MaterialXTest/MaterialXGenGlsl/GenGlsl.h | 7 ++-- .../MaterialXTest/MaterialXGenMdl/GenMdl.cpp | 33 ++++++++-------- source/MaterialXTest/MaterialXGenMdl/GenMdl.h | 5 +-- .../MaterialXTest/MaterialXGenMsl/GenMsl.cpp | 34 ++++++----------- source/MaterialXTest/MaterialXGenMsl/GenMsl.h | 7 ++-- .../MaterialXTest/MaterialXGenOsl/GenOsl.cpp | 29 +++++--------- source/MaterialXTest/MaterialXGenOsl/GenOsl.h | 5 +-- .../MaterialXGenShader/GenShader.cpp | 38 +++++++++---------- .../MaterialXGenShader/GenShaderUtil.cpp | 16 ++++---- .../MaterialXGenShader/GenShaderUtil.h | 9 ++--- .../MaterialXTest/MaterialXRender/Render.cpp | 8 +++- .../MaterialXRender/RenderUtil.cpp | 14 +++---- .../MaterialXRenderGlsl/RenderGlsl.cpp | 14 ++++--- .../MaterialXRenderOsl/GenReference.cpp | 9 ++--- .../MaterialXRenderOsl/RenderOsl.cpp | 15 +++++--- 25 files changed, 195 insertions(+), 237 deletions(-) diff --git a/source/MaterialXRenderOsl/OslRenderer.cpp b/source/MaterialXRenderOsl/OslRenderer.cpp index bf258b6762..14dd81a090 100644 --- a/source/MaterialXRenderOsl/OslRenderer.cpp +++ b/source/MaterialXRenderOsl/OslRenderer.cpp @@ -143,6 +143,12 @@ void OslRenderer::renderOSL(const FilePath& dirPath, const string& shaderName, c " does not include proper tokens for rendering"); } + // Set the working directory for rendering. + FileSearchPath searchPath = getDefaultDataSearchPath(); + FilePath rootPath = searchPath.isEmpty() ? FilePath() : searchPath[0]; + FilePath origWorkingPath = FilePath::getCurrentPath(); + rootPath.setCurrentPath(); + // Write scene file const string sceneFileName("scene_template.xml"); std::ofstream shaderFileStream; @@ -178,6 +184,9 @@ void OslRenderer::renderOSL(const FilePath& dirPath, const string& shaderName, c } } + // Restore the working directory after rendering. + origWorkingPath.setCurrentPath(); + // Report errors on a non-zero return value. if (returnValue) { diff --git a/source/MaterialXTest/Main.cpp b/source/MaterialXTest/Main.cpp index 414e5f88b1..4e53d943f4 100644 --- a/source/MaterialXTest/Main.cpp +++ b/source/MaterialXTest/Main.cpp @@ -13,10 +13,8 @@ namespace mx = MaterialX; int main(int argc, char* const argv[]) { Catch::Session session; - -#ifndef _DEBUG session.configData().showDurations = Catch::ShowDurations::Always; -#endif + #ifdef CATCH_PLATFORM_WINDOWS BOOL inDebugger = IsDebuggerPresent(); if (inDebugger) @@ -29,18 +27,6 @@ int main(int argc, char* const argv[]) } #endif - // If the current path has no valid resources folder, as can occur when launching the - // test suite from an IDE, then align the current path with the module path. - mx::FilePath resourcesPath = mx::FilePath::getCurrentPath() / "resources"; - if (!resourcesPath.exists()) - { - resourcesPath = mx::FilePath::getModulePath().getParentPath() / "resources"; - if (resourcesPath.exists()) - { - resourcesPath.getParentPath().setCurrentPath(); - } - } - int returnCode = session.applyCommandLine(argc, argv); if (returnCode != 0) { diff --git a/source/MaterialXTest/MaterialXCore/Document.cpp b/source/MaterialXTest/MaterialXCore/Document.cpp index 5ae6f77c97..29092071cf 100644 --- a/source/MaterialXTest/MaterialXCore/Document.cpp +++ b/source/MaterialXTest/MaterialXCore/Document.cpp @@ -111,22 +111,24 @@ TEST_CASE("Document", "[document]") TEST_CASE("Version", "[document]") { - mx::DocumentPtr doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_ng.mtlx"), doc); - mx::FileSearchPath searchPath("resources/Materials/TestSuite/stdlib/upgrade/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr stdlib = mx::createDocument(); + mx::loadLibraries({ "libraries" }, searchPath, stdlib); + searchPath.append(searchPath.find("resources/Materials/TestSuite/stdlib/upgrade")); // 1.36 to 1.37 { + mx::DocumentPtr doc = mx::createDocument(); mx::readFromXmlFile(doc, "1_36_to_1_37.mtlx", searchPath); + doc->importLibrary(stdlib); REQUIRE(doc->validate()); mx::XmlWriteOptions writeOptions; writeOptions.writeXIncludeEnable = true; - mx::writeToXmlFile(doc, "1_36_to_1_37_updated.mtlx", &writeOptions); + std::string xmlString = mx::writeToXmlString(doc, &writeOptions); mx::DocumentPtr doc2 = mx::createDocument(); - mx::readFromXmlFile(doc2, "1_36_to_1_37_updated.mtlx"); + mx::readFromXmlString(doc2, xmlString); REQUIRE(doc2->validate()); // Check conversion to desired types occurred @@ -160,18 +162,17 @@ TEST_CASE("Version", "[document]") // 1.37 to 1.38 { - doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_ng.mtlx"), doc); + mx::DocumentPtr doc = mx::createDocument(); mx::readFromXmlFile(doc, "1_37_to_1_38.mtlx", searchPath); + doc->importLibrary(stdlib); REQUIRE(doc->validate()); mx::XmlWriteOptions writeOptions; writeOptions.writeXIncludeEnable = false; - mx::writeToXmlFile(doc, "1_37_to_1_38_updated.mtlx", &writeOptions); + std::string xmlString = mx::writeToXmlString(doc, &writeOptions); mx::DocumentPtr doc2 = mx::createDocument(); - mx::readFromXmlFile(doc2, "1_37_to_1_38_updated.mtlx"); + mx::readFromXmlString(doc2, xmlString); REQUIRE(doc2->validate()); // atan2 test diff --git a/source/MaterialXTest/MaterialXCore/Material.cpp b/source/MaterialXTest/MaterialXCore/Material.cpp index e5be7c5f9e..21e9d7db0d 100644 --- a/source/MaterialXTest/MaterialXCore/Material.cpp +++ b/source/MaterialXTest/MaterialXCore/Material.cpp @@ -61,12 +61,9 @@ TEST_CASE("Material", "[material]") TEST_CASE("Material Discovery", "[material]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - - const mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath(currentPath / mx::FilePath("resources/Materials/TestSuite")); - mx::FilePath filename = "stdlib/materials/material_node_discovery.mtlx"; - mx::readFromXmlFile(doc, filename, searchPath); + mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/materials/material_node_discovery.mtlx", searchPath); // 1. Find all materials referenced by material assignments // which are found in connected nodegraphs diff --git a/source/MaterialXTest/MaterialXCore/Node.cpp b/source/MaterialXTest/MaterialXCore/Node.cpp index 9487ab286d..2477d9f486 100644 --- a/source/MaterialXTest/MaterialXCore/Node.cpp +++ b/source/MaterialXTest/MaterialXCore/Node.cpp @@ -139,8 +139,9 @@ TEST_CASE("Node", "[node]") TEST_CASE("Flatten", "[nodegraph]") { // Read an example containing graph-based custom nodes. + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/shader/surface.mtlx"); + mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/shader/surface.mtlx", searchPath); REQUIRE(doc->validate()); // Count root-level, nested, and custom nodes. @@ -174,8 +175,8 @@ TEST_CASE("Flatten", "[nodegraph]") TEST_CASE("Inheritance", "[nodedef]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); mx::loadLibraries({ "libraries" }, searchPath, doc); REQUIRE(doc->validate()); auto nodedef = doc->getNodeDef("ND_standard_surface_surfaceshader"); @@ -559,13 +560,12 @@ TEST_CASE("Organization", "[nodegraph]") TEST_CASE("Tokens", "[nodegraph]") { - mx::DocumentPtr doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_ng.mtlx"), doc); - mx::FileSearchPath searchPath("resources/Materials/TestSuite/stdlib/texture/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr stdlib = mx::createDocument(); + mx::loadLibraries({ "libraries" }, searchPath, stdlib); - mx::readFromXmlFile(doc, "tokenGraph.mtlx", searchPath); - doc->validate(); + mx::DocumentPtr doc = mx::createDocument(); + mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/texture/tokenGraph.mtlx", searchPath); mx::StringVec graphNames = { "Tokenized_Image_2k_png", "Tokenized_Image_4k_jpg" }; mx::StringVec resolutionStrings = { "2k", "4k" }; @@ -597,13 +597,13 @@ TEST_CASE("Tokens", "[nodegraph]") TEST_CASE("Node Definition Creation", "[nodedef]") { - mx::DocumentPtr doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_ng.mtlx"), doc); - mx::FileSearchPath searchPath("resources/Materials/TestSuite/stdlib/definition/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr stdlib = mx::createDocument(); + mx::loadLibraries({ "libraries" }, searchPath, stdlib); - mx::readFromXmlFile(doc, "definition_from_nodegraph.mtlx", searchPath); - REQUIRE(doc->validate()); + mx::DocumentPtr doc = mx::createDocument(); + mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/definition/definition_from_nodegraph.mtlx", searchPath); + doc->importLibrary(stdlib); mx::NodeGraphPtr graph = doc->getNodeGraph("test_colorcorrect"); REQUIRE(graph); @@ -721,7 +721,5 @@ TEST_CASE("Node Definition Creation", "[nodedef]") } REQUIRE(findDefault); } - REQUIRE(doc->validate()); - mx::writeToXmlFile(doc, "definition_from_nodegraph_out.mtlx"); } diff --git a/source/MaterialXTest/MaterialXCore/Traversal.cpp b/source/MaterialXTest/MaterialXCore/Traversal.cpp index 3ffc5a3485..f65228c286 100644 --- a/source/MaterialXTest/MaterialXCore/Traversal.cpp +++ b/source/MaterialXTest/MaterialXCore/Traversal.cpp @@ -176,14 +176,9 @@ TEST_CASE("IntraGraph Traversal", "[traversal]") TEST_CASE("InterGraph Traversal", "[traversal]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath(currentPath); - mx::loadLibraries({ "libraries" }, searchPath, doc); - - mx::FilePath testFile = currentPath / mx::FilePath("resources/Materials/TestSuite/stdlib/nodegraph_inputs/nodegraph_nodegraph.mtlx"); - mx::readFromXmlFile(doc, testFile, searchPath); - REQUIRE(doc->validate()); + mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/nodegraph_inputs/nodegraph_nodegraph.mtlx", searchPath); for (mx::NodeGraphPtr graph : doc->getNodeGraphs()) { diff --git a/source/MaterialXTest/MaterialXCore/Unit.cpp b/source/MaterialXTest/MaterialXCore/Unit.cpp index 80ce2be0c4..01cefe98c6 100644 --- a/source/MaterialXTest/MaterialXCore/Unit.cpp +++ b/source/MaterialXTest/MaterialXCore/Unit.cpp @@ -17,8 +17,10 @@ const float EPSILON = 1e-4f; TEST_CASE("UnitAttribute", "[unit]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); + mx::loadLibraries({ "libraries" }, searchPath, doc); + std::vector unitTypeDefs = doc->getUnitTypeDefs(); REQUIRE(!unitTypeDefs.empty()); @@ -60,8 +62,9 @@ TEST_CASE("UnitAttribute", "[unit]") TEST_CASE("UnitEvaluation", "[unit]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); + mx::loadLibraries({ "libraries" }, searchPath, doc); // // Test distance converter @@ -117,11 +120,11 @@ TEST_CASE("UnitEvaluation", "[unit]") TEST_CASE("UnitDocument", "[unit]") { - mx::FilePath libraryPath("libraries/stdlib"); - mx::FilePath examplesPath("resources/Materials/TestSuite/stdlib/units"); - std::string searchPath = libraryPath.asString() + - mx::PATH_LIST_SEPARATOR + - examplesPath.asString(); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr stdlib = mx::createDocument(); + mx::loadLibraries({ "libraries" }, searchPath, stdlib); + mx::FilePath examplesPath = searchPath.find("resources/Materials/TestSuite/stdlib/units"); + searchPath.append(examplesPath); static const std::string DISTANCE_DEFAULT("meter"); @@ -130,7 +133,7 @@ TEST_CASE("UnitDocument", "[unit]") { mx::DocumentPtr doc = mx::createDocument(); mx::readFromXmlFile(doc, filename, searchPath); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); + doc->importLibrary(stdlib); mx::UnitTypeDefPtr distanceTypeDef = doc->getUnitTypeDef("distance"); REQUIRE(distanceTypeDef); @@ -149,11 +152,14 @@ TEST_CASE("UnitDocument", "[unit]") mx::NodePtr pNode = elem->asA(); if (pNode) { - if (pNode->getInputCount()) { - for (mx::InputPtr input : pNode->getInputs()) { + if (pNode->getInputCount()) + { + for (mx::InputPtr input : pNode->getInputs()) + { const std::string type = input->getType(); const mx::ValuePtr value = input->getValue(); - if (input->hasUnit() && value) { + if (input->hasUnit() && value) + { if (type == "float") { float originalval = value->asA(); diff --git a/source/MaterialXTest/MaterialXFormat/File.cpp b/source/MaterialXTest/MaterialXFormat/File.cpp index 26515f0aec..83e0e01f34 100644 --- a/source/MaterialXTest/MaterialXFormat/File.cpp +++ b/source/MaterialXTest/MaterialXFormat/File.cpp @@ -32,32 +32,27 @@ TEST_CASE("Syntactic operations", "[file]") TEST_CASE("File system operations", "[file]") { - mx::StringVec filenames = + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePathVec examplePaths = { "libraries/stdlib/stdlib_defs.mtlx", "resources/Materials/Examples/StandardSurface/standard_surface_brass_tiled.mtlx", "resources/Materials/Examples/StandardSurface/standard_surface_marble_solid.mtlx", }; - - for (const std::string& filename : filenames) + for (const mx::FilePath& path : examplePaths) { - mx::FilePath path(filename); - REQUIRE(path.exists()); - REQUIRE(mx::FileSearchPath().find(path).exists()); + REQUIRE(searchPath.find(path).exists()); } - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FilePath modulePath = mx::FilePath::getModulePath(); - bool expectedPaths = currentPath == modulePath || - currentPath == modulePath.getParentPath(); - REQUIRE(expectedPaths); + REQUIRE(mx::FilePath::getCurrentPath().exists()); + REQUIRE(mx::FilePath::getModulePath().exists()); } TEST_CASE("File search path operations", "[file]") { - mx::FileSearchPath searchPath = "libraries/stdlib" + - mx::PATH_LIST_SEPARATOR + - "resources/Materials/Examples/StandardSurface"; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + searchPath.append(searchPath.find("libraries/stdlib")); + searchPath.append(searchPath.find("resources/Materials/Examples/StandardSurface")); mx::FilePathVec filenames = { @@ -102,10 +97,8 @@ TEST_CASE("Flatten filenames", "[file]") image2->setInputValue("file", "brass_color.jpg", mx::FILENAME_TYPE_STRING); // 2. Test resolving to absolute paths - mx::FilePath rootPath(mx::FilePath::getCurrentPath()); - - mx::FileSearchPath searchPath; - searchPath.append(rootPath); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath rootPath = searchPath.isEmpty() ? mx::FilePath() : searchPath[0]; mx::flattenFilenames(doc1, searchPath); REQUIRE(nodeGraph->getFilePrefix() == mx::EMPTY_STRING); diff --git a/source/MaterialXTest/MaterialXFormat/XmlIo.cpp b/source/MaterialXTest/MaterialXFormat/XmlIo.cpp index 1aea085bb2..1336a080c4 100644 --- a/source/MaterialXTest/MaterialXFormat/XmlIo.cpp +++ b/source/MaterialXTest/MaterialXFormat/XmlIo.cpp @@ -14,11 +14,11 @@ namespace mx = MaterialX; TEST_CASE("Load content", "[xmlio]") { - mx::FilePath libraryPath("libraries/stdlib"); - mx::FilePath examplesPath("resources/Materials/Examples/StandardSurface"); - mx::FileSearchPath searchPath = libraryPath.asString() + - mx::PATH_LIST_SEPARATOR + - examplesPath.asString(); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath libraryPath = searchPath.find("libraries/stdlib"); + mx::FilePath examplesPath = searchPath.find("resources/Materials/Examples/StandardSurface"); + searchPath.append(libraryPath); + searchPath.append(examplesPath); // Read the standard library. std::vector libs; @@ -242,7 +242,8 @@ TEST_CASE("Load content", "[xmlio]") TEST_CASE("Comments and newlines", "[xmlio]") { - mx::FilePath testPath("resources/Materials/Examples/StandardSurface/standard_surface_chess_set.mtlx"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath testPath = searchPath.find("resources/Materials/Examples/StandardSurface/standard_surface_chess_set.mtlx"); // Read the example file into an XML string buffer. std::string origXml = mx::readFile(testPath); diff --git a/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.cpp b/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.cpp index a357651cb7..54b6fbf420 100644 --- a/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.cpp +++ b/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.cpp @@ -91,10 +91,7 @@ TEST_CASE("GenShader: GLSL Implementation Check", "[genglsl]") TEST_CASE("GenShader: GLSL Unique Names", "[genglsl]") { mx::GenContext context(mx::GlslShaderGenerator::create()); - - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - context.registerSourceCodeSearchPath(currentPath); - + context.registerSourceCodeSearchPath(mx::getDefaultDataSearchPath()); GenShaderUtil::testUniqueNames(context, mx::Stage::PIXEL); } @@ -102,8 +99,7 @@ TEST_CASE("GenShader: Bind Light Shaders", "[genglsl]") { mx::DocumentPtr doc = mx::createDocument(); - mx::FileSearchPath searchPath; - searchPath.append(mx::FilePath::getCurrentPath()); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); loadLibraries({ "libraries" }, searchPath, doc); mx::NodeDefPtr pointLightShader = doc->getNodeDef("ND_point_light"); @@ -146,20 +142,17 @@ const std::string GlslTypeToString(GlslType e) throw() static void generateGlslCode(GlslType type = GlslType::Glsl400) { - mx::FilePathVec testRootPaths; - testRootPaths.push_back("resources/Materials/TestSuite"); - testRootPaths.push_back("resources/Materials/Examples/StandardSurface"); - const mx::FilePath libSearchPath = mx::FilePath::getCurrentPath(); - const mx::FileSearchPath srcSearchPath(libSearchPath.asString()); - bool writeShadersToDisk = false; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); - const mx::GenOptions genOptions; - mx::FilePath optionsFilePath("resources/Materials/TestSuite/_options.mtlx"); + mx::FilePathVec testRootPaths; + testRootPaths.push_back(searchPath.find("resources/Materials/TestSuite")); + testRootPaths.push_back(searchPath.find("resources/Materials/Examples/StandardSurface")); const mx::FilePath logPath("genglsl_" + GlslTypeToString(type) + "_generate_test.txt"); + bool writeShadersToDisk = false; GlslShaderGeneratorTester tester((type == GlslType::GlslVulkan) ? mx::VkShaderGenerator::create() : mx::GlslShaderGenerator::create(), - testRootPaths, libSearchPath, srcSearchPath, logPath, writeShadersToDisk); + testRootPaths, searchPath, logPath, writeShadersToDisk); // Add resource binding context for glsl 4.20 if (type == GlslType::Glsl420) @@ -170,6 +163,8 @@ static void generateGlslCode(GlslType type = GlslType::Glsl400) tester.addUserData(mx::HW::USER_DATA_BINDING_CONTEXT, glslresourceBinding); } + const mx::GenOptions genOptions; + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); tester.validate(genOptions, optionsFilePath); } diff --git a/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.h b/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.h index 5868d7b5b4..97ad62f0e4 100644 --- a/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.h +++ b/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.h @@ -19,9 +19,8 @@ class GlslShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester using ParentClass = GenShaderUtil::ShaderGeneratorTester; GlslShaderGeneratorTester(mx::ShaderGeneratorPtr shaderGenerator, const mx::FilePathVec& testRootPaths, - const mx::FilePath& libSearchPath, const mx::FileSearchPath& srcSearchPath, - const mx::FilePath& logFilePath, bool writeShadersToDisk) : - GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, libSearchPath, srcSearchPath, logFilePath, writeShadersToDisk) + const mx::FileSearchPath& searchPath, const mx::FilePath& logFilePath, bool writeShadersToDisk) : + GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, searchPath, logFilePath, writeShadersToDisk) {} void setTestStages() override @@ -42,7 +41,7 @@ class GlslShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester { ParentClass::setupDependentLibraries(); - mx::FilePath lightDir = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/TestSuite/lights"); + mx::FilePath lightDir = mx::getDefaultDataSearchPath().find("resources/Materials/TestSuite/lights"); loadLibrary(lightDir / mx::FilePath("light_compound_test.mtlx"), _dependLib); loadLibrary(lightDir / mx::FilePath("light_rig_test_1.mtlx"), _dependLib); } diff --git a/source/MaterialXTest/MaterialXGenMdl/GenMdl.cpp b/source/MaterialXTest/MaterialXGenMdl/GenMdl.cpp index c58d754b92..fbe19a5957 100644 --- a/source/MaterialXTest/MaterialXGenMdl/GenMdl.cpp +++ b/source/MaterialXTest/MaterialXGenMdl/GenMdl.cpp @@ -112,9 +112,9 @@ class MdlStringResolver : public mx::StringResolver std::ofstream* logFile, std::initializer_list additionalSearchpaths) { - // Note: These paths are based on - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FilePath coreModulePath = currentPath / std::string(MATERIALX_INSTALL_MDL_MODULE_PATH) / "mdl"; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath rootPath = searchPath.isEmpty() ? mx::FilePath() : searchPath[0]; + mx::FilePath coreModulePath = rootPath / std::string(MATERIALX_INSTALL_MDL_MODULE_PATH) / "mdl"; mx::FilePath coreModulePath2 = coreModulePath / mx::FilePath("materialx"); // use the source search paths as base @@ -130,7 +130,7 @@ class MdlStringResolver : public mx::StringResolver } // add additional search paths for the tests - paths.append(currentPath); + paths.append(rootPath); paths.append(coreModulePath); paths.append(coreModulePath2); for (const auto& addSp : additionalSearchpaths) @@ -209,7 +209,7 @@ void MdlShaderGeneratorTester::preprocessDocument(mx::DocumentPtr doc) if (!_mdlCustomResolver) _mdlCustomResolver = MdlStringResolver::create(); - _mdlCustomResolver->initialize(doc, &_logFile, { _libSearchPath.asString() }); + _mdlCustomResolver->initialize(doc, &_logFile, { _searchPath.asString() }); mx::flattenFilenames(doc, _mdlCustomResolver->getMdlSearchPaths(), _mdlCustomResolver); } @@ -282,18 +282,19 @@ void MdlShaderGeneratorTester::compileSource(const std::vector& so renderCommand += " --mdl_path \"" + moduleToTestPath.asString() + "\""; renderCommand += " --mdl_path \"" + moduleToTestPath.getParentPath().asString() + "\""; - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath rootPath = searchPath.isEmpty() ? mx::FilePath() : searchPath[0]; // set environment - std::string iblFile = (currentPath / "resources/lights/san_giuseppe_bridge.hdr").asString(); + std::string iblFile = (rootPath / "resources/lights/san_giuseppe_bridge.hdr").asString(); renderCommand += " --hdr \"" + iblFile + "\" --hdr_rotate 90"; // set scene renderCommand += " --uv_scale 0.5 1.0 --uv_offset 0.0 0.0 --uv_repeat"; renderCommand += " --camera 0 0 3 0 0 0 --fov 45"; // set the material - // compute the MDL module name as fully qualified name wrt to the "currentPath/resources" as MDL search path + // compute the MDL module name as fully qualified name wrt to the "rootPath/resources" as MDL search path std::string mdlModuleName = "::resources::"; - for (size_t s = currentPath.size() + 1; s < moduleToTestPath.size(); ++s) + for (size_t s = rootPath.size() + 1; s < moduleToTestPath.size(); ++s) { mdlModuleName += moduleToTestPath[s] + "::"; } @@ -348,24 +349,22 @@ void MdlShaderGeneratorTester::compileSource(const std::vector& so TEST_CASE("GenShader: MDL Shader Generation", "[genmdl]") { - mx::FilePathVec testRootPaths; - testRootPaths.push_back("resources/Materials/TestSuite"); - testRootPaths.push_back("resources/Materials/Examples"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); - const mx::FilePath libSearchPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath srcSearchPath(libSearchPath.asString()); - srcSearchPath.append(libSearchPath / mx::FilePath("libraries/stdlib/genmdl")); + mx::FilePathVec testRootPaths; + testRootPaths.push_back(searchPath.find("resources/Materials/TestSuite")); + testRootPaths.push_back(searchPath.find("resources/Materials/Examples/StandardSurface")); const mx::FilePath logPath("genmdl_mdl_generate_test.txt"); // Write shaders and try to compile only if mdlc exe specified. std::string mdlcExec(MATERIALX_MDLC_EXECUTABLE); bool writeShadersToDisk = !mdlcExec.empty(); - MdlShaderGeneratorTester tester(mx::MdlShaderGenerator::create(), testRootPaths, libSearchPath, srcSearchPath, logPath, writeShadersToDisk); + MdlShaderGeneratorTester tester(mx::MdlShaderGenerator::create(), testRootPaths, searchPath, logPath, writeShadersToDisk); tester.addSkipLibraryFiles(); mx::GenOptions genOptions; genOptions.targetColorSpaceOverride = "lin_rec709"; - mx::FilePath optionsFilePath("resources/Materials/TestSuite/_options.mtlx"); + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); tester.validate(genOptions, optionsFilePath); } diff --git a/source/MaterialXTest/MaterialXGenMdl/GenMdl.h b/source/MaterialXTest/MaterialXGenMdl/GenMdl.h index 04425262d5..179688a05a 100644 --- a/source/MaterialXTest/MaterialXGenMdl/GenMdl.h +++ b/source/MaterialXTest/MaterialXGenMdl/GenMdl.h @@ -21,9 +21,8 @@ class MdlShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester using ParentClass = GenShaderUtil::ShaderGeneratorTester; MdlShaderGeneratorTester(mx::ShaderGeneratorPtr shaderGenerator, const std::vector& testRootPaths, - const mx::FilePath& libSearchPath, const mx::FileSearchPath& srcSearchPath, - const mx::FilePath& logFilePath, bool writeShadersToDisk) : - GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, libSearchPath, srcSearchPath, logFilePath, writeShadersToDisk) + const mx::FileSearchPath& searchPath, const mx::FilePath& logFilePath, bool writeShadersToDisk) : + GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, searchPath, logFilePath, writeShadersToDisk) {} void setTestStages() override diff --git a/source/MaterialXTest/MaterialXGenMsl/GenMsl.cpp b/source/MaterialXTest/MaterialXGenMsl/GenMsl.cpp index 8393acfca7..e3bcc7cfa1 100644 --- a/source/MaterialXTest/MaterialXGenMsl/GenMsl.cpp +++ b/source/MaterialXTest/MaterialXGenMsl/GenMsl.cpp @@ -90,20 +90,15 @@ TEST_CASE("GenShader: MSL Implementation Check", "[genmsl]") TEST_CASE("GenShader: MSL Unique Names", "[genmsl]") { mx::GenContext context(mx::MslShaderGenerator::create()); - - mx::FilePath searchPath = mx::FilePath::getCurrentPath() / mx::FilePath("libraries"); - context.registerSourceCodeSearchPath(searchPath); - + context.registerSourceCodeSearchPath(mx::getDefaultDataSearchPath()); GenShaderUtil::testUniqueNames(context, mx::Stage::PIXEL); } TEST_CASE("GenShader: MSL Bind Light Shaders", "[genmsl]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - - mx::FileSearchPath searchPath; - searchPath.append(mx::FilePath::getCurrentPath() / mx::FilePath("libraries")); - loadLibraries({ "targets", "stdlib", "pbrlib", "lights" }, searchPath, doc); + mx::loadLibraries({ "libraries" }, searchPath, doc); mx::NodeDefPtr pointLightShader = doc->getNodeDef("ND_point_light"); mx::NodeDefPtr spotLightShader = doc->getNodeDef("ND_spot_light"); @@ -111,7 +106,7 @@ TEST_CASE("GenShader: MSL Bind Light Shaders", "[genmsl]") REQUIRE(spotLightShader != nullptr); mx::GenContext context(mx::MslShaderGenerator::create()); - context.registerSourceCodeSearchPath(mx::FilePath::getCurrentPath() / mx::FilePath("libraries")); + context.registerSourceCodeSearchPath(searchPath); mx::HwShaderGenerator::bindLightShader(*pointLightShader, 42, context); REQUIRE_THROWS(mx::HwShaderGenerator::bindLightShader(*spotLightShader, 42, context)); @@ -124,27 +119,22 @@ TEST_CASE("GenShader: MSL Bind Light Shaders", "[genmsl]") static void generateMslCode() { - const mx::FilePath testRootPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/TestSuite"); - const mx::FilePath testRootPath2 = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/Examples/StandardSurface"); - const mx::FilePath testRootPath3 = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/Examples/UsdPreviewSurface"); - mx::FilePathVec testRootPaths; - testRootPaths.push_back(testRootPath); - testRootPaths.push_back(testRootPath2); - testRootPaths.push_back(testRootPath3); - const mx::FilePath libSearchPath = mx::FilePath::getCurrentPath() / mx::FilePath("libraries"); - const mx::FileSearchPath srcSearchPath(libSearchPath.asString()); - bool writeShadersToDisk = false; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); - const mx::GenOptions genOptions; - mx::FilePath optionsFilePath = testRootPath / mx::FilePath("_options.mtlx"); + mx::FilePathVec testRootPaths; + testRootPaths.push_back(searchPath.find("resources/Materials/TestSuite")); + testRootPaths.push_back(searchPath.find("resources/Materials/Examples/StandardSurface")); const mx::FilePath logPath("genmsl_msl23_layout_generate_test.txt"); - MslShaderGeneratorTester tester(mx::MslShaderGenerator::create(), testRootPaths, libSearchPath, srcSearchPath, logPath, writeShadersToDisk); + bool writeShadersToDisk = false; + MslShaderGeneratorTester tester(mx::MslShaderGenerator::create(), testRootPaths, searchPath, logPath, writeShadersToDisk); // Set binding context to handle resource binding layouts tester.addUserData(mx::HW::USER_DATA_BINDING_CONTEXT, mx::MslResourceBindingContext::create()); + const mx::GenOptions genOptions; + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); tester.validate(genOptions, optionsFilePath); } diff --git a/source/MaterialXTest/MaterialXGenMsl/GenMsl.h b/source/MaterialXTest/MaterialXGenMsl/GenMsl.h index 114ab854d3..78cb7a8f4e 100644 --- a/source/MaterialXTest/MaterialXGenMsl/GenMsl.h +++ b/source/MaterialXTest/MaterialXGenMsl/GenMsl.h @@ -23,9 +23,8 @@ class MslShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester using ParentClass = GenShaderUtil::ShaderGeneratorTester; MslShaderGeneratorTester(mx::ShaderGeneratorPtr shaderGenerator, const mx::FilePathVec& testRootPaths, - const mx::FilePath& libSearchPath, const mx::FileSearchPath& srcSearchPath, - const mx::FilePath& logFilePath, bool writeShadersToDisk) : - GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, libSearchPath, srcSearchPath, logFilePath, writeShadersToDisk) + const mx::FileSearchPath& searchPath, const mx::FilePath& logFilePath, bool writeShadersToDisk) : + GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, searchPath, logFilePath, writeShadersToDisk) {} void setTestStages() override @@ -46,7 +45,7 @@ class MslShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester { ParentClass::setupDependentLibraries(); - mx::FilePath lightDir = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/TestSuite/lights"); + mx::FilePath lightDir = mx::getDefaultDataSearchPath().find("resources/Materials/TestSuite/lights"); loadLibrary(lightDir / mx::FilePath("light_compound_test.mtlx"), _dependLib); loadLibrary(lightDir / mx::FilePath("light_rig_test_1.mtlx"), _dependLib); } diff --git a/source/MaterialXTest/MaterialXGenOsl/GenOsl.cpp b/source/MaterialXTest/MaterialXGenOsl/GenOsl.cpp index b6e71732b6..9d3fa78db4 100644 --- a/source/MaterialXTest/MaterialXGenOsl/GenOsl.cpp +++ b/source/MaterialXTest/MaterialXGenOsl/GenOsl.cpp @@ -95,18 +95,13 @@ TEST_CASE("GenShader: OSL Implementation Check", "[genosl]") TEST_CASE("GenShader: OSL Unique Names", "[genosl]") { mx::GenContext context(mx::OslShaderGenerator::create()); - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - context.registerSourceCodeSearchPath(currentPath); - context.registerSourceCodeSearchPath(currentPath / mx::FilePath("libraries/stdlib/genosl/include")); - + context.registerSourceCodeSearchPath(mx::getDefaultDataSearchPath()); GenShaderUtil::testUniqueNames(context, mx::Stage::PIXEL); } TEST_CASE("GenShader: OSL Metadata", "[genosl]") { - mx::FileSearchPath searchPath; - searchPath.append(mx::FilePath::getCurrentPath()); - + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); mx::loadLibraries({ "libraries" }, searchPath, doc); @@ -176,33 +171,27 @@ TEST_CASE("GenShader: OSL Metadata", "[genosl]") // Custom generators can override this method to customize which metadata gets registered. generator->registerShaderMetadata(doc, context); - // Generate the shader and write to file for inspection. + // Generate the shader. mx::ShaderPtr shader = generator->generate(stdSurf1->getName(), stdSurf1, context); REQUIRE(shader != nullptr); - const std::string filepath = mx::FilePath::getCurrentPath() / mx::FilePath("standardSurfaceWithMetadata.osl"); - std::ofstream file; - file.open(filepath); - file << shader->getSourceCode(); } static void generateOslCode() { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePathVec testRootPaths; - testRootPaths.push_back("resources/Materials/TestSuite"); - testRootPaths.push_back("resources/Materials/Examples"); + testRootPaths.push_back(searchPath.find("resources/Materials/TestSuite")); + testRootPaths.push_back(searchPath.find("resources/Materials/Examples/StandardSurface")); - const mx::FilePath libSearchPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath srcSearchPath(libSearchPath.asString()); - srcSearchPath.append(libSearchPath / mx::FilePath("libraries/stdlib/genosl/include")); - srcSearchPath.append(mx::FilePath::getCurrentPath()); const mx::FilePath logPath("genosl_vanilla_generate_test.txt"); bool writeShadersToDisk = false; - OslShaderGeneratorTester tester(mx::OslShaderGenerator::create(), testRootPaths, libSearchPath, srcSearchPath, logPath, writeShadersToDisk); + OslShaderGeneratorTester tester(mx::OslShaderGenerator::create(), testRootPaths, searchPath, logPath, writeShadersToDisk); tester.addSkipLibraryFiles(); const mx::GenOptions genOptions; - mx::FilePath optionsFilePath("resources/Materials/TestSuite/_options.mtlx"); + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); tester.validate(genOptions, optionsFilePath); } diff --git a/source/MaterialXTest/MaterialXGenOsl/GenOsl.h b/source/MaterialXTest/MaterialXGenOsl/GenOsl.h index b214574493..a4ccdc2003 100644 --- a/source/MaterialXTest/MaterialXGenOsl/GenOsl.h +++ b/source/MaterialXTest/MaterialXGenOsl/GenOsl.h @@ -19,9 +19,8 @@ class OslShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester using ParentClass = GenShaderUtil::ShaderGeneratorTester; OslShaderGeneratorTester(mx::ShaderGeneratorPtr shaderGenerator, const std::vector& testRootPaths, - const mx::FilePath& libSearchPath, const mx::FileSearchPath& srcSearchPath, - const mx::FilePath& logFilePath, bool writeShadersToDisk) : - GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, libSearchPath, srcSearchPath, logFilePath, writeShadersToDisk) + const mx::FileSearchPath& searchPath, const mx::FilePath& logFilePath, bool writeShadersToDisk) : + GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, searchPath, logFilePath, writeShadersToDisk) {} void setTestStages() override diff --git a/source/MaterialXTest/MaterialXGenShader/GenShader.cpp b/source/MaterialXTest/MaterialXGenShader/GenShader.cpp index 293398cbc1..9b25a1d6b7 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShader.cpp +++ b/source/MaterialXTest/MaterialXGenShader/GenShader.cpp @@ -58,11 +58,9 @@ TEST_CASE("GenShader: Utilities", "[genshader]") TEST_CASE("GenShader: Valid Libraries", "[genshader]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - - mx::FileSearchPath searchPath; - searchPath.append(mx::FilePath::getCurrentPath()); - loadLibraries({ "libraries/targets", "libraries/stdlib", "libraries/pbrlib" }, searchPath, doc); + loadLibraries({ "libraries" }, searchPath, doc); std::string validationErrors; bool valid = doc->validate(&validationErrors); @@ -109,11 +107,10 @@ TEST_CASE("GenShader: TypeDesc Check", "[genshader]") TEST_CASE("GenShader: Shader Translation", "[translate]") { - const mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath(currentPath); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::ShaderTranslatorPtr shaderTranslator = mx::ShaderTranslator::create(); - mx::FilePath testPath = currentPath / mx::FilePath("resources/Materials/Examples/StandardSurface"); + mx::FilePath testPath = searchPath.find("resources/Materials/Examples/StandardSurface"); for (mx::FilePath& mtlxFile : testPath.getFilesInDirectory(mx::MTLX_EXTENSION)) { mx::DocumentPtr doc = mx::createDocument(); @@ -147,12 +144,11 @@ TEST_CASE("GenShader: Shader Translation", "[translate]") TEST_CASE("GenShader: Transparency Regression Check", "[genshader]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr libraries = mx::createDocument(); - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath(currentPath); mx::loadLibraries({ "libraries" }, searchPath, libraries); - const mx::FilePath resourcePath(currentPath / "resources"); + const mx::FilePath resourcePath = searchPath.find("resources"); mx::StringVec failedTests; mx::FilePathVec testFiles = { "Materials/Examples/StandardSurface/standard_surface_default.mtlx", @@ -200,8 +196,9 @@ TEST_CASE("GenShader: Transparency Regression Check", "[genshader]") void testDeterministicGeneration(mx::DocumentPtr libraries, mx::GenContext& context) { - const mx::FilePath testFile = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/Examples/StandardSurface/standard_surface_marble_solid.mtlx"); - const mx::string testElement = "SR_marble1"; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath testFile = searchPath.find("resources/Materials/Examples/StandardSurface/standard_surface_marble_solid.mtlx"); + mx::string testElement = "SR_marble1"; const size_t numRuns = 10; mx::vector testDocs(numRuns); @@ -234,9 +231,9 @@ void testDeterministicGeneration(mx::DocumentPtr libraries, mx::GenContext& cont TEST_CASE("GenShader: Deterministic Generation", "[genshader]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr libraries = mx::createDocument(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); - mx::loadLibraries({ "libraries/targets", "libraries/stdlib", "libraries/pbrlib", "libraries/bxdf" }, searchPath, libraries); + mx::loadLibraries({ "libraries" }, searchPath, libraries); #ifdef MATERIALX_BUILD_GEN_GLSL { @@ -270,8 +267,9 @@ TEST_CASE("GenShader: Deterministic Generation", "[genshader]") void checkPixelDependencies(mx::DocumentPtr libraries, mx::GenContext& context) { - const mx::FilePath testFile = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/Examples/GltfPbr/gltf_pbr_boombox.mtlx"); - const mx::string testElement = "Material_boombox"; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath testFile = searchPath.find("resources/Materials/Examples/GltfPbr/gltf_pbr_boombox.mtlx"); + mx::string testElement = "Material_boombox"; mx::DocumentPtr testDoc = mx::createDocument(); mx::readFromXmlFile(testDoc, testFile); @@ -290,9 +288,9 @@ void checkPixelDependencies(mx::DocumentPtr libraries, mx::GenContext& context) TEST_CASE("GenShader: Track Dependencies", "[genshader]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr libraries = mx::createDocument(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); - mx::loadLibraries({ "libraries/targets", "libraries/stdlib", "libraries/pbrlib", "libraries/bxdf" }, searchPath, libraries); + mx::loadLibraries({ "libraries" }, searchPath, libraries); #ifdef MATERIALX_BUILD_GEN_GLSL { @@ -382,9 +380,9 @@ TEST_CASE("GenShader: Track Application Variables", "[genshader]") const mx::string testElement = "surfacematerial"; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr libraries = mx::createDocument(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); - mx::loadLibraries({ "libraries/targets", "libraries/stdlib", "libraries/pbrlib", "libraries/bxdf" }, searchPath, libraries); + mx::loadLibraries({ "libraries" }, searchPath, libraries); mx::DocumentPtr testDoc = mx::createDocument(); mx::readFromXmlString(testDoc, testDocumentString); diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp index 2db73f3bb6..75b69ff93b 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp @@ -70,11 +70,11 @@ void checkImplementations(mx::GenContext& context, const mx::StringSet& generatorSkipNodeDefs, unsigned int expectedSkipCount) { - mx::DocumentPtr doc = mx::createDocument(); const mx::ShaderGenerator& shadergen = context.getShaderGenerator(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr doc = mx::createDocument(); loadLibraries({ "libraries/targets", "libraries/stdlib", "libraries/pbrlib" }, searchPath, doc); const std::string& target = shadergen.getTarget(); @@ -280,7 +280,7 @@ void testUniqueNames(mx::GenContext& context, const std::string& stage) { mx::DocumentPtr doc = mx::createDocument(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); loadLibraries({ "libraries/targets", "libraries/stdlib" }, searchPath, doc); const std::string exampleName = "unique_names"; @@ -478,7 +478,7 @@ void ShaderGeneratorTester::setupDependentLibraries() _dependLib = mx::createDocument(); // Load the standard libraries. - loadLibraries({ "libraries" }, _libSearchPath, _dependLib, _skipLibraryFiles); + loadLibraries({ "libraries" }, _searchPath, _dependLib, _skipLibraryFiles); } void ShaderGeneratorTester::addSkipFiles() @@ -603,10 +603,9 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons // Load in all documents to test mx::StringVec errorLog; - mx::FileSearchPath searchPath(_libSearchPath); for (const auto& testRoot : _testRootPaths) { - mx::loadDocuments(testRoot, searchPath, _skipFiles, overrideFiles, _documents, _documentPaths, + mx::loadDocuments(testRoot, _searchPath, _skipFiles, overrideFiles, _documents, _documentPaths, nullptr, &errorLog); } CHECK(errorLog.empty()); @@ -627,7 +626,7 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons // Create our context mx::GenContext context(_shaderGenerator); context.getOptions() = generateOptions; - context.registerSourceCodeSearchPath(_srcSearchPath); + context.registerSourceCodeSearchPath(_searchPath); // Register shader metadata defined in the libraries. _shaderGenerator->registerShaderMetadata(_dependLib, context); @@ -783,7 +782,8 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons } else { - path = mx::FilePath::getCurrentPath(); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + path = searchPath.isEmpty() ? mx::FilePath() : searchPath[0]; } std::vector sourceCodePaths; diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h index 7bd2cd2459..7be195e9de 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h @@ -156,13 +156,11 @@ class ShaderGeneratorTester { public: ShaderGeneratorTester(mx::ShaderGeneratorPtr shaderGenerator, const mx::FilePathVec& testRootPaths, - const mx::FilePath& libSearchPath, const mx::FileSearchPath& srcSearchPath, - const mx::FilePath& logFilePath, bool writeShadersToDisk) : + const mx::FileSearchPath& searchPath, const mx::FilePath& logFilePath, bool writeShadersToDisk) : _shaderGenerator(shaderGenerator), _targetString(shaderGenerator ? shaderGenerator->getTarget() : "NULL"), _testRootPaths(testRootPaths), - _libSearchPath(libSearchPath), - _srcSearchPath(srcSearchPath), + _searchPath(searchPath), _logFilePath(logFilePath), _writeShadersToDisk(writeShadersToDisk) { @@ -251,8 +249,7 @@ class ShaderGeneratorTester mx::DocumentPtr _dependLib; const mx::FilePathVec _testRootPaths; - const mx::FileSearchPath _libSearchPath; - const mx::FileSearchPath _srcSearchPath; + const mx::FileSearchPath _searchPath; const mx::FilePath _logFilePath; bool _writeShadersToDisk; diff --git a/source/MaterialXTest/MaterialXRender/Render.cpp b/source/MaterialXTest/MaterialXRender/Render.cpp index 994b82c323..49b8ecb4ba 100644 --- a/source/MaterialXTest/MaterialXRender/Render.cpp +++ b/source/MaterialXTest/MaterialXRender/Render.cpp @@ -11,6 +11,8 @@ #include #include +#include + #ifdef MATERIALX_BUILD_OIIO #include #endif @@ -82,7 +84,8 @@ struct GeomHandlerTestOptions void testGeomHandler(GeomHandlerTestOptions& options) { - mx::FilePath imagePath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath imagePath = searchPath.find("resources/Geometry/"); mx::FilePathVec files; unsigned int loadFailed = 0; @@ -156,7 +159,8 @@ struct ImageHandlerTestOptions void testImageHandler(ImageHandlerTestOptions& options) { - mx::FilePath imagePath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Images/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath imagePath = searchPath.find("resources/Images/"); mx::FilePathVec files; unsigned int loadFailed = 0; diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index 2f16cef541..af392a9a05 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -132,11 +132,14 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) testfileOverride.insert(filterFile); } + // Data search path + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::ScopedTimer ioTimer(&profileTimes.ioTime); mx::FilePathVec dirs; for (const auto& root : options.renderTestPaths) { - mx::FilePathVec testRootDirs = root.getSubDirectories(); + mx::FilePathVec testRootDirs = searchPath.find(root).getSubDirectories(); dirs.insert(std::end(dirs), std::begin(testRootDirs), std::end(testRootDirs)); } ioTimer.endTimer(); @@ -144,11 +147,6 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) // Add files to skip addSkipFiles(); - // Library search path - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath; - searchPath.append(currentPath); - // Load in the library dependencies once // This will be imported in each test document below ioTimer.startTimer(); @@ -177,8 +175,8 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) _shaderGenerator->getUnitSystem()->setUnitConverterRegistry(registry); mx::GenContext context(_shaderGenerator); - context.registerSourceCodeSearchPath(currentPath); - context.registerSourceCodeSearchPath(currentPath / mx::FilePath("libraries/stdlib/genosl/include")); + context.registerSourceCodeSearchPath(searchPath); + context.registerSourceCodeSearchPath(searchPath.find("libraries/stdlib/genosl/include")); // Set target unit space context.getOptions().targetDistanceUnit = "meter"; diff --git a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp index 2e8a8b4cf2..719914a62e 100644 --- a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp +++ b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp @@ -73,7 +73,7 @@ class GlslShaderRenderTester : public RenderUtil::ShaderRenderTester void GlslShaderRenderTester::loadAdditionalLibraries(mx::DocumentPtr document, GenShaderUtil::TestSuiteOptions& options) { - mx::FilePath lightDir = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/TestSuite/lights"); + mx::FilePath lightDir = mx::getDefaultDataSearchPath().find("resources/Materials/TestSuite/lights"); for (const auto& lightFile : options.lightFiles) { loadLibrary(lightDir / mx::FilePath(lightFile), document); @@ -128,6 +128,7 @@ void GlslShaderRenderTester::createRenderer(std::ostream& log) // Set image handler on renderer mx::StbImageLoaderPtr stbLoader = mx::StbImageLoader::create(); mx::ImageHandlerPtr imageHandler = _renderer->createImageHandler(stbLoader); + imageHandler->setSearchPath(mx::getDefaultDataSearchPath()); #if defined(MATERIALX_BUILD_OIIO) mx::OiioImageLoaderPtr oiioLoader = mx::OiioImageLoader::create(); imageHandler->addLoader(oiioLoader); @@ -174,6 +175,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, mx::ScopedTimer totalGLSLTime(&profileTimes.languageTimes.totalTime); const mx::ShaderGenerator& shadergen = context.getShaderGenerator(); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); // Perform validation if requested if (testOptions.validateElementToRender) @@ -271,7 +273,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, { if (!testOptions.renderGeometry.isAbsolute()) { - geomPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry") / testOptions.renderGeometry; + geomPath = searchPath.find("resources/Geometry") / testOptions.renderGeometry; } else { @@ -280,7 +282,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, } else { - geomPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry/sphere.obj"); + geomPath = searchPath.find("resources/Geometry/sphere.obj"); } if (!geomHandler->hasGeometry(geomPath)) @@ -450,9 +452,9 @@ void GlslShaderRenderTester::runBake(mx::DocumentPtr doc, const mx::FileSearchPa TEST_CASE("Render: GLSL TestSuite", "[renderglsl]") { - GlslShaderRenderTester renderTester(mx::GlslShaderGenerator::create()); - - mx::FilePath optionsFilePath("resources/Materials/TestSuite/_options.mtlx"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); + GlslShaderRenderTester renderTester(mx::GlslShaderGenerator::create()); renderTester.validate(optionsFilePath); } diff --git a/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp b/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp index d828faf373..722bf4b9b3 100644 --- a/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp +++ b/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp @@ -22,9 +22,8 @@ namespace mx = MaterialX; TEST_CASE("GenReference: OSL Reference", "[genreference]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr stdlib = mx::createDocument(); - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath(currentPath); loadLibraries({ "libraries/targets", "libraries/stdlib" }, searchPath, stdlib); // Create renderer if requested. @@ -38,7 +37,7 @@ TEST_CASE("GenReference: OSL Reference", "[genreference]") oslIncludePaths.append(mx::FilePath(MATERIALX_OSL_INCLUDE_PATH)); // Add in library include path for compile testing as the generated // shader's includes are not added with absolute paths. - oslIncludePaths.append(currentPath / mx::FilePath("libraries/stdlib/genosl/include")); + oslIncludePaths.append(searchPath.find("libraries/stdlib/genosl/include")); oslRenderer->setOslIncludePath(oslIncludePaths); } @@ -46,11 +45,11 @@ TEST_CASE("GenReference: OSL Reference", "[genreference]") mx::ShaderGeneratorPtr generator = mx::OslShaderGenerator::create(); mx::GenContext context(generator); context.getOptions().addUpstreamDependencies = false; - context.registerSourceCodeSearchPath(currentPath); + context.registerSourceCodeSearchPath(searchPath); context.getOptions().fileTextureVerticalFlip = true; // Create output directory. - mx::FilePath outputPath = mx::FilePath::getCurrentPath() / mx::FilePath("reference/osl"); + mx::FilePath outputPath = searchPath.find("reference/osl"); outputPath.getParentPath().createDirectory(); outputPath.createDirectory(); diff --git a/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp b/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp index 41b2a30d85..c23f74cba3 100644 --- a/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp +++ b/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp @@ -15,6 +15,8 @@ #include +#include + namespace mx = MaterialX; namespace @@ -132,6 +134,7 @@ void OslShaderRenderTester::createRenderer(std::ostream& log) mx::StbImageLoaderPtr stbLoader = mx::StbImageLoader::create(); mx::ImageHandlerPtr imageHandler = mx::ImageHandler::create(stbLoader); + imageHandler->setSearchPath(mx::getDefaultDataSearchPath()); #if defined(MATERIALX_BUILD_OIIO) mx::OiioImageLoaderPtr oiioLoader = mx::OiioImageLoader::create(); imageHandler->addLoader(oiioLoader); @@ -143,7 +146,8 @@ void OslShaderRenderTester::createRenderer(std::ostream& log) // Pre-compile some required shaders for testrender if (!oslcExecutable.empty() && !testRenderExecutable.empty()) { - mx::FilePath shaderPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Utilities/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath shaderPath = searchPath.find("resources/Utilities/"); _renderer->setOslOutputFilePath(shaderPath); const std::string OSL_EXTENSION("osl"); @@ -309,7 +313,8 @@ bool OslShaderRenderTester::runRenderer(const std::string& shaderName, _renderer->setOslShaderOutput(outputName, outputType); // Set scene template file. For now we only have the constant color scene file - mx::FilePath sceneTemplatePath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Utilities/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath sceneTemplatePath = searchPath.find("resources/Utilities/"); sceneTemplatePath = sceneTemplatePath / sceneTemplateFile; _renderer->setOslTestRenderSceneTemplateFile(sceneTemplatePath.asString()); @@ -366,9 +371,9 @@ TEST_CASE("Render: OSL TestSuite", "[renderosl]") return; } - OslShaderRenderTester renderTester(mx::OslShaderGenerator::create()); - - mx::FilePath optionsFilePath("resources/Materials/TestSuite/_options.mtlx"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); + OslShaderRenderTester renderTester(mx::OslShaderGenerator::create()); renderTester.validate(optionsFilePath); } From 83f9380628c3c4d8cc4b55c366fcb2dbd618f517 Mon Sep 17 00:00:00 2001 From: Dhruv Govil Date: Fri, 2 Jun 2023 18:02:14 -0700 Subject: [PATCH 02/18] Add DisplayP3 colorspaces (#1368) This PR adds support for the [DisplayP3](https://www.color.org/chardata/rgb/DisplayP3.xalter) colorspace, a wider gamut range for computer displays that is standard in the Apple ecosystem and elsewhere. More information can be found in this [developer video](https://developer.apple.com/videos/play/wwdc2017/821/). DisplayP3 uses the DCI-P3 gamut with a D65 white point and an sRGB transfer curve. In keeping with discussions with the OCIO group, we've opted to name it in the form `_` for consistency. Hence we add the following two colorspaces: * srgb_displayp3 * lin_displayp3 --- .../inprog_v1.39/MaterialX.Specification.md | 2 + libraries/README.md | 14 +++- libraries/cmlib/cmlib_defs.mtlx | 20 ++++++ libraries/cmlib/cmlib_ng.mtlx | 70 +++++++++++++++++++ .../color_management/color_management.mtlx | 28 ++++++++ 5 files changed, 132 insertions(+), 2 deletions(-) diff --git a/documents/Specification/inprog_v1.39/MaterialX.Specification.md b/documents/Specification/inprog_v1.39/MaterialX.Specification.md index c0dc5c9b48..6230618a5e 100644 --- a/documents/Specification/inprog_v1.39/MaterialX.Specification.md +++ b/documents/Specification/inprog_v1.39/MaterialX.Specification.md @@ -339,6 +339,8 @@ By default, MaterialX supports the following color spaces as defined in ACES 1.2 * `lin_srgb` * `adobergb` * `lin_adobergb` +* `srgb_displayp3` +* `lin_displayp3` The working color space of a MaterialX document is defined by the `colorspace` attribute of its root <materialx> element, and it is strongly recommended that all <materialx> elements define a specific `colorspace` if they wish to use a color-managed workflow rather than relying on a default colorspace setting from an external configuration file. diff --git a/libraries/README.md b/libraries/README.md index bf932e9b47..15916f76e2 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -42,8 +42,18 @@ This folder contains the standard data libraries for MaterialX, providing declar - [lama](bxdf/lama) : Graph definitions of the [MaterialX Lama](https://rmanwiki.pixar.com/display/REN24/MaterialX+Lama) node set. ## Color Management Library -- MaterialX shader generation natively supports a small set of common spaces for input colors, with all color transforms implemented as language-independent MaterialX graphs. The canonical definitions of these color transforms may be found in the OpenColorIO configuration for [ACES 1.2](https://github.com/colour-science/OpenColorIO-Configs/tree/feature/aces-1.2-config/aces_1.2). - - lin_rec709, g18_rec709, g22_rec709, rec709_display, acescg (lin_ap1), g22_ap1, srgb_texture, lin_adobergb, adobergb +- MaterialX shader generation natively supports a small set of common spaces for input colors, with all color transforms implemented as language-independent MaterialX graphs.The canonical definitions of these color transforms may be found in the OpenColorIO configuration for [ACES 1.2](https://github.com/colour-science/OpenColorIO-Configs/tree/feature/aces-1.2-config/aces_1.2). + - lin_rec709 + - g18_rec709 + - g22_rec709 + - rec709_display + - acescg (lin_ap1) + - g22_ap1 + - srgb_texture + - lin_adobergb + - adobergb + - srgb_displayp3 + - lin_displayp3 - [cmlib](cmlib) - [cmlib_defs.mtlx](cmlib/cmlib_defs.mtlx) : Nodedef declarations. - [cmlib_ng.mtlx](cmlib/cmlib_ng.mtlx) : Nodegraph definitions. diff --git a/libraries/cmlib/cmlib_defs.mtlx b/libraries/cmlib/cmlib_defs.mtlx index 4f413e9baa..dc912581d2 100644 --- a/libraries/cmlib/cmlib_defs.mtlx +++ b/libraries/cmlib/cmlib_defs.mtlx @@ -87,4 +87,24 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/cmlib/cmlib_ng.mtlx b/libraries/cmlib/cmlib_ng.mtlx index 8216504c02..8045e80a4d 100644 --- a/libraries/cmlib/cmlib_ng.mtlx +++ b/libraries/cmlib/cmlib_ng.mtlx @@ -294,4 +294,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Materials/TestSuite/stdlib/color_management/color_management.mtlx b/resources/Materials/TestSuite/stdlib/color_management/color_management.mtlx index 1e77bc129a..ea2a9455ce 100644 --- a/resources/Materials/TestSuite/stdlib/color_management/color_management.mtlx +++ b/resources/Materials/TestSuite/stdlib/color_management/color_management.mtlx @@ -64,6 +64,20 @@ + + + + + + + + + + + + + + @@ -127,5 +141,19 @@ + + + + + + + + + + + + + + From 07311f53d07548fdea91f2c55cef85be65b475d4 Mon Sep 17 00:00:00 2001 From: Doug Smythe Date: Thu, 8 Jun 2023 15:44:23 -0700 Subject: [PATCH 03/18] Specification Updates (#1371) Updates to the v1.39 markdown spec docs. Main Specification: - Removed the earlier "Shader Nodes" section and moved its important contents to a later existing also-named "Shader Nodes" section, cleaned up the text and node descriptions. - Added clarification paragraph to "Output Nodes" section to say that "colorspace", "width", "height" and "bitdepth" are only applicable in applications that save output images, and do not affect values output to other nodes connected to this output (Slack discussion) - Added descriptions of the new <checkerboard>, <round> and <distance> nodes. Also added note about integer-output variants of <floor> and <ceil>. - Added note that geomcolor, geompropvalue and geompropvalueuniform can take a "colorspace" attribute for color3/4 values (Slack discussion) - Removed notes on "add" and "multiply" about shader-semantic variants, because you can't actually add or multiply *shaders*, only BSDF's/EDF's/VDF's, and those are covered in the PBRSpec doc. PBRSpec: - Renamed "Shader Nodes" section to "PBR Shader Nodes" to avoid conflict with main specification heading - Added links to the "displacement" and "surface_unlit" nodes in the main Spec doc Supplement: - Added "blend" input to the triplanar nodes, as this is a "no brainer" to be added officially. - Some minor formatting fixes. README: - Updated to include new/updated nodes and a few other updates. --- .../inprog_v1.39/MaterialX.PBRSpec.md | 7 +- .../inprog_v1.39/MaterialX.Specification.md | 86 +++++++++++-------- .../inprog_v1.39/MaterialX.Supplement.md | 14 +-- .../inprog_v1.39/README_v1.39.md | 17 ++-- 4 files changed, 71 insertions(+), 53 deletions(-) diff --git a/documents/Specification/inprog_v1.39/MaterialX.PBRSpec.md b/documents/Specification/inprog_v1.39/MaterialX.PBRSpec.md index 58138c8308..9a9fc7527a 100644 --- a/documents/Specification/inprog_v1.39/MaterialX.PBRSpec.md +++ b/documents/Specification/inprog_v1.39/MaterialX.PBRSpec.md @@ -37,7 +37,7 @@ This document describes a number of shader-semantic nodes implementing widely-us  [BSDF Nodes](#bsdf-nodes)  [EDF Nodes](#edf-nodes)  [VDF Nodes](#vdf-nodes) - [Shader Nodes](#shader-nodes) + [PBR Shader Nodes](#pbr-shader-nodes)  [Utility Nodes](#utility-nodes) **[Shading Model Examples](#shading-model-examples)** @@ -303,7 +303,7 @@ The PBS nodes also make use of the following standard MaterialX types: * `anisotropy` (float): Anisotropy factor, controlling the scattering direction, range [-1.0, 1.0]. Negative values give backwards scattering, positive values give forward scattering, and a value of 0.0 (the default) gives uniform scattering. -## Shader Nodes +## PBR Shader Nodes @@ -334,6 +334,9 @@ The PBS nodes also make use of the following standard MaterialX types: * `intensity` (color3): Intensity multiplier for the light's emittance. Defaults to (1.0, 1.0, 1.0). * `exposure` (float): Exposure control for the light's emittance. Defaults to 0.0. +Note that the standard library includes definitions for [**`displacement`**](./MaterialX.Specification.md#node-displacement) and [**`surface_unlit`**](./MaterialX.Specification.md#node-surfaceunlit) shader nodes. + + ## Utility Nodes diff --git a/documents/Specification/inprog_v1.39/MaterialX.Specification.md b/documents/Specification/inprog_v1.39/MaterialX.Specification.md index 6230618a5e..cad8990256 100644 --- a/documents/Specification/inprog_v1.39/MaterialX.Specification.md +++ b/documents/Specification/inprog_v1.39/MaterialX.Specification.md @@ -64,7 +64,6 @@ This document describes the core MaterialX specification. Companion documents [   [Conditional Nodes](#conditional-nodes)   [Channel Nodes](#channel-nodes)   [Convolution Nodes](#convolution-nodes) -  [Shader Nodes](#shader-nodes)  [Standard Node Inputs](#standard-node-inputs)  [Standard UI Attributes](#standard-ui-attributes) @@ -90,7 +89,7 @@ This document describes the core MaterialX specification. Companion documents [    [Example Custom Node Defined by a Nodegraph](#example-custom-node-defined-by-a-nodegraph)   [Custom Node Use](#custom-node-use)  [Shader Nodes](#shader-nodes) -  [Standard Shader-Semantic Operator Nodes](#standard-shader-semantic-operator-nodes) +  [Standard Library Shader Nodes](#standard-library-shader-nodes)   [AOV Output Elements](#aov-output-elements)    [AOVOutput Example](#aovoutput-example)  [Material Nodes](#material-nodes) @@ -620,13 +619,14 @@ Attributes for Output elements: * `nodename` (string, optional): the name of a node at the same scope within the document, whose result value will be output. This attribute is required for <output> elements within a node graph, but is not allowed in <output> elements within a <nodedef>. * `output` (string, optional): if the node specified by `nodename` has multiple outputs, the name of the specific output to connect this <output> to. * `uniform` (boolean, optional): If set to "true", then the output of this node is treated as a uniform value, and this output may be connected to a uniform input of the same (or compatible) type. It is up to the application creating the nodegraph to ensure that the value actually is uniform. Default is "false". + +MaterialX also supports the following additional attributes for Output elements in applications which process node graphs in 2D space and save or cache outputs as images for efficiency, such as texture baking or image caching. These attributes do **not** affect values from this <output> connected to other nodes, e.g. they would remain in the working colorspace and retain full resolution and bitdepth precision. + * `colorspace` (string, optional): the name of the color space for the output image. Applications that support color space management are expected to perform the required transformations of output colors into this space. * `width` (integer, optional): the expected width in pixels of the output image. * `height` (integer, optional): the expected height in pixels of the output image. * `bitdepth` (integer, optional): the expected per-channel bit depth of the output image, which may be used to capture expected color quantization effects. Common values for `bitdepth` are 8, 16, 32, and 64. It is up to the application to determine what the internal representation of any declared bit depth is (e.g. scaling factor, signed or unsigned, etc.). -The `colorspace`, `width`, `height` and `bitdepth` attributes are intended to be used in applications which process node graphs in 2D space and save or cache outputs as images for efficiency. - ## Standard Source Nodes @@ -747,6 +747,15 @@ Standard Procedural nodes: * `center` (float): a value representing the V-coordinate of the split; all pixels above "center" will be `valuet`, all pixels below "center" will be `valueb`. Default is 0.5. * `texcoord` (vector2): the name of a vector2-type node specifying the 2D texture coordinate at which the split position is evaluated. Default is to use the first set of texture coordinates. + + +* **`checkerboard`**: a 2D checkerboard pattern. + * `color1` (color3): The first color used in the checkerboard pattern. + * `color2` (color3): The second color used in the checkerboard pattern. + * `freq` (vector2): The frequency of checkers, with higher values producing smaller squares. Default is (8, 8). + * `offset` (vector2): Shift the pattern in 2d space. Default is (0, 0). + * `texcoord` (vector2): The input 2d space. Default is the first texture coordinates. + * **`noise2d`**: 2D Perlin noise in 1, 2, 3 or 4 channels. @@ -889,6 +898,8 @@ Standard Geometric nodes: * `geomprop` (uniform string): the geometric property to be referenced. * `default` (same type as the geomprop's value): a value to return if the specified `geomprop` is not defined on the current geometry. +Additionally, the `geomcolor`, `geompropvalue` and `geompropvalueuniform` nodes for color3/color4-type properties can take a `colorspace` attribute to declare what colorspace the color property value is in; the default is "none" for no colorspace declaration (and hence no colorspace conversion). + @@ -971,7 +982,7 @@ Operator nodes process one or more required input streams to form an output. Li The inputs of compositing operators are called "fg" and "bg" (plus "alpha" for float and color3 variants, and "mix" for all variants of the `mix` operator), while the inputs of other operators are called "in" if there is exactly one input, or "in1", "in2" etc. if there are more than one input. If an implementation does not support a particular operator, it should pass through the "bg", "in" or "in1" input unchanged. -This section defines the Operator Nodes that all MaterialX implementations are expected to support. Standard Operator Nodes are grouped into the following classifications: [Math Nodes](#math-nodes), [Adjustment Nodes](#adjustment-nodes), [Compositing Nodes](#compositing-nodes), [Conditional Nodes](#conditional-nodes), [Channel Nodes](#channel-nodes), [Convolution Nodes](#convolution-nodes) and [Shader Nodes](#shader-nodes). +This section defines the Operator Nodes that all MaterialX implementations are expected to support. Standard Operator Nodes are grouped into the following classifications: [Math Nodes](#math-nodes), [Adjustment Nodes](#adjustment-nodes), [Compositing Nodes](#compositing-nodes), [Conditional Nodes](#conditional-nodes), [Channel Nodes](#channel-nodes) and [Convolution Nodes](#convolution-nodes). @@ -982,7 +993,7 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m -* **`add`**: add a value to the incoming float/color/vector/matrix. See also the [Shader Nodes](#shader-nodes) section below for additional `add` variants supporting shader-semantic types. +* **`add`**: add a value to the incoming float/color/vector/matrix. * `in1` (float or colorN or vectorN or matrixNN): the value or nodename for the primary input * `in2` (same type as `in1` or float): the value or nodename to add; for matrix types, the default is the zero matrix. @@ -994,7 +1005,7 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m -* **`multiply`**: multiply an incoming float/color/vector/matrix by a value. Multiplication of two vectors is interpreted as a component-wise vector multiplication, while multiplication of two matrices is interpreted as a standard matrix product. To multiply a vector and a matrix, use one of the `transform*` nodes. See also the [Shader Nodes](#shader-nodes) section below for additional `multiply` variants supporting shader-semantic types. +* **`multiply`**: multiply an incoming float/color/vector/matrix by a value. Multiplication of two vectors is interpreted as a component-wise vector multiplication, while multiplication of two matrices is interpreted as a standard matrix product. To multiply a vector and a matrix, use one of the `transform*` nodes. * `in1` (float or colorN or vectorN or matrixNN): the value or nodename for the primary input * `in2` (same type as `in1` or float): the value or nodename to multiply by; default is 1.0 in all channels for float/color/vector types, or the identity matrix for matrix types. @@ -1022,17 +1033,17 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m -* **`floor`**: the per-channel nearest integer value less than or equal to the incoming float/color/vector; the output remains in floating point per-channel, i.e. the same type as the input. +* **`floor`**: the per-channel nearest integer value less than or equal to the incoming float/color/vector. The output remains in floating point per-channel, i.e. the same type as the input, except that the floor(float) also has a variant outputting an integer type. * `in` (float or colorN or vectorN): the input value or nodename -* **`ceil`**: the per-channel nearest integer value greater than or equal to the incoming float/color/vector; the output remains in floating point per-channel, i.e. the same type as the input. +* **`ceil`**: the per-channel nearest integer value greater than or equal to the incoming float/color/vector. The output remains in floating point per-channel, i.e. the same type as the input, except that the ceil(float) also has a variant outputting an integer type. * `in` (float or colorN or vectorN): the input value or nodename -* **`round`**: round each channel of the incoming float/color/vector values to the nearest integer value, e.g "floor(in+0.5)". +* **`round`**: round each channel of the incoming float/color/vector values to the nearest integer value, e.g "floor(in+0.5)"; the round(float) also has a variant outputting an integer type. * `in` (float or colorN or vectorN): the input value or nodename @@ -1133,6 +1144,13 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m * **`magnitude`**: output the float magnitude (vector length) of the incoming vectorN stream; cannot be used on float or colorN streams. Note: the fourth channel in vector4 streams is not treated any differently, e.g. not as a homogeneous "w" value. * `in` (vectorN): the input value or nodename + + + +* **`distance`**: Measures the distance between two points in 2D, 3D, or 4D. + * `in1` (vectorN): the first input value or nodename + * `in2` (same type as `in1`): the second input value or nodename + * **`dotproduct`**: output the (float) dot product of two incoming vectorN streams; cannot be used on float or colorN streams. @@ -1366,7 +1384,7 @@ The Mix node takes two 1-4 channel inputs `fg` and `bg` plus a separate 1-channe | **`mix`** | Fm+B(1-m) | -See also the [Shader Nodes](#shader-nodes) section below for additional `mix` operator variants supporting shader-semantic types. +See also the [Standard Library Shader Nodes](#standard-library-shader-nodes) section below for additional `mix` operator variants supporting shader-semantic types. @@ -1402,7 +1420,7 @@ Conditional nodes are used to compare values of two streams, or to select a valu * **`switch`**: output the value of one of up to ten input streams, according to the value of a selector input `which`. Switch nodes can be of output type float, colorN or vectorN, and have five inputs, in1 through in10 (not all of which must be connected), which must match the output type. - * `in1`, `in2`, `in3`, `in4`, `in5`, `in6`, `in7`, `in8`, `in9`, `in10` (float or colorN or vectorN): the values or nodenames to select from based on the value of the `which` input. The types of the various `inN` inputs must match the type of the `switch` node itself. The default value of all `inN` inputs is 0.0 in all channels. + * `in1`, `in2`, `in3`, `in4`, `in5`, `in6`, `in7`, `in8`, `in9`, `in10` (float or colorN or vectorN): the values or nodenames to select from based on the value of the `which` input. The types of the various `in`N inputs must match the type of the `switch` node itself. The default value of all `in`N inputs is 0.0 in all channels. * `which` (integer or float): a selector to choose which input to take values from; the output comes from input "floor(`which`)+1", clamped to the 1-10 range. So `which`<1 will pass on the value from in1, 1<=`which`<2 will pass the value from in2, 2<=`which`<3 will pass the value from in3, and so on up to 9<=`which` will pass the value from in10. The default value of `which` is 0. @@ -1439,7 +1457,7 @@ Channel nodes are used to perform channel manipulations and data type conversion -* **`combine2`**, **`combine3`**, **`combine4`**: combine the channels from two, three or four streams into the same total number of channels of a single output stream of a specified compatible type; please see the table below for a list of all supported combinations of input and output types. For color output types, no colorspace conversion will take place; the channels are simply copied as-is. +* **`combine2`**, **`combine3`**, **`combine4`**: combine the channels from two, three or four streams into the same total number of channels of a single output stream of a specified compatible type; please see the table below for a list of all supported combinations of input and output types. For colorN output types, no colorspace conversion will take place; the channels are simply copied as-is. * `in1` (float/color3/vector2/vector3): the input value or nodename which will be sent to the N channels of the output; default is 0.0 in all channels * `in2` (float/vector2): the input value or nodename which will be sent to the next N channels of the output; default is 0.0 in all channels * `in3` (float): for **`combine3`** or **`combine4`**, the input value or nodename which will be sent to the next channel of the output after `in2`; default is 0.0 @@ -1497,25 +1515,6 @@ Convolution nodes have one input named "in", and apply a defined convolution fun -### Shader Nodes - -Shader nodes construct a shader (a node with a shader semantic output type) from the specified inputs, which may then be connected to a material. Standard library shaders do not respond to external illumination; please refer to the [**MaterialX Physically Based Shading Nodes**](./MaterialX.PBRSpec.md#materialx-pbs-library) document for definitions of additional nodes and shader constructors which do respond to illumination. - - - - -* **`surface`**: Constructs a surface shader for an unlit surface with a plain unshaded color value. Useful for visualizing texture data or rendering non-PBR materials. Output type "surfaceshader". - * `color` (color3): Color value to display. Default is (0, 0, 0). - * `opacity` (float or color3): Cutout (float) or transmission (color3) opacity for the surface. Default is 1.0, representing a fully-opaque surface. - - - -* **`displacement`**: Constructs a displacement shader describing geometric modification to surfaces. Output type "displacementshader". - * `displacement` (float or vector3): Scalar (along the surface normal direction) or vector displacement (in (dPdu, dPdv, N) tangent/normal space) for each position. Default is 0. - * `scale` (float): Scale factor for the displacement vector. Default is 1.0. - - - ## Standard Node Inputs All standard nodes which define a `defaultinput` or `default` value support the following input: @@ -2172,14 +2171,11 @@ An input with a shader-semantic type may be given a value of "" to indicate no s -### Standard Shader-Semantic Operator Nodes +### Standard Library Shader Nodes -The Standard MaterialX Library defines the following nodes and node variants operating on "shader"-semantic types. +The Standard MaterialX Library defines the following nodes and node variants operating on "shader"-semantic types. Standard library shaders do not respond to external illumination; please refer to the [**MaterialX Physically Based Shading Nodes**](./MaterialX.PBRSpec.md#materialx-pbs-library) document for definitions of additional nodes and shader constructors which do respond to illumination. -* **`mix`**: linear blend between two surface/displacement/volumeshader closures. - * `bg` (surface/displacement/volumeshader): the name of the background shader-semantic node - * `fg` (surface/displacement/volumeshader): the name of the foreground shader-semantic node - * `mix` (float): the blending factor used to mix the two input closures + * **`surface_unlit`**: an unlit surface shader node, representing a surface that can emit and transmit light, but does not receive illumination from light sources or other surfaces. Output type surfaceshader. * `emission` (float): the surface emission amount; default is 1.0 @@ -2188,6 +2184,20 @@ The Standard MaterialX Library defines the following nodes and node variants ope * `transmission_color` (color3): surface transmission color; default is (1, 1, 1) * `opacity` (float): surface cutout opacity; default is 1.0 + + +* **`displacement`**: Constructs a displacement shader describing geometric modification to surfaces. Output type "displacementshader". + * `displacement` (float or vector3): Scalar (along the surface normal direction) or vector displacement (in (dPdu, dPdv, N) tangent/normal space) for each position. Default is 0. + * `scale` (float): Scale factor for the displacement vector. Default is 1.0. + + + +* **`mix`**: linear blend between two surface/displacement/volumeshader closures. + * `bg` (surface/displacement/volumeshader): the name of the background shader-semantic node + * `fg` (surface/displacement/volumeshader): the name of the foreground shader-semantic node + * `mix` (float): the blending factor used to mix the two input closures + + ### AOV Output Elements diff --git a/documents/Specification/inprog_v1.39/MaterialX.Supplement.md b/documents/Specification/inprog_v1.39/MaterialX.Supplement.md index 703fa1275c..5a1a8e7a55 100644 --- a/documents/Specification/inprog_v1.39/MaterialX.Supplement.md +++ b/documents/Specification/inprog_v1.39/MaterialX.Supplement.md @@ -84,6 +84,7 @@ This section describes a number of supplemental nodes for MaterialX. These node * `default` (float or colorN or vectorN): a default value to use if any `fileX` reference can not be resolved (e.g. if a <geomtoken>, [interfacetoken] or {hostattr} is included in the filename but no substitution value or default is defined, or if the resolved file URI cannot be read) The `default` value must be the same type as the `` element itself. If `default` is not defined, the default color value will be 0.0 in all channels. * `position` (vector3): a spatially-varying input specifying the 3D position at which the projection is evaluated. Default is to use the current 3D object-space coordinate. * `normal` (vector3): a spatially-varying input specifying the 3D normal vector used for blending. Default is to use the current object-space surface normal. + * `blend` (float): a 0-1 weighting factor for blending the three axis samples using the geometric normal, with higher values giving softer blending. Default is 1.0. * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. ``` @@ -160,12 +161,13 @@ This section describes a number of supplemental nodes for MaterialX. These node * **`triplanarblend`**: samples data from three inputs, and projects a tiled representation of the images along each of the three respective coordinate axes, computing a weighted blend of the three samples using the geometric normal. - * inx (float or colorN): the image to be projected in the direction from the +X axis back toward the origin. Default is 0 in all channels. - * iny (float or colorN): the image to be projected in the direction from the +Y axis back toward the origin with the +X axis to the right. Default is 0 in all channels. - * inz (float or colorN): the image to be projected in the direction from the +Z axis back toward the origin. Default is 0 in all channels. - * position (vector3): a spatially-varying input specifying the 3D position at which the projection is evaluated. Default is to use the current 3D object-space coordinate. - * normal (vector3): a spatially-varying input specifying the 3D normal vector used for blending. Default is to use the current object-space surface normal. - * filtertype (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. + * `inx` (float or colorN): the image to be projected in the direction from the +X axis back toward the origin. Default is 0 in all channels. + * `iny` (float or colorN): the image to be projected in the direction from the +Y axis back toward the origin with the +X axis to the right. Default is 0 in all channels. + * `inz` (float or colorN): the image to be projected in the direction from the +Z axis back toward the origin. Default is 0 in all channels. + * `position` (vector3): a spatially-varying input specifying the 3D position at which the projection is evaluated. Default is to use the current 3D object-space coordinate. + * `normal` (vector3): a spatially-varying input specifying the 3D normal vector used for blending. Default is to use the current object-space surface normal. + * `blend` (float): a 0-1 weighting factor for blending the three axis samples using the geometric normal, with higher values giving softer blending. Default is 1.0. + * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. diff --git a/documents/Specification/inprog_v1.39/README_v1.39.md b/documents/Specification/inprog_v1.39/README_v1.39.md index f63017ba9d..28e74e195b 100644 --- a/documents/Specification/inprog_v1.39/README_v1.39.md +++ b/documents/Specification/inprog_v1.39/README_v1.39.md @@ -33,7 +33,7 @@ Previously, MaterialX used custom types with a structure of output variables to **Array Types Now Uniform and Static Length** -Many shading languages do not support dynamic array types with a variable length, so MaterialX now only supports arrays with a fixed maximum length, and all array-type node inputs must be uniform; nodes are no longer permitted to output an array type. Array-type inputs may be accompanied by a uniform integer input declaring the number of array elements actually used in the array. Because of this change, the unimplemented <arrayappend> node has been removed. +Many shading languages do not support dynamic array types with a variable length, so MaterialX now only supports arrays with a fixed maximum length, and all array-type node inputs must be uniform; nodes are no longer permitted to output an array type. Array-type inputs may be accompanied by a uniform integer input declaring the number of array elements actually used in the array (the <curveadjust> node has been updated in this way). Because of this change, the unimplemented <arrayappend> node has been removed. **Connectable Uniform Inputs and New Tokenvalue Node** @@ -62,7 +62,7 @@ The standard <swizzle> node using a string of channel names and allowing arbi **New Unlit Surface Shader and Standard Materials** -A new standard <surface> constructor node for unlit surfaces has been added to the standard library. +A new <surface_unlit> node for unlit surfaces has been added to the standard library. Additionally, the standard <surfacematerial> material now supports both single- or double-sided surfaces with the addition of a separate `backsurface` input. @@ -72,13 +72,13 @@ Additionally, the standard <surfacematerial> material now supports both singl Typedefs may now inherit from other types, including built-in types, and may provide hints about their values such as floating-point precision. These new "inherit" and "hint" attributes are themselves merely metadata hints about the types; applications and code generators are still expected to provide their own precise definitions for all custom types. -**New Nodes** +**New and Updated Standard Library Nodes** -The following new standard operator nodes have been added: +The following new operator nodes have been added to the standard library: -* [Procedural nodes](./MaterialX.Specification.md#procedural-nodes): **tokenvalue**, **fractal2d**, **cellnoise1d** +* [Procedural nodes](./MaterialX.Specification.md#procedural-nodes): **tokenvalue**, **checkerboard**, **fractal2d**, **cellnoise1d** * [Geometric nodes](./MaterialX.Specification.md#geometric-nodes): **bump**, **geompropvalueuniform** -* [Math nodes](./MaterialX.Specification.md#math-nodes): boolean **and**, **or**, **not**; **transformcolor**, **creatematrix** +* [Math nodes](./MaterialX.Specification.md#math-nodes): boolean **and**, **or**, **not**; **distance**, **transformcolor** and **creatematrix**, as well as integer-output variants of **floor** and **ceil** * [Adjustment nodes](./MaterialX.Specification.md#adjustment-nodes): **curveinversecubic**, **curveuniformlinear** and **curveuniformcubic** * [Conditional nodes](./MaterialX.Specification.md#conditional-nodes): boolean-output variants of **ifgreater**, **ifgreatereq** and **ifequal**; new **ifelse** node * [Channel nodes](./MaterialX.Specification.md#channel-nodes): **extractrowvector** @@ -103,10 +103,13 @@ The following new standard physically based shading nodes have been added: * The <member> element for <typedef>s and the "member" attribute for inputs have been removed from the Specification, as they had never been implemented and it was not clear how they could be implemented generally. * The "valuerange" and "valuecurve" attributes describing expressions and function curves have been removed, in favor of using the new <curveinversecubic> / <curveuniformcubic> / etc. nodes. +* The <geomcolor>, <geompropvalue> and <geompropvalueuniform> nodes for color3/4-type values can now take a "colorspace" attribute to declare the colorspace of the property value. * The <cellnoise2d> and <cellnoise3d> nodes now support vectorN output types in addition to float output. +* The <noise2d/3d>, <fractal2d/3d>, <cellnoise2d/3d> and <worleynoise2d/3d> nodes now support a "period" input. * The <worleynoise2d> and <worleynoise3d> nodes now support a number of different distance metrics. -* The <time> node no longer has a "frames per second" input: the application is now always expected to generate the "current time in seconds" using an appropriate method. "Fps" was removed because varible-rate real-time applications have no static "fps", and it's generally not good to bake a situation-dependent value like fps into a shading network. +* The <time> node no longer has a "frames per second" input: the application is now always expected to generate the "current time in seconds" using an appropriate method. The "fps" input was removed because variable-rate real-time applications have no static "fps", and it's generally not good to bake a situation-dependent value like fps into a shading network. * A standard "tangent" space is now defined in addition to "model", "object" and "world" spaces, and the <heighttonormal> node now accepts a uniform "space" input to define the space of the output normal vector. +* The <switch> node now supports 10 inputs instead of just 5. * The <surface> and <displacement> nodes are now part of the main Specification rather than being Physically Based Shading nodes. * <Token> elements are now explicitly allowed to be children of compound nodegraphs, and token values may now have defined enum/enumvalues. * Inputs in <nodedef>s may now supply "hints" to code generators as to their intended interpretation, e.g. "transparency" or "opacity". From 5a2a8216a3089d80ba94f52375cabb624eec47b6 Mon Sep 17 00:00:00 2001 From: Jonathan Stone Date: Thu, 8 Jun 2023 15:47:02 -0700 Subject: [PATCH 04/18] Build matrix improvements - Add an initial MacOS 13 build to GitHub Actions. - Generalize existing support for analysis builds without Python integration. --- .github/workflows/main.yml | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8e68529175..5b2354da24 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -59,8 +59,14 @@ jobs: compiler: clang compiler_version: "14" python: 3.11 - cmake_config: -DMATERIALX_DYNAMIC_ANALYSIS=ON clang_format: ON + + - name: Linux_Clang_14_DynamicAnalysis + os: ubuntu-22.04 + compiler: clang + compiler_version: "14" + python: None + cmake_config: -DMATERIALX_DYNAMIC_ANALYSIS=ON dynamic_analysis: ON - name: MacOS_Xcode_11_Python37 @@ -77,9 +83,9 @@ jobs: python: 3.9 - name: MacOS_Xcode_14_Python311 - os: macos-12 + os: macos-13 compiler: xcode - compiler_version: "14.2" + compiler_version: "14.3" python: 3.11 test_shaders: ON @@ -147,6 +153,7 @@ jobs: Add-Content $env:GITHUB_PATH "$PWD/vcpkg/installed/x64-windows/tools" - name: Install Python ${{ matrix.python }} + if: matrix.python != 'None' uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} @@ -192,7 +199,7 @@ jobs: working-directory: build - name: Python Tests - if: matrix.dynamic_analysis != 'ON' + if: matrix.python != 'None' run: | python MaterialXTest/main.py python MaterialXTest/genshader.py @@ -249,7 +256,7 @@ jobs: working-directory: build/render - name: Upload Installed Package - if: matrix.dynamic_analysis != 'ON' + if: matrix.python != 'None' uses: actions/upload-artifact@v3 with: name: MaterialX_${{ matrix.name }} From e4d04a3be2e3b128ea430baab47718482f0d5a77 Mon Sep 17 00:00:00 2001 From: Doug Smythe Date: Tue, 13 Jun 2023 08:37:46 -0700 Subject: [PATCH 05/18] Merge supplemental nodes into main specification (#1377) Descriptions of all of the "Supplemental Nodes" are moved from the Supplemental Notes doc to the main Specification doc, as discussed in various forums. The previously-supplemental nodes now have a subtle "(NG)" identifier in the description, and the text has been updated to explain this difference (There are likely several original standard nodes that should get this "(NG)" identifier). The README file was updated to reflect the above change, as well as a few previous Spec updates that weren't yet reflected in the README. I also added link target elements for each individual Merge node so they can be linked to from the outside. --- .../inprog_v1.39/MaterialX.Specification.md | 218 +++++++++++++++- .../inprog_v1.39/MaterialX.Supplement.md | 238 +----------------- .../inprog_v1.39/README_v1.39.md | 23 +- 3 files changed, 217 insertions(+), 262 deletions(-) diff --git a/documents/Specification/inprog_v1.39/MaterialX.Specification.md b/documents/Specification/inprog_v1.39/MaterialX.Specification.md index cad8990256..3a90a269fb 100644 --- a/documents/Specification/inprog_v1.39/MaterialX.Specification.md +++ b/documents/Specification/inprog_v1.39/MaterialX.Specification.md @@ -550,7 +550,7 @@ where _nodecategory_ is the general "category" of the node (e.g. "image", "add" Node elements may optionally specify a `version` string attribute in "_major_[._minor_]" format, requesting that a specific version of that node's definition be used instead of the default version. Normally, the types of a node's inputs and outputs are sufficient to disambiguate which signature of the applicable version of a node is intended, but if necessary, a node instantiation may also declare a specific nodedef name to precisely define exactly which node signature is desired. Please refer to the [Custom Node Declaration NodeDef Elements](#custom-node-declaration-nodedef-elements) section below for further details. -MaterialX defines a number of Standard Nodes which all implementations should support as described to the degree their architecture and capabilities allow. One can define new nodes by declaring their parameter interfaces and providing portable or target-specific implementations. Please see the [Custom Nodes](#custom-nodes) section for notes and implementation details. +MaterialX defines a number of Standard Nodes which all implementations should support as described to the degree their architecture and capabilities allow. These standard nodes are grouped into [Standard Source Nodes](#standard-source-nodes) and [Standard Operator Nodes](#standard-operator-nodes); these groups are further divided into additional subcategories of nodes. In the descriptions below, a node with an "(NG)" annotation indicates a node that is implemented using a nodegraph in the MaterialX distribution, while unannotated nodes are implemented natively in the various renderer shading languages. One can define new nodes by declaring their parameter interfaces and providing portable nodegraph or target-specific shading language implementations. Please see the [Custom Nodes](#custom-nodes) section for notes and implementation details. @@ -664,10 +664,37 @@ Standard Texture nodes: * `vaddressmode` (uniform string): determines how V coordinates outside the 0-1 range are processed before sampling the image; see below. Default is "periodic". * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. + + +* **`tiledimage`** (NG): samples data from a single image, with provisions for tiling and offsetting the image across uv space. + * `file` (uniform filename): the URI of an image file. The filename can include one or more substitutions to change the file name (including frame number) that is accessed, as described in the [Filename Substitutions](#filename-substitutions) section. + * `default` (float or colorN or vectorN): a default value to use if the `file` reference can not be resolved (e.g. if a <geomtoken>, [interfacetoken] or {hostattr} is included in the filename but no substitution value or default is defined, or if the resolved file URI cannot be read), or if the specified `layer` does not exist in the file. The `default` value must be the same type as the `` element itself. If `default` is not defined, the default color value will be 0.0 in all channels. + * `texcoord` (vector2): the name of a vector2-type node specifying the 2D texture coordinate at which the image data is read. Default is to use the current u,v coordinate. + * `uvtiling` (vector2): the tiling rate for the given image along the U and V axes. Mathematically equivalent to multiplying the incoming texture coordinates by the given vector value. Default value is (1.0, 1.0). + * `uvoffset` (vector2): the offset for the given image along the U and V axes. Mathematically equivalent to subtracting the given vector value from the incoming texture coordinates. Default value is (0.0, 0.0). + * `realworldimagesize` (vector2): the real-world size represented by the `file` image, with unittype "distance". A `unit` attribute may be provided to indicate the units that `realworldimagesize` is expressed in. + * `realworldtilesize` (vector2): the real-world size of a single square 0-1 UV tile, with unittype "distance". A `unit` attribute may be provided to indicate the units that `realworldtilesize` is expressed in. + * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. + + + +* **`triplanarprojection`** (NG): samples data from three images (or layers within multi-layer images), and projects a tiled representation of the images along each of the three respective coordinate axes, computing a weighted blend of the three samples using the geometric normal. + * `filex` (uniform filename): the URI of an image file to be projected in the direction from the +X axis back toward the origin. + * `filey` (uniform filename): the URI of an image file to be projected in the direction from the +Y axis back toward the origin with the +X axis to the right. + * `filez` (uniform filename): the URI of an image file to be projected in the direction from the +Z axis back toward the origin. + * `layerx` (uniform string): the name of the layer to extract from a multi-layer input file for the x-axis projection. If no value for `layerx` is provided and the input file has multiple layers, then the "default" layer will be used, or "rgba" if there is no "default" layer. Note: the number of channels defined by the `type` of the `` must match the number of channels in the named layer. + * `layery` (uniform string): the name of the layer to extract from a multi-layer input file for the y-axis projection. + * `layerz` (uniform string): the name of the layer to extract from a multi-layer input file for the z-axis projection. + * `default` (float or colorN or vectorN): a default value to use if any `fileX` reference can not be resolved (e.g. if a <geomtoken>, [interfacetoken] or {hostattr} is included in the filename but no substitution value or default is defined, or if the resolved file URI cannot be read) The `default` value must be the same type as the `` element itself. If `default` is not defined, the default color value will be 0.0 in all channels. + * `position` (vector3): a spatially-varying input specifying the 3D position at which the projection is evaluated. Default is to use the current 3D object-space coordinate. + * `normal` (vector3): a spatially-varying input specifying the 3D normal vector used for blending. Default is to use the current object-space surface normal. + * `blend` (float): a 0-1 weighting factor for blending the three axis samples using the geometric normal, with higher values giving softer blending. Default is 1.0. + * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. + -The following values are supported by `uaddressmode` and `vaddressmode` inputs: +The following values are supported by `uaddressmode` and `vaddressmode` inputs of [image](#node-image) nodes: * “constant”: Texture coordinates outside the 0-1 range return the value of the node's `default` input. * “clamp”: Texture coordinates are clamped to the 0-1 range before sampling the image. @@ -687,8 +714,6 @@ Texture nodes using `file*` inputs also support the following inputs to handle b Arbitrary frame number expressions and speed changes are not supported. -Additional texture nodes, including **``** and **``**, may be found in the [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md#supplemental-texture-nodes) document. - ### Procedural Nodes @@ -731,6 +756,15 @@ Standard Procedural nodes: * `valueb` (float or colorN or vectorN): the value at the bottom (V=0) edge * `texcoord` (vector2): the name of a vector2-type node specifying the 2D texture coordinate at which the ramp interpolation is evaluated. Default is to use the first set of texture coordinates. + + +* **`ramp4`** (NG): a 4-corner bilinear value ramp. + * `valuetl` (float or colorN or vectorN): the value at the top-left (U0V1) corner + * `valuetr` (float or colorN or vectorN): the value at the top-right (U1V1) corner + * `valuebl` (float or colorN or vectorN): the value at the bottom-left (U0V0) corner + * `valuebr` (float or colorN or vectorN): the value at the bottom-right (U1V0) corner + * `texcoord` (vector2, optional): the name of a vector2-type node specifying the 2D texture coordinate at which the ramp interpolation is evaluated. Default is to use the first set of texture coordinates. + * **`splitlr`**: a left-right split matte, split at a specified U value. @@ -826,10 +860,38 @@ Standard Procedural nodes: * `jitter` (float): amount to jitter the cell center position, with smaller values creating a more regular pattern. Default is 1.0. * `position` (vector3): the 3D position at which the noise is evaluated. Default is to use the current 3D object-space coordinate. + + +* **`unifiednoise2d`** (NG): a single node supporting 2D Perlin, Cell, Worley or Fractal noise in a unified interface. + * `type` (integer): The type of noise function to use. One of 0 (Perlin), 1 (Cell), 2 (Worley), or 3 (Fractal); default is Perlin. + * `texcoord` (vector2): the input 2d space. Default is the first texture coordinates. + * `freq` (vector2): Adjusts the noise frequency, with higher values producing smaller noise shapes. Default is (1,1). + * `offset` (vector2): Shift the noise in 2d space. Default is (0,0). + * `jitter` (float): Adjust uniformity of Worley noise; for other noise types jitters the results. + * `outmin` (float): The lowest values fit to the noise. Default is 0.0. + * `outmax` (float): The highest values fit to the noise. Default is 1.0. + * `clampoutput` (boolean): Clamp the output to the min and max output values. + * `octaves` (integer): The number of octaves of Fractal noise to be generated. Default is 3. + * `lacunarity` (float): The exponential scale between successive octaves of Fractal noise. Default is 2.0. + * `diminish` (float): The rate at which noise amplitude is diminished for each octave of Fractal noise. Default is 0.5. + + + +* **`unifiednoise3d`** (NG): a single node supporting 3D Perlin, Cell, Worley or Fractal noise in a unified interface. + * `type` (integer): The type of noise function to use. One of 0 (Perlin), 1 (Cell), 2 (Worley), or 3 (Fractal); default is Perlin. + * `position` (vector3): the input 3d space. Default is position in object-space. + * `freq` (vector3): Adjusts the noise frequency, with higher values producing smaller noise shapes. Default is (1,1,1). + * `offset` (vector3): Shift the noise in 3d space. Default is (0,0,0). + * `jitter` (float): Adjust uniformity of Worley noise; for other noise types jitters the results. + * `outmin` (float): The lowest values fit to the noise. Default is 0.0. + * `outmax` (float): The highest values fit to the noise. Default is 1.0. + * `clampoutput` (boolean): Clamp the output to the min and max output values. + * `octaves` (integer): The number of octaves of Fractal noise to be generated. Default is 3. + * `lacunarity` (float): The exponential scale between successive octaves of Fractal noise. Default is 2.0. + * `diminish` (float): The rate at which noise amplitude is diminished for each octave of Fractal noise. Default is 0.5. -To scale or offset the noise pattern generated by `noise3d`, `fractal3d` or `cellnoise3d`, use a <position> or other [Geometric Node](#geometric-nodes) (see below) connected to vector3 <multiply> and/or <add> nodes, in turn connected to the noise node's `position` input. To scale or offset `rampX`, `splitX`, `noise2d` or `cellnoise2d` input coordinates, use a <texcoord> or similar Geometric node processed by vector2 <multiply>, <rotate> and/or <add> nodes, and connect to the node's `texcoord` input. -Additional procedural nodes, including **``**, may be found in the [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md#supplemental-procedural-nodes) document. +To scale or offset the noise pattern generated by `noise3d`, `fractal3d` or `cellnoise3d`, use a <position> or other [Geometric Node](#geometric-nodes) (see below) connected to vector3 <multiply> and/or <add> nodes, in turn connected to the noise node's `position` input. To scale or offset `rampX`, `splitX`, `noise2d` or `cellnoise2d` input coordinates, use a <texcoord> or similar Geometric node processed by vector2 <multiply>, <rotate> and/or <add> nodes, and connect to the node's `texcoord` input. @@ -1050,7 +1112,13 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m * **`power`**: raise incoming float/color values to the specified exponent, commonly used for "gamma" adjustment. * `in1` (float or colorN or vectorN): the value or nodename for the primary input - * `in2` (same type as `in` or float): exponent value or nodename; output = pow(in1, in2); default is 1.0 in all channels + * `in2` (same type as `in1` or float): exponent value or nodename; output = pow(in1, in2); default is 1.0 in all channels + + + +* **`safepower`** (NG): raise incoming float/color values to the specified exponent. Unlike the standard [<power>](#node-power) node, negative `in1` values for <safepower> will result in negative output values, e.g. `out = sign(in1)*pow(abs(in1),in2)`. + * `in1` (float or colorN or vectorN): the value or nodename for the primary input + * `in2` (same type as `in1` or float): exponent value or nodename; default is 1.0 in all channels @@ -1242,19 +1310,45 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m * `amount` (float): the amount to rotate, specified in degrees; default is 0. * `axis` (vector3): For vector3 inputs only, the unit axis vector about which to rotate; default is (0,1,0). + + +* **`place2d`** (NG): transform incoming UV texture coordinates for 2D texture placement. + * `texcoord` (vector2): the input UV coordinate to transform; defaults to the current surface index=0 uv coordinate. + * `pivot` (vector2): the pivot coordinate for scale and rotate: this is subtracted from u,v before applying scale/rotate, then added back after. Default is (0,0). + * `scale` (vector2): divide the u,v coord (after subtracting `pivot`) by this, so a scale (2,2) makes the texture image appear twice as big. Negative values can be used to flip or flop the texture space. Default is (1,1). + * `rotate` (float): rotate u,v coord (after subtracting pivot) by this amount in degrees, so a positive value rotates UV coords counter-clockwise, and the image clockwise. Default is 0. + * `offset` (vector2): subtract this amount from the scaled/rotated/“pivot added back” UV coordinate; since U0,V0 is typically the lower left corner, a positive offset moves the texture image up and right. Default is (0,0). + * `operationorder` (integer enum): the order in which to perform the transform operations. "0" or "SRT" performs -pivot scale rotate translate +pivot as per the original implementation matching the behavior of certain DCC packages, and "1" or "TRS" performs -pivot translate rotate scale +pivot which does not introduce texture shear. Default is 0 "SRT" for backward compatibility. + + + +* **`triplanarblend`** (NG): samples data from three inputs, and projects a tiled representation of the images along each of the three respective coordinate axes, computing a weighted blend of the three samples using the geometric normal. + * `inx` (float or colorN): the image to be projected in the direction from the +X axis back toward the origin. Default is 0 in all channels. + * `iny` (float or colorN): the image to be projected in the direction from the +Y axis back toward the origin with the +X axis to the right. Default is 0 in all channels. + * `inz` (float or colorN): the image to be projected in the direction from the +Z axis back toward the origin. Default is 0 in all channels. + * `position` (vector3): a spatially-varying input specifying the 3D position at which the projection is evaluated. Default is to use the current 3D object-space coordinate. + * `normal` (vector3): a spatially-varying input specifying the 3D normal vector used for blending. Default is to use the current object-space surface normal. + * `blend` (float): a 0-1 weighting factor for blending the three axis samples using the geometric normal, with higher values giving softer blending. Default is 1.0. + * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. + * **`dot`**: a no-op, passes its input through to its output unchanged. Users can use dot nodes to shape edge connection paths or provide documentation checkpoints in node graph layout UI's. Dot nodes may also pass uniform values from <constant>, <tokenvalue> or other nodes with uniform="true" outputs to uniform <input>s and <token>s. * `in` (any type): the nodename to be connected to the Dot node's "in" input. Unlike inputs on other node types, the <dot> node's input is specifically disallowed to provide a `channels` attribute: input data can only be passed through unmodified. -Additional math nodes, including **``**, **``** and **``** may be found in the [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md#supplemental-math-nodes) document. - ### Adjustment Nodes Adjustment nodes have one input named "in", and apply a specified function to values in the incoming stream. + + +* **`contrast`** (NG): increase or decrease contrast of incoming float/color values using a linear slope multiplier. + * `in` (float or colorN or vectorN): the input value or nodename + * `amount` (same type as `in` or float): slope multiplier for contrast adjustment, 0.0 to infinity range. Values greater than 1.0 increase contrast, values between 0.0 and 1.0 reduce contrast. Default is 1.0 in all channels. + * `pivot` (same type as `in` or float): center pivot value of contrast adjustment; this is the value that will not change as contrast is adjusted. Default is 0.5 in all channels. + * **`remap`**: linearly remap incoming values from one range of float/color/vector values to another. @@ -1264,6 +1358,17 @@ Adjustment nodes have one input named "in", and apply a specified function to va * `outlow` (same type as `in` or float): low value for output range; default is 0.0 in all channels * `outhigh` (same type as `in` or float): high value for output range; default is 1.0 in all channels + + +* **`range`** (NG): remap incoming values from one range of float/color/vector values to another, optionally applying a gamma correction "in the middle". Input values below `inlow` or above `inhigh` are extrapolated unless `doclamp` is true, in which case the output values will be clamped to the `outlow`..`outhigh` range. + * `in` (float or colorN or vectorN): the input value or nodename + * `inlow` (same type as `in` or float): low value for input range. Default is 0.0 in all channels. + * `inhigh` (same type as `in` or float): high value for input range. Default is 1.0 in all channels. + * `gamma` (same type as `in` or float): inverse exponent applied to input value after first transforming from `inlow`..`inhigh` to 0..1; `gamma` values greater than 1.0 make midtones brighter. Default is 1.0 in all channels. + * `outlow` (same type as `in` or float): low value for output range. Default is 0.0 in all channels. + * `outhigh` (same type as `in` or float): high value for output range. Default is 1.0 in all channels. + * `doclamp` (boolean): If true, the output is clamped to the range `outlow`..`outhigh`. Default is false. + * **`smoothstep`**: output a smooth (hermite-interpolated) remapping of input values from low-high to output 0-1. @@ -1289,6 +1394,22 @@ Adjustment nodes have one input named "in", and apply a specified function to va * `in` (float): the input interpolant value or nodename * `knotvalues` (uniform floatarray or colorNarray or vectorNarray): the array of at least 2 values to interpolate between. + + +* **`curveadjust`** (NG): output a smooth remapping of input values using the centripetal Catmull-Rom cubic spline curve defined by specified knot values, using an inverse spline lookup on input knot values and a forward spline through output knot values. All channels of the input will be remapped using the same curve. + * `in` (float or colorN or vectorN): the input value or nodename + * `numknots` (uniform integer): the number of values in the knots and knotvalues arrays + * `knots` (uniform floatarray): the list of input values defining the curve for the remapping. At least 2 and at most 16 values must be provided. + * `knotvalues` (uniform floatarray): the list of output values defining the curve for the remapping. Must be the same length as knots. + + + +* **`curvelookup`** (NG): output a float, colorN or vectorN value smoothly interpolated between a number of knotvalue values, using the position of in within knots as the knotvalues interpolant. + * `in` (float): the input interpolant value or nodename + * `numknots` (uniform integer): the number of values in the knots and knotvalues arrays + * `knots` (uniform floatarray): the list of knot values to interpolate in within. At least 2 and at most 16 values must be provided. + * `knotvalues` (uniform floatarray or colorNarray or vectorNarray): the values at each knot position to interpolate between. Must be the same length as knots. + * **`luminance`**: (color3 or color4 only) output a grayscale value containing the luminance of the incoming RGB color in all color channels, computed using the dot product of the incoming color with the luma coefficients of the working colorspace; the alpha channel is left unchanged if present. @@ -1305,7 +1426,31 @@ Adjustment nodes have one input named "in", and apply a specified function to va * **`hsvtorgb`**: (color3 or color4 only) convert an incoming color from HSV to RGB space; the alpha channel is left unchanged if present. This conversion is not affected by the current color space. * `in` (color3/color4): the input value or nodename -Additional adjustment nodes, including **``**, **``**, **``**, **``**, **``**, **``** and **``** may be found in the [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md#supplemental-adjustment-nodes) document. + + +* **`hsvadjust`** (NG): adjust the hue, saturation and value of an RGB color by converting the input color to HSV, adding amount.x to the hue, multiplying the saturation by amount.y, multiplying the value by amount.z, then converting back to RGB. A positive "amount.x" rotates hue in the "red to green to blue" direction, with amount of 1.0 being the equivalent to a 360 degree (e.g. no-op) rotation. Negative or greater-than-1.0 hue adjustment values are allowed, wrapping at the 0-1 boundaries. For color4 inputs, the alpha value is unchanged. + * `in` (color3 or color4): the input value or nodename + * `amount` (vector3): the HSV adjustment; a value of (0, 1, 1) is "no change" and is the default. + + + +* **`saturate`** (NG): (color3 or color4 only) adjust the saturation of a color; the alpha channel will be unchanged if present. Note that this operation is **not** equivalent to the "amount.y" saturation adjustment of `hsvadjust`, as that operator does not take the working or any other colorspace into account. + * `in` (float or colorN or vectorN): the input value or nodename + * `amount` (float): a multiplier for saturation; the saturate operator performs a linear interpolation between the luminance of the incoming color value (copied to all three color channels) and the incoming color value itself. Note that setting amount to 0 will result in an R=G=B gray value equal to the value that the `luminance` node (below) returns. Default is 1.0. + * `lumacoeffs` (uniform color3): the luma coefficients of the current working color space; if no specific color space can be determined, the ACEScg (ap1) luma coefficients [0.272287, 0.6740818, 0.0536895] will be used. Applications which support color management systems may choose to retrieve this value from the CMS to pass to the <saturate> node's implementation directly, rather than exposing it to the user. + + + +* **`colorcorrect`** (NG): Combines various adjustment nodes into one artist-friendly color correction node. For color4 inputs, the alpha value is unchanged. + * `in` (color3 or color4): the input color to be adjusted. + * `hue` (float): Rotates the color hue, with values wrapping at 0-1 boundaries; default is 0. + * `saturation` (float): Multiplies the input color saturation level; default is 1. + * `gamma` (float): Applies a gamma correction to the color; default is 1. + * `lift` (float): Raise the dark color values, leaving the white values unchanged; default is 0. + * `gain` (float): Multiplier increases lighter color values, leaving black values unchanged; default is 1. + * `contrast` (float): Linearly increase or decrease the color contrast; default is 1. + * `contrastpivot` (float): Pivot value around which contrast applies. This value will not change as contrast is adjusted; default is 0.5. + * `exposure` (float): Multplier which increases or decreases color brightness by 2^value; default is 0. @@ -1333,6 +1478,14 @@ Premult nodes operate on 4-channel (color4) inputs/outputs, have one input named Blend nodes take two 1-4 channel inputs and apply the same operator to all channels (the math for alpha is the same as for R or RGB). In the Blend Operator table, "F" and "B" refer to any individual channel of the `fg` and `bg` inputs respectively. Blend nodes support an optional float input `mix`, which can be used to mix the original `bg` value (`mix`=0) with the result of the blend operation (`mix`=1, the default). + + + + + + + + | Blend Operator | Each Channel Output | Supported Types | | --- | --- | --- | @@ -1349,6 +1502,13 @@ Blend nodes take two 1-4 channel inputs and apply the same operator to all chann Merge nodes take two 4-channel (color4) inputs and use the built-in alpha channel(s) to control the compositing of the `fg` and `bg` inputs. In the Merge Operator table, "F" and "B" refer to the non-alpha channels of the `fg` and `bg` inputs respectively, and "f" and "b" refer to the alpha channels of the `fg` and `bg` inputs. Merge nodes are not defined for 1-channel or 3-channel inputs, and cannot be used on vectorN streams. Merge nodes support an optional float input `mix`, which can be used to mix the original `bg` value (`mix`=0) with the result of the blend operation (`mix`=1, the default). + + + + + + + | Merge Operator | RGB output | Alpha Output | | --- | --- | --- | @@ -1364,6 +1524,9 @@ Merge nodes take two 4-channel (color4) inputs and use the built-in alpha channe Masking nodes take one 1-4 channel input `in` plus a separate float `mask` input and apply the same operator to all channels (if present, the math for alpha is the same as for R or RGB). The default value for the `mask` input is 1.0 for the `inside` operator, and 0.0 for the `outside` operator In the Masking Operator table, "F" refers to any individual channel of the `in` input. + + + | Masking Operator | Each Channel Output | | --- | --- | @@ -1378,6 +1541,8 @@ Note: for all types, `inside` is equivalent to the `multiply` node: both operato The Mix node takes two 1-4 channel inputs `fg` and `bg` plus a separate 1-channel (float) or N-channel (same type and number of channels as `fg` and `bg`) `mix` input and mixes the `fg` and `bg` according to the mix value, either uniformly for a "float" `mix` type, or per-channel for non-float `mix` types. The equation for "mix" is as follows, with "F" and "B" referring to any channel of the `fg` and `bg` inputs respectively (which can be float, colorN or vectorN but must match), and "m" referring to the float `mix` input value (which has a default value of 0): + + | Mix Operator | Each Channel Output | | --- | --- | @@ -1463,6 +1628,37 @@ Channel nodes are used to perform channel manipulations and data type conversion * `in3` (float): for **`combine3`** or **`combine4`**, the input value or nodename which will be sent to the next channel of the output after `in2`; default is 0.0 * `in4` (float): for **`combine4`**, the input value or nodename which will be sent to the last channel of the output; default is 0.0 + + +* **`separate2`** (NG): output each of the channels of a vector2 as a separate float output. + * `in` (vector2): the input value or nodename + * `outx` (**output**, float): the value of x channel. + * `outy` (**output**, float): the value of y channel. + + + +* **`separate3`** (NG): output each of the channels of a color3 or vector3 as a separate float output. + * `in` (color3 or vector3): the input value or nodename + * `outr`/`outx` (**output**, float): the value of the red (for color3 streams) or x (for vector3 streams) channel. + * `outg`/`outy` (**output**, float): the value of the green (for color3 streams) or y (for vector3 streams) channel. + * `outb`/`outz` (**output**, float): the value of the blue (for color3 streams) or z (for vector3 streams) channel. + + + +* **`separate4`** (NG): output each of the channels of a color4 or vector4 as a separate float output. + * `in` (color4 or vector4): the input value or nodename + * `outr`/`outx` (**output**, float): the value of the red (for color4 streams) or x (for vector4 streams) channel. + * `outg`/`outy` (**output**, float): the value of the green (for color4 streams) or y (for vector4 streams) channel. + * `outb`/`outz` (**output**, float): the value of the blue (for color4 streams) or z (for vector4 streams) channel. + * `outa`/`outw` (**output**, float): the value of the alpha (for color4 streams) or w (for vector4 streams) channel. + + + +* **`separatecolor4`** (NG): output the RGB and alpha channels of a color4 as separate outputs. + * `in` (color4): the input value or nodename + * `outcolor` (output, color3): the RGB channel values. + * `outa` (output, float): the value of the alpha channel. + The following input/output data type conversions are supported by **`convert`**: @@ -1490,8 +1686,6 @@ Table of allowable input/output types for **`combine2`**, **`combine3`**, **`com | `combine2` | `vector4` | `vector2` "xy" | `vector2` "zw" | n/a | n/a | "xyzw" | -Additional channel nodes, including **``** and **``**, may be found in the [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md#supplemental-channel-nodes) document. - ### Convolution Nodes diff --git a/documents/Specification/inprog_v1.39/MaterialX.Supplement.md b/documents/Specification/inprog_v1.39/MaterialX.Supplement.md index 5a1a8e7a55..1a3d322dcc 100644 --- a/documents/Specification/inprog_v1.39/MaterialX.Supplement.md +++ b/documents/Specification/inprog_v1.39/MaterialX.Supplement.md @@ -14,21 +14,15 @@ March 25, 2023 # Introduction -This document details additional information about MaterialX and how it may be incorporated into studio pipelines. The document describes a number of additional Supplemental Nodes providing enhanced functionality over the basic Standard Nodes, as well as a recommended naming convention for node definition elements and a directory structure to define packages of node definitions and implementations from various sources. +This document details additional information about MaterialX and how it may be incorporated into studio pipelines. The document describes recommended naming convention for node definition elements and a directory structure to define packages of node definitions and implementations from various sources. +Previous versions of the MaterialX Supplemental Notes document included descriptions of additional node types: these node descriptions have now been folded back into the [Main Specification Document](./MaterialX.Specification.md#nodes) alongside all the other standard node descriptions. ## Table of Contents **[Introduction](#introduction)** -**[Supplemental Nodes](#supplemental-nodes)** - [Supplemental Texture Nodes](#supplemental-texture-nodes) - [Supplemental Procedural Nodes](#supplemental-procedural-nodes) - [Supplemental Math Nodes](#supplemental-math-nodes) - [Supplemental Adjustment Nodes](#supplemental-adjustment-nodes) - [Supplemental Channel Nodes](#supplemental-channel-nodes) - **[Recommended Element Naming Conventions](#recommended-element-naming-conventions)** **[Material and Node Library File Structure](#material-and-node-library-file-structure)** @@ -42,234 +36,6 @@ This document details additional information about MaterialX and how it may be i -# Supplemental Nodes - -The MaterialX Specification defines a number of Standard Nodes, which all implementations of MaterialX are expected to support, to the degree their host applications allow. These nodes are the basic "building blocks" upon which more complex node functionality can be built. - -This section describes a number of supplemental nodes for MaterialX. These nodes are considered part of MaterialX, but are typically implemented using graphs of standard MaterialX nodes rather than being implemented for specific targets. Certain applications may choose to implement these supplemental nodes using native coding languages for efficiency. It is also expected that various applications will choose to extend these supplemental nodes with additional parameters and additional functionality. - - -### Supplemental Texture Nodes - - - -* **`tiledimage`**: samples data from a single image, with provisions for tiling and offsetting the image across uv space. - * `file` (uniform filename): the URI of an image file. The filename can include one or more substitutions to change the file name (including frame number) that is accessed, as described in [Filename Substitutions in the main Specification document](./MaterialX.Specification.md#filename-substitutions). - * `default` (float or colorN or vectorN): a default value to use if the `file` reference can not be resolved (e.g. if a <geomtoken>, [interfacetoken] or {hostattr} is included in the filename but no substitution value or default is defined, or if the resolved file URI cannot be read), or if the specified `layer` does not exist in the file. The `default` value must be the same type as the `` element itself. If `default` is not defined, the default color value will be 0.0 in all channels. - * `texcoord` (vector2): the name of a vector2-type node specifying the 2D texture coordinate at which the image data is read. Default is to use the current u,v coordinate. - * `uvtiling` (vector2): the tiling rate for the given image along the U and V axes. Mathematically equivalent to multiplying the incoming texture coordinates by the given vector value. Default value is (1.0, 1.0). - * `uvoffset` (vector2): the offset for the given image along the U and V axes. Mathematically equivalent to subtracting the given vector value from the incoming texture coordinates. Default value is (0.0, 0.0). - * `realworldimagesize` (vector2): the real-world size represented by the `file` image, with unittype "distance". A `unit` attribute may be provided to indicate the units that `realworldimagesize` is expressed in. - * `realworldtilesize` (vector2): the real-world size of a single square 0-1 UV tile, with unittype "distance". A `unit` attribute may be provided to indicate the units that `realworldtilesize` is expressed in. - * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. - - ``` - - - - - - - ``` - - - -* **`triplanarprojection`**: samples data from three images (or layers within multi-layer images), and projects a tiled representation of the images along each of the three respective coordinate axes, computing a weighted blend of the three samples using the geometric normal. - * `filex` (uniform filename): the URI of an image file to be projected in the direction from the +X axis back toward the origin. - * `filey` (uniform filename): the URI of an image file to be projected in the direction from the +Y axis back toward the origin with the +X axis to the right. - * `filez` (uniform filename): the URI of an image file to be projected in the direction from the +Z axis back toward the origin. - * `layerx` (uniform string): the name of the layer to extract from a multi-layer input file for the x-axis projection. If no value for `layerx` is provided and the input file has multiple layers, then the "default" layer will be used, or "rgba" if there is no "default" layer. Note: the number of channels defined by the `type` of the `` must match the number of channels in the named layer. - * `layery` (uniform string): the name of the layer to extract from a multi-layer input file for the y-axis projection. - * `layerz` (uniform string): the name of the layer to extract from a multi-layer input file for the z-axis projection. - * `default` (float or colorN or vectorN): a default value to use if any `fileX` reference can not be resolved (e.g. if a <geomtoken>, [interfacetoken] or {hostattr} is included in the filename but no substitution value or default is defined, or if the resolved file URI cannot be read) The `default` value must be the same type as the `` element itself. If `default` is not defined, the default color value will be 0.0 in all channels. - * `position` (vector3): a spatially-varying input specifying the 3D position at which the projection is evaluated. Default is to use the current 3D object-space coordinate. - * `normal` (vector3): a spatially-varying input specifying the 3D normal vector used for blending. Default is to use the current object-space surface normal. - * `blend` (float): a 0-1 weighting factor for blending the three axis samples using the geometric normal, with higher values giving softer blending. Default is 1.0. - * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. - - ``` - - - - - - - ``` - - -### Supplemental Procedural Nodes - - - -* **`ramp4`**: a 4-corner bilinear value ramp. - * `valuetl` (float or colorN or vectorN): the value at the top-left (U0V1) corner - * `valuetr` (float or colorN or vectorN): the value at the top-right (U1V1) corner - * `valuebl` (float or colorN or vectorN): the value at the bottom-left (U0V0) corner - * `valuebr` (float or colorN or vectorN): the value at the bottom-right (U1V0) corner - * `texcoord` (vector2, optional): the name of a vector2-type node specifying the 2D texture coordinate at which the ramp interpolation is evaluated. Default is to use the first set of texture coordinates. - - - -* **`unifiednoise2d`**: a single node supporting 2D Perlin, Cell, Worley or Fractal noise in a unified interface. - * `type` (integer): The type of noise function to use. One of 0 (Perlin), 1 (Cell), 2 (Worley), or 3 (Fractal); default is Perlin. - * `texcoord` (vector2): the input 2d space. Default is the first texture coordinates. - * `freq` (vector2): Adjusts the noise frequency, with higher values producing smaller noise shapes. Default is (1,1). - * `offset` (vector2): Shift the noise in 2d space. Default is (0,0). - * `jitter` (float): Adjust uniformity of Worley noise; for other noise types jitters the results. - * `outmin` (float): The lowest values fit to the noise. Default is 0.0. - * `outmax` (float): The highest values fit to the noise. Default is 1.0. - * `clampoutput` (boolean): Clamp the output to the min and max output values. - * `octaves` (integer): The number of octaves of Fractal noise to be generated. Default is 3. - * `lacunarity` (float): The exponential scale between successive octaves of Fractal noise. Default is 2.0. - * `diminish` (float): The rate at which noise amplitude is diminished for each octave of Fractal noise. Default is 0.5. - - - -* **`unifiednoise3d`**: a single node supporting 3D Perlin, Cell, Worley or Fractal noise in a unified interface. - * `type` (integer): The type of noise function to use. One of 0 (Perlin), 1 (Cell), 2 (Worley), or 3 (Fractal); default is Perlin. - * `position` (vector3): the input 3d space. Default is position in object-space. - * `freq` (vector3): Adjusts the noise frequency, with higher values producing smaller noise shapes. Default is (1,1,1). - * `offset` (vector3): Shift the noise in 3d space. Default is (0,0,0). - * `jitter` (float): Adjust uniformity of Worley noise; for other noise types jitters the results. - * `outmin` (float): The lowest values fit to the noise. Default is 0.0. - * `outmax` (float): The highest values fit to the noise. Default is 1.0. - * `clampoutput` (boolean): Clamp the output to the min and max output values. - * `octaves` (integer): The number of octaves of Fractal noise to be generated. Default is 3. - * `lacunarity` (float): The exponential scale between successive octaves of Fractal noise. Default is 2.0. - * `diminish` (float): The rate at which noise amplitude is diminished for each octave of Fractal noise. Default is 0.5. - - - -### Supplemental Math Nodes - - - -* **`place2d`**: transform incoming UV texture coordinates for 2D texture placement. - * `texcoord` (vector2): the input UV coordinate to transform; defaults to the current surface index=0 uv coordinate. - * `pivot` (vector2): the pivot coordinate for scale and rotate: this is subtracted from u,v before applying scale/rotate, then added back after. Default is (0,0). - * `scale` (vector2): divide the u,v coord (after subtracting `pivot`) by this, so a scale (2,2) makes the texture image appear twice as big. Negative values can be used to flip or flop the texture space. Default is (1,1). - * `rotate` (float): rotate u,v coord (after subtracting pivot) by this amount in degrees, so a positive value rotates UV coords counter-clockwise, and the image clockwise. Default is 0. - * `offset` (vector2): subtract this amount from the scaled/rotated/“pivot added back” UV coordinate; since U0,V0 is typically the lower left corner, a positive offset moves the texture image up and right. Default is (0,0). - * `operationorder` (integer enum): the order in which to perform the transform operations. "0" or "SRT" performs -pivot scale rotate translate +pivot as per the original implementation matching the behavior of certain DCC packages, and "1" or "TRS" performs -pivot translate rotate scale +pivot which does not introduce texture shear. Default is 0 "SRT" for backward compatibility. - - - -* **`safepower`**: raise incoming float/color values to the specified exponent. Unlike the standard <power> node, negative `in1` values for <safepower> will result in negative output values, e.g. `out = sign(in1)*pow(abs(in1),in2)`. - * `in1` (float or colorN or vectorN): the value or nodename for the primary input - * `in2` (same type as `in` or float): exponent value or nodename; default is 1.0 in all channels - - - -* **`triplanarblend`**: samples data from three inputs, and projects a tiled representation of the images along each of the three respective coordinate axes, computing a weighted blend of the three samples using the geometric normal. - * `inx` (float or colorN): the image to be projected in the direction from the +X axis back toward the origin. Default is 0 in all channels. - * `iny` (float or colorN): the image to be projected in the direction from the +Y axis back toward the origin with the +X axis to the right. Default is 0 in all channels. - * `inz` (float or colorN): the image to be projected in the direction from the +Z axis back toward the origin. Default is 0 in all channels. - * `position` (vector3): a spatially-varying input specifying the 3D position at which the projection is evaluated. Default is to use the current 3D object-space coordinate. - * `normal` (vector3): a spatially-varying input specifying the 3D normal vector used for blending. Default is to use the current object-space surface normal. - * `blend` (float): a 0-1 weighting factor for blending the three axis samples using the geometric normal, with higher values giving softer blending. Default is 1.0. - * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. - - - -### Supplemental Adjustment Nodes - - - -* **`contrast`**: increase or decrease contrast of incoming float/color values using a linear slope multiplier. - * `in` (float or colorN or vectorN): the input value or nodename - * `amount` (same type as `in` or float): slope multiplier for contrast adjustment, 0.0 to infinity range. Values greater than 1.0 increase contrast, values between 0.0 and 1.0 reduce contrast. Default is 1.0 in all channels. - * `pivot` (same type as `in` or float): center pivot value of contrast adjustment; this is the value that will not change as contrast is adjusted. Default is 0.5 in all channels. - - - -* **`range`**: remap incoming values from one range of float/color/vector values to another, optionally applying a gamma correction "in the middle". Input values below `inlow` or above `inhigh` are extrapolated unless `doclamp` is true, in which case the output values will be clamped to the `outlow`..`outhigh` range. - * `in` (float or colorN or vectorN): the input value or nodename - * `inlow` (same type as `in` or float): low value for input range. Default is 0.0 in all channels. - * `inhigh` (same type as `in` or float): high value for input range. Default is 1.0 in all channels. - * `gamma` (same type as `in` or float): inverse exponent applied to input value after first transforming from `inlow`..`inhigh` to 0..1; `gamma` values greater than 1.0 make midtones brighter. Default is 1.0 in all channels. - * `outlow` (same type as `in` or float): low value for output range. Default is 0.0 in all channels. - * `outhigh` (same type as `in` or float): high value for output range. Default is 1.0 in all channels. - * `doclamp` (boolean): If true, the output is clamped to the range `outlow`..`outhigh`. Default is false. - - - -* **`hsvadjust`**: adjust the hue, saturation and value of an RGB color by converting the input color to HSV, adding amount.x to the hue, multiplying the saturation by amount.y, multiplying the value by amount.z, then converting back to RGB. A positive "amount.x" rotates hue in the "red to green to blue" direction, with amount of 1.0 being the equivalent to a 360 degree (e.g. no-op) rotation. Negative or greater-than-1.0 hue adjustment values are allowed, wrapping at the 0-1 boundaries. For color4 inputs, the alpha value is unchanged. - * `in` (color3 or color4): the input value or nodename - * `amount` (vector3): the HSV adjustment; a value of (0, 1, 1) is "no change" and is the default. - - - -* **`saturate`**: (color3 or color4 only) adjust the saturation of a color; the alpha channel will be unchanged if present. Note that this operation is **not** equivalent to the "amount.y" saturation adjustment of `hsvadjust`, as that operator does not take the working or any other colorspace into account. - * `in` (float or colorN or vectorN): the input value or nodename - * `amount` (float): a multiplier for saturation; the saturate operator performs a linear interpolation between the luminance of the incoming color value (copied to all three color channels) and the incoming color value itself. Note that setting amount to 0 will result in an R=G=B gray value equal to the value that the `luminance` node (below) returns. Default is 1.0. - * `lumacoeffs` (uniform color3): the luma coefficients of the current working color space; if no specific color space can be determined, the ACEScg (ap1) luma coefficients [0.272287, 0.6740818, 0.0536895] will be used. Applications which support color management systems may choose to retrieve this value from the CMS to pass to the <saturate> node's implementation directly, rather than exposing it to the user. - - - -* **`colorcorrect`**: Combines various adjustment nodes into one artist-friendly color correction node. For color4 inputs, the alpha value is unchanged. - * `in` (color3 or color4): the input color to be adjusted. - * `hue` (float): Rotates the color hue, with values wrapping at 0-1 boundaries; default is 0. - * `saturation` (float): Multiplies the input color saturation level; default is 1. - * `gamma` (float): Applies a gamma correction to the color; default is 1. - * `lift` (float): Raise the dark color values, leaving the white values unchanged; default is 0. - * `gain` (float): Multiplier increases lighter color values, leaving black values unchanged; default is 1. - * `contrast` (float): Linearly increase or decrease the color contrast; default is 1. - * `contrastpivot` (float): Pivot value around which contrast applies. This value will not change as contrast is adjusted; default is 0.5. - * `exposure` (float): Multplier which increases or decreases color brightness by 2^value; default is 0. - - - -* **`curveadjust`**: output a smooth remapping of input values using the centripetal Catmull-Rom cubic spline curve defined by specified knot values, using an inverse spline lookup on input knot values and a forward spline through output knot values. All channels of the input will be remapped using the same curve. - * `in` (float or colorN or vectorN): the input value or nodename - * `numknots` (uniform integer): the number of values in the knots and knotvalues arrays - * `knots` (uniform floatarray): the list of input values defining the curve for the remapping. At least 2 and at most 16 values must be provided. - * `knotvalues` (uniform floatarray): the list of output values defining the curve for the remapping. Must be the same length as knots. - - - -* **`curvelookup`**: output a float, colorN or vectorN value smoothly interpolated between a number of knotvalue values, using the position of in within knots as the knotvalues interpolant. - * `in` (float): the input interpolant value or nodename - * `numknots` (uniform integer): the number of values in the knots and knotvalues arrays - * `knots` (uniform floatarray): the list of knot values to interpolate in within. At least 2 and at most 16 values must be provided. - * `knotvalues` (uniform floatarray or colorNarray or vectorNarray): the values at each knot position to interpolate between. Must be the same length as knots. - - - -### Supplemental Channel Nodes - - - -* **`separate2`**: output each of the channels of a vector2 as a separate float output. - * `in` (vector2): the input value or nodename - * `outx` (**output**, float): the value of x channel. - * `outy` (**output**, float): the value of y channel. - - - -* **`separate3`**: output each of the channels of a color3 or vector3 as a separate float output. - * `in` (color3 or vector3): the input value or nodename - * `outr`/`outx` (**output**, float): the value of the red (for color3 streams) or x (for vector3 streams) channel. - * `outg`/`outy` (**output**, float): the value of the green (for color3 streams) or y (for vector3 streams) channel. - * `outb`/`outz` (**output**, float): the value of the blue (for color3 streams) or z (for vector3 streams) channel. - - - -* **`separate4`**: output each of the channels of a color4 or vector4 as a separate float output. - * `in` (color4 or vector4): the input value or nodename - * `outr`/`outx` (**output**, float): the value of the red (for color4 streams) or x (for vector4 streams) channel. - * `outg`/`outy` (**output**, float): the value of the green (for color4 streams) or y (for vector4 streams) channel. - * `outb`/`outz` (**output**, float): the value of the blue (for color4 streams) or z (for vector4 streams) channel. - * `outa`/`outw` (**output**, float): the value of the alpha (for color4 streams) or w (for vector4 streams) channel. - - - -* **`separatecolor4`**: output the RGB and alpha channels of a color4 as separate outputs. - * `in` (color4): the input value or nodename - * `outcolor` (output, color3): the RGB channel values. - * `outa` (output, float): the value of the alpha channel. - - - # Recommended Element Naming Conventions While MaterialX elements can be given any valid name as described in the MaterialX Names section of the main specification, adhering to the following recommended naming conventions will make it easier to predict the name of a nodedef for use in implementation and nodegraph elements as well as help reduce the possibility of elements from different sources having the same name. diff --git a/documents/Specification/inprog_v1.39/README_v1.39.md b/documents/Specification/inprog_v1.39/README_v1.39.md index 28e74e195b..a04dfb5caa 100644 --- a/documents/Specification/inprog_v1.39/README_v1.39.md +++ b/documents/Specification/inprog_v1.39/README_v1.39.md @@ -9,7 +9,7 @@ The documents in this folder comprise the complete MaterialX Specification, vers * [**MaterialX Specification**](./MaterialX.Specification.md) - the main Specification, describing definitions, core functionality and the standard node library * [**MaterialX Physically Based Shading Nodes**](./MaterialX.PBRSpec.md) - describes BSDF and other shading function nodes useful in constructing complex layered rendering shaders using node graphs * [**MaterialX Geometry Extensions**](./MaterialX.GeomExts.md) - additional MaterialX elements to define geometry-related information such as collections, properties and material assignments -* [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md) - describes a number of additional node types built from the standard nodes as well as recommended naming and structuring conventions for libraries of custom node definitions +* [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md) - describes recommended naming and structuring conventions for libraries of custom node definitions

@@ -45,7 +45,7 @@ Similarly, <token>s in materials and other node instances may now be connecte **Standardized Color Space Names** -The [standard colorspace names](./MaterialX.Specification.md#color-spaces-and-color-management-systems) in MaterialX have now been defined explicitly in the Specification, and are aligned to their definitions in the ACES 1.2 OCIO config file. With this change, there is no need for a definition of "cms" or "cmsconfig" in MaterialX documents, so those two attributes have been deprecated. +The [standard colorspace names](./MaterialX.Specification.md#color-spaces-and-color-management-systems) in MaterialX have now been defined explicitly in the Specification, and are aligned to their definitions in the ACES 1.2 OCIO config file. With this change, there is no need for a definition of "cms" or "cmsconfig" in MaterialX documents, so those two attributes have been deprecated. Additionally, two new colorspaces, "srgb_displayp3" and "lin_displayp3" have been added as standard colorspaces. **Disambiguated Nodedef and Nodegraph References** @@ -74,21 +74,16 @@ Typedefs may now inherit from other types, including built-in types, and may pro **New and Updated Standard Library Nodes** -The following new operator nodes have been added to the standard library: +In 1.39, we are removing the distinction between "standard nodes" and "supplemental nodes", and descriptions of both can now be found in the main Specification document. Nodes that are implemented in the standard distribution using nodegraphs are annotated with "(NG)" in the Spec to differentiate them from nodes implemented in each rendering target's native shading language. -* [Procedural nodes](./MaterialX.Specification.md#procedural-nodes): **tokenvalue**, **checkerboard**, **fractal2d**, **cellnoise1d** +Additionally, the following new operator nodes have been added to the standard library: + +* [Procedural nodes](./MaterialX.Specification.md#procedural-nodes): **tokenvalue**, **checkerboard**, **fractal2d**, **cellnoise1d**, **unifiednoise2d**, **unifiednoise3d** * [Geometric nodes](./MaterialX.Specification.md#geometric-nodes): **bump**, **geompropvalueuniform** -* [Math nodes](./MaterialX.Specification.md#math-nodes): boolean **and**, **or**, **not**; **distance**, **transformcolor** and **creatematrix**, as well as integer-output variants of **floor** and **ceil** -* [Adjustment nodes](./MaterialX.Specification.md#adjustment-nodes): **curveinversecubic**, **curveuniformlinear** and **curveuniformcubic** +* [Math nodes](./MaterialX.Specification.md#math-nodes): boolean **and**, **or**, **not**; **distance**, **transformcolor**, **creatematrix** and **triplanarblend**, as well as integer-output variants of **floor** and **ceil** +* [Adjustment nodes](./MaterialX.Specification.md#adjustment-nodes): **curveinversecubic**, **curveuniformlinear**, **curveuniformcubic** and **colorcorrect** * [Conditional nodes](./MaterialX.Specification.md#conditional-nodes): boolean-output variants of **ifgreater**, **ifgreatereq** and **ifequal**; new **ifelse** node -* [Channel nodes](./MaterialX.Specification.md#channel-nodes): **extractrowvector** - -Additionally, the following new supplemental nodes have been added: - -* [Procedural nodes](./MaterialX.Supplement.md#supplemental-procedural-nodes): **unifiednoise2d**, **unifiednoise3d** -* [Math nodes](./MaterialX.Supplement.md#supplemental-math-nodes): **triplanarblend** -* [Adjustment nodes](./MaterialX.Supplement.md#supplemental-adjustment-nodes): **colorcorrect** -* [Channel nodes](./MaterialX.Supplement.md#supplemental-channel-nodes): **separatecolor4** +* [Channel nodes](./MaterialX.Specification.md#channel-nodes): **extractrowvector** and **separatecolor4** **New Physically Based Shading Nodes** From d7b6129ad67c4ae6f611be3569bb1a3b9c27c750 Mon Sep 17 00:00:00 2001 From: Doug Smythe Date: Wed, 14 Jun 2023 09:09:20 -0700 Subject: [PATCH 06/18] Move 1.39 Specification Docs (#1378) Move the v1.39 md spec docs up to the main Specifications folder, and the older v1.38 pdf files into a new v1.38 subfolder. No change to document contents. --- .../{inprog_v1.39 => }/MaterialX.GeomExts.md | 0 .../{inprog_v1.39 => }/MaterialX.PBRSpec.md | 0 .../{inprog_v1.39 => }/MaterialX.Specification.md | 0 .../{inprog_v1.39 => }/MaterialX.Supplement.md | 0 .../{inprog_v1.39/README_v1.39.md => README.md} | 0 .../media/MaterialX_1.39_Overview_v5.png | Bin .../{inprog_v1.39 => }/media/PBSdiagram.png | Bin .../{inprog_v1.39 => }/media/nodegraph1.png | Bin .../{inprog_v1.39 => }/media/nodegraph2.png | Bin .../{ => v1.38}/MaterialX.v1.38.PBRSpec.pdf | Bin .../{ => v1.38}/MaterialX.v1.38.Spec.pdf | Bin .../{ => v1.38}/MaterialX.v1.38.Supplement.pdf | Bin 12 files changed, 0 insertions(+), 0 deletions(-) rename documents/Specification/{inprog_v1.39 => }/MaterialX.GeomExts.md (100%) rename documents/Specification/{inprog_v1.39 => }/MaterialX.PBRSpec.md (100%) rename documents/Specification/{inprog_v1.39 => }/MaterialX.Specification.md (100%) rename documents/Specification/{inprog_v1.39 => }/MaterialX.Supplement.md (100%) rename documents/Specification/{inprog_v1.39/README_v1.39.md => README.md} (100%) rename documents/Specification/{inprog_v1.39 => }/media/MaterialX_1.39_Overview_v5.png (100%) rename documents/Specification/{inprog_v1.39 => }/media/PBSdiagram.png (100%) rename documents/Specification/{inprog_v1.39 => }/media/nodegraph1.png (100%) rename documents/Specification/{inprog_v1.39 => }/media/nodegraph2.png (100%) rename documents/Specification/{ => v1.38}/MaterialX.v1.38.PBRSpec.pdf (100%) rename documents/Specification/{ => v1.38}/MaterialX.v1.38.Spec.pdf (100%) rename documents/Specification/{ => v1.38}/MaterialX.v1.38.Supplement.pdf (100%) diff --git a/documents/Specification/inprog_v1.39/MaterialX.GeomExts.md b/documents/Specification/MaterialX.GeomExts.md similarity index 100% rename from documents/Specification/inprog_v1.39/MaterialX.GeomExts.md rename to documents/Specification/MaterialX.GeomExts.md diff --git a/documents/Specification/inprog_v1.39/MaterialX.PBRSpec.md b/documents/Specification/MaterialX.PBRSpec.md similarity index 100% rename from documents/Specification/inprog_v1.39/MaterialX.PBRSpec.md rename to documents/Specification/MaterialX.PBRSpec.md diff --git a/documents/Specification/inprog_v1.39/MaterialX.Specification.md b/documents/Specification/MaterialX.Specification.md similarity index 100% rename from documents/Specification/inprog_v1.39/MaterialX.Specification.md rename to documents/Specification/MaterialX.Specification.md diff --git a/documents/Specification/inprog_v1.39/MaterialX.Supplement.md b/documents/Specification/MaterialX.Supplement.md similarity index 100% rename from documents/Specification/inprog_v1.39/MaterialX.Supplement.md rename to documents/Specification/MaterialX.Supplement.md diff --git a/documents/Specification/inprog_v1.39/README_v1.39.md b/documents/Specification/README.md similarity index 100% rename from documents/Specification/inprog_v1.39/README_v1.39.md rename to documents/Specification/README.md diff --git a/documents/Specification/inprog_v1.39/media/MaterialX_1.39_Overview_v5.png b/documents/Specification/media/MaterialX_1.39_Overview_v5.png similarity index 100% rename from documents/Specification/inprog_v1.39/media/MaterialX_1.39_Overview_v5.png rename to documents/Specification/media/MaterialX_1.39_Overview_v5.png diff --git a/documents/Specification/inprog_v1.39/media/PBSdiagram.png b/documents/Specification/media/PBSdiagram.png similarity index 100% rename from documents/Specification/inprog_v1.39/media/PBSdiagram.png rename to documents/Specification/media/PBSdiagram.png diff --git a/documents/Specification/inprog_v1.39/media/nodegraph1.png b/documents/Specification/media/nodegraph1.png similarity index 100% rename from documents/Specification/inprog_v1.39/media/nodegraph1.png rename to documents/Specification/media/nodegraph1.png diff --git a/documents/Specification/inprog_v1.39/media/nodegraph2.png b/documents/Specification/media/nodegraph2.png similarity index 100% rename from documents/Specification/inprog_v1.39/media/nodegraph2.png rename to documents/Specification/media/nodegraph2.png diff --git a/documents/Specification/MaterialX.v1.38.PBRSpec.pdf b/documents/Specification/v1.38/MaterialX.v1.38.PBRSpec.pdf similarity index 100% rename from documents/Specification/MaterialX.v1.38.PBRSpec.pdf rename to documents/Specification/v1.38/MaterialX.v1.38.PBRSpec.pdf diff --git a/documents/Specification/MaterialX.v1.38.Spec.pdf b/documents/Specification/v1.38/MaterialX.v1.38.Spec.pdf similarity index 100% rename from documents/Specification/MaterialX.v1.38.Spec.pdf rename to documents/Specification/v1.38/MaterialX.v1.38.Spec.pdf diff --git a/documents/Specification/MaterialX.v1.38.Supplement.pdf b/documents/Specification/v1.38/MaterialX.v1.38.Supplement.pdf similarity index 100% rename from documents/Specification/MaterialX.v1.38.Supplement.pdf rename to documents/Specification/v1.38/MaterialX.v1.38.Supplement.pdf From 7c9a7090e68ef8f055b49735be4e7e6188984300 Mon Sep 17 00:00:00 2001 From: SShikhali <99796443+SShikhali@users.noreply.github.com> Date: Wed, 14 Jun 2023 21:06:38 +0200 Subject: [PATCH 07/18] Deterministic code generation (#1376) These changes make code generation deterministic by changing std::set to std::vector when removing unused nodes --- source/MaterialXGenShader/ShaderGraph.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/source/MaterialXGenShader/ShaderGraph.cpp b/source/MaterialXGenShader/ShaderGraph.cpp index 4fd92c11aa..e6ad6eca29 100644 --- a/source/MaterialXGenShader/ShaderGraph.cpp +++ b/source/MaterialXGenShader/ShaderGraph.cpp @@ -1073,7 +1073,8 @@ void ShaderGraph::optimize(GenContext& context) if (numEdits > 0) { - std::set usedNodes; + std::set usedNodesSet; + std::vector usedNodesVec; // Traverse the graph to find nodes still in use for (ShaderGraphOutputSocket* outputSocket : getOutputSockets()) @@ -1084,7 +1085,12 @@ void ShaderGraph::optimize(GenContext& context) { for (ShaderGraphEdge edge : ShaderGraph::traverseUpstream(upstreamPort)) { - usedNodes.insert(edge.upstream->getNode()); + ShaderNode* node = edge.upstream->getNode(); + if (usedNodesSet.count(node) == 0) + { + usedNodesSet.insert(node); + usedNodesVec.push_back(node); + } } } } @@ -1092,7 +1098,7 @@ void ShaderGraph::optimize(GenContext& context) // Remove any unused nodes for (ShaderNode* node : _nodeOrder) { - if (usedNodes.count(node) == 0) + if (usedNodesSet.count(node) == 0) { // Break all connections disconnect(node); @@ -1102,8 +1108,7 @@ void ShaderGraph::optimize(GenContext& context) } } - _nodeOrder.resize(usedNodes.size()); - _nodeOrder.assign(usedNodes.begin(), usedNodes.end()); + _nodeOrder = usedNodesVec; } } From 1c9d7ce0ddcfe8c19b3f096ec18e3d2b119c6d88 Mon Sep 17 00:00:00 2001 From: Bernard Kwok Date: Wed, 14 Jun 2023 18:32:06 -0400 Subject: [PATCH 08/18] Fix input default values on cmlib definitions (#1369) Fix so that cmlib inputs have default values, otherwise validate() will fail. --- libraries/cmlib/cmlib_defs.mtlx | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/libraries/cmlib/cmlib_defs.mtlx b/libraries/cmlib/cmlib_defs.mtlx index dc912581d2..bee30267ef 100644 --- a/libraries/cmlib/cmlib_defs.mtlx +++ b/libraries/cmlib/cmlib_defs.mtlx @@ -8,82 +8,82 @@ --> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + From 14e766dc5800f7704081d49eaf3b6711328498d0 Mon Sep 17 00:00:00 2001 From: Niklas Harrysson Date: Fri, 16 Jun 2023 02:46:33 +0200 Subject: [PATCH 09/18] Assign unique names to MDL implementations (#1380) This change list addresses issue #1354, renaming the implementation elements to unique names. --- libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx | 4 ++-- source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx b/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx index eb1e60167f..ab0d776936 100644 --- a/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx +++ b/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx @@ -614,8 +614,8 @@ - - + + diff --git a/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp b/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp index 722bf4b9b3..a7f7293755 100644 --- a/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp +++ b/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp @@ -81,7 +81,7 @@ TEST_CASE("GenReference: OSL Reference", "[genreference]") continue; } - mx::InterfaceElementPtr interface = nodedef->getImplementation(); + mx::InterfaceElementPtr interface = nodedef->getImplementation(generator->getTarget()); if (!interface) { logFile << "Skip generating reference for unimplemented node '" << nodeName << "'" << std::endl; From 9aa9b3aa7eb2ca3bc717894a0863c0d0ee8c1976 Mon Sep 17 00:00:00 2001 From: Chris Rydalch Date: Sat, 17 Jun 2023 13:07:39 -0500 Subject: [PATCH 10/18] Add Random Float and Random Color nodes (#1330) Add Random Float and Random Color nodes, to simplify generating randomized signals in a material. --- libraries/stdlib/stdlib_defs.mtlx | 46 ++++++++++ libraries/stdlib/stdlib_ng.mtlx | 137 ++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) diff --git a/libraries/stdlib/stdlib_defs.mtlx b/libraries/stdlib/stdlib_defs.mtlx index 00cce2d02b..4aa82582be 100644 --- a/libraries/stdlib/stdlib_defs.mtlx +++ b/libraries/stdlib/stdlib_defs.mtlx @@ -1024,6 +1024,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -307,6 +307,7 @@ + @@ -323,6 +324,7 @@ + @@ -339,6 +341,7 @@ + @@ -355,6 +358,7 @@ + @@ -371,6 +375,7 @@ + @@ -387,6 +392,7 @@ + diff --git a/libraries/stdlib/stdlib_ng.mtlx b/libraries/stdlib/stdlib_ng.mtlx index 394f953e11..1d09f160af 100644 --- a/libraries/stdlib/stdlib_ng.mtlx +++ b/libraries/stdlib/stdlib_ng.mtlx @@ -262,41 +262,54 @@ - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -369,41 +382,54 @@ - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -476,41 +502,54 @@ - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -583,41 +622,54 @@ - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -693,38 +745,51 @@ - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -797,41 +862,54 @@ - + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + From 35df9b1bdd2fc0834e867c86f2a7278280486f63 Mon Sep 17 00:00:00 2001 From: Bernard Kwok Date: Thu, 29 Jun 2023 12:28:51 -0400 Subject: [PATCH 15/18] Add Python binding for glTF loader (#1391) --- source/PyMaterialX/PyMaterialXRender/PyModule.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/PyMaterialX/PyMaterialXRender/PyModule.cpp b/source/PyMaterialX/PyMaterialXRender/PyModule.cpp index 9c6a191108..1c60142346 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyModule.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyModule.cpp @@ -19,6 +19,7 @@ void bindPyOiioImageLoader(py::module& mod); void bindPyTinyObjLoader(py::module& mod); void bindPyCamera(py::module& mod); void bindPyShaderRenderer(py::module& mod); +void bindPyCgltfLoader(py::module& mod); PYBIND11_MODULE(PyMaterialXRender, mod) { @@ -36,4 +37,5 @@ PYBIND11_MODULE(PyMaterialXRender, mod) bindPyTinyObjLoader(mod); bindPyCamera(mod); bindPyShaderRenderer(mod); + bindPyCgltfLoader(mod); } From 275677638684e7fe00eeb06145934cc59718fb77 Mon Sep 17 00:00:00 2001 From: Niklas Harrysson Date: Thu, 29 Jun 2023 19:49:36 +0200 Subject: [PATCH 16/18] Align default values for conductor_bsdf with specification (#1379) The default values on the nodedef for conductor_bsdf was by mistake setup for Copper while the specification has values set for Gold. This change list fixes this by changing the nodedef values to align with the specification. --- libraries/pbrlib/pbrlib_defs.mtlx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/pbrlib/pbrlib_defs.mtlx b/libraries/pbrlib/pbrlib_defs.mtlx index 0a931cdc7d..e63625a45d 100644 --- a/libraries/pbrlib/pbrlib_defs.mtlx +++ b/libraries/pbrlib/pbrlib_defs.mtlx @@ -76,8 +76,8 @@ --> - - + + From f409d4143b8c1ca874fb16684f7a1a7863fe1b87 Mon Sep 17 00:00:00 2001 From: Jonathan Stone Date: Fri, 30 Jun 2023 15:45:01 -0700 Subject: [PATCH 17/18] Improvements to GGX importance sampling (#1390) - Implement the paper "Sampling Visible GGX Normals with Spherical Caps" by Jonathan Dupuy and Anis Benyoub, which improves the performance and spatial continuity of VNDF sampling. - Switch to VNDF sampling for FIS environment lights, improving the convergence of this lighting path for lower sample counts. - Additional optimizations for FIS environment lights, leveraging the improved term cancellation in VNDF sampling. Test results: - GLSL render performance is improved for all tested materials, e.g. an increase from 205 fps to 214 fps for standard_surface_default.mtlx with 16 environment samples on an NVIDIA RTX A6000. - Convergence of FIS environment lights is improved for all tested materials, with the maximum visual error between 16 and 16384 environment samples reduced from 0.1098 to 0.0745 for a rough gold material. --- .../genglsl/lib/mx_environment_fis.glsl | 17 +++---- .../genglsl/lib/mx_microfacet_specular.glsl | 46 ++++--------------- 2 files changed, 19 insertions(+), 44 deletions(-) diff --git a/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl b/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl index d66f3a8cfb..575991e28d 100644 --- a/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl +++ b/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl @@ -23,6 +23,7 @@ vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distributio // Compute derived properties. float NdotV = clamp(V.z, M_FLOAT_EPS, 1.0); float avgAlpha = mx_average_alpha(alpha); + float G1V = mx_ggx_smith_G1(NdotV, avgAlpha); // Integrate outgoing radiance using filtered importance sampling. // http://cgg.mff.cuni.cz/~jaroslav/papers/2008-egsr-fis/2008-egsr-fis-final-embedded.pdf @@ -33,18 +34,16 @@ vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distributio vec2 Xi = mx_spherical_fibonacci(i, envRadianceSamples); // Compute the half vector and incoming light direction. - vec3 H = mx_ggx_importance_sample_NDF(Xi, alpha); + vec3 H = mx_ggx_importance_sample_VNDF(Xi, V, alpha); vec3 L = fd.refraction ? mx_refraction_solid_sphere(-V, H, fd.ior.x) : -reflect(V, H); // Compute dot products for this sample. - float NdotH = clamp(H.z, M_FLOAT_EPS, 1.0); float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0); float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0); - float LdotH = VdotH; // Sample the environment light from the given direction. vec3 Lw = tangentToWorld * L; - float pdf = mx_ggx_PDF(H, LdotH, alpha); + float pdf = mx_ggx_NDF(H, alpha) * G1V / (4.0 * NdotV); float lod = mx_latlong_compute_lod(Lw, pdf, float($envRadianceMips - 1), envRadianceSamples); vec3 sampleColor = mx_latlong_map_lookup(Lw, $envMatrix, lod, $envRadiance); @@ -61,13 +60,15 @@ vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distributio // From https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf // incidentLight = sampleColor * NdotL // microfacetSpecular = D * F * G / (4 * NdotL * NdotV) - // pdf = D * NdotH / (4 * VdotH) + // pdf = D * G1V / (4 * NdotV); // radiance = incidentLight * microfacetSpecular / pdf - radiance += sampleColor * FG * VdotH / (NdotV * NdotH); + radiance += sampleColor * FG; } - // Normalize and return the final radiance. - radiance /= float(envRadianceSamples); + // Apply the global component of the geometric term and normalize. + radiance /= G1V * float(envRadianceSamples); + + // Return the final radiance. return radiance; } diff --git a/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl b/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl index cd165c0615..63aba17869 100644 --- a/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl +++ b/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl @@ -61,48 +61,22 @@ float mx_ggx_NDF(vec3 H, vec2 alpha) return 1.0 / (M_PI * alpha.x * alpha.y * mx_square(denom)); } -// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf -// Appendix B.1 Equation 3 -float mx_ggx_PDF(vec3 H, float LdotH, vec2 alpha) -{ - float NdotH = H.z; - return mx_ggx_NDF(H, alpha) * NdotH / (4.0 * LdotH); -} - -// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf -// Appendix B.2 Equation 15 -vec3 mx_ggx_importance_sample_NDF(vec2 Xi, vec2 alpha) -{ - float phi = 2.0 * M_PI * Xi.x; - float tanTheta = sqrt(Xi.y / (1.0 - Xi.y)); - vec3 H = vec3(tanTheta * alpha.x * cos(phi), - tanTheta * alpha.y * sin(phi), - 1.0); - return normalize(H); -} - -// http://jcgt.org/published/0007/04/01/paper.pdf -// Appendix A Listing 1 +// https://ggx-research.github.io/publication/2023/06/09/publication-ggx.html vec3 mx_ggx_importance_sample_VNDF(vec2 Xi, vec3 V, vec2 alpha) { // Transform the view direction to the hemisphere configuration. V = normalize(vec3(V.xy * alpha, V.z)); - // Construct an orthonormal basis from the view direction. - float len = length(V.xy); - vec3 T1 = (len > 0.0) ? vec3(-V.y, V.x, 0.0) / len : vec3(1.0, 0.0, 0.0); - vec3 T2 = cross(V, T1); - - // Parameterization of the projected area. - float r = sqrt(Xi.y); + // Sample a spherical cap in (-V.z, 1]. float phi = 2.0 * M_PI * Xi.x; - float t1 = r * cos(phi); - float t2 = r * sin(phi); - float s = 0.5 * (1.0 + V.z); - t2 = (1.0 - s) * sqrt(1.0 - mx_square(t1)) + s * t2; - - // Reprojection onto hemisphere. - vec3 H = t1 * T1 + t2 * T2 + sqrt(max(0.0, 1.0 - mx_square(t1) - mx_square(t2))) * V; + float z = (1.0 - Xi.y) * (1.0 + V.z) - V.z; + float sinTheta = sqrt(clamp(1.0 - z * z, 0.0, 1.0)); + float x = sinTheta * cos(phi); + float y = sinTheta * sin(phi); + vec3 c = vec3(x, y, z); + + // Compute the microfacet normal. + vec3 H = c + V; // Transform the microfacet normal back to the ellipsoid configuration. H = normalize(vec3(H.xy * alpha, max(H.z, 0.0))); From cdd53d2c2bb369c89aa7b3344243d9687a04491b Mon Sep 17 00:00:00 2001 From: Jonathan Stone Date: Fri, 30 Jun 2023 16:05:41 -0700 Subject: [PATCH 18/18] Improvements to mxformat.py example - Simplify the functionality of mxformat.py, making it a more straightforward example of MaterialX Python usage. - Add exception handling to mxformat.py, allowing it to skip documents that cannot be parsed. - Reformat and upgrade a handful of additional documents in the repository. --- .github/workflows/main.yml | 2 +- .../lights/genmsl/lights_genmsl_impl.mtlx | 2 +- libraries/stdlib/stdlib_defs.mtlx | 2 +- libraries/stdlib/stdlib_ng.mtlx | 14 ++-- python/Scripts/mxformat.py | 81 +++++++------------ resources/Lights/goegap_split.mtlx | 2 +- .../Lights/san_giuseppe_bridge_split.mtlx | 2 +- resources/Lights/table_mountain_split.mtlx | 2 +- .../stdlib/upgrade/1_36_to_1_37.mtlx | 8 +- 9 files changed, 47 insertions(+), 68 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5b2354da24..fecdbf7a52 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -203,7 +203,7 @@ jobs: run: | python MaterialXTest/main.py python MaterialXTest/genshader.py - python Scripts/mxformat.py ../resources/Materials/TestSuite/stdlib/upgrade --yes --upgradeVersion True + python Scripts/mxformat.py ../resources/Materials/TestSuite/stdlib/upgrade --yes --upgrade python Scripts/mxvalidate.py ../resources/Materials/Examples/StandardSurface/standard_surface_marble_solid.mtlx --stdlib --verbose python Scripts/mxdoc.py --docType md ../libraries/pbrlib/pbrlib_defs.mtlx python Scripts/mxdoc.py --docType html ../libraries/bxdf/standard_surface.mtlx diff --git a/libraries/lights/genmsl/lights_genmsl_impl.mtlx b/libraries/lights/genmsl/lights_genmsl_impl.mtlx index 1b1782638c..90b4917a8a 100644 --- a/libraries/lights/genmsl/lights_genmsl_impl.mtlx +++ b/libraries/lights/genmsl/lights_genmsl_impl.mtlx @@ -1,5 +1,5 @@ - + diff --git a/libraries/stdlib/stdlib_defs.mtlx b/libraries/stdlib/stdlib_defs.mtlx index 4b9a6c1d34..e3b4344ff2 100644 --- a/libraries/stdlib/stdlib_defs.mtlx +++ b/libraries/stdlib/stdlib_defs.mtlx @@ -2280,7 +2280,7 @@ - + diff --git a/python/Scripts/mxformat.py b/python/Scripts/mxformat.py index 81ba3f72ea..8f6ad731e1 100644 --- a/python/Scripts/mxformat.py +++ b/python/Scripts/mxformat.py @@ -1,49 +1,38 @@ #!/usr/bin/env python ''' -Format MaterialX document content by reanding and writing files. Optionally -upgrade the document version. +Reformat a folder of MaterialX documents in place, optionally upgrading +the documents to the latest version of the standard. ''' import sys, os, argparse import MaterialX as mx -def getFiles(rootPath): - filelist = [] - for subdir, dirs, files in os.walk(rootPath): - for file in files: - if file.endswith('mtlx'): - filelist.append(os.path.join(subdir, file)) - return filelist - def main(): - parser = argparse.ArgumentParser(description="Format document by reading the file and writing it back out. Optionally update to the latest version.") + parser = argparse.ArgumentParser(description="Reformat a folder of MaterialX documents in place.") parser.add_argument("--yes", dest="yes", action="store_true", help="Proceed without asking for confirmation from the user.") - parser.add_argument('--checkForChanges', dest='checkForChanges', type=mx.stringToBoolean, default=True, help='Check if a file has changed. Default is True') - parser.add_argument('--upgradeVersion', dest='upgradeVersion', type=mx.stringToBoolean, default=False, help='Upgrade the document version. Default is False') + parser.add_argument('--upgrade', dest='upgrade', action="store_true", help='Upgrade documents to the latest version of the standard.') parser.add_argument(dest="inputFolder", help="An input folder to scan for MaterialX documents.") opts = parser.parse_args() - fileList = [] - rootPath = opts.inputFolder - if os.path.isdir(rootPath): - fileList = getFiles(rootPath) - else: - fileList.append(rootPath) - - # Preserve version, comments and newlines - readOptions = mx.XmlReadOptions() - readOptions.readComments = True - readOptions.readNewlines = True - readOptions.upgradeVersion = opts.upgradeVersion - validDocs = dict() - for filename in fileList: - doc = mx.createDocument() - try: - mx.readFromXmlFile(doc, filename, mx.FileSearchPath(), readOptions) - validDocs[filename] = doc - except mx.Exception: - pass + for root, dirs, files in os.walk(opts.inputFolder): + for file in files: + if file.endswith('.mtlx'): + filename = os.path.join(root, file) + doc = mx.createDocument() + try: + readOptions = mx.XmlReadOptions() + readOptions.readComments = True + readOptions.readNewlines = True + readOptions.upgradeVersion = opts.upgrade + try: + mx.readFromXmlFile(doc, filename, mx.FileSearchPath(), readOptions) + except Exception as err: + print('Skipping "' + file + '" due to exception: ' + str(err)) + continue + validDocs[filename] = doc + except mx.Exception: + pass if not validDocs: print('No MaterialX documents were found in "%s"' % (opts.inputFolder)) @@ -54,31 +43,21 @@ def main(): mxVersion = mx.getVersionIntegers() if not opts.yes: - question = 'Would you like to update all %i documents in place (y/n)?' % len(validDocs) - if opts.upgradeVersion: - question = 'Would you like to update all %i documents to MaterialX v%i.%i in place (y/n)?' % (len(validDocs), mxVersion[0], mxVersion[1]) + if opts.upgrade: + question = 'Would you like to upgrade all %i documents to MaterialX v%i.%i in place (y/n)?' % (len(validDocs), mxVersion[0], mxVersion[1]) + else: + question = 'Would you like to reformat all %i documents in place (y/n)?' % len(validDocs) answer = input(question) if answer != 'y' and answer != 'Y': return - writeCount = 0 for (filename, doc) in validDocs.items(): - writeFile = True - if opts.checkForChanges: - origString = mx.readFile(filename) - docString = mx.writeToXmlString(doc) - if origString == docString: - writeFile = False - - if writeFile: - writeCount = writeCount + 1 - print('- Updated file %s.' % filename) - mx.writeToXmlFile(doc, filename) + mx.writeToXmlFile(doc, filename) - if opts.upgradeVersion: - print('Updated %i documents to MaterialX v%i.%i' % (writeCount, mxVersion[0], mxVersion[1])) + if opts.upgrade: + print('Upgraded %i documents to MaterialX v%i.%i' % (len(validDocs), mxVersion[0], mxVersion[1])) else: - print('Updated %i documents ' % writeCount) + print('Reformatted %i documents ' % len(validDocs)) if __name__ == '__main__': main() diff --git a/resources/Lights/goegap_split.mtlx b/resources/Lights/goegap_split.mtlx index 026d07b45f..e046df5bf4 100644 --- a/resources/Lights/goegap_split.mtlx +++ b/resources/Lights/goegap_split.mtlx @@ -1,5 +1,5 @@ - + diff --git a/resources/Lights/san_giuseppe_bridge_split.mtlx b/resources/Lights/san_giuseppe_bridge_split.mtlx index e7975ae900..570c7b4f70 100644 --- a/resources/Lights/san_giuseppe_bridge_split.mtlx +++ b/resources/Lights/san_giuseppe_bridge_split.mtlx @@ -1,5 +1,5 @@ - + diff --git a/resources/Lights/table_mountain_split.mtlx b/resources/Lights/table_mountain_split.mtlx index 634f9facb3..b16050465f 100644 --- a/resources/Lights/table_mountain_split.mtlx +++ b/resources/Lights/table_mountain_split.mtlx @@ -1,5 +1,5 @@ - + diff --git a/resources/Materials/TestSuite/stdlib/upgrade/1_36_to_1_37.mtlx b/resources/Materials/TestSuite/stdlib/upgrade/1_36_to_1_37.mtlx index 0a0b3fd720..84db1c6101 100644 --- a/resources/Materials/TestSuite/stdlib/upgrade/1_36_to_1_37.mtlx +++ b/resources/Materials/TestSuite/stdlib/upgrade/1_36_to_1_37.mtlx @@ -1,10 +1,10 @@ - - + -->