diff --git a/.gitignore b/.gitignore
index f2d3d5dccd..0cc9f65cde 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,8 @@
build
dist
.DS_Store
+.cache
+
+# temporary addition while we're iterating on building the specification from source
+documents/SpecificationBuild
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 32770e0b57..3fd575fcc4 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -36,7 +36,9 @@ project(MaterialX VERSION ${MATERIALX_LIBRARY_VERSION})
option(MATERIALX_BUILD_PYTHON "Build the MaterialX Python package from C++ bindings. Requires Python 3.9 or greater." OFF)
option(MATERIALX_BUILD_VIEWER "Build the MaterialX Viewer." OFF)
option(MATERIALX_BUILD_GRAPH_EDITOR "Build the MaterialX Graph Editor." OFF)
-option(MATERIALX_BUILD_DOCS "Create HTML documentation using Doxygen. Requires that Doxygen be installed." OFF)
+option(MATERIALX_BUILD_DOCS "(deprecated) use MATERIALX_BUILD_DOXYGEN_DOCS." OFF)
+option(MATERIALX_BUILD_DOXYGEN_DOCS "Create HTML documentation using Doxygen. Requires that Doxygen be installed." OFF)
+option(MATERIALX_BUILD_SPECIFICATION_DOCS "Create Specification documentation, using the data library templates." OFF)
option(MATERIALX_BUILD_GEN_GLSL "Build the GLSL shader generator back-end." ON)
option(MATERIALX_BUILD_GEN_OSL "Build the OSL shader generator back-end." ON)
@@ -51,6 +53,8 @@ option(MATERIALX_BUILD_BENCHMARK_TESTS "Build benchmark tests." OFF)
option(MATERIALX_BUILD_SHARED_LIBS "Build MaterialX libraries as shared rather than static." OFF)
option(MATERIALX_BUILD_DATA_LIBRARY "Build generated products from the MaterialX data library." OFF)
+option(MATERIALX_BUILD_EXPAND_TEMPLATE_ELEMS "Process the data library files at build time to expand any template elements." ON)
+option(MATERIALX_BUILD_BAKE_NAMED_VALUES "Process the data library files at build time to bake out the named values." ON)
option(MATERIALX_BUILD_MONOLITHIC "Build a single monolithic MaterialX library." OFF)
option(MATERIALX_BUILD_USE_CCACHE "Enable the use of ccache to speed up build time, if present." ON)
option(MATERIALX_PYTHON_LTO "Enable link-time optimizations for MaterialX Python." ON)
@@ -68,6 +72,10 @@ if (MATERIALX_BUILD_IOS)
set(CMAKE_SYSTEM_NAME iOS)
endif()
+list(APPEND CMAKE_MODULE_PATH
+ ${PROJECT_SOURCE_DIR}/cmake/macros)
+include(Public)
+
# Apple ecosystem cross-compilation
# https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-ios-tvos-visionos-or-watchos
set(MATERIALX_BUILD_APPLE_EMBEDDED OFF)
@@ -107,6 +115,11 @@ if (MATERIALX_BUILD_JS)
set(MATERIALX_BUILD_TESTS OFF)
endif()
+# to maintain legacy cmake argument
+if (MATERIALX_BUILD_DOCS)
+ set(MATERIALX_BUILD_DOXYGEN_DOCS ON)
+endif()
+
# All hardware shading languages currently depend on the GLSL shader generator.
if(MATERIALX_BUILD_GEN_MSL)
set(MATERIALX_BUILD_GEN_GLSL ON)
@@ -437,6 +450,16 @@ else()
set(BUILD_SHARED_LIBS "OFF")
endif()
+# If we're baking the named "Constant:" entries - then we have to also expand any
+# template elements
+if (MATERIALX_BUILD_BAKE_NAMED_VALUES)
+ set(MATERIALX_BUILD_EXPAND_TEMPLATE_ELEMS ON)
+endif()
+
+if (MATERIALX_BUILD_EXPAND_TEMPLATE_ELEMS OR MATERIALX_BUILD_BAKE_NAMED_VALUES)
+ set(MATERIALX_BUILD_DATA_LIBRARY ON)
+endif()
+
# Build a monolithic target - needs to be added before the other build targets that may be included.
if (MATERIALX_BUILD_MONOLITHIC)
set(MATERIALX_MONOLITHIC_TARGET MaterialX)
@@ -446,6 +469,32 @@ endif()
# Add core subdirectories
add_subdirectory(source/MaterialXCore)
add_subdirectory(source/MaterialXFormat)
+if (MATERIALX_BUILD_EXPAND_TEMPLATE_ELEMS OR MATERIALX_BUILD_BAKE_NAMED_VALUES)
+ if (CMAKE_CROSSCOMPILING)
+ set(_MaterialXBuildLibrary "${CMAKE_BINARY_DIR}/BUILD_TOOLS/MaterialXBuildLibrary")
+
+ add_custom_command(
+ OUTPUT ${_MaterialXBuildLibrary}
+ COMMAND ${CMAKE_COMMAND}
+ -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
+ -DCMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${CMAKE_BINARY_DIR}/BUILD_TOOLS
+ -DMATERIALX_MAJOR_VERSION=${MATERIALX_MAJOR_VERSION}
+ -DMATERIALX_MINOR_VERSION=${MATERIALX_MINOR_VERSION}
+ -DMATERIALX_BUILD_VERSION=${MATERIALX_BUILD_VERSION}
+ -DMATERIALX_NAMESPACE=${MATERIALX_NAMESPACE}
+ -DMATERIALX_BUILD_BAKE_NAMED_VALUES=ON
+ -B"BUILD_TOOLS"
+ -H"${CMAKE_SOURCE_DIR}/source/MaterialXBuildTools"
+ COMMAND ${CMAKE_COMMAND} --build BUILD_TOOLS
+ DEPENDS MaterialXCore MaterialXFormat
+ )
+ add_custom_target(MaterialXBuildLibrary ALL
+ DEPENDS ${_MaterialXBuildLibrary})
+ else()
+ set(_MaterialXBuildLibrary MaterialXBuildLibrary)
+ add_subdirectory(source/MaterialXBuildTools/buildLibrary)
+ endif()
+endif()
# Add shader generation subdirectories
add_subdirectory(source/MaterialXGenShader)
@@ -467,6 +516,7 @@ if(MATERIALX_BUILD_GEN_GLSL OR MATERIALX_BUILD_GEN_OSL OR MATERIALX_BUILD_GEN_MD
add_subdirectory(source/MaterialXGenMsl)
endif()
add_subdirectory(libraries)
+ add_subdirectory(templates)
endif()
# Add rendering and viewer subdirectories
@@ -511,8 +561,11 @@ if(MATERIALX_BUILD_PYTHON)
add_subdirectory(python)
endif()
-if(MATERIALX_BUILD_DOCS)
+if(MATERIALX_BUILD_DOXYGEN_DOCS OR MATERIALX_BUILD_SPECIFICATION_DOCS)
add_subdirectory(documents)
+ if (MATERIALX_BUILD_SPECIFICATION_DOCS)
+ add_subdirectory(source/MaterialXBuildTools/buildDocs)
+ endif()
endif()
if(MATERIALX_BUILD_JS)
diff --git a/cmake/macros/Public.cmake b/cmake/macros/Public.cmake
new file mode 100644
index 0000000000..4ac9d55dfc
--- /dev/null
+++ b/cmake/macros/Public.cmake
@@ -0,0 +1,127 @@
+# Shared functions need to be extracted to allow them to be shared with the subproject
+# in source/MaterialXBuildTools
+
+function(assign_source_group prefix)
+ foreach(_source IN ITEMS ${ARGN})
+ if(IS_ABSOLUTE "${_source}")
+ file(RELATIVE_PATH _source_rel "${CMAKE_CURRENT_SOURCE_DIR}" "${_source}")
+ else()
+ set(_source_rel "${_source}")
+ endif()
+ get_filename_component(_source_path "${_source_rel}" PATH)
+ string(REPLACE "/" "\\" _source_path_msvc "${_source_path}")
+ source_group("${prefix}\\${_source_path_msvc}" FILES "${_source}")
+ endforeach()
+endfunction(assign_source_group)
+
+function(mx_add_library MATERIALX_MODULE_NAME)
+ set(options ADD_OBJECTIVE_C_CODE)
+ set(oneValueArgs EXPORT_DEFINE)
+ set(multiValueArgs
+ SOURCE_FILES
+ HEADER_FILES
+ INLINED_FILES
+ MTLX_MODULES)
+ cmake_parse_arguments(args
+ "${options}"
+ "${oneValueArgs}"
+ "${multiValueArgs}"
+ ${ARGN})
+
+ if (APPLE AND args_ADD_OBJECTIVE_C_CODE)
+ file(GLOB_RECURSE materialx_source_oc "${CMAKE_CURRENT_SOURCE_DIR}/*.m*")
+ set_source_files_properties(${materialx_source_oc} PROPERTIES
+ COMPILE_FLAGS "-x objective-c++")
+ set(args_SOURCE_FILES ${args_SOURCE_FILES} ${materialx_source_oc})
+ endif()
+
+ assign_source_group("Source Files" ${args_SOURCE_FILES})
+ assign_source_group("Source Files" ${args_INLINED_FILES})
+ assign_source_group("Header Files" ${args_HEADER_FILES})
+
+ if (NOT MATERIALX_BUILD_MONOLITHIC)
+ set(TARGET_NAME ${MATERIALX_MODULE_NAME})
+ add_library(${TARGET_NAME})
+
+ # Create version resource
+ if(MATERIALX_BUILD_SHARED_LIBS AND MSVC)
+ configure_file(${PROJECT_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
+ target_sources(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
+ endif()
+
+ target_link_libraries(${TARGET_NAME}
+ PUBLIC
+ ${args_MTLX_MODULES}
+ ${CMAKE_DL_LIBS})
+
+ target_include_directories(${TARGET_NAME}
+ PUBLIC
+ $
+ $
+ PRIVATE
+ ${EXTERNAL_INCLUDE_DIRS})
+
+ set_target_properties(
+ ${TARGET_NAME} PROPERTIES
+ OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX}
+ COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}"
+ LINK_FLAGS "${EXTERNAL_LINK_FLAGS}"
+ INSTALL_RPATH "${MATERIALX_SAME_DIR_RPATH}"
+ VERSION "${MATERIALX_LIBRARY_VERSION}"
+ SOVERSION "${MATERIALX_MAJOR_VERSION}")
+ else()
+ set(TARGET_NAME ${MATERIALX_MONOLITHIC_TARGET})
+ add_library(${MATERIALX_MODULE_NAME} ALIAS ${MATERIALX_MONOLITHIC_TARGET})
+
+ # Store the aliased MaterialX modules name to create CMake export aliases later.
+ set_property(GLOBAL APPEND PROPERTY MATERIALX_MODULES ${MATERIALX_MODULE_NAME})
+ endif()
+
+ set_target_properties(${TARGET_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden)
+ set_target_properties(${TARGET_NAME} PROPERTIES CMAKE_VISIBILITY_INLINES_HIDDEN 1)
+
+ target_sources(${TARGET_NAME}
+ PRIVATE
+ ${args_SOURCE_FILES}
+ PUBLIC
+ FILE_SET
+ mxHeaders
+ TYPE
+ HEADERS
+ BASE_DIRS
+ ${CMAKE_CURRENT_SOURCE_DIR}/..
+ ${CMAKE_CURRENT_BINARY_DIR}/..
+ FILES
+ ${args_HEADER_FILES}
+ ${args_INLINED_FILES})
+
+ target_include_directories(${TARGET_NAME} PUBLIC
+ $)
+
+ target_compile_definitions(${TARGET_NAME} PRIVATE "-D${args_EXPORT_DEFINE}")
+
+ if(NOT SKBUILD)
+ if(NOT MATERIALX_BUILD_MONOLITHIC)
+ install(TARGETS ${MATERIALX_MODULE_NAME}
+ EXPORT MaterialX
+ ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH}
+ LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH}
+ RUNTIME DESTINATION ${MATERIALX_INSTALL_BIN_PATH}
+ FILE_SET mxHeaders DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH})
+ endif()
+
+ if(MSVC)
+ if(MATERIALX_BUILD_SHARED_LIBS)
+ install(FILES $
+ DESTINATION ${MATERIALX_INSTALL_BIN_PATH} OPTIONAL)
+ else()
+ install(FILES "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/$/$.pdb"
+ DESTINATION ${MATERIALX_INSTALL_LIB_PATH} OPTIONAL)
+ endif()
+ endif()
+ endif()
+
+ # Pass TARGET_NAME back to call site, so the caller can modify the build target.
+ set(TARGET_NAME ${TARGET_NAME} PARENT_SCOPE)
+endfunction()
+
diff --git a/documents/CMakeLists.txt b/documents/CMakeLists.txt
index 6284540d4b..8c4499c228 100644
--- a/documents/CMakeLists.txt
+++ b/documents/CMakeLists.txt
@@ -1,45 +1,81 @@
-set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
-set(DOXYGEN_HTML_OUTPUT_DIR ${DOXYGEN_OUTPUT_DIR}/html)
-set(DOXYGEN_INPUT_LIST ${CMAKE_CURRENT_BINARY_DIR}/MainPage.md)
-
-set(MATERIALX_DOXYGEN_SOURCE_FOLDERS
- ${PROJECT_SOURCE_DIR}/source/MaterialXCore
- ${PROJECT_SOURCE_DIR}/source/MaterialXFormat
- ${PROJECT_SOURCE_DIR}/source/MaterialXGenShader
- ${PROJECT_SOURCE_DIR}/source/MaterialXGenShader/Nodes
- ${PROJECT_SOURCE_DIR}/source/MaterialXGenGlsl
- ${PROJECT_SOURCE_DIR}/source/MaterialXGenGlsl/Nodes
- ${PROJECT_SOURCE_DIR}/source/MaterialXGenOsl
- ${PROJECT_SOURCE_DIR}/source/MaterialXGenMdl
- ${PROJECT_SOURCE_DIR}/source/MaterialXRender
- ${PROJECT_SOURCE_DIR}/source/MaterialXRenderHw
- ${PROJECT_SOURCE_DIR}/source/MaterialXRenderGlsl
- ${PROJECT_SOURCE_DIR}/source/MaterialXRenderOsl)
-
-find_package(Doxygen REQUIRED)
-
-foreach(FOLDER ${MATERIALX_DOXYGEN_SOURCE_FOLDERS})
- file(GLOB FOLDER_HEADERS ${FOLDER}/*.h)
- list(APPEND DOXYGEN_INPUT_LIST ${FOLDER_HEADERS})
-endforeach()
-
-string (REPLACE ";" " " DOXYGEN_INPUT_STR "${DOXYGEN_INPUT_LIST}")
-
-configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
-
-add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/MainPage.md
- COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/DeveloperGuide/MainPage.md ${CMAKE_CURRENT_BINARY_DIR}
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/DeveloperGuide/MainPage.md)
-
-add_custom_command(OUTPUT ${DOXYGEN_HTML_OUTPUT_DIR}/index.html
- COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/DoxygenAwesome ${CMAKE_CURRENT_BINARY_DIR}
- COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Images ${CMAKE_CURRENT_BINARY_DIR}
- COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
- WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
- DEPENDS ${DOXYGEN_INPUT_LIST} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
- COMMENT "Generating HTML documentation: ${DOXYGEN_HTML_OUTPUT_DIR}/index.html")
-add_custom_target(MaterialXDocs ALL DEPENDS ${DOXYGEN_HTML_OUTPUT_DIR}/index.html)
-
-install(DIRECTORY ${DOXYGEN_HTML_OUTPUT_DIR}
- DESTINATION "documents" MESSAGE_NEVER)
+
+if (MATERIALX_BUILD_DOXYGEN_DOCS)
+
+ set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
+ set(DOXYGEN_HTML_OUTPUT_DIR ${DOXYGEN_OUTPUT_DIR}/html)
+ set(DOXYGEN_INPUT_LIST ${CMAKE_CURRENT_BINARY_DIR}/MainPage.md)
+
+ set(MATERIALX_DOXYGEN_SOURCE_FOLDERS
+ ${PROJECT_SOURCE_DIR}/source/MaterialXCore
+ ${PROJECT_SOURCE_DIR}/source/MaterialXFormat
+ ${PROJECT_SOURCE_DIR}/source/MaterialXGenShader
+ ${PROJECT_SOURCE_DIR}/source/MaterialXGenShader/Nodes
+ ${PROJECT_SOURCE_DIR}/source/MaterialXGenGlsl
+ ${PROJECT_SOURCE_DIR}/source/MaterialXGenGlsl/Nodes
+ ${PROJECT_SOURCE_DIR}/source/MaterialXGenOsl
+ ${PROJECT_SOURCE_DIR}/source/MaterialXGenMdl
+ ${PROJECT_SOURCE_DIR}/source/MaterialXRender
+ ${PROJECT_SOURCE_DIR}/source/MaterialXRenderHw
+ ${PROJECT_SOURCE_DIR}/source/MaterialXRenderGlsl
+ ${PROJECT_SOURCE_DIR}/source/MaterialXRenderOsl)
+
+ find_package(Doxygen REQUIRED)
+
+ foreach(FOLDER ${MATERIALX_DOXYGEN_SOURCE_FOLDERS})
+ file(GLOB FOLDER_HEADERS ${FOLDER}/*.h)
+ list(APPEND DOXYGEN_INPUT_LIST ${FOLDER_HEADERS})
+ endforeach()
+
+ string (REPLACE ";" " " DOXYGEN_INPUT_STR "${DOXYGEN_INPUT_LIST}")
+
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
+
+ add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/MainPage.md
+ COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/DeveloperGuide/MainPage.md ${CMAKE_CURRENT_BINARY_DIR}
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/DeveloperGuide/MainPage.md)
+
+ add_custom_command(OUTPUT ${DOXYGEN_HTML_OUTPUT_DIR}/index.html
+ COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/DoxygenAwesome ${CMAKE_CURRENT_BINARY_DIR}
+ COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Images ${CMAKE_CURRENT_BINARY_DIR}
+ COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
+ WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ DEPENDS ${DOXYGEN_INPUT_LIST} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
+ COMMENT "Generating HTML documentation: ${DOXYGEN_HTML_OUTPUT_DIR}/index.html")
+ add_custom_target(MaterialXDocs ALL DEPENDS ${DOXYGEN_HTML_OUTPUT_DIR}/index.html)
+
+ install(DIRECTORY ${DOXYGEN_HTML_OUTPUT_DIR}
+ DESTINATION "documents" MESSAGE_NEVER)
+endif()
+
+
+if (MATERIALX_BUILD_SPECIFICATION_DOCS)
+ set(MATERIALX_DATA_LIBRARY_TEMPLATE_DIR ${PROJECT_SOURCE_DIR}/templates)
+ set(MATERIALX_SPECIFICATION_TEMPLATE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/SpecificationTemplates)
+ #Ulitmately we may decide to move this directly to ${CMAKE_CURRENT_SOURCE_DIR}/Specification
+ set(MATERIALX_SPECIFICATION_DEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/SpecificationBuild)
+
+ set(DOCS_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/nodes)
+ set(NODE_MD_MANIFEST ${DOCS_OUTPUT_DIR}/_manifest.txt)
+
+ file(GLOB_RECURSE MATERIALX_DATA_LIBRARY_MTLX_SOURCE_FILES
+ LIST_DIRECTORIES false
+ ${MATERIALX_DATA_LIBRARY_TEMPLATE_DIR}/*.mtlx)
+
+ file(GLOB_RECURSE MATERIALX_MD_SOURCE_FILES
+ LIST_DIRECTORIES false
+ ${MATERIALX_SPECIFICATION_TEMPLATE_SOURCE_DIR}/*.template.md)
+
+ add_custom_command(
+ OUTPUT ${NODE_MD_MANIFEST}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${DOCS_OUTPUT_DIR}
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${MATERIALX_SPECIFICATION_DEST_DIR}
+ COMMAND MaterialXBuildDocs --sourceLibraryRoot ${MATERIALX_DATA_LIBRARY_TEMPLATE_DIR} --destDocRoot ${DOCS_OUTPUT_DIR} --sourceMDRoot ${MATERIALX_SPECIFICATION_TEMPLATE_SOURCE_DIR} --destMDRoot ${MATERIALX_SPECIFICATION_DEST_DIR} --manifestFile ${NODE_MD_MANIFEST}
+ DEPENDS ${MATERIALX_DATA_LIBRARY_MTLX_SOURCE_FILES} ${MATERIALX_MD_SOURCE_FILES} MaterialXBuildDocs
+ )
+
+ add_custom_target(MaterialXBuildSpecificationDocs ALL
+ DEPENDS ${NODE_MD_MANIFEST})
+
+endif()
+
diff --git a/documents/SpecificationTemplates/MaterialX.StandardNodes.template.md b/documents/SpecificationTemplates/MaterialX.StandardNodes.template.md
new file mode 100644
index 0000000000..d4c74962f9
--- /dev/null
+++ b/documents/SpecificationTemplates/MaterialX.StandardNodes.template.md
@@ -0,0 +1,1328 @@
+
+
+
+# MaterialX Standard Nodes
+
+**Version 1.39**
+Doug Smythe - Industrial Light & Magic
+Jonathan Stone - Lucasfilm Advanced Development Group
+March 15, 2025
+
+
+# Introduction
+
+The MaterialX Specification defines a content schema to describe materials, image processing and shading networks and how the nodes in those networks access textural and geometric information, in a platform- and shading-language-independent manner.
+
+This document describes a specific set of **Standard Nodes** that can be used to read and process image and geometric attribute data, as well as create new image data procedurally. These "stdlib" nodes are an essential core part of all MaterialX implementations. Additional nodes are described in companion documents [**MaterialX Physically Based Shading Nodes**](./MaterialX.PBRSpec.md) and [**MaterialX NPR Shading Nodes**](./MaterialX.NPRSpec.md).
+
+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.
+
+
+## Table of Contents
+
+**[Introduction](#introduction)**
+
+**[Standard Source Nodes](#standard-source-nodes)**
+ [Texture Nodes](#texture-nodes)
+ [Procedural Nodes](#procedural-nodes)
+ [Noise Nodes](#noise-nodes)
+ [Shape Nodes](#shape-nodes)
+ [Geometric Nodes](#geometric-nodes)
+ [Application Nodes](#application-nodes)
+
+**[Standard Operator Nodes](#standard-operator-nodes)**
+ [Math Nodes](#math-nodes)
+ [Logical Operator Nodes](#logical-operator-nodes)
+ [Adjustment Nodes](#adjustment-nodes)
+ [Compositing Nodes](#compositing-nodes)
+ [Conditional Nodes](#conditional-nodes)
+ [Channel Nodes](#channel-nodes)
+ [Convolution Nodes](#convolution-nodes)
+
+**[Standard Shader Nodes](#standard-shader-nodes)**
+
+
+
+
+# Standard Source Nodes
+
+Source nodes use external data and/or procedural functions to form an output; they do not have any required inputs. Each source node must define its output type.
+
+This section defines the Source Nodes that all MaterialX implementations are expected to support. Standard Source Nodes are grouped into the following classifications: [Texture Nodes](#texture-nodes), [Procedural Nodes](#procedural-nodes), [Noise Nodes](#noise-nodes), [Shape Nodes](#shape-nodes), [Geometric Nodes](#geometric-nodes) and [Application Nodes](#application-nodes).
+
+
+## Texture Nodes
+
+Texture nodes are used to read filtered image data from image or texture map files for processing within a node graph.
+
+```xml
+
+
+
+
+
+
+
+
+```
+
+Standard Texture nodes:
+
+
+
+### `image`
+
+Samples data from a single image, or from a layer within a multi-layer image. When used in the context of rendering a geometry, the image is mapped onto the geometry based on geometry UV coordinates, with the lower-left corner of an image mapping to the (0,0) UV coordinate (or to the fractional (0,0) UV coordinate for tiled images).
+
+The type of the <image> node determines the number of channels output, which may be less than the number of channels in the image file, outputting the first N channels from the image file. So a `float` <image> would return the Red channel of an RGB image, and a `color3` <image> would return the RGB channels of an RGBA image. If the type of the <image> node has more channels than the referenced image file, then the output will contain zero values in all channels beyond the N channels of the image file.
+
+The `file` input can include one or more substitutions to change the file name that is accessed, as described in the [Filename Substitutions](./MaterialX.Specification.md#filename-substitutions) section in the main Specification document. The `filtertype` input supports options `closest` (nearest-neighbor single-sample), `linear`, and `cubic`.
+
+@MX_TABLE_image@
+
+
+
+### `tiledimage`
+Samples data from a single image, with provisions for tiling and offsetting the image across uv space.
+
+The `file` input can include one or more substitutions to change the file name that is accessed, as described in the [Filename Substitutions](./MaterialX.Specification.md#filename-substitutions) section in the main Specification document.
+
+@MX_TABLE_tiledimage@
+
+
+
+### `latlongimage`
+Samples an equiangular map along a view direction with adjustable latitudinal offset.
+
+The `file` input can include one or more substitutions to change the file name that is accessed, as described in the [Filename Substitutions](./MaterialX.Specification.md#filename-substitutions) section in the main Specification document.
+
+@MX_TABLE_latlongimage@
+
+
+
+### `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.
+
+@MX_TABLE_triplanarprojection@
+
+
+
+
+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.
+* “periodic”: Texture coordinates outside the 0-1 range "wrap around", effectively being processed by a modulo 1 operation before sampling the image.
+* "mirror": Texture coordinates outside the 0-1 range will be mirrored back into the 0-1 range, e.g. u=-0.01 will return the u=0.01 texture coordinate value, and u=1.01 will return the u=0.99 texture coordinate value.
+
+
+Texture nodes using `file*` inputs also support the following inputs to handle boundary conditions for image file frame ranges for all `file*` inputs:
+
+* `framerange` (uniform string): a string "_minframe_-_maxframe_", e.g. "10-99", to specify the range of frames that the image file is allowed to have, usually the range of image files on disk. Default is unbounded.
+* `frameoffset` (integer): a number that is added to the current frame number to get the image file frame number. E.g. if `frameoffset` is 25, then processing frame 100 will result in reading frame 125 from the imagefile sequence. Default is no frame offset.
+* `frameendaction` (uniform string): what to do when the resolved image frame number is outside the `framerange` range:
+ * "constant": Return the value of the node's `default` input (default action)
+ * "clamp": Hold the minframe image for all frames before _minframe_ and hold the maxframe image for all frames after _maxframe_
+ * "periodic": Frame numbers "wrap around", so after the _maxframe_ it will start again at _minframe_ (and similar before _minframe_ wrapping back around to _maxframe_)
+ * "mirror": Frame numbers "mirror" or "ping-pong" at the endpoints of framerange, so a read of the frame after _maxframe_ will return the image from frame _maxframe_-1, and a read of the frame before _minframe_ will return the image from frame _minframe_+1.
+
+Arbitrary frame number expressions and speed changes are not supported.
+
+
+
+## Procedural Nodes
+
+Procedural nodes are used to generate value data programmatically.
+
+```xml
+
+
+
+
+
+
+
+```
+
+Standard Procedural nodes:
+
+
+
+### `constant`
+Outputs a constant value.
+
+@MX_TABLE_constant@
+
+
+
+
+### `ramplr`
+A left-to-right linear value ramp
+
+@MX_TABLE_ramplr@
+
+
+
+### `ramptb`
+A top-to-bottom linear value ramp.
+
+@MX_TABLE_ramptb@
+
+
+
+### `ramp4`
+A 4-corner bilinear value ramp.
+
+@MX_TABLE_ramp4@
+
+
+
+### `splitlr`
+A left-right split matte, split at a specified `U` value.
+
+@MX_TABLE_splitlr@
+
+
+
+### `splittb`
+A top-bottom split matte, split at a specified `V`` value.
+
+@MX_TABLE_splittb@
+
+
+
+* **`randomfloat`**: Produces a stable randomized float value between 'min' and 'max', based on an 'input' signal and 'seed' value. Uses a 2d cellnoise function to produce the output.
+ * `in` (float or integer): Initial randomization seed, default is 0.
+ * `min` (float): The minimum output value, default is 0.0.
+ * `max` (float): The maximum output value, default is 1.0.
+ * `seed` (integer): Additional randomization seed, default is 0.
+
+
+
+* **`randomcolor`**: Produces a randomized RGB color within a randomized hue, saturation and brightness range, based on an 'input' signal and 'seed' value. Output type color3.
+ * `in` (float or integer): Initial randomization seed, default is 0.
+ * `huelow` (float): The minimum hue value, default is 0.0.
+ * `huehigh` (float): The maximum hue value, default is 1.0.
+ * `saturationlow` (float): The minimum saturation value, default is 0.0.
+ * `saturationhigh` (float): The maximum saturation value, default is 1.0.
+ * `brightnesslow` (float): The minimum brightness value, default is 0.0.
+ * `brightnesshigh` (float): The maximum brightness value, default is 1.0.
+ * `seed` (integer): Additional randomization seed, default is 0.
+
+
+To scale or offset `rampX` or `splitX` 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.
+
+
+
+## Noise Nodes
+
+Noise nodes are used to generate value data using one of several procedural noise functions.
+
+```xml
+
+
+
+
+```
+
+Standard Noise nodes:
+
+
+
+### `noise2d`
+2D Perlin noise in 1, 2, 3 or 4 channels.
+
+@MX_TABLE_noise2d@
+
+
+
+### `noise3d`
+3D Perlin noise in 1, 2, 3 or 4 channels.
+
+@MX_TABLE_noise3d@
+
+
+
+* **`fractal2d`**: Zero-centered 2D Fractal noise in 1, 2, 3 or 4 channels, created by summing several octaves of 2D Perlin noise, increasing the frequency and decreasing the amplitude at each octave.
+ * `amplitude` (float or vectorN): the center-to-peak amplitude of the noise (peak-to-peak amplitude is 2x this value). Default is 1.0.
+ * `octaves` (integer): the number of octaves of noise to be summed. Default is 3.
+ * `lacunarity` (float or vectorN): the exponential scale between successive octaves of noise; must be an integer value if period is non-zero so the result is properly tileable. Default is 2.0. VectorN-output types can provide either a float (isotropic) or vectorN (anisotropic) values for `lacunarity` and `diminish`.
+ * `diminish` (float or vectorN): the rate at which noise amplitude is diminished for each octave. Should be between 0.0 and 1.0; default is 0.5. VectorN-output types can provide either a float (isotropic) or vectorN (anisotropic) values for `lacunarity` and `diminish`.
+ * `texcoord` (vector2): the 2D texture coordinate at which the noise is evaluated. Default is to use the first set of texture coordinates.
+
+
+
+### `fractal3d`
+Zero-centered 3D Fractal noise in 1, 2, 3 or 4 channels, created by summing several octaves of 3D Perlin noise, increasing the frequency and decreasing the amplitude at each octave.
+
+@MX_TABLE_fractal3d@
+
+
+
+### `cellnoise2d`
+2D cellular noise, 1 or 3 channels (type float or vector3).
+
+@MX_TABLE_cellnoise2d@
+
+
+
+### `cellnoise3d`
+3D cellular noise, 1 or 3 channels (type float or vector3).
+
+@MX_TABLE_cellnoise3d@
+
+
+
+### `worleynoise2d`
+2D Worley noise using centered jitter, outputting float (distance metric to closest feature), vector2 (distance metrics to closest 2 features) or vector3 (distance metrics to closest 3 features).
+
+@MX_TABLE_worleynoise2d@
+
+
+
+### `worleynoise3d`
+3D Worley noise using centered jitter, outputting float (distance metric to closest feature), vector2 (distance metrics to closest 2 features) or vector3 (distance metrics to closest 3 features).
+
+@MX_TABLE_worleynoise3d@
+
+
+
+### `unifiednoise2d`
+(NG): a single node supporting 2D Perlin, Cell, Worley or Fractal noise in a unified interface.
+
+@MX_TABLE_unifiednoise2d@
+
+
+
+### `unifiednoise3d`
+(NG): a single node supporting 3D Perlin, Cell, Worley or Fractal noise in a unified interface.
+
+@MX_TABLE_unifiednoise3d@
+
+
+To scale or offset the noise pattern generated by a 3D noise node such as `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 the noise pattern generated by a 2D noise node such as `noise2d` or `cellnoise2d`, use a <texcoord> or similar Geometric node processed by vector2 <multiply>, <rotate> and/or <add> nodes, and connect to the node's `texcoord` input.
+
+
+
+## Shape Nodes
+
+Shape nodes are used to generate shapes or patterns in UV space.
+
+```xml
+
+
+
+
+
+```
+
+Standard Shape nodes:
+
+
+
+### `checkerboard`
+2D checkerboard pattern.
+
+@MX_TABLE_checkerboard@
+
+
+
+### `line`
+2D line pattern.
+
+@MX_TABLE_line@
+
+
+
+### `circle`
+2D circle(disk) pattern.
+
+@MX_TABLE_circle@
+
+
+
+### `cloverleaf`
+2D cloverleaf pattern: four semicircles on the edges of a square defined by center and radius.
+
+@MX_TABLE_cloverleaf@
+
+
+
+### `hexagon`
+2D hexagon pattern.
+
+@MX_TABLE_hexagon@
+
+
+
+### `grid`
+Creates a grid pattern of (1, 1, 1) white lines on a (0, 0, 0) black background with the given tiling, offset, and line thickness. Pattern can be regular or staggered.
+
+@MX_TABLE_grid@
+
+
+
+### `crosshatch`
+Creates a crosshatch pattern with the given tiling, offset, and line thickness. Pattern can be regular or staggered.
+
+@MX_TABLE_crosshatch@
+
+
+
+### `tiledcircles`
+Creates a black and white pattern of circles with a defined tiling and size (diameter). Pattern can be regular or staggered.
+
+@MX_TABLE_tiledcircles@
+
+
+
+### `tiledcloverleafs`
+Creates a black and white pattern of cloverleafs with a defined tiling and size (diameter of the circles circumscribing the shape). Pattern can be regular or staggered.
+
+@MX_TABLE_tiledcloverleafs@
+
+
+
+### `tiledhexagons`
+Creates a black and white pattern of hexagons with a defined tiling and size (diameter of the circles circumscribing the shape). Pattern can be regular or staggered.
+
+@MX_TABLE_tiledhexagons@
+
+
+
+## Geometric Nodes
+
+Geometric nodes are used to reference local geometric properties from within a node graph:
+
+```xml
+
+
+
+
+```
+
+Standard Geometric nodes:
+
+
+
+### `position`
+The coordinates associated with the currently-processed data, as defined in a specific coordinate space.
+
+@MX_TABLE_position@
+
+
+
+### `normal`
+The normalized geometric normal associated with the currently-processed data, as defined in a specific coordinate space.
+
+@MX_TABLE_normal@
+
+
+
+### `tangent`
+The geometric tangent vector associated with the currently-processed data, as defined in a specific coordinate space.
+
+@MX_TABLE_tangent@
+
+
+
+### `bitangent`
+The geometric bi-tangent vector associated with the currently-processed data, as defined in a specific coordinate space.
+
+@MX_TABLE_bitangent@
+
+
+
+### `bump`
+The normalized normal computed by offsetting the surface world space position along its world space normal.
+
+@MX_TABLE_bump@
+
+
+
+### `texcoord`
+The 2D or 3D texture coordinates associated with the currently-processed data
+
+@MX_TABLE_texcoord@
+
+
+
+### `geomcolor`
+The color associated with the current geometry at the current position, generally bound via per-vertex color values. The type must match the type of the "color" bound to the geometry.
+
+@MX_TABLE_geomcolor@
+
+
+
+### `geompropvalue`
+The value of the specified varying geometric property (defined using ) of the currently-bound geometry. This node's type must match that of the referenced geomprop.
+
+@MX_TABLE_geompropvalue@
+
+
+
+### `geompropvalueuniform`
+The value of the specified uniform geometric property (defined using ) of the currently-bound geometry. This node's type must match that of the referenced geomprop.
+
+@MX_TABLE_geompropvalueuniform@
+
+
+Additionally, the `geomcolor` and `geompropvalue` 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).
+
+
+
+## Application Nodes
+
+Application nodes are used to reference application-defined properties within a node graph, and have no inputs:
+
+```xml
+
+
+```
+
+Standard Application nodes:
+
+
+
+### `frame`
+The current frame number as defined by the host environment.
+
+@MX_TABLE_frame@
+
+
+
+### `time`
+The current time in seconds, as defined by the host environment.
+
+@MX_TABLE_time@
+
+
+# Standard Operator Nodes
+
+Operator nodes process one or more required input streams to form an output. Like other nodes, each operator must define its output type, which in most cases also determines the type(s) of the required input streams.
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+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) and [Convolution Nodes](#convolution-nodes).
+
+
+
+## Math Nodes
+
+Math nodes have one or two spatially-varying inputs, and are used to perform a math operation on values in one spatially-varying input stream, or to combine two spatially-varying input streams using a specified math operation. The given math operation is performed for each channel of the input stream(s), and the data type of each input must either match that of the input stream(s), or be a float value that will be applied to each channel separately.
+
+
+
+
+### `add`
+Add a value to the incoming float/color/vector/matrix
+
+@MX_TABLE_add@
+
+
+
+### `subtract`
+Subtract a value from the incoming float/color/vector/matrix
+
+@MX_TABLE_subtract@
+
+
+
+### `multiply`
+Multiply two values together. Scalar and vector types multiply component-wise, while matrices multiply with the standard matrix product.
+
+@MX_TABLE_multiply@
+
+
+
+### `divide`
+Divide one value by another. Scalar and vector types divide component-wise, while for matrices `in1` is multiplied with the inverse of `in2`.
+
+@MX_TABLE_divide@
+
+
+
+### `modulo`
+The remaining fraction after dividing an incoming float/color/vector by a value and subtracting the integer portion. Modulo always returns a non-negative result.
+
+@MX_TABLE_modulo@
+
+
+
+### `fract`
+Returns the fractional part of the floating-point input.
+
+@MX_TABLE_fract@
+
+
+
+### `invert`
+subtract the incoming float, color, or vector from `amount` in all channels, outputting: `amount - in`.
+
+@MX_TABLE_invert@
+
+
+
+### `absval`
+The per-channel absolute value of the incoming float/color/vector.
+
+@MX_TABLE_absval@
+
+
+
+### `sign`
+The per-channel sign of the incoming float/color/vector value: -1 for negative, +1 for positive, or 0 for zero.
+
+@MX_TABLE_sign@
+
+
+
+### `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.
+
+@MX_TABLE_floor@
+
+
+
+### `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.
+
+@MX_TABLE_ceil@
+
+
+
+### `round`
+Round each channel of the incoming float/color/vector values to the nearest integer value.
+
+@MX_TABLE_round@
+
+
+
+### `power`
+Raise incoming float/color values to the specified exponent, commonly used for "gamma" adjustment.
+
+@MX_TABLE_power@
+
+
+
+### `safepower`
+Raise incoming float/color values to the specified exponent. Negative "in1" values will result in negative output values.
+
+@MX_TABLE_safepower@
+
+
+
+### `sin`
+The sine of the incoming value, which is expected to be expressed in radians.
+
+@MX_TABLE_sin@
+
+
+
+### `cos`
+The cosine of the incoming value, which is expected to be expressed in radians.
+
+@MX_TABLE_cos@
+
+
+
+### `tan`
+The tangent of the incoming value, which is expected to be expressed in radians.
+
+@MX_TABLE_tan@
+
+
+
+### `asin`
+The arcsine of the incoming value. The output will be expressed in radians.
+
+@MX_TABLE_asin@
+
+
+
+### `acos`
+The arccosine of the incoming value. The output will be expressed in radians.
+
+@MX_TABLE_acos@
+
+
+
+### `atan2`
+the arctangent of the expression (`iny`/`inx`). The output will be expressed in radians.
+
+@MX_TABLE_atan2@
+
+
+
+### `sqrt`
+The square root of the incoming value.
+
+@MX_TABLE_sqrt@
+
+
+
+### `ln`
+The natural logarithm of the incoming value.
+
+@MX_TABLE_ln@
+
+
+
+### `exp`
+$e$ to the power of the incoming value.
+
+@MX_TABLE_exp@
+
+
+
+### `clamp`
+Clamp incoming values per-channel to a specified range of float/color/vector values.
+
+@MX_TABLE_clamp@
+
+
+
+### `trianglewave`
+Generate a triangle wave from the given scalar input. The generated wave ranges from zero to one and repeats on integer boundaries.
+
+@MX_TABLE_trianglewave@
+
+
+
+### `min`
+Select the minimum of the two incoming values
+
+@MX_TABLE_min@
+
+
+
+### `max`
+Select the maximum of the two incoming values
+
+@MX_TABLE_max@
+
+
+
+### `normalize`
+Output the incoming vectorN stream normalized.
+
+@MX_TABLE_normalize@
+
+
+
+### `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.
+
+@MX_TABLE_magnitude@
+
+
+
+### `distance`
+Measures the distance between two points in 2D, 3D, or 4D.
+
+@MX_TABLE_distance@
+
+
+
+### `dotproduct`
+Output the (float) dot product of two incoming vectorN streams; cannot be used on float or colorN streams.
+
+@MX_TABLE_dotproduct@
+
+
+
+### `crossproduct`
+Output the (vector3) cross product of two incoming vector3 streams; cannot be used on any other stream type. A disabled crossproduct node passes through the value of `in1` unchanged.
+
+@MX_TABLE_crossproduct@
+
+
+
+### `transformpoint`
+Transform the incoming vector3 coordinate from one specified space to another; cannot be used on any other stream type.
+
+@MX_TABLE_transformpoint@
+
+
+
+### `transformvector`
+Transform the incoming vector3 coordinate from one specified space to another; cannot be used on any other stream type.
+
+@MX_TABLE_transformvector@
+
+
+
+### `transformnormal`
+Transform the incoming vector3 normal from one specified space to another; cannot be used on any other stream type.
+
+@MX_TABLE_transformnormal@
+
+
+
+### `transformmatrix`
+Transform the incoming vectorN by the specified matrix.
+
+@MX_TABLE_transformmatrix@
+
+
+
+### `normalmap`
+Transform a normal vector from the encoded tangent space to world space. The input normal vector is assumed to be encoded with all channels in the [0-1] range, as would commonly be output from a normal map.
+
+@MX_TABLE_normalmap@
+
+
+
+### `creatematrix`
+Build a 3x3 or 4x4 matrix from three vector3 or four vector3 or vector4 inputs. A matrix44 may also be created from vector3 input values, in which case the fourth value will be set to 0.0 for `in1`-`in3`, and to 1.0 for `in4` when creating the matrix44.
+
+@MX_TABLE_creatematrix@
+
+
+
+### `transpose`
+Transpose the incoming matrix
+
+@MX_TABLE_transpose@
+
+
+
+### `determinant`
+Output the determinant of the incoming matrix.
+
+@MX_TABLE_determinant@
+
+
+
+### `invertmatrix`
+Invert the incoming matrix.
+
+@MX_TABLE_invertmatrix@
+
+
+
+### `rotate2d`
+Rotate the incoming 2D vector about the origin.
+
+@MX_TABLE_rotate2d@
+
+
+
+### `rotate3d`
+Rotate the incoming 3D vector about the specified unit axis vector.
+
+@MX_TABLE_rotate3d@
+
+
+
+### `reflect`
+Reflect the incoming 3D vector about a surface normal vector.
+
+@MX_TABLE_reflect@
+
+
+
+### `refract`
+Refract the incoming 3D vector through a surface with the given surface normal and relative index of refraction.
+
+@MX_TABLE_refract@
+
+
+
+### `place2d`
+Transform incoming 2D texture coordinates from one frame of reference to another.
+
+@MX_TABLE_place2d@
+
+
+
+* **`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> 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.
+
+
+## Logical Operator Nodes
+
+Logical operator nodes have one or two boolean typed inputs, and are used to construct higher level logical flow through the nodegraph.
+
+
+
+### `and`
+logically AND the two input boolean values
+
+@MX_TABLE_and@
+
+
+
+### `or`
+logically Inclusive OR the two input boolean values
+
+@MX_TABLE_or@
+
+
+
+### `xor`
+logically Exclusive OR the two input boolean values
+
+@MX_TABLE_xor@
+
+
+
+### `not`
+logically NOT the input boolean value
+
+@MX_TABLE_not@
+
+
+## Adjustment Nodes
+
+Adjustment nodes have one input named "in", and apply a specified function to values in the incoming stream.
+
+
+
+### `contrast`
+Increase or decrease the contrast of the incoming `in` values using `amount` as a linear slope multiplier.
+
+@MX_TABLE_contrast@
+
+
+
+### `remap`
+Linearly remap incoming values from one range of values [`inlow`, `inhigh`] to another [`outlow`, `outhigh`].
+
+@MX_TABLE_remap@
+
+
+
+### `range`
+Remap incoming values from one range of values to another, optionally applying a gamma correction "in the middle".
+
+@MX_TABLE_range@
+
+
+
+### `smoothstep`
+Output a smooth, hermite-interpolated remapping of input values from [`low`, `high`] to [0,1].
+
+@MX_TABLE_smoothstep@
+
+
+
+### `luminance`
+Output a grayscale value containing the luminance of the incoming RGB color in all color channels.
+
+Applications which support color management systems may choose to retrieve the luma coefficients of the working colorspace from the CMS to pass to the `luminance` node's implementation directly, rather than exposing it to the user.
+
+@MX_TABLE_luminance@
+
+
+
+### `rgbtohsv`
+Convert an incoming color from RGB to HSV space (with H and S ranging from 0 to 1); the alpha channel is left unchanged if present. This conversion is not affected by the current color space.
+
+@MX_TABLE_rgbtohsv@
+
+
+
+### `hsvtorgb`
+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.
+
+@MX_TABLE_hsvtorgb@
+
+
+
+### `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.
+
+@MX_TABLE_hsvadjust@
+
+
+
+### `saturate`
+Adjust the saturation of a color, the alpha channel will be unchanged if present.
+
+@MX_TABLE_saturate@
+
+
+
+### `colorcorrect`
+Combines various adjustment nodes into one artist-friendly color correction node. For color4 inputs, the alpha value is unchanged.
+
+@MX_TABLE_colorcorrect@
+
+
+## Compositing Nodes
+
+Compositing nodes have two (required) inputs named `fg` and `bg`, and apply a function to combine them. Compositing nodes are split into five subclassifications: [Premult Nodes](#premult-nodes), [Blend Nodes](#blend-nodes), [Merge Nodes](#merge-nodes), [Masking Nodes](#masking-nodes), and the [Mix Node](#mix-node).
+
+
+### Premult Nodes
+
+Premult nodes operate on 4-channel (color4) inputs/outputs, have one input named `in`, and either apply or unapply the alpha to the float or RGB color.
+
+
+
+### `premult`
+Multiply the R or RGB channels of the input by the Alpha channel of the input.
+
+@MX_TABLE_premult@
+
+
+
+### `unpremult`
+Divide the RGB channels of the input by the Alpha channel of the input. If the Alpha value is zero, it is passed through unchanged.
+
+@MX_TABLE_unpremult@
+
+
+### Blend Nodes
+
+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); below, "F" and "B" refer to any individual channel of the `fg` and `bg` inputs respectively.
+
+
+
+
+### `plus`
+Add two 1-4 channel inputs, with optional mixing between the bg input and the result.
+
+@MX_TABLE_plus@
+
+
+
+### `minus`
+Subtract two 1-4 channel inputs, with optional mixing between the bg input and the result.
+
+@MX_TABLE_minus@
+
+
+
+### `difference`
+Absolute-value difference of two 1-4 channel inputs, with optional mixing between the bg input and the result.
+
+@MX_TABLE_difference@
+
+
+
+### `burn`
+Take two 1-4 channel inputs and apply the same operator to all channels:
+```
+1-(1-B)/F
+```
+
+@MX_TABLE_burn@
+
+
+
+### `dodge`
+Take two 1-4 channel inputs and apply the same operator to all channels:
+```
+B/(1-F)
+```
+
+@MX_TABLE_dodge@
+
+
+
+### `screen`
+Take two 1-4 channel inputs and apply the same operator to all channels:
+```
+1-(1-F)*(1-B)
+```
+
+@MX_TABLE_screen@
+
+
+
+### `overlay`
+Take two 1-4 channel inputs and apply the same operator to all channels:
+```
+2FB if B<0.5;
+1-2(1-F)(1-B) if B>=0.5
+```
+
+@MX_TABLE_overlay@
+
+
+### Merge Nodes
+
+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; "F" and "B" refer to individual non-alpha channels of the `fg` and `bg` inputs respectively, while "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.
+
+
+
+
+### `disjointover`
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+```
+F+B if f+b<=1
+F+B(1-f)/b if f+b>1
+alpha: min(f+b,1)
+```
+
+@MX_TABLE_disjointover@
+
+
+
+### `in`
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+```
+RGB = Fb
+Alpha = fb
+```
+
+@MX_TABLE_in@
+
+
+
+### `mask`
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+```
+Bf (alpha: bf)
+```
+
+@MX_TABLE_mask@
+
+
+
+### `matte`
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+```
+Ff+B(1-f) (alpha: f+b(1-f))
+```
+
+@MX_TABLE_matte@
+
+
+
+### `out`
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+```
+F(1-b) (alpha: f(1-b))
+```
+
+@MX_TABLE_out@
+
+
+
+### `over`
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+```
+F+B(1-f) (alpha: f+b(1-f))
+```
+
+@MX_TABLE_over@
+
+
+### Masking Nodes
+
+Masking nodes take one 1-4 channel input `in` plus a separate float `mask` input and apply the same operator to all "in" channels; "F" refers to any individual channel of the `in` input, while "m" refers to the mask input.
+
+
+
+
+### `inside`
+An "inside" mask operation returning Fm
+
+@MX_TABLE_inside@
+
+
+
+### `outside`
+An "outside" mask operation returning F(1-m)
+
+@MX_TABLE_outside@
+
+
+### Mix Node
+
+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; "F" refers to any individual channel of the `in` input, while "m" refers to the appropriate channel of the mix input.
+
+
+
+### `mix`
+A "mix" operation blending from "bg" to "fg" according to the mix amount, returning Fm+B(1-m)
+
+@MX_TABLE_mix@
+
+See also the [Standard Shader Nodes](#standard-shader-nodes) section below for additional shader-semantic variants of the [`mix` node](#node-mix-shader).
+
+
+
+## Conditional Nodes
+
+Conditional nodes are used to compare values of two streams, or to select a value from one of several streams.
+
+
+
+
+### `ifgreater`
+
+Output the value of the `in1` or `in2` stream depending on whether the `value1` input is greater than the `value2` input.
+
+@MX_TABLE_ifgreater@
+
+
+
+### `ifgreatereq`
+
+Output the value of the `in1` or `in2` stream depending on whether the `value1` input is greater or equal to the `value2` input.
+
+@MX_TABLE_ifgreatereq@
+
+
+
+### `ifequal`
+
+@MX_TABLE_ifequal@
+
+
+
+### `switch`
+
+Output the value of one of up to ten input streams, according to the value of a selector input `which`. Note that not all inputs need to be connected. The output has the same type as `in1`, with a default value of __zero__.
+
+@MX_TABLE_switch@
+
+
+
+## Channel Nodes
+
+Channel nodes are used to perform channel manipulations and data type conversions on streams.
+
+
+
+
+### `extract`
+
+Isolate a single float channel from a __vectorN__ or __colorN__ stream. The output value is of type `float` with a default value of __zero__.
+
+@MX_TABLE_extract@
+
+The valid range for `index` should be clamped to $[0,N)$ in the user interface, where __N__ is the size of the input vector stream. `index` is a uniform, non-varying value. Any `index` values outside of the valid range should result in an error.
+
+
+
+* **`convert`**: convert a stream from one data type to another. Only certain unambiguous conversions are supported; see list below.
+ * `in` (boolean or integer or float or colorN or vectorN): the input value or nodename
+
+
+
+### `combine2`
+
+Combine the channels from two streams into the same number of channels of a single output stream of a compatible type.
+
+@MX_TABLE_combine2@
+
+
+
+### `combine3`
+
+Combine the channels from three streams into the same number of channels of a single output stream of a compatible type.
+
+@MX_TABLE_combine3@
+
+
+
+### `combine4`
+
+Combine the channels from four streams into the same number of channels of a single output stream of a compatible type.
+
+@MX_TABLE_combine4@
+
+
+
+### `separate2`
+
+Split the channels of a 2-channel stream into separate float outputs.
+
+@MX_TABLE_separate2@
+
+For the vector2-input `in`, `outx` and `outy` correspond to the x- and y-components of `in`..
+
+
+
+### `separate3`
+
+Split the channels of a 3-channel stream into separate float outputs.
+
+@MX_TABLE_separate3@
+
+When the input `in` is a color3, `outr`, `outg`, and `outb` correspond to the r-, g-, and b-components of `in`, respectively.
+
+When the input `in` is a vector3, `outx`, `outy`, and `outz` correspond to the x-, y-, and z-components of `in`, respectively.
+
+
+
+### `separate4`
+
+Split the channels of a 4-channel stream into separate float outputs.
+
+@MX_TABLE_separate4@
+
+When the input `in` is a color4, `outr`, `outg`, `outb`, and `outa` correspond to the r-, g-, b-, and alpha components of `in`, respectively.
+
+When the input `in` is a vector4, `outx`, `outy`, `outz`, and `outw` correspond to the x-, y-, z-, and w-components of `in`, respectively.
+
+
+## Convolution Nodes
+
+Convolution nodes have one input named "in", and apply a defined convolution function on the input stream. Some of these nodes may not be implementable in ray tracing applications; they are provided for the benefit of purely 2D image processing applications.
+
+
+
+
+### `blur`
+
+Applies a convolution blur to the input stream.
+
+@MX_TABLE_blur@
+
+
+
+### `heighttonormal`
+
+Convert a scalar height map to a tangent-space normal map of type `vector3`. The output normal map is encoded with all channels in the [0-1] range, enabling its storage in unsigned image formats.
+
+@MX_TABLE_heighttonormal@
+
+
+# Standard Shader Nodes
+
+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, as well as [**MaterialX NPR Shading Nodes**](./MaterialX.NPRSpec.md) for definitions of shaders and nodes applicable to non-photorealistic rendering.
+
+
+
+* **`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
+ * `emission_color` (color3): surface emission color; default is (1, 1, 1)
+ * `transmission` (float): the surface transmission amount; default is 0
+ * `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
diff --git a/documents/SpecificationTemplates/nprlib_spec.template.md b/documents/SpecificationTemplates/nprlib_spec.template.md
new file mode 100644
index 0000000000..c487049d97
--- /dev/null
+++ b/documents/SpecificationTemplates/nprlib_spec.template.md
@@ -0,0 +1,53 @@
+# NPR Nodes
+
+NPR nodes support both physically-based and non-physically-based rendering.
+
+### viewdirection
+
+The current scene view direction, as defined by the shading environment.
+
+@MX_TABLE_viewdirection@
+
+The view direction is a normalized vector from the camera/eye, to visible objects in the scene. In a PBR shading context, this would only include the primary ray direction, and not any secondary/reflection rays.
+
+### facingratio
+
+The geometric facing ratio of surface normals and the view direction.
+
+@MX_TABLE_facingratio@
+
+Facing ratio is computed as the dot product between the view direction and geometric normal. Output values of 1.0 are facing towards the view direction, while values of -1.0 are facing away. Values of 0 are surface normals perpendicular to the view direction.
+
+### gooch_shade
+
+Computes the color from single-pass shading portion of the Gooch[^Gooch1998] lighting model.
+
+@MX_TABLE_gooch_shade@
+
+Gooch shade provides an illustrative shading effect by blending colors based on the angle between the surface normal and the light direction. It also provides a very simple specular highlight, on top of the warm and cool colors.
+
+The `warm_color` input corresponds to $C_w$ in the model, and represents colors facing towards the `light_direction`.
+
+The `cool_color` input corresponds to $C_c$, and represents colors facing away from the `light_direction`.
+
+The `specular_intensity` input corresponds to $N$, and represents the intensity of the highlight.
+
+The `shininess` input corresponds to $H$, and represents the size of the highlight across surface.
+
+The `light_direction` input corresponds to $L$, and represents the world space direction of the light.
+
+### Gooch Equations
+
+$$M = \frac{1+({N} \cdot {L})}{2}$$
+
+$$D = (1-{M}) * {C_w} + {M} * {C_c}$$
+
+$$E = reflect({V},{N})$$
+
+$$S = ( max(E \cdot -{L}, 0 )^{H} ) * {N}$$
+
+$$out = {D} + {S}$$
+
+# References
+
+[^Gooch1998]: Gooch et al., **A Non-Photorealistic Lighting Model For Automatic Technical Illustration**, , 1998.
\ No newline at end of file
diff --git a/documents/SpecificationTemplates/pbrlib_spec.template.md b/documents/SpecificationTemplates/pbrlib_spec.template.md
new file mode 100644
index 0000000000..cd46a59a19
--- /dev/null
+++ b/documents/SpecificationTemplates/pbrlib_spec.template.md
@@ -0,0 +1,108 @@
+# pbrlib
+
+### oren_nayar_diffuse_bsdf
+Constructs a diffuse reflection BSDF based on the Oren-Nayar reflectance model.
+
+@MX_TABLE_oren_nayar_diffuse_bsdf@
+ |
+
+The `color` input represents the diffuse albedo, and corresponds to $\rho$ (rho) in the Oren-Nayar model.
+
+The `roughness` input corresponds to $\sigma$ (sigma) in the Oren-Nayar model, and a `roughness` of 0.0 produces Lambertian reflectance.
+
+The `energy_compensation` input selects between the Qualitative Oren-Nayar[^Oren1994] and Energy-Preserving Oren-Nayar[^Portsmouth2025] models of diffuse reflectance.
+
+### Qualitative Oren-Nayar Reflectance Equations
+
+$$A=1-0.5\left(\frac{\sigma^2}{\sigma^2+0.33}\right)$$
+
+$$B=1-0.45\left(\frac{\sigma^2}{\sigma^2+0.09}\right)$$
+
+$$g(\omega_i,\omega_o)=\cos_{(\theta_i-\theta_o)}\sin_{{max}(\theta_i,\theta_o)}\tan_{{min}(\theta_i,\theta_o)}$$
+
+$$L_r(\omega_i,\omega_o)=\frac{\rho}{\pi}(A+Bg(\omega_i,\omega_o))$$
+
+### dielectric_bsdf
+Constructs a reflection and/or transmission BSDF based on a microfacet model and a Fresnel curve for dielectrics[^Walter2007].
+
+@MX_TABLE_dielectric_bsdf@
+
+The `tint` input corresponds to $t$ in the dielectric model, and represents a non-physical color weight to tint the reflected and transmitted light.
+
+The `ior` input corresponds to $\eta$ (eta) in the dielectric model, and represents the real-valued index of refraction of the surface.
+
+The `scatter_mode` input selects between `R` (reflection only), `T` (transmission only), and `RT` (both reflection and transmision).
+
+If reflection scattering is enabled, this node may be layered vertically over a base BSDF for the surface beneath the dielectric layer. By chaining multiple `dielectric_bsdf` nodes you can describe a surface with multiple specular lobes.
+
+If transmission scattering is enabled, this node may be layered over a VDF describing the surface interior to handle absorption and scattering inside the medium, useful for colored glass, turbid water, etc.
+
+#### Dielectric Equations
+
+These equations are based on a standard optimized implementation[^Lagarde2013], rather than the original paper[^Walter2007].
+
+$$c=\cos_{\theta}$$
+
+$$g=\sqrt{\eta^2+c^2-1}$$
+
+$$R=t\frac{(g-c)^2}{2(g-c)^2}\left(1+\frac{(c(g+c)-1)^2}{(c(g-c)+1)^2}\right)$$
+
+### generalized_schlick_bsdf
+Constructs a reflection and/or transmission BSDF based on a microfacet model and a generalized Schlick Fresnel curve[^Hoffman2023].
+
+@MX_TABLE_generalized_schlick_bsdf@
+
+The `color0` and `color90` inputs correspond to $r_0$ and $r_{90}$ in the generalized Schlick model.
+
+The `color82` input corresponds to $t$ in the Generalized Schlick model.
+
+The `exponent` input is the exponent for Schlick blending between `color0` and `color90`, and corresponds to $\alpha$ in the generalized Schlick model.
+
+The `scatter_mode` input selects between `R` (reflection only), `T` (transmission only), and `RT` (both reflection and transmision).
+
+If reflection scattering is enabled, this node may be layered vertically over a base BSDF for the surface beneath the generalized Schlick layer. By chaining multiple `generalized_schlick_bsdf` nodes you can describe a surface with multiple specular lobes.
+
+If transmission scattering is enabled, this node may be layered over a VDF describing the surface interior to handle absorption and scattering inside the medium, useful for colored glass, turbid water, etc.
+
+#### Generalized Schlick Equations
+
+$$\theta_{max}=\arccos(\frac{1}{7})$$
+
+$$a=\frac{(r_0+(r_{90}-r_0))(1-\cos_{\theta_{max}})^\alpha(1-t)}{\cos_{\theta_{max}}(1-\cos_{\theta_{max}})^6}$$
+
+$$F_\theta=(r_0+(r_{90}-r_0))(1-\cos_{\theta})^\alpha-a\cos_{\theta}(1-\cos_{\theta})^6$$
+
+### sheen_bsdf
+Constructs a microfacet BSDF for the back-scattering properties of cloth-like materials.
+
+@MX_TABLE_sheen_bsdf@
+
+The `color` input corresponds to $c$ in the sheen model, and represents a non-physical color tint on the sheen lobe.
+
+The `roughness` input corresponds to $r$ in the sheen model, and represents the degree to which the microfibers diverge from the normal direction.
+
+The `mode` option selects between two available sheen models, Conty-Kulla[^Conty2017] and Zeltner[^Zeltner2022].
+
+This node may be layered vertically over a base BSDF for the surface beneath the sheen layer.
+
+#### Conty-Kulla Sheen Equations
+
+$$D_\theta=\frac{(2 + \frac{1}{r})(1 - {\cos_\theta}^2)^\frac{0.5}{r}}{2\pi}$$
+
+$$L_r(\omega_i,\omega_o,\omega_h)=\frac{cD(\theta_h)}{cos{\theta_i}+cos{\theta_o}-cos{\theta_i}cos{\theta_o}}$$
+
+# References
+
+[^Conty2017]: Alejandro Conty, Christopher Kulla, **Production Friendly Microfacet Sheen BRDF**, , 2017
+
+[^Hoffman2023]: Naty Hoffman, **Generalization of Adobe's Fresnel Model**, , 2023
+
+[^Lagarde2013]: Sébastien Lagarde, **Memo on Fresnel equations**, https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/, 2013
+
+[^Oren1994]: Michael Oren, Shree K. Nayar, **Generalization of Lambert’s Reflectance Model**, , 1994
+
+[^Portsmouth2025]: Portsmouth et al., **EON: A practical energy-preserving rough diffuse BRDF**, , 2025.
+
+[^Walter2007]: Bruce Walter et al., **Microfacet Models for Refraction through Rough Surfaces**, , 2007
+
+[^Zeltner2022]: Tizian Zeltner et al., **Practical Multiple-Scattering Sheen Using Linearly Transformed Cosines**, , 2022
\ No newline at end of file
diff --git a/documents/SpecificationTemplates/placeholder.template.md b/documents/SpecificationTemplates/placeholder.template.md
new file mode 100644
index 0000000000..4d43737a98
--- /dev/null
+++ b/documents/SpecificationTemplates/placeholder.template.md
@@ -0,0 +1,1779 @@
+## Place holder for specification document
+
+To generate the markdown table for the node ports just add the string `@MX_TABLE_@` in the markdown file.
+
+
+## Texture Nodes
+
+### image
+Samples data from a single image, or from a layer within a multi-layer image.
+
+@MX_TABLE_image@
+
+---
+
+### tiledimage
+Samples data from a single image, with provisions for tiling and offsetting the image across uv space.
+
+@MX_TABLE_tiledimage@
+
+---
+
+### latlongimage
+Samples an equiangular map along a view direction with adjustable latitudinal offset.
+
+@MX_TABLE_latlongimage@
+
+---
+
+### 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.
+
+@MX_TABLE_triplanarprojection@
+
+
+## Procedural Nodes
+
+### constant
+Outputs a constant value.
+
+@MX_TABLE_constant@
+
+The output, `out`, of this node corresponds directly to its input, `value`, in both value and type.
+
+$$out = value$$
+
+---
+
+### ramplr
+A left-to-right linear value ramp
+
+@MX_TABLE_ramplr@
+
+The ramp interpolation is calculated using the variable `U`, which corresponds to the `x` co-ordinate of the `texcoord` input.
+
+$$U = Clamp(texcoord.x, 0.0, 1.0)$$
+
+By default the first set of UV coordinates will be used for `texcoord`.
+
+The output, `out`, of this node is the linear interpolation of `value_l` and `value_r` with respect to interpolant parameter `U`:
+
+\[
+\begin{align*}
+U &= \text{Clamp}(\text{texcoord}_x, 0.0, 1.0) \\
+\text{out}_i &= \text{value}_{r_i} \cdot U + \text{value}_{l_i} \cdot (1 - U)
+\end{align*}
+\]
+
+---
+
+### ramptb
+A top-to-bottom linear value ramp.
+
+@MX_TABLE_ramptb@
+
+The ramp interpolation is calculated using the variable `V`, which corresponds to the `y` co-ordinate of the `texcoord` input.
+
+$$V = Clamp(texcoord.y, 0.0, 1.0)$$
+
+By default the first set of UV coordinates will be used for `texcoord`.
+
+The output, `out`, of this node is the linear interpolation of `value_l` and `value_r` with respect to interpolant parameter `V`:
+
+\[
+\begin{align*}
+V &= \text{Clamp}(\text{texcoord}_y, 0.0, 1.0) \\
+\text{out}_i &= \text{value}_{r_i} \cdot V + \text{value}_{l_i} \cdot (1 - V)
+\end{align*}
+\]
+
+---
+
+### ramp4
+A 4-corner bilinear value ramp.
+
+@MX_TABLE_ramp4@
+
+
+The output, `out`, is given by a bilinear interpolation of `valuetl`, `valuetr`, `valuebl`, `valuebr`, with respect to interpolant parameters derived from the `texcoord` parameter
+
+
+\[
+\begin{align*}
+S &= \text{Clamp}(\text{texcoord}_x, 0.0, 1.0) \\
+T &= \text{Clamp}(\text{texcoord}_y, 0.0, 1.0) \\
+\text{Top}_{\text{mix}} &= \text{Mix}(\text{valuetl}, \text{valuetr}, S) \\
+\text{Bottom}_{\text{mix}} &= \text{Mix}(\text{valuebl}, \text{valuebr}, S) \\
+\text{out} &= \text{Mix}(\text{Top}_{\text{mix}}, \text{Bottom}_{\text{mix}}, T)
+\end{align*}
+\]
+
+---
+
+### splitlr
+A left-right split matte, split at a specified `U` value.
+
+@MX_TABLE_splitlr@
+
+The output, `out`, is given by linear interpolation of `value_l` and `value_r` with respect to linear interpolant, `T`, given by:
+
+\[
+\begin{align*}
+T &= \text{aastep}(\text{center}, \text{texcoord}_x) \\
+\text{out}_i &= \text{value}_{r_i} \cdot T + \text{value}_l \cdot (1 - T)
+\end{align*}
+\]
+
+---
+
+### splittb
+A top-bottom split matte, split at a specified `V`` value.
+
+@MX_TABLE_splittb@
+
+The output, `out`, is given by linear interpolation of `value_l` and `value_r` with respect to linear interpolant, `T`, given by:
+
+\[
+\begin{align*}
+T &= \text{aastep}(\text{center}, \text{texcoord}_y) \\
+\text{out}_i &= \text{value}_{r_i} \cdot T + \text{value}_l \cdot (1 - T)
+\end{align*}
+\]
+
+---
+
+### noise2d
+2D Perlin noise in 1, 2, 3 or 4 channels.
+
+@MX_TABLE_noise2d@
+
+---
+
+### noise3d
+@MX_TABLE_noise3d@
+
+---
+
+### fractal3d
+@MX_TABLE_fractal3d@
+
+---
+
+### cellnoise2s
+@MX_TABLE_cellnoise2d@
+
+---
+
+### cellnoise3d
+@MX_TABLE_cellnoise3d@
+
+---
+
+### worleynoise2d
+@MX_TABLE_worleynoise2d@
+
+---
+
+### worleynoise3d
+@MX_TABLE_worleynoise3d@
+
+---
+
+### unifiednoise2d
+@MX_TABLE_unifiednoise2d@
+
+The `type` input must be used to select an underlying noise to evaluate.
+
+| `type` | `node` |
+|--------|------------------|
+| 0 | `noise2d` |
+| 1 | `cellnoise2d` |
+| 2 | `worleynoise2d` |
+| 3 | `fractalnoise3d` |
+
+** NOTE there is currently no `fractalnoise2d` - texcoord is promoted to position**
+
+The `texcoord` input must be scaled by the `freq` input value and then have the `offset` input value added to it. This computed result is then used as the `texcoord` input in the corresponding noise type.
+
+$$computedTexcoord_i = (texcoord_i * freq_i) + offset_i$$
+
+The `jitter` input must be used as the `jitter` input of the `worleynoise2d` nose if that noise type is selected corresponding input
+
+The `octaves`, `lacunarity` and `diminish` inputs must be used as the corresponding inputs of the `fractalnoise2d` node if that type is selected.
+
+The `out` output must be computed by taking the computed result from the selected noise type. That value must then remapped to the range defined by the `outmin` and `outmax` inputs, and if the `clampoutput` input is true, then the remapped value must be clamped between these two values.
+
+$$remapped = computed * (outmax - outmin) + outmin$$
+
+$$out =
+\begin{cases}
+remapped & \text{if } clampoutput \text{ is } false \text{ or } remapped \in [outmin, outmax]\\
+outmin & \text{if } clampoutput \text{ is } true \text{ and } remapped < outmin\\
+outmax & \text{if } clampoutput \text{ is } true \text{ and } remapped > outmax\\
+\end{cases}$$
+
+---
+
+### unifiednoise3d
+@MX_TABLE_unifiednoise3d@
+
+The `type` input must be used to select an underlying noise to evaluate.
+
+| `type` | `node` |
+|--------|------------------|
+| 0 | `noise3d` |
+| 1 | `cellnoise3d` |
+| 2 | `worleynoise3d` |
+| 3 | `fractalnoise3d` |
+
+The `position` input must be scaled by the `freq` input value and then have the `offset` input value added to it. This computed result is then used as the `position` input in the corresponding noise type.
+
+$$computedPosition_i = (position_i * freq_i) + offset_i$$
+
+The `jitter` input must be used as the `jitter` input of the `worleynoise3d` nose if that noise type is selected corresponding input
+
+The `octaves`, `lacunarity` and `diminish` inputs must be used as the corresponding inputs of the `fractalnoise3d` node if that type is selected.
+
+The `out` output must be computed by taking the computed result from the selected noise type. That value must then remapped to the range defined by the `outmin` and `outmax` inputs, and if the `clampoutput` input is true, then the remapped value must be clamped between these two values.
+
+$$remapped = computed * (outmax - outmin) + outmin$$
+
+$$out =
+\begin{cases}
+remapped & \text{if } clampoutput \text{ is } false \text{ or } remapped \in [outmin, outmax]\\
+outmin & \text{if } clampoutput \text{ is } true \text{ and } remapped < outmin\\
+outmax & \text{if } clampoutput \text{ is } true \text{ and } remapped > outmax\\
+\end{cases}$$
+
+---
+
+### randomfloat:
+Produces a stable randomized float value between 'min' and 'max', based on an 'input' signal and 'seed' value. Uses a 2d cellnoise function to produce the output.
+
+@MX_TABLE_randomfloat@
+
+\[
+\begin{aligned}
+s &= \text{float}(\text{seed}) \\
+x &= \begin{cases}
+ \text{in} \cdot 4096, & \text{if in is float} \\
+ \text{float}(\text{in}), & \text{if in is integer}
+\end{cases} \\
+\vec{v} &= (x, s) \\
+n &= \text{cellnoise2d}(\vec{v}) \quad \text{where } n \in [0, 1] \\
+\text{out} &= \min + n \cdot (\max - \min)
+\end{aligned}
+\]
+
+---
+
+### randomcolor:
+Produces a randomized RGB color within a randomized hue, saturation and brightness range, based on an 'input' signal and 'seed' value. Output type color3.
+
+@MX_TABLE_randomcolor@
+
+The output, `out`, is a random color computed by the method below:
+
+\[
+\begin{aligned}
+\text{seed}_f &= \text{float}(\text{seed}) \\
+\text{seed}_\text{hue} &= \left\lceil \text{seed}_f + 413.3 \right\rceil \\
+\text{seed}_\text{sat} &= \left\lceil \text{seed}_f + 1522.4 \right\rceil \\
+\text{seed}_\text{val} &= \left\lceil \text{seed}_f + 1813.8 \right\rceil \\
+\text{rand}_\text{hue} &= \text{RandomFloat}(\text{input}, \text{seed}_\text{hue}) \\
+\text{rand}_\text{sat} &= \text{RandomFloat}(\text{input}, \text{seed}_\text{sat}) \\
+\text{rand}_\text{val} &= \text{RandomFloat}(\text{input}, \text{seed}_\text{val}) \\
+\text{hue} &= \text{hue}_{\text{low}} + \text{rand}_\text{hue} \cdot (\text{hue}_{\text{high}} - \text{hue}_{\text{low}}) \\
+\text{sat} &= \text{sat}_{\text{low}} + \text{rand}_\text{sat} \cdot (\text{sat}_{\text{high}} - \text{sat}_{\text{low}}) \\
+\text{val} &= \text{val}_{\text{low}} + \text{rand}_\text{val} \cdot (\text{val}_{\text{high}} - \text{val}_{\text{low}}) \\
+\text{out} &= \text{HSVtoRGB}(\text{hue}, \text{sat}, \text{val})
+\end{aligned}
+\]
+
+## Shape Nodes
+
+### checkerboard
+2D checkerboard pattern.
+
+
+@MX_TABLE_checkerboard@
+Draws a checkerboard pattern
+[there will be a vector illustration figure here]
+
+---
+
+### line
+2D line pattern.
+
+
+@MX_TABLE_line@
+
+Returns 1 if texcoord is at less than radius distance from a line segment defined by point1 and point2; otherwise returns 0. Note that this makes the shapes at the end
+of the lines semi-circles.
+[there will be a vector illustration figure here]
+
+---
+
+### circle
+2D circle(disk) pattern.
+
+@MX_TABLE_circle@
+
+Returns 1 if texcoord is inside a circle defined by center and radius; otherwise returns 0
+[there will be a vector illustration figure here]
+
+---
+
+### cloverleaf
+2D cloverleaf pattern: four semicircles on the edges of a square defined by center and radius.
+
+@MX_TABLE_cloverleaf@
+
+Returns 1 if texcoord is inside a cloverleaf shape described by four semicircles on the edges of a square defined by center and radius; otherwise returns 0.
+[there will be a vector illustration figure here]
+
+---
+
+### hexagon
+2D hexagon pattern.
+
+@MX_TABLE_hexagon@
+
+Returns 1 if texcoord is inside a hexagon shape inscribed by a circle defined by center and radius; otherwise returns 0.
+[there will be a vector illustration figure here]
+
+---
+
+### grid
+Creates a grid pattern of (1, 1, 1) white lines on a (0, 0, 0) black background with the given tiling, offset, and line thickness. Pattern can be regular or staggered.
+
+@MX_TABLE_grid@
+
+---
+
+### crosshatch
+Creates a crosshatch pattern with the given tiling, offset, and line thickness. Pattern can be regular or staggered.
+
+@MX_TABLE_crosshatch@
+
+---
+
+### tiledcircles
+Creates a black and white pattern of circles with a defined tiling and size (diameter). Pattern can be regular or staggered.
+
+@MX_TABLE_tiledcircles@
+
+---
+
+### tiledcloverleafs
+Creates a black and white pattern of cloverleafs with a defined tiling and size (diameter of the circles circumscribing the shape). Pattern can be regular or staggered.
+
+@MX_TABLE_tiledcloverleafs@
+
+---
+
+### tiledhexagons
+Creates a black and white pattern of hexagons with a defined tiling and size (diameter of the circles circumscribing the shape). Pattern can be regular or staggered.
+
+@MX_TABLE_tiledhexagons@
+
+## Geometric Nodes
+
+### position
+The coordinates associated with the currently-processed data, as defined in a specific coordinate space.
+
+
+@MX_TABLE_position@
+
+
+Outputs the three-dimensional coordinate (x,y,z) of the currently processed data.
+
+---
+
+### normal
+The normalized geometric normal associated with the currently-processed data, as defined in a specific coordinate space.
+
+@MX_TABLE_normal@
+
+Outputs the normalized observer/ray facing normal. If the geometry has texture coordinates, this is equal to the normalized cross product of tangent and bitangent.
+When going between the different coordinate spaces, the transformation matrices of the spaces are applied to the normal as their inverse transpose
+(or equivalently, transpose inverse), with the exception of the model space, which
+
+---
+
+### tangent
+The geometric tangent vector associated with the currently-processed data, as defined in a specific coordinate space.
+
+@MX_TABLE_tangent@
+
+
+Outputs the un-normalized partial derivative of "position" with respect to the first element of the texture coordinate touple for the given texture index. If there are no
+texture coordinates on the geometry, or the texture coordinate index is out of range, the output falls back to the default vector3(0,0,0). Note that the tangent is
+not necessarily orthogonal to the "bitangent".
+For curves, tangent goes along the length of the curve.
+
+---
+
+### bitangent
+The geometric bi-tangent vector associated with the currently-processed data, as defined in a specific coordinate space.
+
+@MX_TABLE_bitangent@
+
+Outputs the un-normalized partial derivative at "position" with respect to the second element 'v' in the texture coordinate touple ('u','v'). If there are no
+texture coordinates on the geometry, or the texture coordinate index is out of range, the output falls back to the default vector3(0,0,0). For curves, bitangent goes across the width of the curve.
+
+---
+
+### bump
+The normalized normal computed by offsetting the surface world space position along its world space normal.
+
+@MX_TABLE_bump@
+
+Outputs the un-normalized partial derivative at "position" with respect to the second element 'v' in the texture coordinate touple ('u','v'). If there are no
+texture coordinates on the geometry, or the texture coordinate index is out of range, the output falls back to the default vector3(0,0,0). For curves, bitangent goes across the width of the curve.
+
+---
+
+### texcoord
+The 2D or 3D texture coordinates associated with the currently-processed data
+
+@MX_TABLE_texcoord@
+
+
+---
+
+
+### geomcolor
+The color associated with the current geometry at the current position, generally bound via per-vertex color values. The type must match the type of the "color" bound to the geometry.
+
+@MX_TABLE_geomcolor@
+
+---
+
+### geompropvalue
+The value of the specified varying geometric property (defined using ) of the currently-bound geometry. This node's type must match that of the referenced geomprop.
+
+@MX_TABLE_geompropvalue@
+
+---
+
+### geompropvalueuniform
+The value of the specified uniform geometric property (defined using ) of the currently-bound geometry. This node's type must match that of the referenced geomprop.
+
+@MX_TABLE_geompropvalueuniform@
+
+---
+
+### Geometric Spaces
+
+There are two types of spaces: affine spaces are defined by an affine linear transform (scale, rotate, translate, shear), and mapping spaces that maps each point
+
+|Space | Description |
+|-----------------|---------------------------------------------------|
+|"model" | optional mapping space
+|"object" | position on the deformed (displaced and subdivided) geometry before any transforms are applied |
+|"world" | position in object space with all transforms applied|
+
+## Application Nodes
+
+### frame
+The current frame number as defined by the host environment.
+
+@MX_TABLE_frame@
+
+Applications may use any appropriate method to communicate the current frame number to the node implementation, whether via an internal state variable, a custom input, or other approach.
+
+### time
+
+The current time in seconds, as defined by the host environment.
+
+@MX_TABLE_time@
+
+Applications may use any appropriate method to communicate the current time to the node implementation, whether via an internal state variable, a custom input, dividing the current frame number by a local "frames per second" value, or other method; real-time applications may return some variation of wall-clock time.
+
+
+## Math Nodes
+
+### add
+Add a value to the incoming float/color/vector/matrix
+
+@MX_TABLE_add@
+
+`out` shall be computed as the component-wise addition of `in1` and `in2`
+
+```math
+\mathtt{out}_{i,j} = \mathtt{in1}_{i,j} + \mathtt{in2}_{i,j}
+```
+
+---
+
+### subtract
+Subtract a value from the incoming float/color/vector/matrix
+
+@MX_TABLE_subtract@
+
+`out` shall be computed as the component-wise subtraction of `in2` from `in1`
+
+```math
+\mathtt{out}_{i,j} = \mathtt{in2}_{i,j} - \mathtt{in1}_{i,j}
+```
+
+---
+
+### multiply
+Multiply two values together. Scalar and vector types multiply component-wise, while matrices multiply with the standard matrix product.
+
+@MX_TABLE_multiply@
+
+For scalar and vector types, `out` shall be computed as the component-wise multiplication of `in1` with `in2`
+
+```math
+\mathtt{out}_{i} = \mathtt{in1}_{i} * \mathtt{in2}_{i}
+```
+
+For matrix types, `out` shall be computed as the matrix inner product.
+
+---
+
+### divide
+Divide one value by another. Scalar and vector types divide component-wise, while for matrices `in1` is multiplied with the inverse of `in2`.
+
+@MX_TABLE_divide@
+
+For scalar and vector types, `out` shall be computed as the component-wise division of `in2` by `in1`
+
+```math
+\mathtt{out}_{i} = \mathtt{in1}_{i} / \mathtt{in2}_{i}
+```
+
+For matrix types, `out` shall be computed as the matrix inner product of `in1` and the inverse of `in2`.
+
+```math
+\mathtt{out} = \mathtt{in}_1 \mathtt{in}_2^{-1}
+```
+
+---
+
+### modulo
+The remaining fraction after dividing an incoming float/color/vector by a value and subtracting the integer portion. Modulo always returns a non-negative result.
+
+@MX_TABLE_modulo@
+
+`out` shall be computed as the absolute value of the component-wise modulo of `in1` and `in2`
+```math
+\mathtt{out}_{i} = \lvert \mathtt{in1}_{i} \space mod \space \mathtt{in2}_{i} \rvert
+```
+
+---
+
+### fract
+Returns the fractional part of the floating-point input.
+
+@MX_TABLE_fract@
+
+`out` shall be computed component-wise as the fractional part of `in1`.
+```math
+\begin{align}
+x &= \lvert \mathtt{in}_{i} \rvert \\
+\mathtt{out}_{i} &= x - \lfloor x \rfloor \\
+\end{align}
+```
+
+---
+
+### invert
+subtract the incoming float, color, or vector from `amount` in all channels, outputting: `amount - in`.
+
+@MX_TABLE_invert@
+
+`out` shall be computed as `in` subtracted from `amount`:
+```math
+\mathtt{out}_{i} = \mathtt{amount}_{i} - \mathtt{in}_{i}
+```
+
+---
+
+### absval
+The per-channel absolute value of the incoming float/color/vector.
+
+@MX_TABLE_absval@
+
+`out` shall be computed as the channel-wise absolute value of `in`:
+```math
+\mathtt{out}_{i} = \lvert \mathtt{in}_{i} \rvert
+```
+
+---
+
+### 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.
+
+
+@MX_TABLE_floor@
+
+`out` shall be computed as the channel-wise floor of `in`:
+```math
+\mathtt{out}_{i} = \lfloor \mathtt{in}_{i} \rfloor
+```
+
+---
+
+### 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.
+
+@MX_TABLE_ceil@
+
+`out` shall be computed as the channel-wise ceil of `in`:
+
+```math
+\mathtt{out}_{i} = \lceil \mathtt{in}_{i} \rceil
+```
+
+---
+
+### round
+Round each channel of the incoming float/color/vector values to the nearest integer value.
+
+@MX_TABLE_round@
+
+Round each channel of `in` to the nearest whole number, storing the result in the corresponding channel of `out`. Rounding shall be calculated using IEEE 754 round-to-nearest-ties-away-from-zero mode.
+
+```math
+\mathtt{out}_{i} = \lfloor \mathtt{in}_{i} \rceil
+```
+
+---
+
+### power
+Raise incoming float/color values to the specified exponent, commonly used for "gamma" adjustment.
+
+@MX_TABLE_power@
+
+`out` shall be computed as the component-wise raising of `in1` to the `in2`th power:
+
+```math
+\mathtt{out}_{i} = \mathtt{in1}_{i}^{\mathtt{in2}_{i}}
+```
+
+---
+
+### safepower
+Raise incoming float/color values to the specified exponent. Negative "in1" values will result in negative output values.
+
+@MX_TABLE_power@
+
+`out` shall be computed as the component-wise raising of `in1` to the `in2`th power:
+
+```math
+\mathtt{out}_{i} = (sgn \space \mathtt{in1}_{i}) \mathtt{in1}_{i}^{\lvert \mathtt{in2}_{i} \rvert}
+```
+
+---
+
+### sin
+The sine of the incoming value, which is expected to be expressed in radians.
+
+@MX_TABLE_sin@
+
+`out` shall be computed as the component-wise sin of `in`. `in` shall be in radians.
+
+```math
+\mathtt{out}_{i} = \sin{\mathtt{in}_{i}}
+```
+---
+
+### cos
+The cosine of the incoming value, which is expected to be expressed in radians.
+
+@MX_TABLE_cos@
+
+`out` shall be computed as the component-wise cosine of `in`. `in` shall be in radians:
+
+```math
+\mathtt{out}_{i} = \cos{\mathtt{in}_{i}}
+```
+---
+
+### tan
+The tangent of the incoming value, which is expected to be expressed in radians.
+
+@MX_TABLE_tan@
+
+`out` shall be computed as the component-wise tangent of `in`. `in` shall be in radians:
+
+```math
+\mathtt{out}_{i} = \tan{\mathtt{in}_{i}}
+```
+---
+
+### asin
+The arcsine of the incoming value. The output will be expressed in radians.
+
+@MX_TABLE_asin@
+
+`out` shall be computed as the component-wise arcsine of `in`. `out` shall be in radians.
+
+```math
+\mathtt{out}_{i} = \sin^{-1}{\mathtt{in}_{i}}
+```
+---
+
+### acos
+The arccosine of the incoming value. The output will be expressed in radians.
+
+@MX_TABLE_acos@
+
+`out` shall be computed as the component-wise arccosine of `in`. `out` shall be in radians.
+
+```math
+\mathtt{out}_{i} = \cos^{-1}{\mathtt{in}_{i}}
+```
+---
+
+### atan2
+the arctangent of the expression (`iny`/`inx`). The output will be expressed in radians.
+
+@MX_TABLE_atan2@
+
+> [!WARNING]
+> Need to define all the rules about 0 values?
+
+`out` shall be computed as the component-wise arctangent of `iny` divided by `inx`. `out` shall be in radians.
+
+```math
+\mathtt{out}_{i} = \tan^{-1}{\frac{\mathtt{iny}_{i}}{\mathtt{inx}_{i}}}
+```
+
+---
+
+### sqrt
+The square root of the incoming value.
+
+@MX_TABLE_sqrt@
+
+`out` shall be computed as the component-wise square root of `in`.
+
+```math
+\mathtt{out}_{i} = \sqrt{\max(\mathtt{in}_{i}, 0)}
+```
+
+---
+
+### ln
+The natural logarithm of the incoming value.
+
+@MX_TABLE_ln@
+
+`out` shall be computed as the component-wise natural logarithm of `in`.
+
+```math
+\mathtt{out}_{i} = \ln{\mathtt{in}_{i}}
+```
+
+---
+
+### exp
+$e$ to the power of the incoming value.
+
+@MX_TABLE_exp@
+
+`out` shall be computed as the component-wise raising of the base of natural logarithms, $e$, to the power `in`.
+
+```math
+\mathtt{out}_{i} = e^{\mathtt{in}_{i}}
+```
+
+---
+
+### sign
+The per-channel sign of the incoming float/color/vector value: -1 for negative, +1 for positive, or 0 for zero.
+
+@MX_TABLE_sign@
+
+`out` shall be computed as the channel-wise sign of `in`:
+```math
+\mathtt{out}_{i} = sgn \space \mathtt{in}_{i}
+```
+
+---
+
+### clamp
+Clamp incoming values per-channel to a specified range of float/color/vector values.
+
+@MX_TABLE_clamp@
+
+`out` shall be computed as the component-wise clamping of `in` between `low` and `high`. That is:
+
+```math
+\mathtt{out}_{i} = \max(\mathtt{low}_{i}, \min(\mathtt{high}_{i}, {\mathtt{in}_{i}}))
+```
+---
+
+### min
+Select the minimum of the two incoming values
+
+@MX_TABLE_min@
+
+`out` shall be computed as the component-wise minimum of `in1` and `in2`:
+```math
+\mathtt{out}_{i} = \min(\mathtt{in1}_{i}, \mathtt{in2}_{i})
+```
+
+---
+
+### max
+Select the maximum of the two incoming values
+
+@MX_TABLE_max@
+
+`out` shall be computed as the component-wise maximum of `in1` and `in2`:
+```math
+\mathtt{out}_{i} = \max(\mathtt{in1}_{i}, \mathtt{in2}_{i})
+```
+
+---
+
+### normalize
+Output the incoming vectorN stream normalized.
+
+@MX_TABLE_normalize@
+
+`out` shall be computed as `in` divided by the euclidean norm of `in`:
+
+```math
+\mathtt{out} = \frac{\mathtt{in}}{\lVert \mathtt{in} \rVert}
+```
+
+---
+
+### 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.
+
+@MX_TABLE_magnitude@
+
+`out` shall be computed as the euclidean norm of `in`:
+
+```math
+\mathtt{out} = \lVert \mathtt{in} \rVert = \sqrt{\sum_{i=0}^{N-1} {\mathtt{in}_i}^2}
+```
+
+---
+
+### distance
+Measures the distance between two points in 2D, 3D, or 4D.
+
+@MX_TABLE_distance@
+
+`out` shall be computed as the magnitude of the vector pointing from `in1` to `in2`, that is the euclidean norm of `in2 - in1`:
+
+```math
+\mathtt{out} = \lVert \mathtt{in2} - \mathtt{in1} \rVert
+```
+
+---
+
+### dotproduct
+Output the (float) dot product of two incoming vectorN streams; cannot be used on float or colorN streams.
+
+@MX_TABLE_dotproduct@
+
+`out` shall be computed as the dot (inner) product of `in1` and `in2`
+
+```math
+\mathtt{out} = \mathtt{in1} \cdot \mathtt{in2}
+```
+
+---
+
+### crossproduct
+Output the (vector3) cross product of two incoming vector3 streams; cannot be used on any other stream type. A disabled crossproduct node passes through the value of `in1` unchanged.
+
+@MX_TABLE_crossproduct@
+
+`out` shall be computed as the cross product of `in1` and `in2`:
+
+```math
+\mathtt{out} = \mathtt{in1} \times \mathtt{in2}
+```
+If the node is disabled, `out` shall be `in1`.
+
+---
+
+### transformpoint
+Transform the incoming vector3 coordinate from one specified space to another; cannot be used on any other stream type.
+
+@MX_TABLE_transformpoint@
+
+
+`in` shall be reinterpreted as a 4-dimensional row vector, _p_, by the addition of a homogeneous coordinate with a value of 1. _p_ shall be multipled with the matrix _M_.
+
+The matrix, _M_, shall be computed by the renderer as the matrix necessary to transform from `fromspace` to `tospace`. If either of the named spaces are unknown to the renderer, or no suitable matrix can be computed, _M_ shall be the identity matrix.
+
+_p'_ shall be computed as multiplication of _p_ with _M_.
+
+`out` shall be computed from _p'_ by dividing the first three coordinates by the homogeneous coordinate:
+
+```math
+\begin{align}
+p &= \lbrack \mathtt{in}_{0}, \mathtt{in}_{1}, \mathtt{in}_{2}, 1 \rbrack \\
+p' &= p \cdot M \\
+\mathtt{out} &= \frac{\lbrack p'_{0}, p'_{1}, p'_{2} \rbrack }{p'_{3}}
+\end{align}
+```
+
+> [!WARNING]
+> What did we decide about common vs world?
+
+---
+
+### transformvector
+Transform the incoming vector3 coordinate from one specified space to another; cannot be used on any other stream type.
+
+@MX_TABLE_transformvector@
+
+`in` shall be reinterpreted as a 4-dimensional row vector, _v_, by the addition of a homogeneous coordinate with a value of 0. _v_ shall be multipled with the matrix _M_.
+
+The matrix, _M_, shall be computed by the renderer as the matrix necessary to transform from `fromspace` to `tospace`. If either of the named spaces are unknown to the renderer, or no suitable matrix can be computed, _M_ shall be the identity matrix.
+
+_v'_ shall be computed as multiplication of _v_ with _M_.
+
+`out` shall be computed from _v'_ by removing the homogeneous coordinate.
+
+```math
+\begin{align}
+
+\vec{v} &= [\mathtt{in}_{0}, \mathtt{in}_{1}, \mathtt{in}_{2}, 1] \notag \\
+\vec{v}' &= \vec{v} \cdot M \notag \\
+\mathtt{out} &= [\vec{v}'_{0}, \vec{v}'_{1}, \vec{v}'_{2}] \notag
+
+\end{align}
+```
+
+> [!WARNING]
+> What did we decide about common vs world?
+
+---
+
+### transformnormal
+Transform the incoming vector3 normal from one specified space to another; cannot be used on any other stream type.
+
+@MX_TABLE_transformnormal@
+
+---
+
+### transformmatrix
+Transform the incoming vectorN by the specified matrix.
+
+@MX_TABLE_transformmatrix@
+
+---
+
+### normalmap
+Transform a normal vector from the encoded tangent space to world space. The input normal vector is assumed to be encoded with all channels in the [0-1] range, as would commonly be output from a normal map.
+
+@MX_TABLE_normalmap@
+
+---
+
+---
+
+### creatematrix
+
+Build a 3x3 or 4x4 matrix from three vector3 or four vector3 or vector4 inputs. A matrix44 may also be created from vector3 input values, in which case the fourth value will be set to 0.0 for `in1`-`in3`, and to 1.0 for `in4` when creating the matrix44.
+
+@MX_TABLE_creatematrix@
+
+---
+
+### hextilednormalmap
+Transform a normal vector from the encoded tangent space to world space. The input normal vector is assumed to be encoded with all channels in the [0-1] range, as would commonly be output from a normal map.
+
+@MX_TABLE_hextilednormalmap@
+
+---
+
+### transpose
+Transpose the incoming matrix
+
+@MX_TABLE_transpose@
+
+`out` shall be computed as the transpose of `in`, that is:
+
+```math
+\mathrm{out} = \mathrm{in}^T
+```
+
+---
+
+### determinant
+Output the determinant of the incoming matrix.
+
+@MX_TABLE_determinant@
+
+`out` shall be computed as the determinant of `in`, that is:
+
+```math
+\mathrm{out} = \det(\mathrm{in})
+```
+
+---
+
+### invertmatrix
+Invert the incoming matrix.
+
+@MX_TABLE_invertmatrix@
+
+`out` shall be computed as the matrix inverse of `in`. If `in` is not invertible, every component out `out` shall be set to `NaN`.
+
+---
+
+### rotate2d
+Rotate the incoming 2D vector about the origin.
+
+@MX_TABLE_rotate2d@
+
+`out` shall be computed as the right-handed rotation of `in` about the origin by `amount` degrees. That is, `in` shall be multiplied by the matrix:
+
+```math
+\begin{bmatrix}
+\cos\theta & -\sin\theta \\
+\sin\theta & \cos\theta
+\end{bmatrix}
+```
+
+where:
+```math
+\theta = \frac{\mathrm{amount} \cdot \pi}{180}
+```
+
+---
+
+### rotate3d
+Rotate the incoming 3D vector about the specified unit axis vector.
+
+@MX_TABLE_rotate3d@
+
+`out` shall be computed as the conjugation of `in` with a unit quaternion `q`:
+
+```math
+\begin{align}
+\boldsymbol{q} &= \alpha_0 \sin\frac{\theta}{2} \boldsymbol{i} + \alpha_1 \sin\frac{\theta}{2} \boldsymbol{j} + \alpha_2 \sin\frac{\theta}{2} \boldsymbol{k} + \cos\frac{\theta}{2} \notag \\
+\mathtt{out} &= \boldsymbol{q} \space \omega \space \boldsymbol{q^{-1}} \notag
+\end{align}
+```
+
+where:
+```math
+\begin{align}
+\omega &= \mathtt{in} \notag \\
+\theta &= \frac{\mathtt{amount}\cdot\pi}{180} \notag \\
+\alpha &= \mathtt{axis} \notag \\
+\end{align}
+```
+
+---
+
+### place2d
+Transform incoming 2D texture coordinates from one frame of reference to another.
+
+@MX_TABLE_place2d@
+
+
+---
+
+### trianglewave
+Generate a triangle wave from the given scalar input. The generated wave ranges from zero to one and repeats on integer boundaries.
+
+@MX_TABLE_trianglewave@
+
+`out` shall be computed as a triangle wave over the domain of `in`, with the range [0, 1] and period 1 according to the equation:
+
+```math
+\mathtt{out} = 2 \lvert \mathtt{in} - \lfloor \mathtt{in} + \frac{1}{2} \rfloor \rvert
+```
+> [!WARNING]
+> define defaultinput?
+
+---
+
+### reflect
+Reflect the incoming 3D vector about a surface normal vector.
+
+@MX_TABLE_reflect@
+
+`out` shall be computed as the reflection of `in` about `normal`:
+
+```math
+\mathtt{out} = \omega_i -2 \lparen \omega_i \cdot N \rparen N
+```
+
+where:
+
+```math
+\begin{align}
+
+\omega_i &= \mathtt{in} \notag \\
+N &= \mathtt{normal} \notag
+
+\end{align}
+```
+
+---
+
+
+### refract
+Refract the incoming 3D vector through a surface with the given surface normal and relative index of refraction.
+
+@MX_TABLE_refract@
+
+`out` shall be computed as the refraction of `in` through the interface whose surface normal is `normal` and has relative index of refraction `ior`:
+
+```math
+\mathtt{out} = -\eta \omega_i + \lparen \eta \cos\theta_i - \cos\theta_t \rparen N
+```
+
+where:
+```math
+\begin{align}
+\omega_i &= \mathtt{in} \notag \\
+N &= \mathtt{normal} \notag \\
+\eta &= \mathtt{ior} \notag \\
+\theta_i &= \cos^{-1}(\omega_i \cdot N) \notag \\
+\theta_t &= \sqrt{\eta^{2} \sin^2{\theta_i}} \notag \\
+\end{align}
+```
+
+
+
+## Adjustment Nodes
+
+### contrast
+Increase or decrease the contrast of the incoming `in` values using `amount` as a linear slope multiplier.
+
+@MX_TABLE_contrast@
+
+The output is determined as follows:
+
+$out = (in - pivot) \cdot amount + pivot$
+
+Note that the contrast increases when `amount` $>$ 1, and decreases when `amount` is between 0 and 1. Also `pivot` will not change as contrast is adjusted.
+
+---
+
+### remap
+Linearly remap incoming values from one range of values [`inlow`, `inhigh`] to another [`outlow`, `outhigh`].
+
+@MX_TABLE_remap@
+
+The output is determined as follows:
+
+$out = outlow + \left( \dfrac{in - inlow}{inhigh - inlow} \right) \cdot (outhigh - outlow)$
+
+
+
+### range
+Remap incoming values from one range of values to another, optionally applying a gamma correction "in the middle".
+
+@MX_TABLE_range@
+
+This node first remaps the input from [`inlow`, `inhigh`] to [0,1], using the remap node. It then applies gamma correction, using $1/gamma$ as the exponent. Note that gamma values greater than `1.0` make midtones brighter. This gamma corrected result is then remaped from [0,1], to [`outlow`, `outhigh`]. Then based on the value of `doclamp` the final output may be clamped to [`outlow`, `outhigh`].
+
+---
+
+### smoothstep
+Output a smooth, hermite-interpolated remapping of input values from [`low`, `high`] to [0,1].
+
+
+@MX_TABLE_smoothstep@
+
+This remaps the input to [0,1], using a hermite-interpolated or smoothstep remapping.
+Note that inputs outside the [`low`, `high`] range are clamped.
+
+$out = \begin{cases}
+ 1, \ in \geq high\\
+ 0, \ in \leq low \\
+ remap(low, high, in), \ otherwise
+\end{cases}$
+
+---
+
+### luminance
+Output a grayscale value containing the luminance of the incoming RGB color in all color channels
+
+@MX_TABLE_luminance@
+
+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 `in` with the luma coefficients `lumacoeffs` of the working colorspace; the alpha channel is left unchanged if present.
+
+---
+
+### rgbtohsv
+Convert an incoming color from RGB to HSV space (with H and S ranging from 0 to 1); the alpha channel is left unchanged if present. This conversion is not affected by the current color space.
+
+@MX_TABLE_rgbtohsv@
+
+See Foley & van Dam
+
+---
+
+### hsvtorgb
+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.
+
+@MX_TABLE_hsvtorgb@
+
+See Foley & van Dam
+---
+
+### 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.
+
+@MX_TABLE_hsvadjust@
+
+Note:
+- `amount.x` $>1$ rotates hue in the "red to green to blue" direction,
+- `amount.x` = 1.0 is equivalent to a 360 degree (e.g. no-op) rotation.
+- `amount.x` values that are $<$ 0 or $>$ 1 are allowed, the values are wrapped at the 0-1 boundaries.
+- The internal conversions between RGB and HSV spaces are not affected by the current color space.
+- For color4 inputs, the alpha value is unchanged.
+
+---
+
+### saturate
+Adjust the saturation of a color, the alpha channel will be unchanged if present.
+
+@MX_TABLE_saturate@
+
+The `amount` value is used to perform linear interpolation between the incomihng color and the grayscale luminance of the input computed using the provided luma coefficients.
+
+Note that this operation is not equivalent to the saturation adjustment of the `hsvadjust` node, as that operator does not take the working or any other colorspace into account.
+
+---
+
+### colorcorrect
+Combines various adjustment nodes into one artist-friendly color correction node. For color4 inputs, the alpha value is unchanged.
+
+@MX_TABLE_colorcorrect@
+
+Note
+- `hue` values that are $<$ 0 or $>$ 1 are allowed, but the values are wrapped at the 0-1 boundaries.
+- the color brightness is increased or decreased by $2^{\large exposure}$.
+- the `constrastpivot` value does not change as contrast is adjusted.
+
+
+## Compositing Nodes
+
+### mix
+Mix between two inputs according to an input mix amount.
+
+@MX_TABLE_mix@
+
+**TODO** determine if we're including the `xxxshader` variants here? It doesn't align with the subscript behavior description below.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective components of the `fg` and `bg` inputs, using the `mix` input as the interpolant.
+
+$$out_i = mix * fg_i + (1.0 - mix) * bg_i$$
+
+---
+
+### premult
+Multiply the R or RGB channels of the input by the Alpha channel of the input.
+
+@MX_TABLE_premult@
+
+Each of the `r`, `g` and `b` components of the `out` output should be calculated by multiplying the respective components of the `in` input by the `a` component of the `in` input. The `a` component of `out` should be equal to the `a` component of the `in` input.
+
+$$out_i =
+\begin{cases}
+in_i * in_a & \text{if } i \text{ is } r,g,b\\
+in_a & \text{if } i \text{ is } a
+\end{cases}$$
+
+
+
+---
+
+### unpremult
+Divide the RGB channels of the input by the Alpha channel of the input. If the Alpha value is zero, it is passed through unchanged.
+
+@MX_TABLE_power@
+
+If the `a` component of the `in` input is zero, then each component of `out` should be equal
+
+Each of the `r`, `g` and `b` components of the `out` output should be calculated by dividing the respective components of the `in` input by the `a` component of the `in` input. The `a` component of `out` should be equal to the `a` component of the `in` input.
+
+**TODO** - what happens if `in.a` is zero?
+
+$$out_i =
+\begin{cases}
+in_i & \text{if } in_a = 0 \\
+\frac{in_i}{in_a} & \text{else if } i \text{ is } r,g,b\\
+in_a & \text{else if } i \text{ is } a \\
+\end{cases}$$
+
+
+## Blend Nodes
+
+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 additional float input `mix`, which is 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 |
+|------------------|----------------------------------------------|------------------------|
+| **`plus`** | B+F | float, colorN |
+| **`minus`** | B-F | float, colorN |
+| **`difference`** | abs(B-F) | float, colorN |
+| **`burn`** | 1-(1-B)/F | float, colorN |
+| **`dodge`** | B/(1-F) | float, colorN |
+| **`screen`** | 1-(1-F)(1-B) | float, colorN |
+| **`overlay`** | 2FB if B<0.5;
1-2(1-F)(1-B) if B>=0.5 | float, colorN |
+
+
+### plus
+Add two 1-4 channel inputs, with optional mixing between the bg input and the result.
+
+@MX_TABLE_plus@
+
+Each component of the `fg` input must be added to the corresponding component of the `bg` input to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i=mix*(bg_i + fg_i) + (1.0-mix)*bg_i$$
+
+
+
+---
+
+### minus
+Subtract two 1-4 channel inputs, with optional mixing between the bg input and the result.
+
+@MX_TABLE_minus@
+
+Each component of the `fg` input must be subtracted from the corresponding component of the `bg` input to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i=mix*(bg_i - fg_i) + (1.0-mix)*bg_i$$
+
+---
+
+### difference
+Absolute-value difference of two 1-4 channel inputs, with optional mixing between the bg input and the result.
+
+@MX_TABLE_difference@
+
+Each component of the `fg` input must be subtracted from the corresponding component of the `bg` input to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the the absolute value of this intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i = mix* | bg_i - fg_i | + (1.0-mix)*bg_i$$
+
+---
+
+### burn
+Take two 1-4 channel inputs and apply the same operator to all channels:
+1-(1-B)/F
+
+@MX_TABLE_burn@
+
+Each component of the `out` output should be set to zero if the respective component of the `fg` input is zero or less.
+
+Otherwise, each component of the `bg` input is inverted and then divided by the respective component of the `fg` input, and finally inverted again to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i =
+\begin{cases}
+mix*(1.0 - (\frac{1.0 - bg_i}{fg_i}) + (1.0-mix)*bg_i & \text{if } fg_i > 0\\
+0 & \text{if } fg_i <= 0
+\end{cases}$$
+
+---
+
+### dodge
+Take two 1-4 channel inputs and apply the same operator to all channels:
+B/(1-F)
+
+@MX_TABLE_dodge@
+
+Each component of the `out` output should be set to zero if the respective component of the `fg` input when inverted is zero or less.
+
+Otherwise, each component of the `bg` input is divided by the respective component of `fg` inverted to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i =
+\begin{cases}
+mix*\frac{bg_i}{1.0 - fg_i} + (1.0-mix)*bg_i & \text{if } (1.0 - fg_i) > 0\\
+0 & \text{if } (1.0 - fg_i) <= 0
+\end{cases}$$
+
+---
+
+### screen
+Take two 1-4 channel inputs and apply the same operator to all channels:
+1-(1-F)*(1-B)
+
+@MX_TABLE_screen@
+
+Each respective component of the `fg` input and the `bg` must be inverted and then multiplied together, this product should then inverted again to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i = mix*(1.0 - (1.0 - fg_i) * (1.0 - bg_)) + (1.0-mix)*bg_i$$
+
+---
+
+### overlay
+Take two 1-4 channel inputs and apply the same operator to all channels:
+2FB if B<0.5;
+1-2(1-F)(1-B) if B>=0.5
+
+@MX_TABLE_overlay@
+
+For each component of the `bg` input, if the value is less than 0.5 then both the respective `fg` and `bg` components are multiplied together and then multiplied by two to create an intermediate result. Otherwise, if the component of `bg` is greater than or equal to 0.5, then both the `fg` and `bg` components are inverted, multiplied together and then multiplied by 2. Finally this value is inverted to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i =
+\begin{cases}
+mix*(2 * fg_i * bg_i) + (1.0-mix)*bg_i & \text{ if } bg_i < 0.5 \\
+mix*(1 - 2(1 - fg_i)(1 - bg_i)) + (1.0-mix)*bg_i & \text{ if } bg_i >= 0.5
+\end{cases}$$
+
+
+## Merge Nodes
+
+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 node descriptions below, `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).
+
+### disjointover
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+F+B if f+b<=1
+F+B(1-f)/b if f+b>1
+alpha: min(f+b,1)
+
+| **`disjointover`** | F+B if f+b<=1;
F+B(1-f)/b if f+b>1 | min(f+b,1) |
+
+
+@MX_TABLE_disjointover@
+
+If the sum of the `a` components of both the `fg` and `bg` inputs is less than or equal to 1, then each respective component of the `fg` and `bg` inputs is summed to create an intermediate result. Otherwise, if the sum of the `a` components of both the `fg` and `bg` inputs is greater than 1, then each component of the `bg` input is multiplied by the ratio of inverse of the `a` component of the `fg` input to the `a` component of the `bg` input, finally the corresponding component of the `fg` input is added to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i =
+\begin{cases}
+mix*(fg_i + bg_i) + (1.0-mix)*bg_i & \text{ if } fg_a + bg_a <= 1 \\
+mix*(fg_i+bg_i(1-fg_a)/bg_a) + (1.0-mix)*bg_i & \text{ if } fg_a + bg_a > 1
+\end{cases}$$
+
+---
+
+### in
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+* RGB = Fb
+* Alpha = fb
+
+@MX_TABLE_in@
+
+Each component of the `fg` input must be multiplied by the `a` component of the `bg` input to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the the absolute value of this intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i = mix*(fg_i*bg_a) + (1.0-mix) * bg_i$$
+
+---
+
+### mask
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+Bf (alpha: bf)
+
+@MX_TABLE_mask@
+
+Each component of the `bg` input must be multiplied by the `a` component of the `fg` input to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the the absolute value of this intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i = mix*(bg_i*fg_a) + (1.0-mix)*bg_i$$
+
+---
+
+### matte
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+Ff+B(1-f) (alpha: f+b(1-f))
+
+@MX_TABLE_matte@
+
+Each of the `r`,`g` and `b` components of `fg` and `bg` inputs are linearly interpolated, using the `a` component of the `fg` input as the interpolant to create an intermediate result. The `a` component of the `fg` input is used as the `a` component of this intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the the absolute value of this intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i =
+\begin{cases}
+mix*(fg_i*fg_a + bg_i*(1.0-fg_a)) + (1.0-mix)*bg_i & \text{ if } i \text{ in } r,g,b \\
+mix*(fg_i + bg_i*(1.0-fg_a)) + (1.0-mix)*bg_i & \text{ if } i = a
+\end{cases}$$
+
+---
+
+### out
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+F(1-b) (alpha: f(1-b))
+
+@MX_TABLE_out@
+
+Each component of the `fg` input must be multiplied by the inverted `a` component of the `bg` input to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the the absolute value of this intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i = mix * (fg_i*(1.0-bg_a)) + (1.0-mix) * bg_i$$
+
+---
+
+### over
+Take two color4 inputs and use the built-in alpha channel(s) to control the compositing of the fg and bg inputs:
+F+B(1-f) (alpha: f+b(1-f))
+
+@MX_TABLE_over@
+
+Each component of the `bg` input must be multiplied by the inverted `a` component of the `fg` input, and then added to the corresponding component of the `fg` input to create an intermediate result.
+
+Each component of the `out` output should be calculated by linearly interpolating the respective component of the the absolute value of this intermediate result and the respective component of `bg` using the `mix` input as the interpolant.
+
+$$out_i = mix * (fg_i + (bg_i*(1.0-fg_a))) + (1.0-mix) * bg_i$$
+
+
+## Masking Nodes
+
+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).
+
+### inside
+Take one 1-4 channel input "in" and multiply each channel by a separate float "mask" input.
+
+@MX_TABLE_inside@
+
+The components of `out` must be calculated by multiplying the components `in` by the float input `mask`.
+
+$$out_i = in_i * mask$$
+
+---
+
+### outside
+Take one 1-4 channel input `in` and multiply each channel by the inverse of a separate float `mask` input.
+
+@MX_TABLE_outside@
+
+The components of `out` must be calculated by multiplying the components `in` by the inverse of the float input `mask`.
+
+$$out_i = in_i * (1.0 - mask)$$
+
+
+## Conditional Nodes
+
+Conditional nodes are used to compare values of two streams, or to select a value from one of several streams.
+
+### ifgreater
+
+Output the value of the `in1` or `in2` stream depending on whether the `value1` input is greater than the `value2` input.
+
+@MX_TABLE_ifgreater@
+
+---
+
+### ifgreatereq
+
+Output the value of the `in1` or `in2` stream depending on whether the `value1` input is greater or equal to the `value2` input.
+
+@MX_TABLE_ifgreatereq@
+
+---
+
+### ifequal
+
+@MX_TABLE_ifequal@
+
+---
+
+### switch
+
+Output the value of one of up to ten input streams, according to the value of a selector input `which`. Note that not all inputs need to be connected. The output has the same type as `in1`, with a default value of __zero__.
+
+@MX_TABLE_switch@
+
+The value of `which` determines the output as follows:
+
+$out = \begin{cases}
+ in1, \ &which < 1\\
+ in2, \ 1 \leq &which < 2 \\
+ in3, \ 2 \leq &which < 3 \\
+ ... \\
+ in10, \ 9 \leq &which < 10 \\
+\end{cases}$
+
+For values `which` $\geq 10$ we return __zero__
+
+## Channel Nodes
+
+Channel nodes are used to perform channel manipulations and data type conversions on streams.
+
+### extract
+
+Isolate a single float channel from a __vectorN__ or __colorN__ stream. The output value is of type `float` with a default value of __zero__.
+
+@MX_TABLE_extract@
+
+The valid range for `index` should be clamped to $[0,N)$ in the user interface, where __N__ is the size of the input vector stream. `index` is a uniform, non-varying value. Any `index` values outside of the valid range should result in an error.
+
+---
+
+### separate2
+
+Split the channels of a 2-channel stream into separate float outputs.
+
+@MX_TABLE_separate2@
+
+For the vector2-input `in`, `outx` and `outy` correspond to the x- and y-components of `in`..
+
+---
+
+### separate3
+
+Split the channels of a 3-channel stream into separate float outputs.
+
+@MX_TABLE_separate3@
+
+When the input `in` is a color3, `outr`, `outg`, and `outb` correspond to the r-, g-, and b-components of `in`, respectively.
+
+When the input `in` is a vector3, `outx`, `outy`, and `outz` correspond to the x-, y-, and z-components of `in`, respectively.
+
+---
+
+### separate4
+
+Split the channels of a 4-channel stream into separate float outputs.
+
+@MX_TABLE_separate4@
+
+When the input `in` is a color4, `outr`, `outg`, `outb`, and `outa` correspond to the r-, g-, b-, and alpha components of `in`, respectively.
+
+When the input `in` is a vector4, `outx`, `outy`, `outz`, and `outw` correspond to the x-, y-, z-, and w-components of `in`, respectively.
+
+---
+
+### combine2
+
+Combine the channels from two streams into the same number of channels of a single output stream of a compatible type.
+
+@MX_TABLE_combine2@
+
+---
+
+### combine3
+
+Combine the channels from three streams into the same number of channels of a single output stream of a compatible type.
+
+@MX_TABLE_combine3@
+
+---
+
+### combine4
+
+Combine the channels from four streams into the same number of channels of a single output stream of a compatible type.
+
+@MX_TABLE_combine4@
+
+
+## Convolution Nodes
+
+Convolution nodes have one input named `in`, and apply a defined convolution function to the input stream.
+
+### blur
+
+Applies a convolution blur to the input stream.
+
+@MX_TABLE_blur@
+
+#### Filter Types
+
+|Name |Description |
+|--------------|----------------------|
+|"box" |Linear box filter |
+|"gaussian" |Gaussian smoothing |
+
+#### Box Filter
+
+Blurs the input, based on the average of neighboring values:
+
+$$B = sum_{i=1}^sc a_i = a_1 + a_2 + ... + a_sc$$
+
+#### Gaussian Filter
+
+Applies a gaussian filter to the input:
+
+$$G(u,v) = \frac{1}{2 \pi \sigma ^2} e ^{- \frac{u^2 + v^2}{2 \sigma ^2}}$$
+
+---
+
+### heighttonormal
+
+Convert a scalar height map to a tangent-space normal map of type `vector3`. The output normal map is encoded with all channels in the [0-1] range, enabling its storage in unsigned image formats.
+
+@MX_TABLE_heighttonormal@
+
+Let the scalar values be represented as a function $h(u, v)$, where $(u, v)$ are coordinates of the image.
+
+The partial derivatives of the height field are:
+$$\frac{\partial h}{\partial u} \text{ and } \frac{\partial h}{\partial v}$$
+
+The normal vector $\vec{n}$ at each point $(x, y)$ can be calculated as:
+$$N = \left(-\frac{\partial h}{\partial u}, -\frac{\partial h}{\partial v}, 1\right)$$
+
+To normalize the normal vector, we divide it by its magnitude:
+$$N = \frac{1}{\sqrt{1 + \left(\frac{\partial h}{\partial u}\right)^2 + \left(\frac{\partial h}{\partial v}\right)^2}} \left(-\frac{\partial h}{\partial u}, -\frac{\partial h}{\partial v}, 1\right)$$
+
+# Logical Operator Nodes
+
+## and
+logically AND the two input boolean values
+
+@MX_TABLE_and@
+
+Output the boolean value resulting from the logical AND of the input values.
+
+---
+
+## or
+logically Inclusive OR the two input boolean values
+
+@MX_TABLE_or@
+
+Output the boolean value resulting from the logical Inclusive OR of the input values.
+
+---
+
+## xor
+logically Exclusive OR the two input boolean values
+
+@MX_TABLE_xor@
+
+Output the boolean value resulting from the logical Exclusive OR of the input values.
+
+---
+
+## not
+logically NOT the input boolean value
+
+@MX_TABLE_not@
+
+Output the boolean value resulting from the logical NOT of the input value.
+
+## Organization Nodes
+
+### dot
+
+
+@MX_TABLE_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 or other nodes with uniform="true" outputs to uniform s and s.
\ No newline at end of file
diff --git a/libraries/stdlib/stdlib_defs.mtlx b/libraries/stdlib/stdlib_defs.mtlx
index d99125b635..cb66a3eb7f 100644
--- a/libraries/stdlib/stdlib_defs.mtlx
+++ b/libraries/stdlib/stdlib_defs.mtlx
@@ -802,18 +802,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -832,6 +820,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -873,18 +875,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -903,6 +893,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -946,46 +950,48 @@
-
-
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
+
-
+
-
+
-
+
+
@@ -1039,46 +1045,48 @@
-
-
+
+
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
+
-
+
-
+
-
+
+
@@ -1249,10 +1257,10 @@
-
-
-
-
+
+
+
+
@@ -1630,16 +1638,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -1665,11 +1663,22 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -1715,16 +1724,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -1750,11 +1749,21 @@
+
+
+
+
+
+
+
+
+
+
@@ -1796,16 +1805,6 @@
-
-
-
-
-
-
-
-
-
-
@@ -1831,6 +1830,16 @@
+
+
+
+
+
+
+
+
+
+
-
+
@@ -3427,7 +3436,7 @@
-
+
@@ -3759,60 +3768,30 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -3831,6 +3810,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -3892,22 +3901,17 @@
-
-
+
+
-
-
+
+
-
-
-
-
-
@@ -3960,17 +3964,22 @@
-
-
+
+
-
-
+
+
+
+
+
+
+
@@ -4033,22 +4042,17 @@
-
-
+
+
-
-
+
+
-
-
-
-
-
@@ -4101,17 +4105,22 @@
-
-
+
+
-
-
+
+
+
+
+
+
+
@@ -4174,22 +4183,17 @@
-
-
+
+
-
-
+
+
-
-
-
-
-
@@ -4242,22 +4246,17 @@
-
-
+
+
-
-
+
+
-
-
-
-
-
@@ -4310,17 +4309,27 @@
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -4416,30 +4425,30 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -4528,30 +4537,30 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -4729,28 +4738,28 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
+
@@ -4834,7 +4843,7 @@
-
+
@@ -4842,7 +4851,7 @@
-
+
@@ -4850,7 +4859,7 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/genglsl/gltf_pbr.mtlx b/templates/bxdf/genglsl/gltf_pbr.mtlx
new file mode 100644
index 0000000000..73504326bc
--- /dev/null
+++ b/templates/bxdf/genglsl/gltf_pbr.mtlx
@@ -0,0 +1,330 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/genglsl/open_pbr_surface.mtlx b/templates/bxdf/genglsl/open_pbr_surface.mtlx
new file mode 100644
index 0000000000..c57d30fe1a
--- /dev/null
+++ b/templates/bxdf/genglsl/open_pbr_surface.mtlx
@@ -0,0 +1,597 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/genglsl/standard_surface.mtlx b/templates/bxdf/genglsl/standard_surface.mtlx
new file mode 100644
index 0000000000..bedae8e366
--- /dev/null
+++ b/templates/bxdf/genglsl/standard_surface.mtlx
@@ -0,0 +1,343 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/genmdl/open_pbr_surface.mtlx b/templates/bxdf/genmdl/open_pbr_surface.mtlx
new file mode 100644
index 0000000000..bbdb1cc9bc
--- /dev/null
+++ b/templates/bxdf/genmdl/open_pbr_surface.mtlx
@@ -0,0 +1,594 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/gltf_pbr.mtlx b/templates/bxdf/gltf_pbr.mtlx
new file mode 100644
index 0000000000..8dde49ca9e
--- /dev/null
+++ b/templates/bxdf/gltf_pbr.mtlx
@@ -0,0 +1,815 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_add.mtlx b/templates/bxdf/lama/lama_add.mtlx
new file mode 100644
index 0000000000..d589e366e6
--- /dev/null
+++ b/templates/bxdf/lama/lama_add.mtlx
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_conductor.mtlx b/templates/bxdf/lama/lama_conductor.mtlx
new file mode 100644
index 0000000000..d3a8cb765f
--- /dev/null
+++ b/templates/bxdf/lama/lama_conductor.mtlx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_dielectric.mtlx b/templates/bxdf/lama/lama_dielectric.mtlx
new file mode 100644
index 0000000000..1aa75e5d5d
--- /dev/null
+++ b/templates/bxdf/lama/lama_dielectric.mtlx
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_diffuse.mtlx b/templates/bxdf/lama/lama_diffuse.mtlx
new file mode 100644
index 0000000000..358c728e39
--- /dev/null
+++ b/templates/bxdf/lama/lama_diffuse.mtlx
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_emission.mtlx b/templates/bxdf/lama/lama_emission.mtlx
new file mode 100644
index 0000000000..3f731442c1
--- /dev/null
+++ b/templates/bxdf/lama/lama_emission.mtlx
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_generalized_schlick.mtlx b/templates/bxdf/lama/lama_generalized_schlick.mtlx
new file mode 100644
index 0000000000..84fa57cd94
--- /dev/null
+++ b/templates/bxdf/lama/lama_generalized_schlick.mtlx
@@ -0,0 +1,172 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_iridescence.mtlx b/templates/bxdf/lama/lama_iridescence.mtlx
new file mode 100644
index 0000000000..7728f96d10
--- /dev/null
+++ b/templates/bxdf/lama/lama_iridescence.mtlx
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_layer.mtlx b/templates/bxdf/lama/lama_layer.mtlx
new file mode 100644
index 0000000000..921ec36933
--- /dev/null
+++ b/templates/bxdf/lama/lama_layer.mtlx
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_mix.mtlx b/templates/bxdf/lama/lama_mix.mtlx
new file mode 100644
index 0000000000..69e6ad3ec6
--- /dev/null
+++ b/templates/bxdf/lama/lama_mix.mtlx
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_sheen.mtlx b/templates/bxdf/lama/lama_sheen.mtlx
new file mode 100644
index 0000000000..8accb41837
--- /dev/null
+++ b/templates/bxdf/lama/lama_sheen.mtlx
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_sss.mtlx b/templates/bxdf/lama/lama_sss.mtlx
new file mode 100644
index 0000000000..f09d2da51d
--- /dev/null
+++ b/templates/bxdf/lama/lama_sss.mtlx
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_surface.mtlx b/templates/bxdf/lama/lama_surface.mtlx
new file mode 100644
index 0000000000..b7e84a510c
--- /dev/null
+++ b/templates/bxdf/lama/lama_surface.mtlx
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/lama/lama_translucent.mtlx b/templates/bxdf/lama/lama_translucent.mtlx
new file mode 100644
index 0000000000..ec3e968e09
--- /dev/null
+++ b/templates/bxdf/lama/lama_translucent.mtlx
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/open_pbr_surface.mtlx b/templates/bxdf/open_pbr_surface.mtlx
new file mode 100644
index 0000000000..87084bf7fe
--- /dev/null
+++ b/templates/bxdf/open_pbr_surface.mtlx
@@ -0,0 +1,676 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/standard_surface.mtlx b/templates/bxdf/standard_surface.mtlx
new file mode 100644
index 0000000000..495958448d
--- /dev/null
+++ b/templates/bxdf/standard_surface.mtlx
@@ -0,0 +1,431 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/translation/open_pbr_to_standard_surface.mtlx b/templates/bxdf/translation/open_pbr_to_standard_surface.mtlx
new file mode 100644
index 0000000000..ec7bb19a98
--- /dev/null
+++ b/templates/bxdf/translation/open_pbr_to_standard_surface.mtlx
@@ -0,0 +1,362 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/translation/standard_surface_to_gltf_pbr.mtlx b/templates/bxdf/translation/standard_surface_to_gltf_pbr.mtlx
new file mode 100644
index 0000000000..fb82cc71c0
--- /dev/null
+++ b/templates/bxdf/translation/standard_surface_to_gltf_pbr.mtlx
@@ -0,0 +1,209 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/translation/standard_surface_to_open_pbr.mtlx b/templates/bxdf/translation/standard_surface_to_open_pbr.mtlx
new file mode 100644
index 0000000000..535f65b700
--- /dev/null
+++ b/templates/bxdf/translation/standard_surface_to_open_pbr.mtlx
@@ -0,0 +1,309 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/translation/standard_surface_to_usd.mtlx b/templates/bxdf/translation/standard_surface_to_usd.mtlx
new file mode 100644
index 0000000000..c92038c0c7
--- /dev/null
+++ b/templates/bxdf/translation/standard_surface_to_usd.mtlx
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/bxdf/usd_preview_surface.mtlx b/templates/bxdf/usd_preview_surface.mtlx
new file mode 100644
index 0000000000..23a01462a7
--- /dev/null
+++ b/templates/bxdf/usd_preview_surface.mtlx
@@ -0,0 +1,438 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/cmlib/cmlib_defs.mtlx b/templates/cmlib/cmlib_defs.mtlx
new file mode 100644
index 0000000000..0f73109ea7
--- /dev/null
+++ b/templates/cmlib/cmlib_defs.mtlx
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/cmlib/cmlib_ng.mtlx b/templates/cmlib/cmlib_ng.mtlx
new file mode 100644
index 0000000000..7271bd944b
--- /dev/null
+++ b/templates/cmlib/cmlib_ng.mtlx
@@ -0,0 +1,392 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/lights/genglsl/lights_genglsl_impl.mtlx b/templates/lights/genglsl/lights_genglsl_impl.mtlx
new file mode 100644
index 0000000000..d9c2d0305b
--- /dev/null
+++ b/templates/lights/genglsl/lights_genglsl_impl.mtlx
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/lights/genglsl/mx_directional_light.glsl b/templates/lights/genglsl/mx_directional_light.glsl
new file mode 100644
index 0000000000..634c5fe99f
--- /dev/null
+++ b/templates/lights/genglsl/mx_directional_light.glsl
@@ -0,0 +1,5 @@
+void mx_directional_light(LightData light, vec3 position, out lightshader result)
+{
+ result.direction = -light.direction;
+ result.intensity = light.color * light.intensity;
+}
diff --git a/templates/lights/genglsl/mx_point_light.glsl b/templates/lights/genglsl/mx_point_light.glsl
new file mode 100644
index 0000000000..c94b2225aa
--- /dev/null
+++ b/templates/lights/genglsl/mx_point_light.glsl
@@ -0,0 +1,8 @@
+void mx_point_light(LightData light, vec3 position, out lightshader result)
+{
+ result.direction = light.position - position;
+ float distance = length(result.direction) + M_FLOAT_EPS;
+ float attenuation = pow(distance + 1.0, light.decay_rate + M_FLOAT_EPS);
+ result.intensity = light.color * light.intensity / attenuation;
+ result.direction /= distance;
+}
diff --git a/templates/lights/genglsl/mx_spot_light.glsl b/templates/lights/genglsl/mx_spot_light.glsl
new file mode 100644
index 0000000000..b3607844cd
--- /dev/null
+++ b/templates/lights/genglsl/mx_spot_light.glsl
@@ -0,0 +1,13 @@
+void mx_spot_light(LightData light, vec3 position, out lightshader result)
+{
+ result.direction = light.position - position;
+ float distance = length(result.direction) + M_FLOAT_EPS;
+ float attenuation = pow(distance + 1.0, light.decay_rate + M_FLOAT_EPS);
+ result.intensity = light.color * light.intensity / attenuation;
+ result.direction /= distance;
+ float low = min(light.inner_angle, light.outer_angle);
+ float high = light.inner_angle;
+ float cosDir = dot(result.direction, -light.direction);
+ float spotAttenuation = smoothstep(low, high, cosDir);
+ result.intensity *= spotAttenuation;
+}
diff --git a/templates/lights/genmsl/lights_genmsl_impl.mtlx b/templates/lights/genmsl/lights_genmsl_impl.mtlx
new file mode 100644
index 0000000000..deba24290a
--- /dev/null
+++ b/templates/lights/genmsl/lights_genmsl_impl.mtlx
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/lights/genmsl/mx_directional_light.metal b/templates/lights/genmsl/mx_directional_light.metal
new file mode 100644
index 0000000000..8f90b4e807
--- /dev/null
+++ b/templates/lights/genmsl/mx_directional_light.metal
@@ -0,0 +1,5 @@
+void mx_directional_light(LightData light, float3 position, thread lightshader& result)
+{
+ result.direction = -light.direction;
+ result.intensity = light.color * light.intensity;
+}
diff --git a/templates/lights/genmsl/mx_point_light.metal b/templates/lights/genmsl/mx_point_light.metal
new file mode 100644
index 0000000000..b4d5b1292e
--- /dev/null
+++ b/templates/lights/genmsl/mx_point_light.metal
@@ -0,0 +1,8 @@
+void mx_point_light(LightData light, float3 position, thread lightshader& result)
+{
+ result.direction = light.position - position;
+ float distance = length(result.direction) + M_FLOAT_EPS;
+ float attenuation = pow(distance + 1.0, light.decay_rate + M_FLOAT_EPS);
+ result.intensity = light.color * light.intensity / attenuation;
+ result.direction /= distance;
+}
diff --git a/templates/lights/genmsl/mx_spot_light.metal b/templates/lights/genmsl/mx_spot_light.metal
new file mode 100644
index 0000000000..cfcc646c42
--- /dev/null
+++ b/templates/lights/genmsl/mx_spot_light.metal
@@ -0,0 +1,13 @@
+void mx_spot_light(LightData light, float3 position, thread lightshader& result)
+{
+ result.direction = light.position - position;
+ float distance = length(result.direction) + M_FLOAT_EPS;
+ float attenuation = pow(distance + 1.0, light.decay_rate + M_FLOAT_EPS);
+ result.intensity = light.color * light.intensity / attenuation;
+ result.direction /= distance;
+ float low = min(light.inner_angle, light.outer_angle);
+ float high = light.inner_angle;
+ float cosDir = dot(result.direction, -light.direction);
+ float spotAttenuation = smoothstep(low, high, cosDir);
+ result.intensity *= spotAttenuation;
+}
diff --git a/templates/lights/lights_defs.mtlx b/templates/lights/lights_defs.mtlx
new file mode 100644
index 0000000000..d0a3d49e5e
--- /dev/null
+++ b/templates/lights/lights_defs.mtlx
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/nprlib/genglsl/nprlib_genglsl_impl.mtlx b/templates/nprlib/genglsl/nprlib_genglsl_impl.mtlx
new file mode 100644
index 0000000000..dd9700b340
--- /dev/null
+++ b/templates/nprlib/genglsl/nprlib_genglsl_impl.mtlx
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/nprlib/genmdl/nprlib_genmdl_impl.mtlx b/templates/nprlib/genmdl/nprlib_genmdl_impl.mtlx
new file mode 100644
index 0000000000..105751fd2b
--- /dev/null
+++ b/templates/nprlib/genmdl/nprlib_genmdl_impl.mtlx
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/nprlib/genmsl/nprlib_genmsl_impl.mtlx b/templates/nprlib/genmsl/nprlib_genmsl_impl.mtlx
new file mode 100644
index 0000000000..28e470b905
--- /dev/null
+++ b/templates/nprlib/genmsl/nprlib_genmsl_impl.mtlx
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/nprlib/genosl/nprlib_genosl_impl.mtlx b/templates/nprlib/genosl/nprlib_genosl_impl.mtlx
new file mode 100644
index 0000000000..9b3d3617ac
--- /dev/null
+++ b/templates/nprlib/genosl/nprlib_genosl_impl.mtlx
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/nprlib/nprlib_defs.mtlx b/templates/nprlib/nprlib_defs.mtlx
new file mode 100644
index 0000000000..9e50a1619e
--- /dev/null
+++ b/templates/nprlib/nprlib_defs.mtlx
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/nprlib/nprlib_ng.mtlx b/templates/nprlib/nprlib_ng.mtlx
new file mode 100644
index 0000000000..e0a0d34e15
--- /dev/null
+++ b/templates/nprlib/nprlib_ng.mtlx
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/pbrlib/genglsl/lib/mx_closure_type.glsl b/templates/pbrlib/genglsl/lib/mx_closure_type.glsl
new file mode 100644
index 0000000000..8f337cc0b7
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_closure_type.glsl
@@ -0,0 +1,17 @@
+// These are defined based on the HwShaderGenerator::ClosureContextType enum
+// if that changes - these need to be updated accordingly.
+
+#define CLOSURE_TYPE_DEFAULT 0
+#define CLOSURE_TYPE_REFLECTION 1
+#define CLOSURE_TYPE_TRANSMISSION 2
+#define CLOSURE_TYPE_INDIRECT 3
+#define CLOSURE_TYPE_EMISSION 4
+
+struct ClosureData {
+ int closureType;
+ vec3 L;
+ vec3 V;
+ vec3 N;
+ vec3 P;
+ float occlusion;
+};
diff --git a/templates/pbrlib/genglsl/lib/mx_environment_fis.glsl b/templates/pbrlib/genglsl/lib/mx_environment_fis.glsl
new file mode 100644
index 0000000000..1d61576650
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_environment_fis.glsl
@@ -0,0 +1,69 @@
+#include "mx_microfacet_specular.glsl"
+
+vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd)
+{
+ // Generate tangent frame.
+ X = normalize(X - dot(X, N) * N);
+ vec3 Y = cross(N, X);
+ mat3 tangentToWorld = mat3(X, Y, N);
+
+ // Transform the view vector to tangent space.
+ V = vec3(dot(V, X), dot(V, Y), dot(V, N));
+
+ // 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
+ vec3 radiance = vec3(0.0);
+ int envRadianceSamples = $envRadianceSamples;
+ for (int i = 0; i < envRadianceSamples; i++)
+ {
+ vec2 Xi = mx_spherical_fibonacci(i, envRadianceSamples);
+
+ // Compute the half vector and incoming light direction.
+ 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 NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ // Sample the environment light from the given direction.
+ vec3 Lw = tangentToWorld * L;
+ 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);
+
+ // Compute the Fresnel term.
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+
+ // Compute the geometric term.
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ // Compute the combined FG term, which simplifies to inverted Fresnel for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - F : F * G;
+
+ // Add the radiance contribution of this sample.
+ // From https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
+ // incidentLight = sampleColor * NdotL
+ // microfacetSpecular = D * F * G / (4 * NdotL * NdotV)
+ // pdf = D * G1V / (4 * NdotV);
+ // radiance = incidentLight * microfacetSpecular / pdf
+ radiance += sampleColor * FG;
+ }
+
+ // Apply the global component of the geometric term and normalize.
+ radiance /= G1V * float(envRadianceSamples);
+
+ // Return the final radiance.
+ return radiance * $envLightIntensity;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ vec3 Li = mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance);
+ return Li * $envLightIntensity;
+}
diff --git a/templates/pbrlib/genglsl/lib/mx_environment_none.glsl b/templates/pbrlib/genglsl/lib/mx_environment_none.glsl
new file mode 100644
index 0000000000..f0a1da5989
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_environment_none.glsl
@@ -0,0 +1,11 @@
+#include "mx_microfacet_specular.glsl"
+
+vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 roughness, int distribution, FresnelData fd)
+{
+ return vec3(0.0);
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return vec3(0.0);
+}
diff --git a/templates/pbrlib/genglsl/lib/mx_environment_prefilter.glsl b/templates/pbrlib/genglsl/lib/mx_environment_prefilter.glsl
new file mode 100644
index 0000000000..778742c449
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_environment_prefilter.glsl
@@ -0,0 +1,30 @@
+#include "mx_microfacet_specular.glsl"
+
+// Return the mip level associated with the given alpha in a prefiltered environment.
+float mx_latlong_alpha_to_lod(float alpha)
+{
+ float lodBias = (alpha < 0.25) ? sqrt(alpha) : 0.5 * alpha + 0.375;
+ return lodBias * float($envRadianceMips - 1);
+}
+
+vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd)
+{
+ N = mx_forward_facing_normal(N, V);
+ vec3 L = fd.refraction ? mx_refraction_solid_sphere(-V, N, fd.ior.x) : -reflect(V, N);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float avgAlpha = mx_average_alpha(alpha);
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+ float G = mx_ggx_smith_G2(NdotV, NdotV, avgAlpha);
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : F * G;
+
+ vec3 Li = mx_latlong_map_lookup(L, $envMatrix, mx_latlong_alpha_to_lod(avgAlpha), $envRadiance);
+ return Li * FG * $envLightIntensity;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ vec3 Li = mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance);
+ return Li * $envLightIntensity;
+}
diff --git a/templates/pbrlib/genglsl/lib/mx_generate_albedo_table.glsl b/templates/pbrlib/genglsl/lib/mx_generate_albedo_table.glsl
new file mode 100644
index 0000000000..5d310e90a6
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_generate_albedo_table.glsl
@@ -0,0 +1,10 @@
+#include "mx_microfacet_sheen.glsl"
+#include "mx_microfacet_specular.glsl"
+
+vec3 mx_generate_dir_albedo_table()
+{
+ vec2 uv = gl_FragCoord.xy / $albedoTableSize;
+ vec2 ggxDirAlbedo = mx_ggx_dir_albedo(uv.x, uv.y, vec3(1, 0, 0), vec3(0, 1, 0)).xy;
+ float sheenDirAlbedo = mx_imageworks_sheen_dir_albedo(uv.x, uv.y);
+ return vec3(ggxDirAlbedo, sheenDirAlbedo);
+}
diff --git a/templates/pbrlib/genglsl/lib/mx_generate_prefilter_env.glsl b/templates/pbrlib/genglsl/lib/mx_generate_prefilter_env.glsl
new file mode 100644
index 0000000000..43d4cbda79
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_generate_prefilter_env.glsl
@@ -0,0 +1,66 @@
+#include "mx_microfacet_specular.glsl"
+
+// Return the alpha associated with the given mip level in a prefiltered environment.
+float mx_latlong_lod_to_alpha(float lod)
+{
+ float lodBias = lod / float($envRadianceMips - 1);
+ return (lodBias < 0.5) ? mx_square(lodBias) : 2.0 * (lodBias - 0.375);
+}
+
+// The inverse of mx_latlong_projection.
+vec3 mx_latlong_map_projection_inverse(vec2 uv)
+{
+ float latitude = (uv.y - 0.5) * M_PI;
+ float longitude = (uv.x - 0.5) * M_PI * 2.0;
+
+ float x = -mx_cos(latitude) * mx_sin(longitude);
+ float y = -mx_sin(latitude);
+ float z = mx_cos(latitude) * mx_cos(longitude);
+
+ return vec3(x, y, z);
+}
+
+vec3 mx_generate_prefilter_env()
+{
+ // The tangent view vector is aligned with the normal.
+ vec3 V = vec3(0.0, 0.0, 1.0);
+ float NdotV = 1.0;
+
+ // Compute derived properties.
+ vec2 uv = gl_FragCoord.xy * pow(2.0, $envPrefilterMip) / vec2(textureSize($envRadianceSampler2D, 0));
+ vec3 worldN = mx_latlong_map_projection_inverse(uv);
+ mat3 tangentToWorld = mx_orthonormal_basis(worldN);
+ float alpha = mx_latlong_lod_to_alpha(float($envPrefilterMip));
+ float G1V = mx_ggx_smith_G1(NdotV, alpha);
+
+ // Integrate the LD term for the given environment and alpha.
+ vec3 radiance = vec3(0.0, 0.0, 0.0);
+ float weight = 0.0;
+ int envRadianceSamples = 1024;
+ for (int i = 0; i < envRadianceSamples; i++)
+ {
+ vec2 Xi = mx_spherical_fibonacci(i, envRadianceSamples);
+
+ // Compute the half vector and incoming light direction.
+ vec3 H = mx_ggx_importance_sample_VNDF(Xi, V, vec2(alpha));
+ vec3 L = -V + 2.0 * H.z * H;
+
+ // Compute dot products for this sample.
+ float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
+
+ // Compute the geometric term.
+ float G = mx_ggx_smith_G2(NdotL, NdotV, alpha);
+
+ // Sample the environment light from the given direction.
+ vec3 Lw = tangentToWorld * L;
+ float pdf = mx_ggx_NDF(H, vec2(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);
+
+ // Add the radiance contribution of this sample.
+ radiance += G * sampleColor;
+ weight += G;
+ }
+
+ return radiance / weight;
+}
diff --git a/templates/pbrlib/genglsl/lib/mx_microfacet.glsl b/templates/pbrlib/genglsl/lib/mx_microfacet.glsl
new file mode 100644
index 0000000000..05c12e907c
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_microfacet.glsl
@@ -0,0 +1,106 @@
+#define M_PI 3.1415926535897932
+#define M_PI_INV (1.0 / M_PI)
+
+float mx_pow5(float x)
+{
+ return mx_square(mx_square(x)) * x;
+}
+
+float mx_pow6(float x)
+{
+ float x2 = mx_square(x);
+ return mx_square(x2) * x2;
+}
+
+// Standard Schlick Fresnel
+float mx_fresnel_schlick(float cosTheta, float F0)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ float x5 = mx_pow5(x);
+ return F0 + (1.0 - F0) * x5;
+}
+vec3 mx_fresnel_schlick(float cosTheta, vec3 F0)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ float x5 = mx_pow5(x);
+ return F0 + (1.0 - F0) * x5;
+}
+
+// Generalized Schlick Fresnel
+float mx_fresnel_schlick(float cosTheta, float F0, float F90)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ float x5 = mx_pow5(x);
+ return mix(F0, F90, x5);
+}
+vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ float x5 = mx_pow5(x);
+ return mix(F0, F90, x5);
+}
+
+// Generalized Schlick Fresnel with a variable exponent
+float mx_fresnel_schlick(float cosTheta, float F0, float F90, float exponent)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ return mix(F0, F90, pow(x, exponent));
+}
+vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90, float exponent)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ return mix(F0, F90, pow(x, exponent));
+}
+
+// Enforce that the given normal is forward-facing from the specified view direction.
+vec3 mx_forward_facing_normal(vec3 N, vec3 V)
+{
+ return (dot(N, V) < 0.0) ? -N : N;
+}
+
+// https://www.graphics.rwth-aachen.de/publication/2/jgt.pdf
+float mx_golden_ratio_sequence(int i)
+{
+ const float GOLDEN_RATIO = 1.6180339887498948;
+ return fract((float(i) + 1.0) * GOLDEN_RATIO);
+}
+
+// https://people.irisa.fr/Ricardo.Marques/articles/2013/SF_CGF.pdf
+vec2 mx_spherical_fibonacci(int i, int numSamples)
+{
+ return vec2((float(i) + 0.5) / float(numSamples), mx_golden_ratio_sequence(i));
+}
+
+// Generate a uniform-weighted sample on the unit hemisphere.
+vec3 mx_uniform_sample_hemisphere(vec2 Xi)
+{
+ float phi = 2.0 * M_PI * Xi.x;
+ float cosTheta = 1.0 - Xi.y;
+ float sinTheta = sqrt(1.0 - mx_square(cosTheta));
+ return vec3(mx_cos(phi) * sinTheta,
+ mx_sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Generate a cosine-weighted sample on the unit hemisphere.
+vec3 mx_cosine_sample_hemisphere(vec2 Xi)
+{
+ float phi = 2.0 * M_PI * Xi.x;
+ float cosTheta = sqrt(Xi.y);
+ float sinTheta = sqrt(1.0 - Xi.y);
+ return vec3(mx_cos(phi) * sinTheta,
+ mx_sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Construct an orthonormal basis from a unit vector.
+// https://graphics.pixar.com/library/OrthonormalB/paper.pdf
+mat3 mx_orthonormal_basis(vec3 N)
+{
+ float sign = (N.z < 0.0) ? -1.0 : 1.0;
+ float a = -1.0 / (sign + N.z);
+ float b = N.x * N.y * a;
+ vec3 X = vec3(1.0 + sign * N.x * N.x * a, sign * b, -sign * N.x);
+ vec3 Y = vec3(b, sign + N.y * N.y * a, -N.y);
+ return mat3(X, Y, N);
+}
diff --git a/templates/pbrlib/genglsl/lib/mx_microfacet_diffuse.glsl b/templates/pbrlib/genglsl/lib/mx_microfacet_diffuse.glsl
new file mode 100644
index 0000000000..f26c15df37
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_microfacet_diffuse.glsl
@@ -0,0 +1,199 @@
+#include "mx_microfacet.glsl"
+
+const float FUJII_CONSTANT_1 = 0.5 - 2.0 / (3.0 * M_PI);
+const float FUJII_CONSTANT_2 = 2.0 / 3.0 - 28.0 / (15.0 * M_PI);
+
+// Qualitative Oren-Nayar diffuse with simplified math:
+// https://www1.cs.columbia.edu/CAVE/publications/pdfs/Oren_SIGGRAPH94.pdf
+float mx_oren_nayar_diffuse(float NdotV, float NdotL, float LdotV, float roughness)
+{
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// Rational quadratic fit to Monte Carlo data for Oren-Nayar directional albedo.
+float mx_oren_nayar_diffuse_dir_albedo_analytic(float NdotV, float roughness)
+{
+ vec2 r = vec2(1.0, 1.0) +
+ vec2(-0.4297, -0.6076) * roughness +
+ vec2(-0.7632, -0.4993) * NdotV * roughness +
+ vec2(1.4385, 2.0315) * mx_square(roughness);
+ return r.x / r.y;
+}
+
+float mx_oren_nayar_diffuse_dir_albedo_table_lookup(float NdotV, float roughness)
+{
+#if DIRECTIONAL_ALBEDO_METHOD == 1
+ if (textureSize($albedoTable, 0).x > 1)
+ {
+ return texture($albedoTable, vec2(NdotV, roughness)).b;
+ }
+#endif
+ return 0.0;
+}
+
+float mx_oren_nayar_diffuse_dir_albedo_monte_carlo(float NdotV, float roughness)
+{
+ NdotV = clamp(NdotV, M_FLOAT_EPS, 1.0);
+ vec3 V = vec3(sqrt(1.0 - mx_square(NdotV)), 0, NdotV);
+
+ float radiance = 0.0;
+ const int SAMPLE_COUNT = 64;
+ for (int i = 0; i < SAMPLE_COUNT; i++)
+ {
+ vec2 Xi = mx_spherical_fibonacci(i, SAMPLE_COUNT);
+
+ // Compute the incoming light direction.
+ vec3 L = mx_uniform_sample_hemisphere(Xi);
+
+ // Compute dot products for this sample.
+ float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+
+ // Compute diffuse reflectance.
+ float reflectance = mx_oren_nayar_diffuse(NdotV, NdotL, LdotV, roughness);
+
+ // Add the radiance contribution of this sample.
+ // uniform_pdf = 1 / (2 * PI)
+ // radiance = (reflectance * NdotL) / (uniform_pdf * PI);
+ radiance += reflectance * NdotL;
+ }
+
+ // Apply global components and normalize.
+ radiance *= 2.0 / float(SAMPLE_COUNT);
+
+ // Return the final directional albedo.
+ return radiance;
+}
+
+float mx_oren_nayar_diffuse_dir_albedo(float NdotV, float roughness)
+{
+#if DIRECTIONAL_ALBEDO_METHOD == 2
+ float dirAlbedo = mx_oren_nayar_diffuse_dir_albedo_monte_carlo(NdotV, roughness);
+#else
+ float dirAlbedo = mx_oren_nayar_diffuse_dir_albedo_analytic(NdotV, roughness);
+#endif
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// Improved Oren-Nayar diffuse from Fujii:
+// https://mimosa-pudica.net/improved-oren-nayar.html
+float mx_oren_nayar_fujii_diffuse_dir_albedo(float cosTheta, float roughness)
+{
+ float A = 1.0 / (1.0 + FUJII_CONSTANT_1 * roughness);
+ float B = roughness * A;
+ float Si = sqrt(max(0.0, 1.0 - mx_square(cosTheta)));
+ float G = Si * (mx_acos(clamp(cosTheta, -1.0, 1.0)) - Si * cosTheta) +
+ 2.0 * ((Si / cosTheta) * (1.0 - Si * Si * Si) - Si) / 3.0;
+ return A + (B * G * M_PI_INV);
+}
+
+float mx_oren_nayar_fujii_diffuse_avg_albedo(float roughness)
+{
+ float A = 1.0 / (1.0 + FUJII_CONSTANT_1 * roughness);
+ return A * (1.0 + FUJII_CONSTANT_2 * roughness);
+}
+
+// Energy-compensated Oren-Nayar diffuse from OpenPBR Surface:
+// https://academysoftwarefoundation.github.io/OpenPBR/
+vec3 mx_oren_nayar_compensated_diffuse(float NdotV, float NdotL, float LdotV, float roughness, vec3 color)
+{
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0) ? s / max(NdotL, NdotV) : s;
+
+ // Compute the single-scatter lobe.
+ float A = 1.0 / (1.0 + FUJII_CONSTANT_1 * roughness);
+ vec3 lobeSingleScatter = color * A * (1.0 + roughness * stinv);
+
+ // Compute the multi-scatter lobe.
+ float dirAlbedoV = mx_oren_nayar_fujii_diffuse_dir_albedo(NdotV, roughness);
+ float dirAlbedoL = mx_oren_nayar_fujii_diffuse_dir_albedo(NdotL, roughness);
+ float avgAlbedo = mx_oren_nayar_fujii_diffuse_avg_albedo(roughness);
+ vec3 colorMultiScatter = mx_square(color) * avgAlbedo /
+ (vec3(1.0) - color * max(0.0, 1.0 - avgAlbedo));
+ vec3 lobeMultiScatter = colorMultiScatter *
+ max(M_FLOAT_EPS, 1.0 - dirAlbedoV) *
+ max(M_FLOAT_EPS, 1.0 - dirAlbedoL) /
+ max(M_FLOAT_EPS, 1.0 - avgAlbedo);
+
+ // Return the sum.
+ return lobeSingleScatter + lobeMultiScatter;
+}
+
+vec3 mx_oren_nayar_compensated_diffuse_dir_albedo(float cosTheta, float roughness, vec3 color)
+{
+ float dirAlbedo = mx_oren_nayar_fujii_diffuse_dir_albedo(cosTheta, roughness);
+ float avgAlbedo = mx_oren_nayar_fujii_diffuse_avg_albedo(roughness);
+ vec3 colorMultiScatter = mx_square(color) * avgAlbedo /
+ (vec3(1.0) - color * max(0.0, 1.0 - avgAlbedo));
+ return mix(colorMultiScatter, color, dirAlbedo);
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(float NdotV, float NdotL, float LdotH, float roughness)
+{
+ float F90 = 0.5 + (2.0 * roughness * mx_square(LdotH));
+ float refL = mx_fresnel_schlick(NdotL, 1.0, F90);
+ float refV = mx_fresnel_schlick(NdotV, 1.0, F90);
+ return refL * refV;
+}
+
+// Compute the directional albedo component of Burley diffuse for the given
+// view angle and roughness. Curve fit provided by Stephen Hill.
+float mx_burley_diffuse_dir_albedo(float NdotV, float roughness)
+{
+ float x = NdotV;
+ float fit0 = 0.97619 - 0.488095 * mx_pow5(1.0 - x);
+ float fit1 = 1.55754 + (-2.02221 + (2.56283 - 1.06244 * x) * x) * x;
+ return mix(fit0, fit1, roughness);
+}
+
+// Evaluate the Burley diffusion profile for the given distance and diffusion shape.
+// Based on https://graphics.pixar.com/library/ApproxBSSRDF/
+vec3 mx_burley_diffusion_profile(float dist, vec3 shape)
+{
+ vec3 num1 = exp(-shape * dist);
+ vec3 num2 = exp(-shape * dist / 3.0);
+ float denom = max(dist, M_FLOAT_EPS);
+ return (num1 + num2) / denom;
+}
+
+// Integrate the Burley diffusion profile over a sphere of the given radius.
+// Inspired by Eric Penner's presentation in http://advances.realtimerendering.com/s2011/
+vec3 mx_integrate_burley_diffusion(vec3 N, vec3 L, float radius, vec3 mfp)
+{
+ float theta = mx_acos(dot(N, L));
+
+ // Estimate the Burley diffusion shape from mean free path.
+ vec3 shape = vec3(1.0) / max(mfp, 0.1);
+
+ // Integrate the profile over the sphere.
+ vec3 sumD = vec3(0.0);
+ vec3 sumR = vec3(0.0);
+ const int SAMPLE_COUNT = 32;
+ const float SAMPLE_WIDTH = (2.0 * M_PI) / float(SAMPLE_COUNT);
+ for (int i = 0; i < SAMPLE_COUNT; i++)
+ {
+ float x = -M_PI + (float(i) + 0.5) * SAMPLE_WIDTH;
+ float dist = radius * abs(2.0 * mx_sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(mx_cos(theta + x), 0.0);
+ sumR += R;
+ }
+
+ return sumD / sumR;
+}
+
+vec3 mx_subsurface_scattering_approx(vec3 N, vec3 L, vec3 P, vec3 albedo, vec3 mfp)
+{
+ float curvature = length(fwidth(N)) / length(fwidth(P));
+ float radius = 1.0 / max(curvature, 0.01);
+ return albedo * mx_integrate_burley_diffusion(N, L, radius, mfp) / vec3(M_PI);
+}
diff --git a/templates/pbrlib/genglsl/lib/mx_microfacet_sheen.glsl b/templates/pbrlib/genglsl/lib/mx_microfacet_sheen.glsl
new file mode 100644
index 0000000000..6301105dd1
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_microfacet_sheen.glsl
@@ -0,0 +1,189 @@
+#include "mx_microfacet.glsl"
+
+// https://fpsunflower.github.io/ckulla/data/s2017_pbs_imageworks_sheen.pdf
+// Equation 2
+float mx_imageworks_sheen_NDF(float NdotH, float roughness)
+{
+ float invRoughness = 1.0 / max(roughness, 0.005);
+ float cos2 = NdotH * NdotH;
+ float sin2 = 1.0 - cos2;
+ return (2.0 + invRoughness) * pow(sin2, invRoughness * 0.5) / (2.0 * M_PI);
+}
+
+float mx_imageworks_sheen_brdf(float NdotL, float NdotV, float NdotH, float roughness)
+{
+ // Microfacet distribution.
+ float D = mx_imageworks_sheen_NDF(NdotH, roughness);
+
+ // Fresnel and geometry terms are ignored.
+ float F = 1.0;
+ float G = 1.0;
+
+ // We use a smoother denominator, as in:
+ // https://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf
+ return D * F * G / (4.0 * (NdotL + NdotV - NdotL*NdotV));
+}
+
+// Rational quadratic fit to Monte Carlo data for Imageworks sheen directional albedo.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ vec2 r = vec2(13.67300, 1.0) +
+ vec2(-68.78018, 61.57746) * NdotV +
+ vec2(799.08825, 442.78211) * roughness +
+ vec2(-905.00061, 2597.49308) * NdotV * roughness +
+ vec2(60.28956, 121.81241) * mx_square(NdotV) +
+ vec2(1086.96473, 3045.55075) * mx_square(roughness);
+ return r.x / r.y;
+}
+
+float mx_imageworks_sheen_dir_albedo_table_lookup(float NdotV, float roughness)
+{
+#if DIRECTIONAL_ALBEDO_METHOD == 1
+ if (textureSize($albedoTable, 0).x > 1)
+ {
+ return texture($albedoTable, vec2(NdotV, roughness)).b;
+ }
+#endif
+ return 0.0;
+}
+
+float mx_imageworks_sheen_dir_albedo_monte_carlo(float NdotV, float roughness)
+{
+ NdotV = clamp(NdotV, M_FLOAT_EPS, 1.0);
+ vec3 V = vec3(sqrt(1.0f - mx_square(NdotV)), 0, NdotV);
+
+ float radiance = 0.0;
+ const int SAMPLE_COUNT = 64;
+ for (int i = 0; i < SAMPLE_COUNT; i++)
+ {
+ vec2 Xi = mx_spherical_fibonacci(i, SAMPLE_COUNT);
+
+ // Compute the incoming light direction and half vector.
+ vec3 L = mx_uniform_sample_hemisphere(Xi);
+ vec3 H = normalize(L + V);
+
+ // Compute dot products for this sample.
+ float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
+ float NdotH = clamp(H.z, M_FLOAT_EPS, 1.0);
+
+ // Compute sheen reflectance.
+ float reflectance = mx_imageworks_sheen_brdf(NdotL, NdotV, NdotH, roughness);
+
+ // Add the radiance contribution of this sample.
+ // uniform_pdf = 1 / (2 * PI)
+ // radiance = reflectance * NdotL / uniform_pdf;
+ radiance += reflectance * NdotL * 2.0 * M_PI;
+ }
+
+ // Return the final directional albedo.
+ return radiance / float(SAMPLE_COUNT);
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+#if DIRECTIONAL_ALBEDO_METHOD == 0
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+#elif DIRECTIONAL_ALBEDO_METHOD == 1
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_table_lookup(NdotV, roughness);
+#else
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_monte_carlo(NdotV, roughness);
+#endif
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// The following functions are adapted from https://github.com/tizian/ltc-sheen.
+// "Practical Multiple-Scattering Sheen Using Linearly Transformed Cosines", Zeltner et al.
+
+// Gaussian fit to directional albedo table.
+float mx_zeltner_sheen_dir_albedo(float x, float y)
+{
+ float s = y*(0.0206607 + 1.58491*y)/(0.0379424 + y*(1.32227 + y));
+ float m = y*(-0.193854 + y*(-1.14885 + y*(1.7932 - 0.95943*y*y)))/(0.046391 + y);
+ float o = y*(0.000654023 + (-0.0207818 + 0.119681*y)*y)/(1.26264 + y*(-1.92021 + y));
+ return exp(-0.5*mx_square((x - m)/s))/(s*sqrt(2.0*M_PI)) + o;
+}
+
+// Rational fits to LTC matrix coefficients.
+float mx_zeltner_sheen_ltc_aInv(float x, float y)
+{
+ return (2.58126*x + 0.813703*y)*y/(1.0 + 0.310327*x*x + 2.60994*x*y);
+}
+
+float mx_zeltner_sheen_ltc_bInv(float x, float y)
+{
+ return sqrt(1.0 - x)*(y - 1.0)*y*y*y/(0.0000254053 + 1.71228*x - 1.71506*x*y + 1.34174*y*y);
+}
+
+// V and N are assumed to be unit vectors.
+mat3 mx_orthonormal_basis_ltc(vec3 V, vec3 N, float NdotV)
+{
+ // Generate a tangent vector in the plane of V and N.
+ // This required to correctly orient the LTC lobe.
+ vec3 X = V - N*NdotV;
+ float lenSqr = dot(X, X);
+ if (lenSqr > 0.0)
+ {
+ X *= mx_inversesqrt(lenSqr);
+ vec3 Y = cross(N, X);
+ return mat3(X, Y, N);
+ }
+
+ // If lenSqr == 0, then V == N, so any orthonormal basis will do.
+ return mx_orthonormal_basis(N);
+}
+
+// Multiplication by directional albedo is handled by the calling function.
+float mx_zeltner_sheen_brdf(vec3 L, vec3 V, vec3 N, float NdotV, float roughness)
+{
+ mat3 toLTC = transpose(mx_orthonormal_basis_ltc(V, N, NdotV));
+ vec3 w = toLTC * L;
+
+ float aInv = mx_zeltner_sheen_ltc_aInv(NdotV, roughness);
+ float bInv = mx_zeltner_sheen_ltc_bInv(NdotV, roughness);
+
+ // Transform w to original configuration (clamped cosine).
+ // |aInv 0 bInv|
+ // wo = M^-1 . w = | 0 aInv 0| . w
+ // | 0 0 1|
+ vec3 wo = vec3(aInv*w.x + bInv*w.z, aInv * w.y, w.z);
+ float lenSqr = dot(wo, wo);
+
+ // D(w) = Do(M^-1.w / ||M^-1.w||) . |M^-1| / ||M^-1.w||^3
+ // = Do(M^-1.w) . |M^-1| / ||M^-1.w||^4
+ // = Do(wo) . |M^-1| / dot(wo, wo)^2
+ // = Do(wo) . aInv^2 / dot(wo, wo)^2
+ // = Do(wo) . (aInv / dot(wo, wo))^2
+ return max(wo.z, 0.0) * M_PI_INV * mx_square(aInv / lenSqr);
+}
+
+vec3 mx_zeltner_sheen_importance_sample(vec2 Xi, vec3 V, vec3 N, float roughness, out float pdf)
+{
+ float NdotV = clamp(dot(N, V), 0.0, 1.0);
+ roughness = clamp(roughness, 0.01, 1.0); // Clamp to range of original impl.
+
+ vec3 wo = mx_cosine_sample_hemisphere(Xi);
+
+ float aInv = mx_zeltner_sheen_ltc_aInv(NdotV, roughness);
+ float bInv = mx_zeltner_sheen_ltc_bInv(NdotV, roughness);
+
+ // Transform wo from original configuration (clamped cosine).
+ // |1/aInv 0 -bInv/aInv|
+ // w = M . wo = | 0 1/aInv 0| . wo
+ // | 0 0 1|
+ vec3 w = vec3(wo.x/aInv - wo.z*bInv/aInv, wo.y / aInv, wo.z);
+
+ float lenSqr = dot(w, w);
+ w *= mx_inversesqrt(lenSqr);
+
+ // D(w) = Do(wo) . ||M.wo||^3 / |M|
+ // = Do(wo / ||M.wo||) . ||M.wo||^4 / |M|
+ // = Do(w) . ||M.wo||^4 / |M| (possible because M doesn't change z component)
+ // = Do(w) . dot(w, w)^2 * aInv^2
+ // = Do(w) . (aInv * dot(w, w))^2
+ pdf = max(w.z, 0.0) * M_PI_INV * mx_square(aInv * lenSqr);
+
+ mat3 fromLTC = mx_orthonormal_basis_ltc(V, N, NdotV);
+ w = fromLTC * w;
+
+ return w;
+}
diff --git a/templates/pbrlib/genglsl/lib/mx_microfacet_specular.glsl b/templates/pbrlib/genglsl/lib/mx_microfacet_specular.glsl
new file mode 100644
index 0000000000..43ea62be10
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_microfacet_specular.glsl
@@ -0,0 +1,510 @@
+#include "mx_microfacet.glsl"
+
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+
+// Parameters for Fresnel calculations
+struct FresnelData
+{
+ // Fresnel model
+ int model;
+ bool airy;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F82;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+};
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Appendix B.2 Equation 13
+float mx_ggx_NDF(vec3 H, vec2 alpha)
+{
+ vec2 He = H.xy / alpha;
+ float denom = dot(He, He) + mx_square(H.z);
+ return 1.0 / (M_PI * alpha.x * alpha.y * mx_square(denom));
+}
+
+// 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));
+
+ // Sample a spherical cap in (-V.z, 1].
+ float phi = 2.0 * M_PI * Xi.x;
+ 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 * mx_cos(phi);
+ float y = sinTheta * mx_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)));
+
+ return H;
+}
+
+// https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
+// Equation 34
+float mx_ggx_smith_G1(float cosTheta, float alpha)
+{
+ float cosTheta2 = mx_square(cosTheta);
+ float tanTheta2 = (1.0 - cosTheta2) / cosTheta2;
+ return 2.0 / (1.0 + sqrt(1.0 + mx_square(alpha) * tanTheta2));
+}
+
+// Height-correlated Smith masking-shadowing
+// http://jcgt.org/published/0003/02/03/paper.pdf
+// Equations 72 and 99
+float mx_ggx_smith_G2(float NdotL, float NdotV, float alpha)
+{
+ float alpha2 = mx_square(alpha);
+ float lambdaL = sqrt(alpha2 + (1.0 - alpha2) * mx_square(NdotL));
+ float lambdaV = sqrt(alpha2 + (1.0 - alpha2) * mx_square(NdotV));
+ return 2.0 * NdotL * NdotV / (lambdaL * NdotV + lambdaV * NdotL);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+vec3 mx_ggx_dir_albedo_analytic(float NdotV, float alpha, vec3 F0, vec3 F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vec4 r = vec4(0.1003, 0.9345, 1.0, 1.0) +
+ vec4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vec4(9.748, 2.229, 8.263, 15.94) * y +
+ vec4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vec4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vec4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vec4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vec4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vec4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vec2 AB = clamp(r.xy / r.zw, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+vec3 mx_ggx_dir_albedo_table_lookup(float NdotV, float alpha, vec3 F0, vec3 F90)
+{
+#if DIRECTIONAL_ALBEDO_METHOD == 1
+ if (textureSize($albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture($albedoTable, vec2(NdotV, alpha)).rg;
+ return F0 * AB.x + F90 * AB.y;
+ }
+#endif
+ return vec3(0.0);
+}
+
+// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
+vec3 mx_ggx_dir_albedo_monte_carlo(float NdotV, float alpha, vec3 F0, vec3 F90)
+{
+ NdotV = clamp(NdotV, M_FLOAT_EPS, 1.0);
+ vec3 V = vec3(sqrt(1.0 - mx_square(NdotV)), 0, NdotV);
+
+ vec2 AB = vec2(0.0);
+ const int SAMPLE_COUNT = 64;
+ for (int i = 0; i < SAMPLE_COUNT; i++)
+ {
+ vec2 Xi = mx_spherical_fibonacci(i, SAMPLE_COUNT);
+
+ // Compute the half vector and incoming light direction.
+ vec3 H = mx_ggx_importance_sample_VNDF(Xi, V, vec2(alpha));
+ vec3 L = -reflect(V, H);
+
+ // Compute dot products for this sample.
+ float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ // Compute the Fresnel term.
+ float Fc = mx_fresnel_schlick(VdotH, 0.0, 1.0);
+
+ // Compute the per-sample geometric term.
+ // https://hal.inria.fr/hal-00996995v2/document, Algorithm 2
+ float G2 = mx_ggx_smith_G2(NdotL, NdotV, alpha);
+
+ // Add the contribution of this sample.
+ AB += vec2(G2 * (1.0 - Fc), G2 * Fc);
+ }
+
+ // Apply the global component of the geometric term and normalize.
+ AB /= mx_ggx_smith_G1(NdotV, alpha) * float(SAMPLE_COUNT);
+
+ // Return the final directional albedo.
+ return F0 * AB.x + F90 * AB.y;
+}
+
+vec3 mx_ggx_dir_albedo(float NdotV, float alpha, vec3 F0, vec3 F90)
+{
+#if DIRECTIONAL_ALBEDO_METHOD == 0
+ return mx_ggx_dir_albedo_analytic(NdotV, alpha, F0, F90);
+#elif DIRECTIONAL_ALBEDO_METHOD == 1
+ return mx_ggx_dir_albedo_table_lookup(NdotV, alpha, F0, F90);
+#else
+ return mx_ggx_dir_albedo_monte_carlo(NdotV, alpha, F0, F90);
+#endif
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ return mx_ggx_dir_albedo(NdotV, alpha, vec3(F0), vec3(F90)).x;
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+vec3 mx_ggx_energy_compensation(float NdotV, float alpha, vec3 Fss)
+{
+ float Ess = mx_ggx_dir_albedo(NdotV, alpha, 1.0, 1.0);
+ return 1.0 + Fss * (1.0 - Ess) / Ess;
+}
+
+float mx_ggx_energy_compensation(float NdotV, float alpha, float Fss)
+{
+ return mx_ggx_energy_compensation(NdotV, alpha, vec3(Fss)).x;
+}
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vec2 alpha)
+{
+ return sqrt(alpha.x * alpha.y);
+}
+
+// Convert a real-valued index of refraction to normal-incidence reflectivity.
+float mx_ior_to_f0(float ior)
+{
+ return mx_square((ior - 1.0) / (ior + 1.0));
+}
+
+// Convert normal-incidence reflectivity to real-valued index of refraction.
+float mx_f0_to_ior(float F0)
+{
+ float sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (1.0 + sqrtF0) / (1.0 - sqrtF0);
+}
+vec3 mx_f0_to_ior(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://renderwonk.com/publications/wp-generalization-adobe/gen-adobe.pdf
+vec3 mx_fresnel_hoffman_schlick(float cosTheta, FresnelData fd)
+{
+ const float COS_THETA_MAX = 1.0 / 7.0;
+ const float COS_THETA_FACTOR = 1.0 / (COS_THETA_MAX * pow(1.0 - COS_THETA_MAX, 6.0));
+
+ float x = clamp(cosTheta, 0.0, 1.0);
+ vec3 a = mix(fd.F0, fd.F90, pow(1.0 - COS_THETA_MAX, fd.exponent)) * (vec3(1.0) - fd.F82) * COS_THETA_FACTOR;
+ return mix(fd.F0, fd.F90, pow(1.0 - x, fd.exponent)) - a * x * mx_pow6(1.0 - x);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ float c = cosTheta;
+ float g2 = ior*ior + c*c - 1.0;
+ if (g2 < 0.0)
+ {
+ // Total internal reflection
+ return 1.0;
+ }
+
+ float g = sqrt(g2);
+ return 0.5 * mx_square((g - c) / (g + c)) *
+ (1.0 + mx_square(((g + c) * c - 1.0) / ((g - c) * c + 1.0)));
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+vec2 mx_fresnel_dielectric_polarized(float cosTheta, float ior)
+{
+ float cosTheta2 = mx_square(clamp(cosTheta, 0.0, 1.0));
+ float sinTheta2 = 1.0 - cosTheta2;
+
+ float t0 = max(ior * ior - sinTheta2, 0.0);
+ float t1 = t0 + cosTheta2;
+ float t2 = 2.0 * sqrt(t0) * cosTheta;
+ float Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * t0 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ float Rp = Rs * (t3 - t4) / (t3 + t4);
+
+ return vec2(Rp, Rs);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ float cosTheta2 = mx_square(clamp(cosTheta, 0.0, 1.0));
+ float sinTheta2 = 1.0 - cosTheta2;
+ vec3 n2 = n * n;
+ vec3 k2 = k * k;
+
+ vec3 t0 = n2 - k2 - vec3(sinTheta2);
+ vec3 a2plusb2 = sqrt(t0 * t0 + 4.0 * n2 * k2);
+ vec3 t1 = a2plusb2 + vec3(cosTheta2);
+ vec3 a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ vec3 t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ vec3 t3 = cosTheta2 * a2plusb2 + vec3(sinTheta2 * sinTheta2);
+ vec3 t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+vec3 mx_fresnel_conductor(float cosTheta, vec3 n, vec3 k)
+{
+ vec3 Rp, Rs;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ return 0.5 * (Rp + Rs);
+}
+
+// https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ vec3 k2 = kappa2 / eta2;
+ vec3 sinThetaSqr = vec3(1.0) - cosTheta * cosTheta;
+ vec3 A = eta2*eta2*(vec3(1.0)-k2*k2) - eta1*eta1*sinThetaSqr;
+ vec3 B = sqrt(A*A + mx_square(2.0*eta2*eta2*k2));
+ vec3 U = sqrt((A+B)/2.0);
+ vec3 V = max(vec3(0.0), sqrt((B-A)/2.0));
+
+ phiS = mx_atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = mx_atan(2.0*eta1*eta2*eta2*cosTheta * (2.0*k2*U - (vec3(1.0)-k2*k2) * V),
+ mx_square(eta2*eta2*(vec3(1.0)+k2*k2)*cosTheta) - eta1*eta1*(U*U+V*V));
+}
+
+// https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html
+vec3 mx_eval_sensitivity(float opd, vec3 shift)
+{
+ // Use Gaussian fits, given by 3 parameters: val, pos and var
+ float phase = 2.0*M_PI * opd;
+ vec3 val = vec3(5.4856e-13, 4.4201e-13, 5.2481e-13);
+ vec3 pos = vec3(1.6810e+06, 1.7953e+06, 2.2084e+06);
+ vec3 var = vec3(4.3278e+09, 9.3046e+09, 6.6121e+09);
+ vec3 xyz = val * sqrt(2.0*M_PI * var) * mx_cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * mx_cos(2.2399e+06 * phase + shift[0]) * exp(- 4.5282e+09 * phase*phase);
+ return xyz / 1.0685e-7;
+}
+
+// A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence
+// https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html
+vec3 mx_fresnel_airy(float cosTheta, FresnelData fd)
+{
+ // XYZ to CIE 1931 RGB color space (using neutral E illuminant)
+ const mat3 XYZ_TO_RGB = mat3(2.3706743, -0.5138850, 0.0052982, -0.9000405, 1.4253036, -0.0146949, -0.4706338, 0.0885814, 1.0093968);
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(fd.tf_ior, eta1);
+ vec3 eta3 = (fd.model == FRESNEL_MODEL_SCHLICK) ? mx_f0_to_ior(fd.F0) : fd.ior;
+ vec3 kappa3 = (fd.model == FRESNEL_MODEL_SCHLICK) ? vec3(0.0) : fd.extinction;
+ float cosThetaT = sqrt(1.0 - (1.0 - mx_square(cosTheta)) * mx_square(eta1 / eta2));
+
+ // First interface
+ vec2 R12 = mx_fresnel_dielectric_polarized(cosTheta, eta2 / eta1);
+ if (cosThetaT <= 0.0)
+ {
+ // Total internal reflection
+ R12 = vec2(1.0);
+ }
+ vec2 T121 = vec2(1.0) - R12;
+
+ // Second interface
+ vec3 R23p, R23s;
+ if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ vec3 f = mx_fresnel_hoffman_schlick(cosThetaT, fd);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosThetaT, eta3 / eta2, kappa3 / eta2, R23p, R23s);
+ }
+
+ // Phase shift
+ float cosB = mx_cos(mx_atan(eta2 / eta1));
+ vec2 phi21 = vec2(cosTheta < cosB ? 0.0 : M_PI, M_PI);
+ vec3 phi23p, phi23s;
+ if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ phi23p = vec3((eta3[0] < eta2) ? M_PI : 0.0,
+ (eta3[1] < eta2) ? M_PI : 0.0,
+ (eta3[2] < eta2) ? M_PI : 0.0);
+ phi23s = phi23p;
+ }
+ else
+ {
+ mx_fresnel_conductor_phase_polarized(cosThetaT, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+ vec3 r123p = max(sqrt(R12.x*R23p), 0.0);
+ vec3 r123s = max(sqrt(R12.y*R23s), 0.0);
+
+ // Iridescence term
+ vec3 I = vec3(0.0);
+ vec3 Cm, Sm;
+
+ // Optical path difference
+ float distMeters = fd.tf_thickness * 1.0e-9;
+ float opd = 2.0 * eta2 * cosThetaT * distMeters;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (mx_square(T121.x) * R23p) / (vec3(1.0) - R12.x*R23p);
+ I += R12.x + Rs;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121.x;
+ for (int m=1; m<=2; m++)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m) * opd, float(m)*(phi23p+vec3(phi21.x)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (mx_square(T121.y) * R23s) / (vec3(1.0) - R12.y*R23s);
+ I += R12.y + Rp;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121.y;
+ for (int m=1; m<=2; m++)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m) * opd, float(m)*(phi23s+vec3(phi21.y)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, 0.0, 1.0);
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd;
+ fd.model = FRESNEL_MODEL_DIELECTRIC;
+ fd.airy = tf_thickness > 0.0;
+ fd.ior = vec3(ior);
+ fd.extinction = vec3(0.0);
+ fd.F0 = vec3(0.0);
+ fd.F82 = vec3(0.0);
+ fd.F90 = vec3(0.0);
+ fd.exponent = 0.0;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ fd.refraction = false;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd;
+ fd.model = FRESNEL_MODEL_CONDUCTOR;
+ fd.airy = tf_thickness > 0.0;
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.F0 = vec3(0.0);
+ fd.F82 = vec3(0.0);
+ fd.F90 = vec3(0.0);
+ fd.exponent = 0.0;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ fd.refraction = false;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F82, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd;
+ fd.model = FRESNEL_MODEL_SCHLICK;
+ fd.airy = tf_thickness > 0.0;
+ fd.ior = vec3(0.0);
+ fd.extinction = vec3(0.0);
+ fd.F0 = F0;
+ fd.F82 = F82;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ fd.refraction = false;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ if (fd.airy)
+ {
+ return mx_fresnel_airy(cosTheta, fd);
+ }
+ else if (fd.model == FRESNEL_MODEL_DIELECTRIC)
+ {
+ return vec3(mx_fresnel_dielectric(cosTheta, fd.ior.x));
+ }
+ else if (fd.model == FRESNEL_MODEL_CONDUCTOR)
+ {
+ return mx_fresnel_conductor(cosTheta, fd.ior, fd.extinction);
+ }
+ else
+ {
+ return mx_fresnel_hoffman_schlick(cosTheta, fd);
+ }
+}
+
+// Compute the refraction of a ray through a solid sphere.
+vec3 mx_refraction_solid_sphere(vec3 R, vec3 N, float ior)
+{
+ R = refract(R, N, 1.0 / ior);
+ vec3 N1 = normalize(R * dot(R, N) - N * 0.5);
+ return refract(R, N1, ior);
+}
+
+vec2 mx_latlong_projection(vec3 dir)
+{
+ float latitude = -mx_asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = mx_atan(dir.x, -dir.z) * M_PI_INV * 0.5 + 0.5;
+ return vec2(longitude, latitude);
+}
+
+vec3 mx_latlong_map_lookup(vec3 dir, mat4 transform, float lod, $texSamplerSignature)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod($texSamplerSampler2D, uv, lod).rgb;
+}
+
+// Return the mip level with the appropriate coverage for a filtered importance sample.
+// https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
+// Section 20.4 Equation 13
+float mx_latlong_compute_lod(vec3 dir, float pdf, float maxMipLevel, int envSamples)
+{
+ const float MIP_LEVEL_OFFSET = 1.5;
+ float effectiveMaxMipLevel = maxMipLevel - MIP_LEVEL_OFFSET;
+ float distortion = sqrt(1.0 - mx_square(dir.y));
+ return max(effectiveMaxMipLevel - 0.5 * log2(float(envSamples) * pdf * distortion), 0.0);
+}
diff --git a/templates/pbrlib/genglsl/lib/mx_shadow.glsl b/templates/pbrlib/genglsl/lib/mx_shadow.glsl
new file mode 100644
index 0000000000..0a0fdb4f07
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_shadow.glsl
@@ -0,0 +1,23 @@
+// https://developer.nvidia.com/gpugems/gpugems3/part-ii-light-and-shadows/chapter-8-summed-area-variance-shadow-maps
+float mx_variance_shadow_occlusion(vec2 moments, float fragmentDepth)
+{
+ const float MIN_VARIANCE = 0.00001;
+
+ // One-tailed inequality valid if fragmentDepth > moments.x.
+ float p = (fragmentDepth <= moments.x) ? 1.0 : 0.0;
+
+ // Compute variance.
+ float variance = moments.y - mx_square(moments.x);
+ variance = max(variance, MIN_VARIANCE);
+
+ // Compute probabilistic upper bound.
+ float d = fragmentDepth - moments.x;
+ float pMax = variance / (variance + mx_square(d));
+ return max(p, pMax);
+}
+
+vec2 mx_compute_depth_moments()
+{
+ float depth = gl_FragCoord.z;
+ return vec2(depth, mx_square(depth));
+}
diff --git a/templates/pbrlib/genglsl/lib/mx_transmission_opacity.glsl b/templates/pbrlib/genglsl/lib/mx_transmission_opacity.glsl
new file mode 100644
index 0000000000..2861d06194
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_transmission_opacity.glsl
@@ -0,0 +1,6 @@
+#include "mx_microfacet_specular.glsl"
+
+vec3 mx_surface_transmission(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd, vec3 tint)
+{
+ return tint;
+}
diff --git a/templates/pbrlib/genglsl/lib/mx_transmission_refract.glsl b/templates/pbrlib/genglsl/lib/mx_transmission_refract.glsl
new file mode 100644
index 0000000000..64e496a384
--- /dev/null
+++ b/templates/pbrlib/genglsl/lib/mx_transmission_refract.glsl
@@ -0,0 +1,14 @@
+#include "mx_microfacet_specular.glsl"
+
+vec3 mx_surface_transmission(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd, vec3 tint)
+{
+ // Approximate the appearance of surface transmission as glossy
+ // environment map refraction, ignoring any scene geometry that might
+ // be visible through the surface.
+ fd.refraction = true;
+ if ($refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
diff --git a/templates/pbrlib/genglsl/mx_add_bsdf.glsl b/templates/pbrlib/genglsl/mx_add_bsdf.glsl
new file mode 100644
index 0000000000..2fc8b0f77a
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_add_bsdf.glsl
@@ -0,0 +1,14 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_add_bsdf(ClosureData closureData, BSDF in1, BSDF in2, out BSDF result)
+{
+ result.response = in1.response + in2.response;
+
+ // We derive the throughput for closure addition as follows:
+ // throughput_1 = 1 - dir_albedo_1
+ // throughput_2 = 1 - dir_albedo_2
+ // throughput_sum = 1 - (dir_albedo_1 + dir_albedo_2)
+ // = 1 - ((1 - throughput_1) + (1 - throughput_2))
+ // = throughput_1 + throughput_2 - 1
+ result.throughput = max(in1.throughput + in2.throughput - 1.0, 0.0);
+}
diff --git a/templates/pbrlib/genglsl/mx_add_edf.glsl b/templates/pbrlib/genglsl/mx_add_edf.glsl
new file mode 100644
index 0000000000..23ab46c033
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_add_edf.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_add_edf(ClosureData closureData, EDF in1, EDF in2, out EDF result)
+{
+ result = in1 + in2;
+}
diff --git a/templates/pbrlib/genglsl/mx_anisotropic_vdf.glsl b/templates/pbrlib/genglsl/mx_anisotropic_vdf.glsl
new file mode 100644
index 0000000000..fe22c56a8e
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_anisotropic_vdf.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_anisotropic_vdf(ClosureData closureData, vec3 absorption, vec3 scattering, float anisotropy, inout BSDF bsdf)
+{
+ // TODO: Add some approximation for volumetric light absorption.
+}
diff --git a/templates/pbrlib/genglsl/mx_artistic_ior.glsl b/templates/pbrlib/genglsl/mx_artistic_ior.glsl
new file mode 100644
index 0000000000..9c4e4058d9
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_artistic_ior.glsl
@@ -0,0 +1,17 @@
+void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, out vec3 ior, out vec3 extinction)
+{
+ // "Artist Friendly Metallic Fresnel", Ole Gulbrandsen, 2014
+ // http://jcgt.org/published/0003/04/03/paper.pdf
+
+ vec3 r = clamp(reflectivity, 0.0, 0.99);
+ vec3 r_sqrt = sqrt(r);
+ vec3 n_min = (1.0 - r) / (1.0 + r);
+ vec3 n_max = (1.0 + r_sqrt) / (1.0 - r_sqrt);
+ ior = mix(n_max, n_min, edge_color);
+
+ vec3 np1 = ior + 1.0;
+ vec3 nm1 = ior - 1.0;
+ vec3 k2 = (np1*np1 * r - nm1*nm1) / (1.0 - r);
+ k2 = max(k2, 0.0);
+ extinction = sqrt(k2);
+}
diff --git a/templates/pbrlib/genglsl/mx_blackbody.glsl b/templates/pbrlib/genglsl/mx_blackbody.glsl
new file mode 100644
index 0000000000..55d5c87541
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_blackbody.glsl
@@ -0,0 +1,48 @@
+/// XYZ to Rec.709 RGB colorspace conversion
+const mat3 XYZ_to_RGB = mat3( 3.2406, -0.9689, 0.0557,
+ -1.5372, 1.8758, -0.2040,
+ -0.4986, 0.0415, 1.0570);
+
+void mx_blackbody(float temperatureKelvin, out vec3 colorValue)
+{
+ float xc, yc;
+ float t, t2, t3, xc2, xc3;
+
+ // if value outside valid range of approximation clamp to accepted temperature range
+ temperatureKelvin = clamp(temperatureKelvin, 1667.0, 25000.0);
+
+ t = 1000.0 / temperatureKelvin;
+ t2 = t * t;
+ t3 = t * t * t;
+
+ // Cubic spline approximation for Kelvin temperature to sRGB conversion
+ // (https://en.wikipedia.org/wiki/Planckian_locus#Approximation)
+ if (temperatureKelvin < 4000.0) { // 1667K <= temperatureKelvin < 4000K
+ xc = -0.2661239 * t3 - 0.2343580 * t2 + 0.8776956 * t + 0.179910;
+ }
+ else { // 4000K <= temperatureKelvin <= 25000K
+ xc = -3.0258469 * t3 + 2.1070379 * t2 + 0.2226347 * t + 0.240390;
+ }
+ xc2 = xc * xc;
+ xc3 = xc * xc * xc;
+
+ if (temperatureKelvin < 2222.0) { // 1667K <= temperatureKelvin < 2222K
+ yc = -1.1063814 * xc3 - 1.34811020 * xc2 + 2.18555832 * xc - 0.20219683;
+ }
+ else if (temperatureKelvin < 4000.0) { // 2222K <= temperatureKelvin < 4000K
+ yc = -0.9549476 * xc3 - 1.37418593 * xc2 + 2.09137015 * xc - 0.16748867;
+ }
+ else { // 4000K <= temperatureKelvin <= 25000K
+ yc = 3.0817580 * xc3 - 5.87338670 * xc2 + 3.75112997 * xc - 0.37001483;
+ }
+
+ if (yc <= 0.0) { // avoid division by zero
+ colorValue = vec3(1.0);
+ return;
+ }
+
+ vec3 XYZ = vec3(xc / yc, 1.0, (1.0 - xc - yc) / yc);
+
+ colorValue = XYZ_to_RGB * XYZ;
+ colorValue = max(colorValue, vec3(0.0));
+}
diff --git a/templates/pbrlib/genglsl/mx_burley_diffuse_bsdf.glsl b/templates/pbrlib/genglsl/mx_burley_diffuse_bsdf.glsl
new file mode 100644
index 0000000000..1399758e3d
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_burley_diffuse_bsdf.glsl
@@ -0,0 +1,33 @@
+#include "lib/mx_closure_type.glsl"
+#include "lib/mx_microfacet_diffuse.glsl"
+
+void mx_burley_diffuse_bsdf(ClosureData closureData, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ vec3 V = closureData.V;
+ vec3 L = closureData.L;
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
+ {
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float LdotH = clamp(dot(L, normalize(L + V)), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * closureData.occlusion * weight * NdotL * M_PI_INV;
+ bsdf.response *= mx_burley_diffuse(NdotV, NdotL, LdotH, roughness);
+ }
+ else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
+ {
+ vec3 Li = mx_environment_irradiance(N) *
+ mx_burley_diffuse_dir_albedo(NdotV, roughness);
+ bsdf.response = Li * color * weight;
+ }
+}
diff --git a/templates/pbrlib/genglsl/mx_chiang_hair_bsdf.glsl b/templates/pbrlib/genglsl/mx_chiang_hair_bsdf.glsl
new file mode 100644
index 0000000000..54853843d3
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_chiang_hair_bsdf.glsl
@@ -0,0 +1,291 @@
+#include "lib/mx_closure_type.glsl"
+#include "lib/mx_microfacet_specular.glsl"
+
+// https://eugenedeon.com/pdfs/egsrhair.pdf
+void mx_deon_hair_absorption_from_melanin(
+ float melanin_concentration,
+ float melanin_redness,
+ // constants converted to color via exp(-c). the defaults are lin_rec709 colors, they may be
+ // transformed to scene-linear rendering color space.
+ vec3 eumelanin_color, // default: (0.657704, 0.498077, 0.254106) == exp(-(0.419, 0.697, 1.37))
+ vec3 pheomelanin_color, // default: (0.829443, 0.670320, 0.349937) == exp(-(0.187, 0.4, 1.05))
+ out vec3 absorption)
+{
+ float melanin = -log(max(1.0 - melanin_concentration, 0.0001));
+ float eumelanin = melanin * (1.0 - melanin_redness);
+ float pheomelanin = melanin * melanin_redness;
+ absorption = max(
+ eumelanin * -log(eumelanin_color) + pheomelanin * -log(pheomelanin_color),
+ vec3(0.0)
+ );
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/152/asset/eurographics2016Fur_Smaller.pdf
+void mx_chiang_hair_absorption_from_color(vec3 color, float betaN, out vec3 absorption)
+{
+ float b2 = betaN* betaN;
+ float b4 = b2 * b2;
+ float b_fac =
+ 5.969 -
+ (0.215 * betaN) +
+ (2.532 * b2) -
+ (10.73 * b2 * betaN) +
+ (5.574 * b4) +
+ (0.245 * b4 * betaN);
+ vec3 sigma = log(min(max(color, 0.001), vec3(1.0))) / b_fac;
+ absorption = sigma * sigma;
+}
+
+void mx_chiang_hair_roughness(
+ float longitudinal,
+ float azimuthal,
+ float scale_TT, // empirical roughness scale from Marschner et al. (2003).
+ float scale_TRT, // default: scale_TT = 0.5, scale_TRT = 2.0
+ out vec2 roughness_R,
+ out vec2 roughness_TT,
+ out vec2 roughness_TRT
+)
+{
+ float lr = clamp(longitudinal, 0.001, 1.0);
+ float ar = clamp(azimuthal, 0.001, 1.0);
+
+ // longitudinal variance
+ float v = 0.726 * lr + 0.812 * lr * lr + 3.7 * pow(lr, 20.0);
+ v = v * v;
+
+ float s = 0.265 * ar + 1.194 * ar * ar + 5.372 * pow(ar, 22.0);
+
+ roughness_R = vec2(v, s);
+ roughness_TT = vec2(v * scale_TT * scale_TT, s);
+ roughness_TRT = vec2(v * scale_TRT * scale_TRT, s);
+}
+
+float mx_hair_transform_sin_cos(float x)
+{
+ return sqrt(max(1.0 - x * x, 0.0));
+}
+
+float mx_hair_I0(float x)
+{
+ float v = 1.0;
+ float n = 1.0;
+ float d = 1.0;
+ float f = 1.0;
+ float x2 = x * x;
+ for (int i = 0; i < 9 ; ++i)
+ {
+ d *= 4.0 * (f * f);
+ n *= x2;
+ v += n / d;
+ f += 1.0;
+ }
+ return v;
+}
+
+float mx_hair_log_I0(float x)
+{
+ if (x > 12.0)
+ return x + 0.5 * (-log(2.0 * M_PI) + log(1.0 / x) + 1.0 / (8.0 * x));
+ else
+ return log(mx_hair_I0(x));
+}
+
+float mx_hair_logistic(float x, float s)
+{
+ if (x > 0.0)
+ x = -x;
+ float f = exp(x / s);
+ return f / (s * (1.0 + f) * (1.0 + f));
+}
+
+float mx_hair_logistic_cdf(float x, float s)
+{
+ return 1.0 / (1.0 + exp(-x / s));
+}
+
+float mx_hair_trimmed_logistic(float x, float s, float a, float b)
+{
+ // the constant can be found in Chiang et al. (2016) Appendix A, eq. (12)
+ s *= 0.626657; // sqrt(M_PI/8)
+ return mx_hair_logistic(x, s) / (mx_hair_logistic_cdf(b, s) - mx_hair_logistic_cdf(a, s));
+}
+
+float mx_hair_phi(int p, float gammaO, float gammaT)
+{
+ float fP = float(p);
+ return 2.0 * fP * gammaT - 2.0 * gammaO + fP * M_PI;
+}
+
+float mx_hair_longitudinal_scattering( // Mp
+ float sinThetaI,
+ float cosThetaI,
+ float sinThetaO,
+ float cosThetaO,
+ float v
+)
+{
+ float inv_v = 1.0 / v;
+ float a = cosThetaO * cosThetaI * inv_v;
+ float b = sinThetaO * sinThetaI * inv_v;
+ if (v < 0.1)
+ return exp(mx_hair_log_I0(a) - b - inv_v + 0.6931 + log(0.5 * inv_v));
+ else
+ return ((exp(-b) * mx_hair_I0(a)) / (2.0 * v * sinh(inv_v)));
+}
+
+float mx_hair_azimuthal_scattering( // Np
+ float phi,
+ int p,
+ float s,
+ float gammaO,
+ float gammaT
+)
+{
+ if (p >= 3)
+ return float(0.5 / M_PI);
+
+ float dphi = phi - mx_hair_phi(p, gammaO, gammaT);
+ if (isinf(dphi))
+ return float(0.5 / M_PI);
+
+ while (dphi > M_PI) dphi -= (2.0 * M_PI);
+ while (dphi < (-M_PI)) dphi += (2.0 * M_PI);
+
+ return mx_hair_trimmed_logistic(dphi, s, -M_PI, M_PI);
+}
+
+void mx_hair_alpha_angles(
+ float alpha,
+ float sinThetaI,
+ float cosThetaI,
+ out vec2 angles[4]
+)
+{
+ // 0:R, 1:TT, 2:TRT, 3:TRRT+
+ for (int i = 0; i <= 3; ++i)
+ {
+ if (alpha == 0.0 || i == 3)
+ angles[i] = vec2(sinThetaI, cosThetaI);
+ else
+ {
+ float m = 2.0 - float(i) * 3.0;
+ float sa = sin(m * alpha);
+ float ca = cos(m * alpha);
+ angles[i].x = sinThetaI * ca + cosThetaI * sa;
+ angles[i].y = cosThetaI * ca - sinThetaI * sa;
+ }
+ }
+}
+
+void mx_hair_attenuation(float f, vec3 T, out vec3 Ap[4]) // Ap
+{
+ // 0:R, 1:TT, 2:TRT, 3:TRRT+
+ Ap[0] = vec3(f);
+ Ap[1] = (1.0 - f) * (1.0 - f) * T;
+ Ap[2] = Ap[1] * T * f;
+ Ap[3] = Ap[2] * T * f / (vec3(1.0) - T * f);
+}
+
+void mx_chiang_hair_bsdf(ClosureData closureData, vec3 tint_R, vec3 tint_TT, vec3 tint_TRT, float ior,
+ vec2 roughness_R, vec2 roughness_TT, vec2 roughness_TRT, float cuticle_angle,
+ vec3 absorption_coefficient, vec3 N, vec3 X, inout BSDF bsdf)
+{
+ vec3 V = closureData.V;
+ vec3 L = closureData.L;
+
+ N = mx_forward_facing_normal(N, V);
+
+ bsdf.throughput = vec3(0.0);
+
+ if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
+ {
+ X = normalize(X - dot(X, N) * N);
+ vec3 Y = cross(N, X);
+
+ float sinThetaO = dot(V, X);
+ float sinThetaI = dot(L, X);
+ float cosThetaO = mx_hair_transform_sin_cos(sinThetaO);
+ float cosThetaI = mx_hair_transform_sin_cos(sinThetaI);
+
+ float y1 = dot(L, N);
+ float x1 = dot(L, Y);
+ float y2 = dot(V, N);
+ float x2 = dot(V, Y);
+ float phi = mx_atan(y1 * x2 - y2 * x1, x1 * x2 + y1 * y2);
+
+ vec3 k1_p = normalize(V - X * dot(V, X));
+ float cosGammaO = dot(N, k1_p);
+ float sinGammaO = mx_hair_transform_sin_cos(cosGammaO);
+ if (dot(k1_p, Y) > 0.0)
+ sinGammaO = -sinGammaO;
+ float gammaO = asin(sinGammaO);
+
+ float sinThetaT = sinThetaO / ior;
+ float cosThetaT = mx_hair_transform_sin_cos(sinThetaT);
+ float etaP = sqrt(max(ior * ior - sinThetaO * sinThetaO, 0.0)) / max(cosThetaO, M_FLOAT_EPS);
+ float sinGammaT = max(min(sinGammaO / etaP, 1.0), -1.0);
+ float cosGammaT = sqrt(1.0 - sinGammaT * sinGammaT);
+ float gammaT = asin(sinGammaT);
+
+ // attenuation
+ vec3 Ap[4];
+ float fresnel = mx_fresnel_dielectric(cosThetaO * cosGammaO, ior);
+ vec3 T = exp(-absorption_coefficient * (2.0 * cosGammaT / cosThetaT));
+ mx_hair_attenuation(fresnel, T, Ap);
+
+ // parameters for each lobe
+ vec2 angles[4];
+ float alpha = cuticle_angle * M_PI - (M_PI / 2.0); // remap [0, 1] to [-PI/2, PI/2]
+ mx_hair_alpha_angles(alpha, sinThetaI, cosThetaI, angles);
+
+ vec3 tint[4];
+ tint[0] = tint_R;
+ tint[1] = tint_TT;
+ tint[2] = tint_TRT;
+ tint[3] = tint_TRT;
+
+ roughness_R = clamp(roughness_R, 0.001, 1.0);
+ roughness_TT = clamp(roughness_TT, 0.001, 1.0);
+ roughness_TRT = clamp(roughness_TRT, 0.001, 1.0);
+
+ vec2 vs[4];
+ vs[0] = roughness_R;
+ vs[1] = roughness_TT;
+ vs[2] = roughness_TRT;
+ vs[3] = roughness_TRT;
+
+ // R, TT, TRT, TRRT+
+ vec3 F = vec3(0.0);
+ for (int i = 0; i <= 3; ++i)
+ {
+ tint[i] = max(tint[i], vec3(0.0));
+ float Mp = mx_hair_longitudinal_scattering(angles[i].x, angles[i].y, sinThetaO, cosThetaO, vs[i].x);
+ float Np = (i == 3) ? (1.0 / 2.0 * M_PI) : mx_hair_azimuthal_scattering(phi, i, vs[i].y, gammaO, gammaT);
+ F += Mp * Np * tint[i] * Ap[i];
+ }
+
+ bsdf.response = F * closureData.occlusion * M_PI_INV;
+ }
+ else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
+ {
+ // This indirect term is a *very* rough approximation.
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ FresnelData fd = mx_init_fresnel_dielectric(ior, 0.0, 1.0);
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 roughness = (roughness_R + roughness_TT + roughness_TRT) / vec2(3.0); // ?
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ // Use GGX to match the behavior of mx_environment_radiance.
+ float F0 = mx_ior_to_f0(ior);
+ vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+ vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, 0, fd);
+ vec3 tint = (tint_R + tint_TT + tint_TRT) / vec3(3.0); // ?
+
+ bsdf.response = Li * comp * tint;
+ }
+}
diff --git a/templates/pbrlib/genglsl/mx_conductor_bsdf.glsl b/templates/pbrlib/genglsl/mx_conductor_bsdf.glsl
new file mode 100644
index 0000000000..633f6d0d50
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_conductor_bsdf.glsl
@@ -0,0 +1,51 @@
+#include "lib/mx_closure_type.glsl"
+#include "lib/mx_microfacet_specular.glsl"
+
+void mx_conductor_bsdf(ClosureData closureData, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ vec3 V = closureData.V;
+ vec3 L = closureData.L;
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd = mx_init_fresnel_conductor(ior_n, ior_k, thinfilm_thickness, thinfilm_ior);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
+ {
+ X = normalize(X - dot(X, N) * N);
+ vec3 Y = cross(N, X);
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * closureData.occlusion * weight / (4.0 * NdotV);
+ }
+ else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
+ {
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+ vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * comp * weight;
+ }
+}
diff --git a/templates/pbrlib/genglsl/mx_dielectric_bsdf.glsl b/templates/pbrlib/genglsl/mx_dielectric_bsdf.glsl
new file mode 100644
index 0000000000..4c59b21802
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_dielectric_bsdf.glsl
@@ -0,0 +1,73 @@
+#include "lib/mx_closure_type.glsl"
+#include "lib/mx_microfacet_specular.glsl"
+
+void mx_dielectric_bsdf(ClosureData closureData, float weight, vec3 tint, float ior, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+ if (closureData.closureType != CLOSURE_TYPE_TRANSMISSION && scatter_mode == 1)
+ {
+ return;
+ }
+
+ vec3 V = closureData.V;
+ vec3 L = closureData.L;
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd = mx_init_fresnel_dielectric(ior, thinfilm_thickness, thinfilm_ior);
+ float F0 = mx_ior_to_f0(ior);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 safeTint = max(tint, 0.0);
+
+ if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
+ {
+ X = normalize(X - dot(X, N) * N);
+ vec3 Y = cross(N, X);
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+ vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ bsdf.response = D * F * G * comp * safeTint * closureData.occlusion * weight / (4.0 * NdotV);
+ }
+ else if (closureData.closureType == CLOSURE_TYPE_TRANSMISSION)
+ {
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+ vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, safeTint) * weight;
+ }
+ }
+ else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
+ {
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+ vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * safeTint * comp * weight;
+ }
+}
diff --git a/templates/pbrlib/genglsl/mx_displacement_float.glsl b/templates/pbrlib/genglsl/mx_displacement_float.glsl
new file mode 100644
index 0000000000..8dcdedbacb
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_displacement_float.glsl
@@ -0,0 +1,5 @@
+void mx_displacement_float(float disp, float scale, out displacementshader result)
+{
+ result.offset = vec3(disp);
+ result.scale = scale;
+}
diff --git a/templates/pbrlib/genglsl/mx_displacement_vector3.glsl b/templates/pbrlib/genglsl/mx_displacement_vector3.glsl
new file mode 100644
index 0000000000..095e015722
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_displacement_vector3.glsl
@@ -0,0 +1,5 @@
+void mx_displacement_vector3(vec3 disp, float scale, out displacementshader result)
+{
+ result.offset = disp;
+ result.scale = scale;
+}
diff --git a/templates/pbrlib/genglsl/mx_generalized_schlick_bsdf.glsl b/templates/pbrlib/genglsl/mx_generalized_schlick_bsdf.glsl
new file mode 100644
index 0000000000..08c7311495
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_generalized_schlick_bsdf.glsl
@@ -0,0 +1,80 @@
+#include "lib/mx_closure_type.glsl"
+#include "lib/mx_microfacet_specular.glsl"
+
+void mx_generalized_schlick_bsdf(ClosureData closureData, float weight, vec3 color0, vec3 color82, vec3 color90, float exponent, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+ if (closureData.closureType != CLOSURE_TYPE_TRANSMISSION && scatter_mode == 1)
+ {
+ return;
+ }
+
+ vec3 V = closureData.V;
+ vec3 L = closureData.L;
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ vec3 safeColor0 = max(color0, 0.0);
+ vec3 safeColor82 = max(color82, 0.0);
+ vec3 safeColor90 = max(color90, 0.0);
+ FresnelData fd = mx_init_fresnel_schlick(safeColor0, safeColor82, safeColor90, exponent, thinfilm_thickness, thinfilm_ior);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
+ {
+ X = normalize(X - dot(X, N) * N);
+ vec3 Y = cross(N, X);
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+ vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, safeColor0, safeColor90) * comp;
+ float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0));
+ bsdf.throughput = vec3(1.0 - avgDirAlbedo * weight);
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * closureData.occlusion * weight / (4.0 * NdotV);
+ }
+ else if (closureData.closureType == CLOSURE_TYPE_TRANSMISSION)
+ {
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+ vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, safeColor0, safeColor90) * comp;
+ float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0));
+ bsdf.throughput = vec3(1.0 - avgDirAlbedo * weight);
+
+ if (scatter_mode != 0)
+ {
+ float avgF0 = dot(safeColor0, vec3(1.0 / 3.0));
+ fd.ior = vec3(mx_f0_to_ior(avgF0));
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, vec3(1.0)) * weight;
+ }
+ }
+ else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
+ {
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+ vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, safeColor0, safeColor90) * comp;
+ float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0));
+ bsdf.throughput = vec3(1.0 - avgDirAlbedo * weight);
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * comp * weight;
+ }
+}
diff --git a/templates/pbrlib/genglsl/mx_generalized_schlick_edf.glsl b/templates/pbrlib/genglsl/mx_generalized_schlick_edf.glsl
new file mode 100644
index 0000000000..919dfa01c7
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_generalized_schlick_edf.glsl
@@ -0,0 +1,13 @@
+#include "lib/mx_closure_type.glsl"
+#include "lib/mx_microfacet.glsl"
+
+void mx_generalized_schlick_edf(ClosureData closureData, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ if (closureData.closureType == CLOSURE_TYPE_EMISSION)
+ {
+ vec3 N = mx_forward_facing_normal(closureData.N, closureData.V);
+ float NdotV = clamp(dot(N, closureData.V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+}
diff --git a/templates/pbrlib/genglsl/mx_layer_bsdf.glsl b/templates/pbrlib/genglsl/mx_layer_bsdf.glsl
new file mode 100644
index 0000000000..2200af8e56
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_layer_bsdf.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_layer_bsdf(ClosureData closureData, BSDF top, BSDF base, out BSDF result)
+{
+ result.response = top.response + base.response * top.throughput;
+ result.throughput = top.throughput * base.throughput;
+}
diff --git a/templates/pbrlib/genglsl/mx_layer_vdf.glsl b/templates/pbrlib/genglsl/mx_layer_vdf.glsl
new file mode 100644
index 0000000000..25a571b78c
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_layer_vdf.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_layer_vdf(ClosureData closureData, BSDF top, BSDF base, out BSDF result)
+{
+ result.response = top.response + base.response;
+ result.throughput = top.throughput + base.throughput;
+}
diff --git a/templates/pbrlib/genglsl/mx_mix_bsdf.glsl b/templates/pbrlib/genglsl/mx_mix_bsdf.glsl
new file mode 100644
index 0000000000..0ee8b152f5
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_mix_bsdf.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_mix_bsdf(ClosureData closureData, BSDF fg, BSDF bg, float mixValue, out BSDF result)
+{
+ result.response = mix(bg.response, fg.response, mixValue);
+ result.throughput = mix(bg.throughput, fg.throughput, mixValue);
+}
diff --git a/templates/pbrlib/genglsl/mx_mix_edf.glsl b/templates/pbrlib/genglsl/mx_mix_edf.glsl
new file mode 100644
index 0000000000..6a81db2727
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_mix_edf.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_mix_edf(ClosureData closureData, EDF fg, EDF bg, float mixValue, out EDF result)
+{
+ result = mix(bg, fg, mixValue);
+}
diff --git a/templates/pbrlib/genglsl/mx_multiply_bsdf_color3.glsl b/templates/pbrlib/genglsl/mx_multiply_bsdf_color3.glsl
new file mode 100644
index 0000000000..a817a8b7c0
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_multiply_bsdf_color3.glsl
@@ -0,0 +1,8 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_multiply_bsdf_color3(ClosureData closureData, BSDF in1, vec3 in2, out BSDF result)
+{
+ vec3 tint = clamp(in2, 0.0, 1.0);
+ result.response = in1.response * tint;
+ result.throughput = in1.throughput;
+}
diff --git a/templates/pbrlib/genglsl/mx_multiply_bsdf_float.glsl b/templates/pbrlib/genglsl/mx_multiply_bsdf_float.glsl
new file mode 100644
index 0000000000..c5736a79dd
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_multiply_bsdf_float.glsl
@@ -0,0 +1,8 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_multiply_bsdf_float(ClosureData closureData, BSDF in1, float in2, out BSDF result)
+{
+ float weight = clamp(in2, 0.0, 1.0);
+ result.response = in1.response * weight;
+ result.throughput = in1.throughput;
+}
diff --git a/templates/pbrlib/genglsl/mx_multiply_edf_color3.glsl b/templates/pbrlib/genglsl/mx_multiply_edf_color3.glsl
new file mode 100644
index 0000000000..9a95773cc7
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_multiply_edf_color3.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_multiply_edf_color3(ClosureData closureData, EDF in1, vec3 in2, out EDF result)
+{
+ result = in1 * in2;
+}
diff --git a/templates/pbrlib/genglsl/mx_multiply_edf_float.glsl b/templates/pbrlib/genglsl/mx_multiply_edf_float.glsl
new file mode 100644
index 0000000000..3987958f01
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_multiply_edf_float.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_multiply_edf_float(ClosureData closureData, EDF in1, float in2, out EDF result)
+{
+ result = in1 * in2;
+}
diff --git a/templates/pbrlib/genglsl/mx_oren_nayar_diffuse_bsdf.glsl b/templates/pbrlib/genglsl/mx_oren_nayar_diffuse_bsdf.glsl
new file mode 100644
index 0000000000..173a0abb6d
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_oren_nayar_diffuse_bsdf.glsl
@@ -0,0 +1,37 @@
+#include "lib/mx_closure_type.glsl"
+#include "lib/mx_microfacet_diffuse.glsl"
+
+void mx_oren_nayar_diffuse_bsdf(ClosureData closureData, float weight, vec3 color, float roughness, vec3 N, bool energy_compensation, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ vec3 V = closureData.V;
+ vec3 L = closureData.L;
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
+ {
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+
+ vec3 diffuse = energy_compensation ?
+ mx_oren_nayar_compensated_diffuse(NdotV, NdotL, LdotV, roughness, color) :
+ mx_oren_nayar_diffuse(NdotV, NdotL, LdotV, roughness) * color;
+ bsdf.response = diffuse * closureData.occlusion * weight * NdotL * M_PI_INV;
+ }
+ else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
+ {
+ vec3 diffuse = energy_compensation ?
+ mx_oren_nayar_compensated_diffuse_dir_albedo(NdotV, roughness, color) :
+ mx_oren_nayar_diffuse_dir_albedo(NdotV, roughness) * color;
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * diffuse * weight;
+ }
+}
diff --git a/templates/pbrlib/genglsl/mx_roughness_anisotropy.glsl b/templates/pbrlib/genglsl/mx_roughness_anisotropy.glsl
new file mode 100644
index 0000000000..39d266ebad
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_roughness_anisotropy.glsl
@@ -0,0 +1,15 @@
+void mx_roughness_anisotropy(float roughness, float anisotropy, out vec2 result)
+{
+ float roughness_sqr = clamp(roughness*roughness, M_FLOAT_EPS, 1.0);
+ if (anisotropy > 0.0)
+ {
+ float aspect = sqrt(1.0 - clamp(anisotropy, 0.0, 0.98));
+ result.x = min(roughness_sqr / aspect, 1.0);
+ result.y = roughness_sqr * aspect;
+ }
+ else
+ {
+ result.x = roughness_sqr;
+ result.y = roughness_sqr;
+ }
+}
diff --git a/templates/pbrlib/genglsl/mx_roughness_dual.glsl b/templates/pbrlib/genglsl/mx_roughness_dual.glsl
new file mode 100644
index 0000000000..f4b538399e
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_roughness_dual.glsl
@@ -0,0 +1,9 @@
+void mx_roughness_dual(vec2 roughness, out vec2 result)
+{
+ if (roughness.y < 0.0)
+ {
+ roughness.y = roughness.x;
+ }
+ result.x = clamp(roughness.x * roughness.x, M_FLOAT_EPS, 1.0);
+ result.y = clamp(roughness.y * roughness.y, M_FLOAT_EPS, 1.0);
+}
diff --git a/templates/pbrlib/genglsl/mx_sheen_bsdf.glsl b/templates/pbrlib/genglsl/mx_sheen_bsdf.glsl
new file mode 100644
index 0000000000..5b264d9956
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_sheen_bsdf.glsl
@@ -0,0 +1,61 @@
+#include "lib/mx_closure_type.glsl"
+#include "lib/mx_microfacet_sheen.glsl"
+
+void mx_sheen_bsdf(ClosureData closureData, float weight, vec3 color, float roughness, vec3 N, int mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ vec3 V = closureData.V;
+ vec3 L = closureData.L;
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
+ {
+ float dirAlbedo;
+ if (mode == 0)
+ {
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotH = clamp(dot(N, H), M_FLOAT_EPS, 1.0);
+
+ vec3 fr = color * mx_imageworks_sheen_brdf(NdotL, NdotV, NdotH, roughness);
+ dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled out by the BRDF denominator.
+ bsdf.response = fr * NdotL * closureData.occlusion * weight;
+ }
+ else
+ {
+ roughness = clamp(roughness, 0.01, 1.0); // Clamp to range of original impl.
+
+ vec3 fr = color * mx_zeltner_sheen_brdf(L, V, N, NdotV, roughness);
+ dirAlbedo = mx_zeltner_sheen_dir_albedo(NdotV, roughness);
+ bsdf.response = dirAlbedo * fr * closureData.occlusion * weight;
+ }
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+ }
+ else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
+ {
+ float dirAlbedo;
+ if (mode == 0)
+ {
+ dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ }
+ else
+ {
+ roughness = clamp(roughness, 0.01, 1.0); // Clamp to range of original impl.
+ dirAlbedo = mx_zeltner_sheen_dir_albedo(NdotV, roughness);
+ }
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+ }
+}
diff --git a/templates/pbrlib/genglsl/mx_subsurface_bsdf.glsl b/templates/pbrlib/genglsl/mx_subsurface_bsdf.glsl
new file mode 100644
index 0000000000..30c96f525c
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_subsurface_bsdf.glsl
@@ -0,0 +1,33 @@
+#include "lib/mx_closure_type.glsl"
+#include "lib/mx_microfacet_diffuse.glsl"
+
+void mx_subsurface_bsdf(ClosureData closureData, float weight, vec3 color, vec3 radius, float anisotropy, vec3 N, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ vec3 V = closureData.V;
+ vec3 L = closureData.L;
+ vec3 P = closureData.P;
+ float occlusion = closureData.occlusion;
+
+ N = mx_forward_facing_normal(N, V);
+
+ if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
+ {
+ vec3 sss = mx_subsurface_scattering_approx(N, L, P, color, radius);
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+ else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
+ {
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * weight;
+ }
+}
diff --git a/templates/pbrlib/genglsl/mx_translucent_bsdf.glsl b/templates/pbrlib/genglsl/mx_translucent_bsdf.glsl
new file mode 100644
index 0000000000..a2792f0fe9
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_translucent_bsdf.glsl
@@ -0,0 +1,28 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_translucent_bsdf(ClosureData closureData, float weight, vec3 color, vec3 N, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ vec3 V = closureData.V;
+ vec3 L = closureData.L;
+
+ // Invert normal since we're transmitting light from the other side
+ N = -N;
+
+ if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
+ {
+ float NdotL = clamp(dot(N, L), 0.0, 1.0);
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+ else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
+ {
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * weight;
+ }
+}
diff --git a/templates/pbrlib/genglsl/mx_uniform_edf.glsl b/templates/pbrlib/genglsl/mx_uniform_edf.glsl
new file mode 100644
index 0000000000..8f5f256060
--- /dev/null
+++ b/templates/pbrlib/genglsl/mx_uniform_edf.glsl
@@ -0,0 +1,9 @@
+#include "lib/mx_closure_type.glsl"
+
+void mx_uniform_edf(ClosureData closureData, vec3 color, out EDF result)
+{
+ if (closureData.closureType == CLOSURE_TYPE_EMISSION)
+ {
+ result = color;
+ }
+}
diff --git a/templates/pbrlib/genglsl/pbrlib_genglsl_impl.mtlx b/templates/pbrlib/genglsl/pbrlib_genglsl_impl.mtlx
new file mode 100644
index 0000000000..f48176ee76
--- /dev/null
+++ b/templates/pbrlib/genglsl/pbrlib_genglsl_impl.mtlx
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/pbrlib/genmdl/pbrlib_genmdl_impl.mtlx b/templates/pbrlib/genmdl/pbrlib_genmdl_impl.mtlx
new file mode 100644
index 0000000000..86f4694578
--- /dev/null
+++ b/templates/pbrlib/genmdl/pbrlib_genmdl_impl.mtlx
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/pbrlib/genmsl/pbrlib_genmsl_impl.mtlx b/templates/pbrlib/genmsl/pbrlib_genmsl_impl.mtlx
new file mode 100644
index 0000000000..15b49ef993
--- /dev/null
+++ b/templates/pbrlib/genmsl/pbrlib_genmsl_impl.mtlx
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/pbrlib/genosl/lib/mx_microfacet.osl b/templates/pbrlib/genosl/lib/mx_microfacet.osl
new file mode 100644
index 0000000000..2c8ff80d13
--- /dev/null
+++ b/templates/pbrlib/genosl/lib/mx_microfacet.osl
@@ -0,0 +1,78 @@
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vector2 mx_square(vector2 x)
+{
+ return x*x;
+}
+
+vector mx_square(vector x)
+{
+ return x*x;
+}
+
+vector4 mx_square(vector4 x)
+{
+ return x*x;
+}
+
+float mx_pow5(float x)
+{
+ return mx_square(mx_square(x)) * x;
+}
+
+color mx_fresnel_conductor(float cosTheta, vector n, vector k)
+{
+ float c2 = cosTheta*cosTheta;
+ vector n2_k2 = n*n + k*k;
+ vector nc2 = 2.0 * n * cosTheta;
+
+ vector rs_a = n2_k2 + c2;
+ vector rp_a = n2_k2 * c2 + 1.0;
+ vector rs = (rs_a - nc2) / (rs_a + nc2);
+ vector rp = (rp_a - nc2) / (rp_a + nc2);
+
+ return 0.5 * (rs + rp);
+}
+
+// Standard Schlick Fresnel
+float mx_fresnel_schlick(float cosTheta, float F0)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ float x5 = mx_pow5(x);
+ return F0 + (1.0 - F0) * x5;
+}
+color mx_fresnel_schlick(float cosTheta, color F0)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ float x5 = mx_pow5(x);
+ return F0 + (1.0 - F0) * x5;
+}
+
+// Generalized Schlick Fresnel
+float mx_fresnel_schlick(float cosTheta, float F0, float F90)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ float x5 = mx_pow5(x);
+ return mix(F0, F90, x5);
+}
+color mx_fresnel_schlick(float cosTheta, color F0, color F90)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ float x5 = mx_pow5(x);
+ return mix(F0, F90, x5);
+}
+
+// Generalized Schlick Fresnel with a variable exponent
+color mx_fresnel_schlick(float cosTheta, float f0, float f90, float exponent)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ return mix(f0, f90, pow(x, exponent));
+}
+color mx_fresnel_schlick(float cosTheta, color f0, color f90, float exponent)
+{
+ float x = clamp(1.0 - cosTheta, 0.0, 1.0);
+ return mix(f0, f90, pow(x, exponent));
+}
diff --git a/templates/pbrlib/genosl/mx_anisotropic_vdf.osl b/templates/pbrlib/genosl/mx_anisotropic_vdf.osl
new file mode 100644
index 0000000000..eb48e72e65
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_anisotropic_vdf.osl
@@ -0,0 +1,8 @@
+void mx_anisotropic_vdf(color absorption, color scattering, float anisotropy, output VDF vdf)
+{
+ // Convert from absorption and scattering coefficients to
+ // extinction coefficient and single-scattering albedo.
+ color extinction = absorption + scattering;
+ color albedo = scattering / extinction;
+ vdf = anisotropic_vdf(albedo, extinction, anisotropy);
+}
diff --git a/templates/pbrlib/genosl/mx_artistic_ior.osl b/templates/pbrlib/genosl/mx_artistic_ior.osl
new file mode 100644
index 0000000000..ca7f1ec75d
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_artistic_ior.osl
@@ -0,0 +1,17 @@
+void mx_artistic_ior(color reflectivity, color edge_color, output vector ior, output vector extinction)
+{
+ // "Artist Friendly Metallic Fresnel", Ole Gulbrandsen, 2014
+ // http://jcgt.org/published/0003/04/03/paper.pdf
+
+ color r = clamp(reflectivity, 0.0, 0.99);
+ color r_sqrt = sqrt(r);
+ color n_min = (1.0 - r) / (1.0 + r);
+ color n_max = (1.0 + r_sqrt) / (1.0 - r_sqrt);
+ ior = mix(n_max, n_min, edge_color);
+
+ color np1 = ior + 1.0;
+ color nm1 = ior - 1.0;
+ color k2 = (np1*np1 * r - nm1*nm1) / (1.0 - r);
+ k2 = max(k2, 0.0);
+ extinction = sqrt(k2);
+}
diff --git a/templates/pbrlib/genosl/mx_blackbody.osl b/templates/pbrlib/genosl/mx_blackbody.osl
new file mode 100644
index 0000000000..95951330f2
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_blackbody.osl
@@ -0,0 +1,49 @@
+void mx_blackbody(float temp, output color color_value)
+{
+ float xc, yc;
+ float t, t2, t3, xc2, xc3;
+
+ // if value outside valid range of approximation clamp to accepted temperature range
+ float temperature = clamp(temp, 1667.0, 25000.0);
+
+ t = 1000.0 / temperature;
+ t2 = t * t;
+ t3 = t * t * t;
+
+ // Cubic spline approximation for Kelvin temperature to sRGB conversion
+ // (https://en.wikipedia.org/wiki/Planckian_locus#Approximation)
+ if (temperature < 4000.0) { // 1667K <= temperature < 4000K
+ xc = -0.2661239 * t3 - 0.2343580 * t2 + 0.8776956 * t + 0.179910;
+ }
+ else { // 4000K <= temperature <= 25000K
+ xc = -3.0258469 * t3 + 2.1070379 * t2 + 0.2226347 * t + 0.240390;
+ }
+ xc2 = xc * xc;
+ xc3 = xc * xc * xc;
+
+ if (temperature < 2222.0) { // 1667K <= temperature < 2222K
+ yc = -1.1063814 * xc3 - 1.34811020 * xc2 + 2.18555832 * xc - 0.20219683;
+ }
+ else if (temperature < 4000.0) { // 2222K <= temperature < 4000K
+ yc = -0.9549476 * xc3 - 1.37418593 * xc2 + 2.09137015 * xc - 0.16748867;
+ }
+ else { // 4000K <= temperature <= 25000K
+ yc = 3.0817580 * xc3 - 5.87338670 * xc2 + 3.75112997 * xc - 0.37001483;
+ }
+
+ if (yc <= 0.0) { // avoid division by zero
+ color_value = color(1.0);
+ return;
+ }
+
+ vector XYZ = vector(xc / yc, 1.0, (1 - xc - yc) / yc);
+
+ /// XYZ to Rec.709 RGB colorspace conversion
+ matrix XYZ_to_RGB = matrix( 3.2406, -0.9689, 0.0557, 0.0,
+ -1.5372, 1.8758, -0.2040, 0.0,
+ -0.4986, 0.0415, 1.0570, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+
+ color_value = transform(XYZ_to_RGB, XYZ);
+ color_value = max(color_value, vector(0.0));
+}
diff --git a/templates/pbrlib/genosl/mx_chiang_hair_bsdf.osl b/templates/pbrlib/genosl/mx_chiang_hair_bsdf.osl
new file mode 100644
index 0000000000..aa1bc45edd
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_chiang_hair_bsdf.osl
@@ -0,0 +1,12 @@
+void mx_chiang_hair_bsdf(color tint_R, color tint_TT, color tint_TRT, float ior,
+ vector2 roughness_R, vector2 roughness_TT, vector2 roughness_TRT,
+ float cuticle_angle, vector absorption_coefficient, normal N, vector U, output BSDF bsdf)
+{
+#if OSL_VERSION_MAJOR >= 1 && OSL_VERSION_MINOR >= 14
+ bsdf = chiang_hair_bsdf(N, U, tint_R, tint_TT, tint_TRT, ior,
+ roughness_R.x, roughness_TT.x, roughness_TRT.x, roughness_R.y, roughness_TT.y, roughness_TRT.y,
+ cuticle_angle, absorption_coefficient);
+#else
+ bsdf = dielectric_bsdf(N, U, color(1), color(0), 0.1, 0.1, ior, "ggx");
+#endif
+}
diff --git a/templates/pbrlib/genosl/mx_chiang_hair_roughness.osl b/templates/pbrlib/genosl/mx_chiang_hair_roughness.osl
new file mode 100644
index 0000000000..002bdc3044
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_chiang_hair_roughness.osl
@@ -0,0 +1,7 @@
+void mx_chiang_hair_roughness(float longitudinal, float azimuthal, float scale_TT, float scale_TRT, output vector2 roughness_R, output vector2 roughness_TT, output vector2 roughness_TRT)
+{
+ // TODO: Write OSL implementation of this node.
+ roughness_R = vector2(0.0, 0.0);
+ roughness_TT = vector2(0.0, 0.0);
+ roughness_TRT = vector2(0.0, 0.0);
+}
diff --git a/templates/pbrlib/genosl/mx_dielectric_bsdf.osl b/templates/pbrlib/genosl/mx_dielectric_bsdf.osl
new file mode 100644
index 0000000000..5d465ea745
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_dielectric_bsdf.osl
@@ -0,0 +1,6 @@
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, float thinfilm_thickness, float thinfilm_ior, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ color reflection_tint = (scatter_mode == "T") ? color(0.0) : tint;
+ color transmission_tint = (scatter_mode == "R") ? color(0.0) : tint;
+ bsdf = weight * dielectric_bsdf(N, U, reflection_tint, transmission_tint, roughness.x, roughness.y, ior, distribution, "thinfilm_thickness", thinfilm_thickness, "thinfilm_ior", thinfilm_ior);
+}
diff --git a/templates/pbrlib/genosl/mx_generalized_schlick_bsdf.osl b/templates/pbrlib/genosl/mx_generalized_schlick_bsdf.osl
new file mode 100644
index 0000000000..9a05300b67
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_generalized_schlick_bsdf.osl
@@ -0,0 +1,6 @@
+void mx_generalized_schlick_bsdf(float weight, color color0, color color82, color color90, float exponent, vector2 roughness, float thinfilm_thickness, float thinfilm_ior, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ color reflection_tint = (scatter_mode == "T") ? color(0.0) : color(1.0);
+ color transmission_tint = (scatter_mode == "R") ? color(0.0) : color(1.0);
+ bsdf = weight * generalized_schlick_bsdf(N, U, reflection_tint, transmission_tint, roughness.x, roughness.y, color0, color90, exponent, distribution, "thinfilm_thickness", thinfilm_thickness, "thinfilm_ior", thinfilm_ior);
+}
diff --git a/templates/pbrlib/genosl/mx_generalized_schlick_edf.osl b/templates/pbrlib/genosl/mx_generalized_schlick_edf.osl
new file mode 100644
index 0000000000..84ecdfc034
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_generalized_schlick_edf.osl
@@ -0,0 +1,8 @@
+#include "lib/mx_microfacet.osl"
+
+void mx_generalized_schlick_edf(color color0, color color90, float exponent, EDF base, output EDF result)
+{
+ float NdotV = fabs(dot(N,-I));
+ color f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
diff --git a/templates/pbrlib/genosl/mx_roughness_anisotropy.osl b/templates/pbrlib/genosl/mx_roughness_anisotropy.osl
new file mode 100644
index 0000000000..48b256e701
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_roughness_anisotropy.osl
@@ -0,0 +1,15 @@
+void mx_roughness_anisotropy(float roughness, float anisotropy, output vector2 result)
+{
+ float roughness_sqr = clamp(roughness*roughness, M_FLOAT_EPS, 1.0);
+ if (anisotropy > 0.0)
+ {
+ float aspect = sqrt(1.0 - clamp(anisotropy, 0.0, 0.98));
+ result.x = min(roughness_sqr / aspect, 1.0);
+ result.y = roughness_sqr * aspect;
+ }
+ else
+ {
+ result.x = roughness_sqr;
+ result.y = roughness_sqr;
+ }
+}
diff --git a/templates/pbrlib/genosl/mx_roughness_dual.osl b/templates/pbrlib/genosl/mx_roughness_dual.osl
new file mode 100644
index 0000000000..6de64c73ad
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_roughness_dual.osl
@@ -0,0 +1,12 @@
+void mx_roughness_dual(vector2 roughness, output vector2 result)
+{
+ result.x = clamp(roughness.x * roughness.x, M_FLOAT_EPS, 1.0);
+ if (roughness.y < 0.0)
+ {
+ result.y = result.x;
+ }
+ else
+ {
+ result.y = clamp(roughness.y * roughness.y, M_FLOAT_EPS, 1.0);
+ }
+}
diff --git a/templates/pbrlib/genosl/mx_subsurface_bsdf.osl b/templates/pbrlib/genosl/mx_subsurface_bsdf.osl
new file mode 100644
index 0000000000..4dea95fb6b
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_subsurface_bsdf.osl
@@ -0,0 +1,8 @@
+void mx_subsurface_bsdf(float weight, color albedo, color radius, float anisotropy, normal N, output BSDF bsdf)
+{
+#if OSL_VERSION_MAJOR >= 1 && OSL_VERSION_MINOR >= 14
+ bsdf = weight * subsurface_bssrdf(N, albedo, radius, anisotropy);
+#else
+ bsdf = weight * subsurface_bssrdf(N, albedo, 1.0, radius, anisotropy);
+#endif
+}
diff --git a/templates/pbrlib/genosl/mx_surface.osl b/templates/pbrlib/genosl/mx_surface.osl
new file mode 100644
index 0000000000..ac454937a6
--- /dev/null
+++ b/templates/pbrlib/genosl/mx_surface.osl
@@ -0,0 +1,6 @@
+void mx_surface(BSDF bsdf, EDF edf, float opacity, int thin_walled, output surfaceshader result)
+{
+ result.bsdf = bsdf;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
diff --git a/templates/pbrlib/genosl/pbrlib_genosl_impl.mtlx b/templates/pbrlib/genosl/pbrlib_genosl_impl.mtlx
new file mode 100644
index 0000000000..fe5210facb
--- /dev/null
+++ b/templates/pbrlib/genosl/pbrlib_genosl_impl.mtlx
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/pbrlib/pbrlib_defs.mtlx b/templates/pbrlib/pbrlib_defs.mtlx
new file mode 100644
index 0000000000..c772e9a39b
--- /dev/null
+++ b/templates/pbrlib/pbrlib_defs.mtlx
@@ -0,0 +1,462 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/pbrlib/pbrlib_ng.mtlx b/templates/pbrlib/pbrlib_ng.mtlx
new file mode 100644
index 0000000000..8e8678675f
--- /dev/null
+++ b/templates/pbrlib/pbrlib_ng.mtlx
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/stdlib/genglsl/lib/mx_geometry.glsl b/templates/stdlib/genglsl/lib/mx_geometry.glsl
new file mode 100644
index 0000000000..de27655705
--- /dev/null
+++ b/templates/stdlib/genglsl/lib/mx_geometry.glsl
@@ -0,0 +1,41 @@
+// Blend 3 normals by blending the gradients
+// Morten S. Mikkelsen, Surface Gradient–Based Bump Mapping Framework, Journal of
+// Computer Graphics Techniques (JCGT), vol. 9, no. 3, 60–90, 2020
+// http://jcgt.org/published/0009/03/04/
+vec3 mx_normals_to_gradient(vec3 N, vec3 Np)
+{
+ float d = dot(N, Np);
+ vec3 g = (d * N - Np) / max(M_FLOAT_EPS, abs(d));
+ return g;
+}
+
+vec3 mx_gradient_blend_3_normals(vec3 N, vec3 N1, float N1_weight, vec3 N2, float N2_weight, vec3 N3, float N3_weight)
+{
+ float w1 = clamp(N1_weight, 0.0, 1.0);
+ float w2 = clamp(N2_weight, 0.0, 1.0);
+ float w3 = clamp(N3_weight, 0.0, 1.0);
+
+ vec3 g1 = mx_normals_to_gradient(N, N1);
+ vec3 g2 = mx_normals_to_gradient(N, N2);
+ vec3 g3 = mx_normals_to_gradient(N, N3);
+
+ // blend
+ vec3 gg = w1 * g1 + w2 * g2 + w3 * g3;
+
+ // gradient to normal
+ return normalize(N - gg);
+}
+
+// This function should be categorized in mx_math.glsl but it causes build errors in MSL
+// so adding here for a workaround
+mat3 mx_axis_rotation_matrix(vec3 a, float r)
+{
+ float s = sin(r);
+ float c = cos(r);
+ float omc = 1.0 - c;
+ return mat3(
+ a.x*a.x*omc + c, a.x*a.y*omc - a.z*s, a.x*a.z*omc + a.y*s,
+ a.y*a.x*omc + a.z*s, a.y*a.y*omc + c, a.y*a.z*omc - a.x*s,
+ a.z*a.x*omc - a.y*s, a.z*a.y*omc + a.x*s, a.z*a.z*omc + c
+ );
+}
diff --git a/templates/stdlib/genglsl/lib/mx_hextile.glsl b/templates/stdlib/genglsl/lib/mx_hextile.glsl
new file mode 100644
index 0000000000..bc8959a00b
--- /dev/null
+++ b/templates/stdlib/genglsl/lib/mx_hextile.glsl
@@ -0,0 +1,137 @@
+// https://www.shadertoy.com/view/4djSRW
+vec2 mx_hextile_hash(vec2 p)
+{
+ vec3 p3 = fract(vec3(p.x, p.y, p.x) * vec3(0.1031, 0.1030, 0.0973));
+ p3 += dot(p3, vec3(p3.y, p3.z, p3.x) + 33.33);
+ return fract((vec2(p3.x, p3.x) + vec2(p3.y, p3.z)) * vec2(p3.z, p3.y));
+}
+
+// Christophe Schlick. “Fast Alternatives to Perlin’s Bias and Gain Functions”.
+// In Graphics Gems IV, Morgan Kaufmann, 1994, pages 401–403.
+// https://dept-info.labri.fr/~schlick/DOC/gem2.html
+float mx_schlick_gain(float x, float r)
+{
+ float rr = clamp(r, 0.001, 0.999); // to avoid glitch
+ float a = (1.0 / rr - 2.0) * (1.0 - 2.0 * x);
+ return (x < 0.5) ? x / (a + 1.0) : (a - x) / (a - 1.0);
+}
+
+struct HextileData
+{
+ vec2 coord1;
+ vec2 coord2;
+ vec2 coord3;
+ vec3 weights;
+ float rot_radian1;
+ float rot_radian2;
+ float rot_radian3;
+ vec2 ddx1;
+ vec2 ddx2;
+ vec2 ddx3;
+ vec2 ddy1;
+ vec2 ddy2;
+ vec2 ddy3;
+};
+
+// Morten S. Mikkelsen, Practical Real-Time Hex-Tiling, Journal of Computer Graphics
+// Techniques (JCGT), vol. 11, no. 2, 77-94, 2022
+// http://jcgt.org/published/0011/03/05/
+HextileData mx_hextile_coord(
+ vec2 coord,
+ float rotation,
+ vec2 rotation_range,
+ float scale,
+ vec2 scale_range,
+ float offset,
+ vec2 offset_range)
+{
+ float sqrt3_2 = sqrt(3.0) * 2.0;
+
+ // scale coord to maintain the original fit
+ vec2 st = coord * sqrt3_2;
+
+ // skew input space into simplex triangle grid
+ // (1, 0, -tan(30), 2*tan(30))
+ mat2 to_skewed = mat2(1.0, 0.0, -0.57735027, 1.15470054);
+ vec2 st_skewed = to_skewed * st;
+
+ // barycentric weights
+ vec2 st_frac = fract(st_skewed);
+ vec3 temp = vec3(st_frac.x, st_frac.y, 0.0);
+ temp.z = 1.0 - temp.x - temp.y;
+
+ float s = step(0.0, -temp.z);
+ float s2 = 2.0 * s - 1.0;
+
+ float w1 = -temp.z * s2;
+ float w2 = s - temp.y * s2;
+ float w3 = s - temp.x * s2;
+
+ // vertex IDs
+ ivec2 base_id = ivec2(floor(st_skewed));
+ int si = int(s);
+ ivec2 id1 = base_id + ivec2(si, si);
+ ivec2 id2 = base_id + ivec2(si, 1 - si);
+ ivec2 id3 = base_id + ivec2(1 - si, si);
+
+ // tile center
+ mat2 inv_skewed = mat2(1.0, 0.0, 0.5, 1.0 / 1.15470054);
+ vec2 ctr1 = inv_skewed * vec2(id1) / vec2(sqrt3_2);
+ vec2 ctr2 = inv_skewed * vec2(id2) / vec2(sqrt3_2);
+ vec2 ctr3 = inv_skewed * vec2(id3) / vec2(sqrt3_2);
+
+ // reuse hash for performance
+ vec2 seed_offset = vec2(0.12345); // to avoid some zeros
+ vec2 rand1 = mx_hextile_hash(vec2(id1) + seed_offset);
+ vec2 rand2 = mx_hextile_hash(vec2(id2) + seed_offset);
+ vec2 rand3 = mx_hextile_hash(vec2(id3) + seed_offset);
+
+ // randomized rotation matrix
+ vec2 rr = mx_radians(rotation_range);
+ float rv1 = mix(rr.x, rr.y, rand1.x * rotation);
+ float rv2 = mix(rr.x, rr.y, rand2.x * rotation);
+ float rv3 = mix(rr.x, rr.y, rand3.x * rotation);
+ float sin_r1 = sin(rv1);
+ float sin_r2 = sin(rv2);
+ float sin_r3 = sin(rv3);
+ float cos_r1 = cos(rv1);
+ float cos_r2 = cos(rv2);
+ float cos_r3 = cos(rv3);
+ mat2 rm1 = mat2(cos_r1, -sin_r1, sin_r1, cos_r1);
+ mat2 rm2 = mat2(cos_r2, -sin_r2, sin_r2, cos_r2);
+ mat2 rm3 = mat2(cos_r3, -sin_r3, sin_r3, cos_r3);
+
+ // randomized scale
+ vec2 sr = scale_range;
+ vec2 scale1 = vec2(mix(1.0, mix(sr.x, sr.y, rand1.y), scale));
+ vec2 scale2 = vec2(mix(1.0, mix(sr.x, sr.y, rand2.y), scale));
+ vec2 scale3 = vec2(mix(1.0, mix(sr.x, sr.y, rand3.y), scale));
+
+ // randomized offset
+ vec2 offset1 = mix(vec2(offset_range.x), vec2(offset_range.y), rand1 * offset);
+ vec2 offset2 = mix(vec2(offset_range.x), vec2(offset_range.y), rand2 * offset);
+ vec2 offset3 = mix(vec2(offset_range.x), vec2(offset_range.y), rand3 * offset);
+
+ HextileData tile_data;
+ tile_data.weights = vec3(w1, w2, w3);
+ tile_data.rot_radian1 = rv1;
+ tile_data.rot_radian2 = rv2;
+ tile_data.rot_radian3 = rv3;
+
+ // get coord
+ tile_data.coord1 = ((coord - ctr1) * rm1 / scale1) + ctr1 + offset1;
+ tile_data.coord2 = ((coord - ctr2) * rm2 / scale2) + ctr2 + offset2;
+ tile_data.coord3 = ((coord - ctr3) * rm3 / scale3) + ctr3 + offset3;
+
+ // derivatives
+ vec2 ddx = dFdx(coord);
+ vec2 ddy = dFdy(coord);
+ tile_data.ddx1 = ddx * rm1 / scale1;
+ tile_data.ddx2 = ddx * rm2 / scale2;
+ tile_data.ddx3 = ddx * rm3 / scale3;
+ tile_data.ddy1 = ddy * rm1 / scale1;
+ tile_data.ddy2 = ddy * rm2 / scale2;
+ tile_data.ddy3 = ddy * rm3 / scale3;
+
+ return tile_data;
+}
diff --git a/templates/stdlib/genglsl/lib/mx_hsv.glsl b/templates/stdlib/genglsl/lib/mx_hsv.glsl
new file mode 100644
index 0000000000..1fb78b2202
--- /dev/null
+++ b/templates/stdlib/genglsl/lib/mx_hsv.glsl
@@ -0,0 +1,91 @@
+/*
+Color transform functions.
+
+These functions are modified versions of the color operators found in Open Shading Language:
+github.com/imageworks/OpenShadingLanguage/blob/master/src/liboslexec/opcolor.cpp
+
+It contains the subset of color operators needed to implement the MaterialX
+standard library. The modifications are for conversions from C++ to GLSL.
+
+Original copyright notice:
+------------------------------------------------------------------------
+Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
+All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+* Neither the name of Sony Pictures Imageworks nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+------------------------------------------------------------------------
+*/
+
+vec3 mx_hsvtorgb(vec3 hsv)
+{
+ // Reference for this technique: Foley & van Dam
+ float h = hsv.x; float s = hsv.y; float v = hsv.z;
+ if (s < 0.0001f) {
+ return vec3 (v, v, v);
+ } else {
+ h = 6.0f * (h - floor(h)); // expand to [0..6)
+ int hi = int(trunc(h));
+ float f = h - float(hi);
+ float p = v * (1.0f-s);
+ float q = v * (1.0f-s*f);
+ float t = v * (1.0f-s*(1.0f-f));
+ if (hi == 0)
+ return vec3 (v, t, p);
+ else if (hi == 1)
+ return vec3 (q, v, p);
+ else if (hi == 2)
+ return vec3 (p, v, t);
+ else if (hi == 3)
+ return vec3 (p, q, v);
+ else if (hi == 4)
+ return vec3 (t, p, v);
+ return vec3 (v, p, q);
+ }
+}
+
+
+vec3 mx_rgbtohsv(vec3 c)
+{
+ // See Foley & van Dam
+ float r = c.x; float g = c.y; float b = c.z;
+ float mincomp = min (r, min(g, b));
+ float maxcomp = max (r, max(g, b));
+ float delta = maxcomp - mincomp; // chroma
+ float h, s, v;
+ v = maxcomp;
+ if (maxcomp > 0.0f)
+ s = delta / maxcomp;
+ else s = 0.0f;
+ if (s <= 0.0f)
+ h = 0.0f;
+ else {
+ if (r >= maxcomp) h = (g-b) / delta;
+ else if (g >= maxcomp) h = 2.0f + (b-r) / delta;
+ else h = 4.0f + (r-g) / delta;
+ h *= (1.0f/6.0f);
+ if (h < 0.0f)
+ h += 1.0f;
+ }
+ return vec3(h, s, v);
+}
diff --git a/templates/stdlib/genglsl/lib/mx_math.glsl b/templates/stdlib/genglsl/lib/mx_math.glsl
new file mode 100644
index 0000000000..d196615e70
--- /dev/null
+++ b/templates/stdlib/genglsl/lib/mx_math.glsl
@@ -0,0 +1,35 @@
+#define M_FLOAT_EPS 1e-8
+
+#define mx_mod mod
+#define mx_inverse inverse
+#define mx_inversesqrt inversesqrt
+#define mx_sin sin
+#define mx_cos cos
+#define mx_tan tan
+#define mx_asin asin
+#define mx_acos acos
+#define mx_atan atan
+#define mx_radians radians
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+vec3 mx_srgb_encode(vec3 color)
+{
+ bvec3 isAbove = greaterThan(color, vec3(0.0031308));
+ vec3 linSeg = color * 12.92;
+ vec3 powSeg = 1.055 * pow(max(color, vec3(0.0)), vec3(1.0 / 2.4)) - 0.055;
+ return mix(linSeg, powSeg, isAbove);
+}
diff --git a/templates/stdlib/genglsl/lib/mx_noise.glsl b/templates/stdlib/genglsl/lib/mx_noise.glsl
new file mode 100644
index 0000000000..d837ccc0e1
--- /dev/null
+++ b/templates/stdlib/genglsl/lib/mx_noise.glsl
@@ -0,0 +1,746 @@
+/*
+Noise Library.
+
+This library is a modified version of the noise library found in
+Open Shading Language:
+github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
+
+It contains the subset of noise types needed to implement the MaterialX
+standard library. The modifications are mainly conversions from C++ to GLSL.
+Produced results should be identical to the OSL noise functions.
+
+Original copyright notice:
+------------------------------------------------------------------------
+Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
+All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+* Neither the name of Sony Pictures Imageworks nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+------------------------------------------------------------------------
+*/
+
+float mx_select(bool b, float t, float f)
+{
+ return b ? t : f;
+}
+
+float mx_negate_if(float val, bool b)
+{
+ return b ? -val : val;
+}
+
+int mx_floor(float x)
+{
+ return int(floor(x));
+}
+
+// return mx_floor as well as the fractional remainder
+float mx_floorfrac(float x, out int i)
+{
+ i = mx_floor(x);
+ return x - float(i);
+}
+
+float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
+{
+ float s1 = 1.0 - s;
+ return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
+}
+vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
+{
+ float s1 = 1.0 - s;
+ return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
+}
+float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
+{
+ float s1 = 1.0 - s;
+ float t1 = 1.0 - t;
+ float r1 = 1.0 - r;
+ return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
+ r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
+}
+vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
+{
+ float s1 = 1.0 - s;
+ float t1 = 1.0 - t;
+ float r1 = 1.0 - r;
+ return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
+ r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
+}
+
+// 2 and 3 dimensional gradient functions - perform a dot product against a
+// randomly chosen vector. Note that the gradient vector is not normalized, but
+// this only affects the overall "scale" of the result, so we simply account for
+// the scale by multiplying in the corresponding "perlin" function.
+float mx_gradient_float(uint hash, float x, float y)
+{
+ // 8 possible directions (+-1,+-2) and (+-2,+-1)
+ uint h = hash & 7u;
+ float u = mx_select(h<4u, x, y);
+ float v = 2.0 * mx_select(h<4u, y, x);
+ // compute the dot product with (x,y).
+ return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
+}
+float mx_gradient_float(uint hash, float x, float y, float z)
+{
+ // use vectors pointing to the edges of the cube
+ uint h = hash & 15u;
+ float u = mx_select(h<8u, x, y);
+ float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
+ return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
+}
+vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
+{
+ return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
+}
+vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
+{
+ return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
+}
+// Scaling factors to normalize the result of gradients above.
+// These factors were experimentally calculated to be:
+// 2D: 0.6616
+// 3D: 0.9820
+float mx_gradient_scale2d(float v) { return 0.6616 * v; }
+float mx_gradient_scale3d(float v) { return 0.9820 * v; }
+vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
+vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
+
+/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
+uint mx_rotl32(uint x, int k)
+{
+ return (x<>(32-k));
+}
+
+void mx_bjmix(inout uint a, inout uint b, inout uint c)
+{
+ a -= c; a ^= mx_rotl32(c, 4); c += b;
+ b -= a; b ^= mx_rotl32(a, 6); a += c;
+ c -= b; c ^= mx_rotl32(b, 8); b += a;
+ a -= c; a ^= mx_rotl32(c,16); c += b;
+ b -= a; b ^= mx_rotl32(a,19); a += c;
+ c -= b; c ^= mx_rotl32(b, 4); b += a;
+}
+
+// Mix up and combine the bits of a, b, and c (doesn't change them, but
+// returns a hash of those three original values).
+uint mx_bjfinal(uint a, uint b, uint c)
+{
+ c ^= b; c -= mx_rotl32(b,14);
+ a ^= c; a -= mx_rotl32(c,11);
+ b ^= a; b -= mx_rotl32(a,25);
+ c ^= b; c -= mx_rotl32(b,16);
+ a ^= c; a -= mx_rotl32(c,4);
+ b ^= a; b -= mx_rotl32(a,14);
+ c ^= b; c -= mx_rotl32(b,24);
+ return c;
+}
+
+// Convert a 32 bit integer into a floating point number in [0,1]
+float mx_bits_to_01(uint bits)
+{
+ return float(bits) / float(uint(0xffffffff));
+}
+
+float mx_fade(float t)
+{
+ return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
+}
+
+uint mx_hash_int(int x)
+{
+ uint len = 1u;
+ uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
+ return mx_bjfinal(seed+uint(x), seed, seed);
+}
+
+uint mx_hash_int(int x, int y)
+{
+ uint len = 2u;
+ uint a, b, c;
+ a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
+ a += uint(x);
+ b += uint(y);
+ return mx_bjfinal(a, b, c);
+}
+
+uint mx_hash_int(int x, int y, int z)
+{
+ uint len = 3u;
+ uint a, b, c;
+ a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
+ a += uint(x);
+ b += uint(y);
+ c += uint(z);
+ return mx_bjfinal(a, b, c);
+}
+
+uint mx_hash_int(int x, int y, int z, int xx)
+{
+ uint len = 4u;
+ uint a, b, c;
+ a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
+ a += uint(x);
+ b += uint(y);
+ c += uint(z);
+ mx_bjmix(a, b, c);
+ a += uint(xx);
+ return mx_bjfinal(a, b, c);
+}
+
+uint mx_hash_int(int x, int y, int z, int xx, int yy)
+{
+ uint len = 5u;
+ uint a, b, c;
+ a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
+ a += uint(x);
+ b += uint(y);
+ c += uint(z);
+ mx_bjmix(a, b, c);
+ a += uint(xx);
+ b += uint(yy);
+ return mx_bjfinal(a, b, c);
+}
+
+uvec3 mx_hash_vec3(int x, int y)
+{
+ uint h = mx_hash_int(x, y);
+ // we only need the low-order bits to be random, so split out
+ // the 32 bit result into 3 parts for each channel
+ uvec3 result;
+ result.x = (h ) & 0xFFu;
+ result.y = (h >> 8 ) & 0xFFu;
+ result.z = (h >> 16) & 0xFFu;
+ return result;
+}
+
+uvec3 mx_hash_vec3(int x, int y, int z)
+{
+ uint h = mx_hash_int(x, y, z);
+ // we only need the low-order bits to be random, so split out
+ // the 32 bit result into 3 parts for each channel
+ uvec3 result;
+ result.x = (h ) & 0xFFu;
+ result.y = (h >> 8 ) & 0xFFu;
+ result.z = (h >> 16) & 0xFFu;
+ return result;
+}
+
+float mx_perlin_noise_float(vec2 p)
+{
+ int X, Y;
+ float fx = mx_floorfrac(p.x, X);
+ float fy = mx_floorfrac(p.y, Y);
+ float u = mx_fade(fx);
+ float v = mx_fade(fy);
+ float result = mx_bilerp(
+ mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
+ mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
+ mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
+ mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
+ u, v);
+ return mx_gradient_scale2d(result);
+}
+
+float mx_perlin_noise_float(vec3 p)
+{
+ int X, Y, Z;
+ float fx = mx_floorfrac(p.x, X);
+ float fy = mx_floorfrac(p.y, Y);
+ float fz = mx_floorfrac(p.z, Z);
+ float u = mx_fade(fx);
+ float v = mx_fade(fy);
+ float w = mx_fade(fz);
+ float result = mx_trilerp(
+ mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
+ mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
+ mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
+ mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
+ mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
+ mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
+ mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
+ mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
+ u, v, w);
+ return mx_gradient_scale3d(result);
+}
+
+vec3 mx_perlin_noise_vec3(vec2 p)
+{
+ int X, Y;
+ float fx = mx_floorfrac(p.x, X);
+ float fy = mx_floorfrac(p.y, Y);
+ float u = mx_fade(fx);
+ float v = mx_fade(fy);
+ vec3 result = mx_bilerp(
+ mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
+ mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
+ mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
+ mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
+ u, v);
+ return mx_gradient_scale2d(result);
+}
+
+vec3 mx_perlin_noise_vec3(vec3 p)
+{
+ int X, Y, Z;
+ float fx = mx_floorfrac(p.x, X);
+ float fy = mx_floorfrac(p.y, Y);
+ float fz = mx_floorfrac(p.z, Z);
+ float u = mx_fade(fx);
+ float v = mx_fade(fy);
+ float w = mx_fade(fz);
+ vec3 result = mx_trilerp(
+ mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
+ mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
+ mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
+ mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
+ mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
+ mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
+ mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
+ mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
+ u, v, w);
+ return mx_gradient_scale3d(result);
+}
+
+float mx_cell_noise_float(float p)
+{
+ int ix = mx_floor(p);
+ return mx_bits_to_01(mx_hash_int(ix));
+}
+
+float mx_cell_noise_float(vec2 p)
+{
+ int ix = mx_floor(p.x);
+ int iy = mx_floor(p.y);
+ return mx_bits_to_01(mx_hash_int(ix, iy));
+}
+
+float mx_cell_noise_float(vec3 p)
+{
+ int ix = mx_floor(p.x);
+ int iy = mx_floor(p.y);
+ int iz = mx_floor(p.z);
+ return mx_bits_to_01(mx_hash_int(ix, iy, iz));
+}
+
+float mx_cell_noise_float(vec4 p)
+{
+ int ix = mx_floor(p.x);
+ int iy = mx_floor(p.y);
+ int iz = mx_floor(p.z);
+ int iw = mx_floor(p.w);
+ return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
+}
+
+vec3 mx_cell_noise_vec3(float p)
+{
+ int ix = mx_floor(p);
+ return vec3(
+ mx_bits_to_01(mx_hash_int(ix, 0)),
+ mx_bits_to_01(mx_hash_int(ix, 1)),
+ mx_bits_to_01(mx_hash_int(ix, 2))
+ );
+}
+
+vec3 mx_cell_noise_vec3(vec2 p)
+{
+ int ix = mx_floor(p.x);
+ int iy = mx_floor(p.y);
+ return vec3(
+ mx_bits_to_01(mx_hash_int(ix, iy, 0)),
+ mx_bits_to_01(mx_hash_int(ix, iy, 1)),
+ mx_bits_to_01(mx_hash_int(ix, iy, 2))
+ );
+}
+
+vec3 mx_cell_noise_vec3(vec3 p)
+{
+ int ix = mx_floor(p.x);
+ int iy = mx_floor(p.y);
+ int iz = mx_floor(p.z);
+ return vec3(
+ mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
+ mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
+ mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
+ );
+}
+
+vec3 mx_cell_noise_vec3(vec4 p)
+{
+ int ix = mx_floor(p.x);
+ int iy = mx_floor(p.y);
+ int iz = mx_floor(p.z);
+ int iw = mx_floor(p.w);
+ return vec3(
+ mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
+ mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
+ mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
+ );
+}
+
+float mx_fractal2d_noise_float(vec2 p, int octaves, float lacunarity, float diminish)
+{
+ float result = 0.0;
+ float amplitude = 1.0;
+ for (int i = 0; i < octaves; ++i)
+ {
+ result += amplitude * mx_perlin_noise_float(p);
+ amplitude *= diminish;
+ p *= lacunarity;
+ }
+ return result;
+}
+
+vec3 mx_fractal2d_noise_vec3(vec2 p, int octaves, float lacunarity, float diminish)
+{
+ vec3 result = vec3(0.0);
+ float amplitude = 1.0;
+ for (int i = 0; i < octaves; ++i)
+ {
+ result += amplitude * mx_perlin_noise_vec3(p);
+ amplitude *= diminish;
+ p *= lacunarity;
+ }
+ return result;
+}
+
+vec2 mx_fractal2d_noise_vec2(vec2 p, int octaves, float lacunarity, float diminish)
+{
+ return vec2(mx_fractal2d_noise_float(p, octaves, lacunarity, diminish),
+ mx_fractal2d_noise_float(p+vec2(19, 193), octaves, lacunarity, diminish));
+}
+
+vec4 mx_fractal2d_noise_vec4(vec2 p, int octaves, float lacunarity, float diminish)
+{
+ vec3 c = mx_fractal2d_noise_vec3(p, octaves, lacunarity, diminish);
+ float f = mx_fractal2d_noise_float(p+vec2(19, 193), octaves, lacunarity, diminish);
+ return vec4(c, f);
+}
+
+float mx_fractal3d_noise_float(vec3 p, int octaves, float lacunarity, float diminish)
+{
+ float result = 0.0;
+ float amplitude = 1.0;
+ for (int i = 0; i < octaves; ++i)
+ {
+ result += amplitude * mx_perlin_noise_float(p);
+ amplitude *= diminish;
+ p *= lacunarity;
+ }
+ return result;
+}
+
+vec3 mx_fractal3d_noise_vec3(vec3 p, int octaves, float lacunarity, float diminish)
+{
+ vec3 result = vec3(0.0);
+ float amplitude = 1.0;
+ for (int i = 0; i < octaves; ++i)
+ {
+ result += amplitude * mx_perlin_noise_vec3(p);
+ amplitude *= diminish;
+ p *= lacunarity;
+ }
+ return result;
+}
+
+vec2 mx_fractal3d_noise_vec2(vec3 p, int octaves, float lacunarity, float diminish)
+{
+ return vec2(mx_fractal3d_noise_float(p, octaves, lacunarity, diminish),
+ mx_fractal3d_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish));
+}
+
+vec4 mx_fractal3d_noise_vec4(vec3 p, int octaves, float lacunarity, float diminish)
+{
+ vec3 c = mx_fractal3d_noise_vec3(p, octaves, lacunarity, diminish);
+ float f = mx_fractal3d_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish);
+ return vec4(c, f);
+}
+
+vec2 mx_worley_cell_position(int x, int y, int xoff, int yoff, float jitter)
+{
+ vec3 tmp = mx_cell_noise_vec3(vec2(x+xoff, y+yoff));
+ vec2 off = vec2(tmp.x, tmp.y);
+
+ off -= 0.5f;
+ off *= jitter;
+ off += 0.5f;
+
+ return vec2(float(x), float(y)) + off;
+}
+
+vec3 mx_worley_cell_position(int x, int y, int z, int xoff, int yoff, int zoff, float jitter)
+{
+ vec3 off = mx_cell_noise_vec3(vec3(x+xoff, y+yoff, z+zoff));
+
+ off -= 0.5f;
+ off *= jitter;
+ off += 0.5f;
+
+ return vec3(float(x), float(y), float(z)) + off;
+}
+
+float mx_worley_distance(vec2 p, int x, int y, int xoff, int yoff, float jitter, int metric)
+{
+ vec2 cellpos = mx_worley_cell_position(x, y, xoff, yoff, jitter);
+ vec2 diff = cellpos - p;
+ if (metric == 2)
+ return abs(diff.x) + abs(diff.y); // Manhattan distance
+ if (metric == 3)
+ return max(abs(diff.x), abs(diff.y)); // Chebyshev distance
+ // Either Euclidean or Distance^2
+ return dot(diff, diff);
+}
+
+float mx_worley_distance(vec3 p, int x, int y, int z, int xoff, int yoff, int zoff, float jitter, int metric)
+{
+ vec3 cellpos = mx_worley_cell_position(x, y, z, xoff, yoff, zoff, jitter);
+ vec3 diff = cellpos - p;
+ if (metric == 2)
+ return abs(diff.x) + abs(diff.y) + abs(diff.z); // Manhattan distance
+ if (metric == 3)
+ return max(max(abs(diff.x), abs(diff.y)), abs(diff.z)); // Chebyshev distance
+ // Either Euclidean or Distance^2
+ return dot(diff, diff);
+}
+
+float mx_worley_noise_float(vec2 p, float jitter, int style, int metric)
+{
+ int X, Y;
+ float dist;
+ vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
+ float sqdist = 1e6f; // Some big number for jitter > 1 (not all GPUs may be IEEE)
+ vec2 minpos = vec2(0,0);
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
+ vec2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
+ if(dist < sqdist)
+ {
+ sqdist = dist;
+ minpos = cellpos;
+ }
+ }
+ }
+ if (style == 1)
+ return mx_cell_noise_float(minpos + p);
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
+
+vec2 mx_worley_noise_vec2(vec2 p, float jitter, int style, int metric)
+{
+ int X, Y;
+ vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
+ vec2 sqdist = vec2(1e6f, 1e6f);
+ vec2 minpos = vec2(0,0);
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
+ vec2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
+ if (dist < sqdist.x)
+ {
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ minpos = cellpos;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.y = dist;
+ }
+ }
+ }
+ if (style == 1)
+ {
+ vec3 tmp = mx_cell_noise_vec3(minpos + p);
+ return vec2(tmp.x,tmp.y);
+ }
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
+
+vec3 mx_worley_noise_vec3(vec2 p, float jitter, int style, int metric)
+{
+ int X, Y;
+ vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
+ vec3 sqdist = vec3(1e6f, 1e6f, 1e6f);
+ vec2 minpos = vec2(0,0);
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
+ vec2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
+ if (dist < sqdist.x)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ minpos = cellpos;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = dist;
+ }
+ else if (dist < sqdist.z)
+ {
+ sqdist.z = dist;
+ }
+ }
+ }
+ if (style == 1)
+ return mx_cell_noise_vec3(minpos + p);
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
+
+float mx_worley_noise_float(vec3 p, float jitter, int style, int metric)
+{
+ int X, Y, Z;
+ vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
+ float sqdist = 1e6f;
+ vec3 minpos = vec3(0,0,0);
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ for (int z = -1; z <= 1; ++z)
+ {
+ float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
+ vec3 cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
+ if(dist < sqdist)
+ {
+ sqdist = dist;
+ minpos = cellpos;
+ }
+ }
+ }
+ }
+ if (style == 1)
+ return mx_cell_noise_float(minpos + p);
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
+
+vec2 mx_worley_noise_vec2(vec3 p, float jitter, int style, int metric)
+{
+ int X, Y, Z;
+ vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
+ vec2 sqdist = vec2(1e6f, 1e6f);
+ vec3 minpos = vec3(0,0,0);
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ for (int z = -1; z <= 1; ++z)
+ {
+ float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
+ vec3 cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
+ if (dist < sqdist.x)
+ {
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ minpos = cellpos;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.y = dist;
+ }
+ }
+ }
+ }
+ if (style == 1)
+ {
+ vec3 tmp = mx_cell_noise_vec3(minpos + p);
+ return vec2(tmp.x,tmp.y);
+ }
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
+
+vec3 mx_worley_noise_vec3(vec3 p, float jitter, int style, int metric)
+{
+ int X, Y, Z;
+ vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
+ vec3 sqdist = vec3(1e6f, 1e6f, 1e6f);
+ vec3 minpos = vec3(0,0,0);
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ for (int z = -1; z <= 1; ++z)
+ {
+ float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
+ vec3 cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
+ if (dist < sqdist.x)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ minpos = cellpos;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = dist;
+ }
+ else if (dist < sqdist.z)
+ {
+ sqdist.z = dist;
+ }
+ }
+ }
+ }
+ if (style == 1)
+ return mx_cell_noise_vec3(minpos + p);
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
diff --git a/templates/stdlib/genglsl/lib/mx_transform_uv.glsl b/templates/stdlib/genglsl/lib/mx_transform_uv.glsl
new file mode 100644
index 0000000000..3329925222
--- /dev/null
+++ b/templates/stdlib/genglsl/lib/mx_transform_uv.glsl
@@ -0,0 +1,5 @@
+vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+{
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+}
diff --git a/templates/stdlib/genglsl/lib/mx_transform_uv_vflip.glsl b/templates/stdlib/genglsl/lib/mx_transform_uv_vflip.glsl
new file mode 100644
index 0000000000..7e1b2e77e2
--- /dev/null
+++ b/templates/stdlib/genglsl/lib/mx_transform_uv_vflip.glsl
@@ -0,0 +1,5 @@
+vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+{
+ uv = uv * uv_scale + uv_offset;
+ return vec2(uv.x, 1.0 - uv.y);
+}
diff --git a/templates/stdlib/genglsl/mx_aastep.glsl b/templates/stdlib/genglsl/mx_aastep.glsl
new file mode 100644
index 0000000000..463c7c2083
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_aastep.glsl
@@ -0,0 +1,5 @@
+float mx_aastep(float threshold, float value)
+{
+ float afwidth = length(vec2(dFdx(value), dFdy(value))) * 0.70710678118654757;
+ return smoothstep(threshold-afwidth, threshold+afwidth, value);
+}
diff --git a/templates/stdlib/genglsl/mx_burn_color3.glsl b/templates/stdlib/genglsl/mx_burn_color3.glsl
new file mode 100644
index 0000000000..8e60f857d6
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_burn_color3.glsl
@@ -0,0 +1,9 @@
+#include "mx_burn_float.glsl"
+
+void mx_burn_color3(vec3 fg, vec3 bg, float mixval, out vec3 result)
+{
+ float f;
+ mx_burn_float(fg.x, bg.x, mixval, f); result.x = f;
+ mx_burn_float(fg.y, bg.y, mixval, f); result.y = f;
+ mx_burn_float(fg.z, bg.z, mixval, f); result.z = f;
+}
diff --git a/templates/stdlib/genglsl/mx_burn_color4.glsl b/templates/stdlib/genglsl/mx_burn_color4.glsl
new file mode 100644
index 0000000000..a1add5a372
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_burn_color4.glsl
@@ -0,0 +1,10 @@
+#include "mx_burn_float.glsl"
+
+void mx_burn_color4(vec4 fg, vec4 bg, float mixval, out vec4 result)
+{
+ float f;
+ mx_burn_float(fg.x, bg.x, mixval, f); result.x = f;
+ mx_burn_float(fg.y, bg.y, mixval, f); result.y = f;
+ mx_burn_float(fg.z, bg.z, mixval, f); result.z = f;
+ mx_burn_float(fg.w, bg.w, mixval, f); result.w = f;
+}
diff --git a/templates/stdlib/genglsl/mx_burn_float.glsl b/templates/stdlib/genglsl/mx_burn_float.glsl
new file mode 100644
index 0000000000..31d981ddb9
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_burn_float.glsl
@@ -0,0 +1,9 @@
+void mx_burn_float(float fg, float bg, float mixval, out float result)
+{
+ if (abs(fg) < M_FLOAT_EPS)
+ {
+ result = 0.0;
+ return;
+ }
+ result = mixval*(1.0 - ((1.0 - bg) / fg)) + ((1.0-mixval)*bg);
+}
diff --git a/templates/stdlib/genglsl/mx_cellnoise2d_float.glsl b/templates/stdlib/genglsl/mx_cellnoise2d_float.glsl
new file mode 100644
index 0000000000..97d1257e97
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_cellnoise2d_float.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_noise.glsl"
+
+void mx_cellnoise2d_float(vec2 texcoord, out float result)
+{
+ result = mx_cell_noise_float(texcoord);
+}
diff --git a/templates/stdlib/genglsl/mx_cellnoise3d_float.glsl b/templates/stdlib/genglsl/mx_cellnoise3d_float.glsl
new file mode 100644
index 0000000000..ae4b848fc8
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_cellnoise3d_float.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_noise.glsl"
+
+void mx_cellnoise3d_float(vec3 position, out float result)
+{
+ result = mx_cell_noise_float(position);
+}
diff --git a/templates/stdlib/genglsl/mx_creatematrix_vector3_matrix33.glsl b/templates/stdlib/genglsl/mx_creatematrix_vector3_matrix33.glsl
new file mode 100644
index 0000000000..aa10ef5b98
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_creatematrix_vector3_matrix33.glsl
@@ -0,0 +1,6 @@
+void mx_creatematrix_vector3_matrix33(vec3 in1, vec3 in2, vec3 in3, out mat3 result)
+{
+ result = mat3(in1.x, in1.y, in1.z,
+ in2.x, in2.y, in2.z,
+ in3.x, in3.y, in3.z);
+}
diff --git a/templates/stdlib/genglsl/mx_creatematrix_vector3_matrix44.glsl b/templates/stdlib/genglsl/mx_creatematrix_vector3_matrix44.glsl
new file mode 100644
index 0000000000..194fad421a
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_creatematrix_vector3_matrix44.glsl
@@ -0,0 +1,7 @@
+void mx_creatematrix_vector3_matrix44(vec3 in1, vec3 in2, vec3 in3, vec3 in4, out mat4 result)
+{
+ result = mat4(in1.x, in1.y, in1.z, 0.0,
+ in2.x, in2.y, in2.z, 0.0,
+ in3.x, in3.y, in3.z, 0.0,
+ in4.x, in4.y, in4.z, 1.0);
+}
diff --git a/templates/stdlib/genglsl/mx_creatematrix_vector4_matrix44.glsl b/templates/stdlib/genglsl/mx_creatematrix_vector4_matrix44.glsl
new file mode 100644
index 0000000000..665a22212b
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_creatematrix_vector4_matrix44.glsl
@@ -0,0 +1,7 @@
+void mx_creatematrix_vector4_matrix44(vec4 in1, vec4 in2, vec4 in3, vec4 in4, out mat4 result)
+{
+ result = mat4(in1.x, in1.y, in1.z, in1.w,
+ in2.x, in2.y, in2.z, in2.w,
+ in3.x, in3.y, in3.z, in3.w,
+ in4.x, in4.y, in4.z, in4.w);
+}
diff --git a/templates/stdlib/genglsl/mx_disjointover_color4.glsl b/templates/stdlib/genglsl/mx_disjointover_color4.glsl
new file mode 100644
index 0000000000..7daaf351aa
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_disjointover_color4.glsl
@@ -0,0 +1,25 @@
+void mx_disjointover_color4(vec4 fg, vec4 bg, float mixval, out vec4 result)
+{
+ float summedAlpha = fg.w + bg.w;
+
+ if (summedAlpha <= 1.0)
+ {
+ result.xyz = fg.xyz + bg.xyz;
+ }
+ else
+ {
+ if (abs(bg.w) < M_FLOAT_EPS)
+ {
+ result.xyz = vec3(0.0);
+ }
+ else
+ {
+ float x = (1.0 - fg.w) / bg.w;
+ result.xyz = fg.xyz + bg.xyz * x;
+ }
+ }
+ result.w = min(summedAlpha, 1.0);
+
+ result.xyz = result.xyz * mixval + (1.0 - mixval) * bg.xyz;
+ result.w = result.w * mixval + (1.0 - mixval) * bg.w;
+}
diff --git a/templates/stdlib/genglsl/mx_dodge_color3.glsl b/templates/stdlib/genglsl/mx_dodge_color3.glsl
new file mode 100644
index 0000000000..aa9cf023a6
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_dodge_color3.glsl
@@ -0,0 +1,9 @@
+#include "mx_dodge_float.glsl"
+
+void mx_dodge_color3(vec3 fg, vec3 bg, float mixval, out vec3 result)
+{
+ float f;
+ mx_dodge_float(fg.x, bg.x, mixval, f); result.x = f;
+ mx_dodge_float(fg.y, bg.y, mixval, f); result.y = f;
+ mx_dodge_float(fg.z, bg.z, mixval, f); result.z = f;
+}
diff --git a/templates/stdlib/genglsl/mx_dodge_color4.glsl b/templates/stdlib/genglsl/mx_dodge_color4.glsl
new file mode 100644
index 0000000000..25888219b6
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_dodge_color4.glsl
@@ -0,0 +1,10 @@
+#include "mx_dodge_float.glsl"
+
+void mx_dodge_color4(vec4 fg , vec4 bg , float mixval, out vec4 result)
+{
+ float f;
+ mx_dodge_float(fg.x, bg.x, mixval, f); result.x = f;
+ mx_dodge_float(fg.y, bg.y, mixval, f); result.y = f;
+ mx_dodge_float(fg.z, bg.z, mixval, f); result.z = f;
+ mx_dodge_float(fg.w, bg.w, mixval, f); result.w = f;
+}
diff --git a/templates/stdlib/genglsl/mx_dodge_float.glsl b/templates/stdlib/genglsl/mx_dodge_float.glsl
new file mode 100644
index 0000000000..f138354138
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_dodge_float.glsl
@@ -0,0 +1,9 @@
+void mx_dodge_float(float fg, float bg, float mixval, out float result)
+{
+ if (abs(1.0 - fg) < M_FLOAT_EPS)
+ {
+ result = 0.0;
+ return;
+ }
+ result = mixval*(bg / (1.0 - fg)) + ((1.0-mixval)*bg);
+}
diff --git a/templates/stdlib/genglsl/mx_fractal2d_float.glsl b/templates/stdlib/genglsl/mx_fractal2d_float.glsl
new file mode 100644
index 0000000000..4cb6f202d1
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_fractal2d_float.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_fractal2d_float(float amplitude, int octaves, float lacunarity, float diminish, vec2 texcoord, out float result)
+{
+ float value = mx_fractal2d_noise_float(texcoord, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
diff --git a/templates/stdlib/genglsl/mx_fractal2d_vector2.glsl b/templates/stdlib/genglsl/mx_fractal2d_vector2.glsl
new file mode 100644
index 0000000000..3d409690a7
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_fractal2d_vector2.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_fractal2d_vector2(vec2 amplitude, int octaves, float lacunarity, float diminish, vec2 texcoord, out vec2 result)
+{
+ vec2 value = mx_fractal2d_noise_vec2(texcoord, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
diff --git a/templates/stdlib/genglsl/mx_fractal2d_vector3.glsl b/templates/stdlib/genglsl/mx_fractal2d_vector3.glsl
new file mode 100644
index 0000000000..3acab91d68
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_fractal2d_vector3.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_fractal2d_vector3(vec3 amplitude, int octaves, float lacunarity, float diminish, vec2 texcoord, out vec3 result)
+{
+ vec3 value = mx_fractal2d_noise_vec3(texcoord, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
diff --git a/templates/stdlib/genglsl/mx_fractal2d_vector4.glsl b/templates/stdlib/genglsl/mx_fractal2d_vector4.glsl
new file mode 100644
index 0000000000..de9085a4e7
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_fractal2d_vector4.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_fractal2d_vector4(vec4 amplitude, int octaves, float lacunarity, float diminish, vec2 texcoord, out vec4 result)
+{
+ vec4 value = mx_fractal2d_noise_vec4(texcoord, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
diff --git a/templates/stdlib/genglsl/mx_fractal3d_float.glsl b/templates/stdlib/genglsl/mx_fractal3d_float.glsl
new file mode 100644
index 0000000000..39a6016a4b
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_fractal3d_float.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_fractal3d_float(float amplitude, int octaves, float lacunarity, float diminish, vec3 position, out float result)
+{
+ float value = mx_fractal3d_noise_float(position, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
diff --git a/templates/stdlib/genglsl/mx_fractal3d_vector2.glsl b/templates/stdlib/genglsl/mx_fractal3d_vector2.glsl
new file mode 100644
index 0000000000..d211f007bb
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_fractal3d_vector2.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_fractal3d_vector2(vec2 amplitude, int octaves, float lacunarity, float diminish, vec3 position, out vec2 result)
+{
+ vec2 value = mx_fractal3d_noise_vec2(position, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
diff --git a/templates/stdlib/genglsl/mx_fractal3d_vector3.glsl b/templates/stdlib/genglsl/mx_fractal3d_vector3.glsl
new file mode 100644
index 0000000000..df21f38648
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_fractal3d_vector3.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_fractal3d_vector3(vec3 amplitude, int octaves, float lacunarity, float diminish, vec3 position, out vec3 result)
+{
+ vec3 value = mx_fractal3d_noise_vec3(position, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
diff --git a/templates/stdlib/genglsl/mx_fractal3d_vector4.glsl b/templates/stdlib/genglsl/mx_fractal3d_vector4.glsl
new file mode 100644
index 0000000000..21d7282495
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_fractal3d_vector4.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_fractal3d_vector4(vec4 amplitude, int octaves, float lacunarity, float diminish, vec3 position, out vec4 result)
+{
+ vec4 value = mx_fractal3d_noise_vec4(position, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
diff --git a/templates/stdlib/genglsl/mx_heighttonormal_vector3.glsl b/templates/stdlib/genglsl/mx_heighttonormal_vector3.glsl
new file mode 100644
index 0000000000..a71a6526de
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_heighttonormal_vector3.glsl
@@ -0,0 +1,28 @@
+void mx_heighttonormal_vector3(float height, float scale, vec2 texcoord, out vec3 result)
+{
+ // Scale factor for parity with traditional Sobel filtering.
+ const float SOBEL_SCALE_FACTOR = 1.0 / 16.0;
+
+ // Compute screen-space gradients of the heightfield and texture coordinates.
+ vec2 dHdS = vec2(dFdx(height), dFdy(height)) * scale * SOBEL_SCALE_FACTOR;
+ vec2 dUdS = vec2(dFdx(texcoord.x), dFdy(texcoord.x));
+ vec2 dVdS = vec2(dFdx(texcoord.y), dFdy(texcoord.y));
+
+ // Construct a screen-space tangent frame.
+ vec3 tangent = vec3(dUdS.x, dVdS.x, dHdS.x);
+ vec3 bitangent = vec3(dUdS.y, dVdS.y, dHdS.y);
+ vec3 n = cross(tangent, bitangent);
+
+ // Handle invalid and mirrored texture coordinates.
+ if (dot(n, n) < M_FLOAT_EPS * M_FLOAT_EPS)
+ {
+ n = vec3(0, 0, 1);
+ }
+ else if (n.z < 0.0)
+ {
+ n *= -1.0;
+ }
+
+ // Normalize and encode the results.
+ result = normalize(n) * 0.5 + 0.5;
+}
diff --git a/templates/stdlib/genglsl/mx_hextiledimage.glsl b/templates/stdlib/genglsl/mx_hextiledimage.glsl
new file mode 100644
index 0000000000..e739725bcd
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_hextiledimage.glsl
@@ -0,0 +1,102 @@
+#include "lib/$fileTransformUv"
+#include "lib/mx_hextile.glsl"
+
+// Morten S. Mikkelsen, Practical Real-Time Hex-Tiling, Journal of Computer Graphics
+// Techniques (JCGT), vol. 11, no. 2, 77-94, 2022
+// http://jcgt.org/published/0011/03/05/
+void mx_hextiledimage_color3(
+ $texSamplerSignature,
+ vec3 default_value,
+ vec2 tex_coord,
+ vec2 tiling,
+ float rotation,
+ vec2 rotation_range,
+ float scale,
+ vec2 scale_range,
+ float offset,
+ vec2 offset_range,
+ float falloff,
+ float falloff_contrast,
+ vec3 lumacoeffs,
+ out vec3 result
+)
+{
+ vec2 coord = mx_transform_uv(tex_coord, tiling, vec2(0.0));
+
+ HextileData tile_data = mx_hextile_coord(coord, rotation, rotation_range, scale, scale_range, offset, offset_range);
+
+ vec3 c1 = textureGrad($texSamplerSampler2D, tile_data.coord1, tile_data.ddx1, tile_data.ddy1).rgb;
+ vec3 c2 = textureGrad($texSamplerSampler2D, tile_data.coord2, tile_data.ddx2, tile_data.ddy2).rgb;
+ vec3 c3 = textureGrad($texSamplerSampler2D, tile_data.coord3, tile_data.ddx3, tile_data.ddy3).rgb;
+
+ // luminance as weights
+ vec3 cw = vec3(dot(c1, lumacoeffs), dot(c2, lumacoeffs), dot(c3, lumacoeffs));
+ cw = mix(vec3(1.0), cw, vec3(falloff_contrast));
+
+ // blend weights
+ vec3 w = cw * pow(tile_data.weights, vec3(7.0));
+ w /= (w.x + w.y + w.z);
+
+ // apply s-curve gain
+ if (falloff != 0.5)
+ {
+ w.x = mx_schlick_gain(w.x, falloff);
+ w.y = mx_schlick_gain(w.y, falloff);
+ w.z = mx_schlick_gain(w.z, falloff);
+ w /= (w.x + w.y + w.z);
+ }
+
+ // blend
+ result = vec3(w.x * c1 + w.y * c2 + w.z * c3);
+}
+
+void mx_hextiledimage_color4(
+ $texSamplerSignature,
+ vec4 default_value,
+ vec2 tex_coord,
+ vec2 tiling,
+ float rotation,
+ vec2 rotation_range,
+ float scale,
+ vec2 scale_range,
+ float offset,
+ vec2 offset_range,
+ float falloff,
+ float falloff_contrast,
+ vec3 lumacoeffs,
+ out vec4 result
+)
+{
+ vec2 coord = mx_transform_uv(tex_coord, tiling, vec2(0.0));
+
+ HextileData tile_data = mx_hextile_coord(coord, rotation, rotation_range, scale, scale_range, offset, offset_range);
+
+ vec4 c1 = textureGrad($texSamplerSampler2D, tile_data.coord1, tile_data.ddx1, tile_data.ddy1);
+ vec4 c2 = textureGrad($texSamplerSampler2D, tile_data.coord2, tile_data.ddx2, tile_data.ddy2);
+ vec4 c3 = textureGrad($texSamplerSampler2D, tile_data.coord3, tile_data.ddx3, tile_data.ddy3);
+
+ // luminance as weights
+ vec3 cw = vec3(dot(c1.rgb, lumacoeffs), dot(c2.rgb, lumacoeffs), dot(c3.rgb, lumacoeffs));
+ cw = mix(vec3(1.0), cw, vec3(falloff_contrast));
+
+ // blend weights
+ vec3 w = cw * pow(tile_data.weights, vec3(7.0));
+ w /= (w.x + w.y + w.z);
+
+ // alpha
+ float a = (c1.a + c2.a + c3.a) / 3.0;
+
+ // apply s-curve gain
+ if (falloff != 0.5)
+ {
+ w.x = mx_schlick_gain(w.x, falloff);
+ w.y = mx_schlick_gain(w.y, falloff);
+ w.z = mx_schlick_gain(w.z, falloff);
+ w /= (w.x + w.y + w.z);
+ a = mx_schlick_gain(a, falloff);
+ }
+
+ // blend
+ result.rgb = vec3(w.x * c1 + w.y * c2 + w.z * c3);
+ result.a = a;
+}
diff --git a/templates/stdlib/genglsl/mx_hextilednormalmap.glsl b/templates/stdlib/genglsl/mx_hextilednormalmap.glsl
new file mode 100644
index 0000000000..73727fa381
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_hextilednormalmap.glsl
@@ -0,0 +1,72 @@
+#include "lib/$fileTransformUv"
+#include "lib/mx_hextile.glsl"
+#include "lib/mx_geometry.glsl"
+
+// Morten S. Mikkelsen, Practical Real-Time Hex-Tiling, Journal of Computer Graphics
+// Techniques (JCGT), vol. 11, no. 2, 77-94, 2022
+// http://jcgt.org/published/0011/03/05/
+void mx_hextilednormalmap_vector3(
+ $texSamplerSignature,
+ vec3 default_value,
+ vec2 tex_coord,
+ vec2 tiling,
+ float rotation,
+ vec2 rotation_range,
+ float scale,
+ vec2 scale_range,
+ float offset,
+ vec2 offset_range,
+ float falloff,
+ float strength,
+ bool flip_g,
+ vec3 N,
+ vec3 T,
+ vec3 B,
+ out vec3 result
+)
+{
+ vec2 coord = mx_transform_uv(tex_coord, tiling, vec2(0.0));
+
+ HextileData tile_data = mx_hextile_coord(coord, rotation, rotation_range, scale, scale_range, offset, offset_range);
+
+ vec3 nm1 = textureGrad($texSamplerSampler2D, tile_data.coord1, tile_data.ddx1, tile_data.ddy1).xyz;
+ vec3 nm2 = textureGrad($texSamplerSampler2D, tile_data.coord2, tile_data.ddx2, tile_data.ddy2).xyz;
+ vec3 nm3 = textureGrad($texSamplerSampler2D, tile_data.coord3, tile_data.ddx3, tile_data.ddy3).xyz;
+
+ nm1.y = flip_g ? 1.0 - nm1.y : nm1.y;
+ nm2.y = flip_g ? 1.0 - nm2.y : nm2.y;
+ nm3.y = flip_g ? 1.0 - nm3.y : nm3.y;
+
+ // normalmap to shading normal
+ nm1 = 2.0 * nm1 - 1.0;
+ nm2 = 2.0 * nm2 - 1.0;
+ nm3 = 2.0 * nm3 - 1.0;
+ mat3 tangent_rot_mat1 = mx_axis_rotation_matrix(N, -tile_data.rot_radian1);
+ mat3 tangent_rot_mat2 = mx_axis_rotation_matrix(N, -tile_data.rot_radian2);
+ mat3 tangent_rot_mat3 = mx_axis_rotation_matrix(N, -tile_data.rot_radian3);
+ vec3 T1 = tangent_rot_mat1 * T * strength;
+ vec3 T2 = tangent_rot_mat2 * T * strength;
+ vec3 T3 = tangent_rot_mat3 * T * strength;
+ vec3 B1 = tangent_rot_mat1 * B * strength;
+ vec3 B2 = tangent_rot_mat2 * B * strength;
+ vec3 B3 = tangent_rot_mat3 * B * strength;
+ vec3 N1 = normalize(T1 * nm1.x + B1 * nm1.y + N * nm1.z);
+ vec3 N2 = normalize(T2 * nm2.x + B2 * nm2.y + N * nm2.z);
+ vec3 N3 = normalize(T3 * nm3.x + B3 * nm3.y + N * nm3.z);
+
+ // blend weights
+ vec3 w = pow(tile_data.weights, vec3(7.0));
+ w /= (w.x + w.y + w.z);
+
+ // apply s-curve gain
+ if (falloff != 0.5)
+ {
+ w.x = mx_schlick_gain(w.x, falloff);
+ w.y = mx_schlick_gain(w.y, falloff);
+ w.z = mx_schlick_gain(w.z, falloff);
+ w /= (w.x + w.y + w.z);
+ }
+
+ // blend
+ result = mx_gradient_blend_3_normals(N, N1, w.x, N2, w.y, N3, w.z);
+}
diff --git a/templates/stdlib/genglsl/mx_hsvtorgb_color3.glsl b/templates/stdlib/genglsl/mx_hsvtorgb_color3.glsl
new file mode 100644
index 0000000000..535f6529b6
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_hsvtorgb_color3.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_hsv.glsl"
+
+void mx_hsvtorgb_color3(vec3 _in, out vec3 result)
+{
+ result = mx_hsvtorgb(_in);
+}
diff --git a/templates/stdlib/genglsl/mx_hsvtorgb_color4.glsl b/templates/stdlib/genglsl/mx_hsvtorgb_color4.glsl
new file mode 100644
index 0000000000..6f401bb202
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_hsvtorgb_color4.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_hsv.glsl"
+
+void mx_hsvtorgb_color4(vec4 _in, out vec4 result)
+{
+ result = vec4(mx_hsvtorgb(_in.rgb), 1.0);
+}
diff --git a/templates/stdlib/genglsl/mx_image_color3.glsl b/templates/stdlib/genglsl/mx_image_color3.glsl
new file mode 100644
index 0000000000..1ec33a7283
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_image_color3.glsl
@@ -0,0 +1,7 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_color3($texSamplerSignature, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result)
+{
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture($texSamplerSampler2D, uv).rgb;
+}
diff --git a/templates/stdlib/genglsl/mx_image_color4.glsl b/templates/stdlib/genglsl/mx_image_color4.glsl
new file mode 100644
index 0000000000..67be33b046
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_image_color4.glsl
@@ -0,0 +1,7 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_color4($texSamplerSignature, int layer, vec4 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec4 result)
+{
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture($texSamplerSampler2D, uv);
+}
diff --git a/templates/stdlib/genglsl/mx_image_float.glsl b/templates/stdlib/genglsl/mx_image_float.glsl
new file mode 100644
index 0000000000..608bb4d5e5
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_image_float.glsl
@@ -0,0 +1,7 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_float($texSamplerSignature, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out float result)
+{
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture($texSamplerSampler2D, uv).r;
+}
diff --git a/templates/stdlib/genglsl/mx_image_vector2.glsl b/templates/stdlib/genglsl/mx_image_vector2.glsl
new file mode 100644
index 0000000000..3a9f140c76
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_image_vector2.glsl
@@ -0,0 +1,7 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_vector2($texSamplerSignature, int layer, vec2 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec2 result)
+{
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture($texSamplerSampler2D, uv).rg;
+}
diff --git a/templates/stdlib/genglsl/mx_image_vector3.glsl b/templates/stdlib/genglsl/mx_image_vector3.glsl
new file mode 100644
index 0000000000..bda1c421bd
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_image_vector3.glsl
@@ -0,0 +1,7 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_vector3($texSamplerSignature, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result)
+{
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture($texSamplerSampler2D, uv).rgb;
+}
diff --git a/templates/stdlib/genglsl/mx_image_vector4.glsl b/templates/stdlib/genglsl/mx_image_vector4.glsl
new file mode 100644
index 0000000000..884b69a5dc
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_image_vector4.glsl
@@ -0,0 +1,7 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_vector4($texSamplerSignature, int layer, vec4 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec4 result)
+{
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture($texSamplerSampler2D, uv);
+}
diff --git a/templates/stdlib/genglsl/mx_luminance_color3.glsl b/templates/stdlib/genglsl/mx_luminance_color3.glsl
new file mode 100644
index 0000000000..d60ba93a60
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_luminance_color3.glsl
@@ -0,0 +1,4 @@
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
diff --git a/templates/stdlib/genglsl/mx_luminance_color4.glsl b/templates/stdlib/genglsl/mx_luminance_color4.glsl
new file mode 100644
index 0000000000..28e7943000
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_luminance_color4.glsl
@@ -0,0 +1,4 @@
+void mx_luminance_color4(vec4 _in, vec3 lumacoeffs, out vec4 result)
+{
+ result = vec4(vec3(dot(_in.rgb, lumacoeffs)), _in.a);
+}
diff --git a/templates/stdlib/genglsl/mx_mix_surfaceshader.glsl b/templates/stdlib/genglsl/mx_mix_surfaceshader.glsl
new file mode 100644
index 0000000000..4ae6a0b446
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_mix_surfaceshader.glsl
@@ -0,0 +1,5 @@
+void mx_mix_surfaceshader(surfaceshader fg, surfaceshader bg, float w, out surfaceshader returnshader)
+{
+ returnshader.color = mix(bg.color, fg.color, w);
+ returnshader.transparency = mix(bg.transparency, fg.transparency, w);
+}
diff --git a/templates/stdlib/genglsl/mx_noise2d_float.glsl b/templates/stdlib/genglsl/mx_noise2d_float.glsl
new file mode 100644
index 0000000000..4949083720
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_noise2d_float.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_noise2d_float(float amplitude, float pivot, vec2 texcoord, out float result)
+{
+ float value = mx_perlin_noise_float(texcoord);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genglsl/mx_noise2d_vector2.glsl b/templates/stdlib/genglsl/mx_noise2d_vector2.glsl
new file mode 100644
index 0000000000..a6c890f551
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_noise2d_vector2.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_noise2d_vector2(vec2 amplitude, float pivot, vec2 texcoord, out vec2 result)
+{
+ vec3 value = mx_perlin_noise_vec3(texcoord);
+ result = value.xy * amplitude + pivot;
+}
diff --git a/templates/stdlib/genglsl/mx_noise2d_vector3.glsl b/templates/stdlib/genglsl/mx_noise2d_vector3.glsl
new file mode 100644
index 0000000000..f965e159a9
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_noise2d_vector3.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_noise2d_vector3(vec3 amplitude, float pivot, vec2 texcoord, out vec3 result)
+{
+ vec3 value = mx_perlin_noise_vec3(texcoord);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genglsl/mx_noise2d_vector4.glsl b/templates/stdlib/genglsl/mx_noise2d_vector4.glsl
new file mode 100644
index 0000000000..3a723962e3
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_noise2d_vector4.glsl
@@ -0,0 +1,8 @@
+#include "lib/mx_noise.glsl"
+
+void mx_noise2d_vector4(vec4 amplitude, float pivot, vec2 texcoord, out vec4 result)
+{
+ vec3 xyz = mx_perlin_noise_vec3(texcoord);
+ float w = mx_perlin_noise_float(texcoord + vec2(19, 73));
+ result = vec4(xyz, w) * amplitude + pivot;
+}
diff --git a/templates/stdlib/genglsl/mx_noise3d_float.glsl b/templates/stdlib/genglsl/mx_noise3d_float.glsl
new file mode 100644
index 0000000000..f30e3a264c
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_noise3d_float.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_noise3d_float(float amplitude, float pivot, vec3 position, out float result)
+{
+ float value = mx_perlin_noise_float(position);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genglsl/mx_noise3d_vector2.glsl b/templates/stdlib/genglsl/mx_noise3d_vector2.glsl
new file mode 100644
index 0000000000..aa22814353
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_noise3d_vector2.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_noise3d_vector2(vec2 amplitude, float pivot, vec3 position, out vec2 result)
+{
+ vec3 value = mx_perlin_noise_vec3(position);
+ result = value.xy * amplitude + pivot;
+}
diff --git a/templates/stdlib/genglsl/mx_noise3d_vector3.glsl b/templates/stdlib/genglsl/mx_noise3d_vector3.glsl
new file mode 100644
index 0000000000..f23313aadf
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_noise3d_vector3.glsl
@@ -0,0 +1,7 @@
+#include "lib/mx_noise.glsl"
+
+void mx_noise3d_vector3(vec3 amplitude, float pivot, vec3 position, out vec3 result)
+{
+ vec3 value = mx_perlin_noise_vec3(position);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genglsl/mx_noise3d_vector4.glsl b/templates/stdlib/genglsl/mx_noise3d_vector4.glsl
new file mode 100644
index 0000000000..9c8cfd98dd
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_noise3d_vector4.glsl
@@ -0,0 +1,8 @@
+#include "lib/mx_noise.glsl"
+
+void mx_noise3d_vector4(vec4 amplitude, float pivot, vec3 position, out vec4 result)
+{
+ vec3 xyz = mx_perlin_noise_vec3(position);
+ float w = mx_perlin_noise_float(position + vec3(19, 73, 29));
+ result = vec4(xyz, w) * amplitude + pivot;
+}
diff --git a/templates/stdlib/genglsl/mx_normalmap.glsl b/templates/stdlib/genglsl/mx_normalmap.glsl
new file mode 100644
index 0000000000..66c50f31f5
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_normalmap.glsl
@@ -0,0 +1,13 @@
+void mx_normalmap_vector2(vec3 value, vec2 normal_scale, vec3 N, vec3 T, vec3 B, out vec3 result)
+{
+ value = (dot(value, value) == 0.0) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+ value = T * value.x * normal_scale.x +
+ B * value.y * normal_scale.y +
+ N * value.z;
+ result = normalize(value);
+}
+
+void mx_normalmap_float(vec3 value, float normal_scale, vec3 N, vec3 T, vec3 B, out vec3 result)
+{
+ mx_normalmap_vector2(value, vec2(normal_scale), N, T, B, result);
+}
diff --git a/templates/stdlib/genglsl/mx_premult_color4.glsl b/templates/stdlib/genglsl/mx_premult_color4.glsl
new file mode 100644
index 0000000000..0f45c39679
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_premult_color4.glsl
@@ -0,0 +1,4 @@
+void mx_premult_color4(vec4 _in, out vec4 result)
+{
+ result = vec4(_in.rgb * _in.a, _in.a);
+}
diff --git a/templates/stdlib/genglsl/mx_ramplr_float.glsl b/templates/stdlib/genglsl/mx_ramplr_float.glsl
new file mode 100644
index 0000000000..3040f138d4
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_ramplr_float.glsl
@@ -0,0 +1,4 @@
+void mx_ramplr_float(float valuel, float valuer, vec2 texcoord, out float result)
+{
+ result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
+}
diff --git a/templates/stdlib/genglsl/mx_ramplr_vector2.glsl b/templates/stdlib/genglsl/mx_ramplr_vector2.glsl
new file mode 100644
index 0000000000..752e5bc3f5
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_ramplr_vector2.glsl
@@ -0,0 +1,4 @@
+void mx_ramplr_vector2(vec2 valuel, vec2 valuer, vec2 texcoord, out vec2 result)
+{
+ result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
+}
diff --git a/templates/stdlib/genglsl/mx_ramplr_vector3.glsl b/templates/stdlib/genglsl/mx_ramplr_vector3.glsl
new file mode 100644
index 0000000000..bc6b9f0bbd
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_ramplr_vector3.glsl
@@ -0,0 +1,4 @@
+void mx_ramplr_vector3(vec3 valuel, vec3 valuer, vec2 texcoord, out vec3 result)
+{
+ result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
+}
diff --git a/templates/stdlib/genglsl/mx_ramplr_vector4.glsl b/templates/stdlib/genglsl/mx_ramplr_vector4.glsl
new file mode 100644
index 0000000000..a3c4c161db
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_ramplr_vector4.glsl
@@ -0,0 +1,4 @@
+void mx_ramplr_vector4(vec4 valuel, vec4 valuer, vec2 texcoord, out vec4 result)
+{
+ result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
+}
diff --git a/templates/stdlib/genglsl/mx_ramptb_float.glsl b/templates/stdlib/genglsl/mx_ramptb_float.glsl
new file mode 100644
index 0000000000..bd452ffa5f
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_ramptb_float.glsl
@@ -0,0 +1,4 @@
+void mx_ramptb_float(float valuet, float valueb, vec2 texcoord, out float result)
+{
+ result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) );
+}
diff --git a/templates/stdlib/genglsl/mx_ramptb_vector2.glsl b/templates/stdlib/genglsl/mx_ramptb_vector2.glsl
new file mode 100644
index 0000000000..494b5f665b
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_ramptb_vector2.glsl
@@ -0,0 +1,4 @@
+void mx_ramptb_vector2(vec2 valuet, vec2 valueb, vec2 texcoord, out vec2 result)
+{
+ result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) );
+}
diff --git a/templates/stdlib/genglsl/mx_ramptb_vector3.glsl b/templates/stdlib/genglsl/mx_ramptb_vector3.glsl
new file mode 100644
index 0000000000..180200a925
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_ramptb_vector3.glsl
@@ -0,0 +1,4 @@
+void mx_ramptb_vector3(vec3 valuet, vec3 valueb, vec2 texcoord, out vec3 result)
+{
+ result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) );
+}
diff --git a/templates/stdlib/genglsl/mx_ramptb_vector4.glsl b/templates/stdlib/genglsl/mx_ramptb_vector4.glsl
new file mode 100644
index 0000000000..ba6f9c6def
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_ramptb_vector4.glsl
@@ -0,0 +1,4 @@
+void mx_ramptb_vector4(vec4 valuet, vec4 valueb, vec2 texcoord, out vec4 result)
+{
+ result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) );
+}
diff --git a/templates/stdlib/genglsl/mx_rgbtohsv_color3.glsl b/templates/stdlib/genglsl/mx_rgbtohsv_color3.glsl
new file mode 100644
index 0000000000..701221c616
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_rgbtohsv_color3.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_hsv.glsl"
+
+void mx_rgbtohsv_color3(vec3 _in, out vec3 result)
+{
+ result = mx_rgbtohsv(_in);
+}
diff --git a/templates/stdlib/genglsl/mx_rgbtohsv_color4.glsl b/templates/stdlib/genglsl/mx_rgbtohsv_color4.glsl
new file mode 100644
index 0000000000..e4e6e001b7
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_rgbtohsv_color4.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_hsv.glsl"
+
+void mx_rgbtohsv_color4(vec4 _in, out vec4 result)
+{
+ result = vec4(mx_rgbtohsv(_in.rgb), 1.0);
+}
diff --git a/templates/stdlib/genglsl/mx_rotate_vector2.glsl b/templates/stdlib/genglsl/mx_rotate_vector2.glsl
new file mode 100644
index 0000000000..d01270d8ce
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_rotate_vector2.glsl
@@ -0,0 +1,7 @@
+void mx_rotate_vector2(vec2 _in, float amount, out vec2 result)
+{
+ float rotationRadians = mx_radians(amount);
+ float sa = mx_sin(rotationRadians);
+ float ca = mx_cos(rotationRadians);
+ result = vec2(ca*_in.x + sa*_in.y, -sa*_in.x + ca*_in.y);
+}
diff --git a/templates/stdlib/genglsl/mx_rotate_vector3.glsl b/templates/stdlib/genglsl/mx_rotate_vector3.glsl
new file mode 100644
index 0000000000..fda1188379
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_rotate_vector3.glsl
@@ -0,0 +1,19 @@
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = mx_sin(angle);
+ float c = mx_cos(angle);
+ float oc = 1.0 - c;
+
+ return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,
+ oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,
+ oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+}
+
+void mx_rotate_vector3(vec3 _in, float amount, vec3 axis, out vec3 result)
+{
+ float rotationRadians = mx_radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
diff --git a/templates/stdlib/genglsl/mx_smoothstep_float.glsl b/templates/stdlib/genglsl/mx_smoothstep_float.glsl
new file mode 100644
index 0000000000..0b317bdf33
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_smoothstep_float.glsl
@@ -0,0 +1,9 @@
+void mx_smoothstep_float(float val, float low, float high, out float result)
+{
+ if (val >= high)
+ result = 1.0;
+ else if (val <= low)
+ result = 0.0;
+ else
+ result = smoothstep(low, high, val);
+}
diff --git a/templates/stdlib/genglsl/mx_splitlr_float.glsl b/templates/stdlib/genglsl/mx_splitlr_float.glsl
new file mode 100644
index 0000000000..7331c480ca
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_splitlr_float.glsl
@@ -0,0 +1,6 @@
+#include "mx_aastep.glsl"
+
+void mx_splitlr_float(float valuel, float valuer, float center, vec2 texcoord, out float result)
+{
+ result = mix(valuel, valuer, mx_aastep(center, texcoord.x));
+}
diff --git a/templates/stdlib/genglsl/mx_splitlr_vector2.glsl b/templates/stdlib/genglsl/mx_splitlr_vector2.glsl
new file mode 100644
index 0000000000..820ac09f28
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_splitlr_vector2.glsl
@@ -0,0 +1,6 @@
+#include "mx_aastep.glsl"
+
+void mx_splitlr_vector2(vec2 valuel, vec2 valuer, float center, vec2 texcoord, out vec2 result)
+{
+ result = mix(valuel, valuer, mx_aastep(center, texcoord.x));
+}
diff --git a/templates/stdlib/genglsl/mx_splitlr_vector3.glsl b/templates/stdlib/genglsl/mx_splitlr_vector3.glsl
new file mode 100644
index 0000000000..7ac28b4477
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_splitlr_vector3.glsl
@@ -0,0 +1,6 @@
+#include "mx_aastep.glsl"
+
+void mx_splitlr_vector3(vec3 valuel, vec3 valuer, float center, vec2 texcoord, out vec3 result)
+{
+ result = mix(valuel, valuer, mx_aastep(center, texcoord.x));
+}
diff --git a/templates/stdlib/genglsl/mx_splitlr_vector4.glsl b/templates/stdlib/genglsl/mx_splitlr_vector4.glsl
new file mode 100644
index 0000000000..7edc7261ae
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_splitlr_vector4.glsl
@@ -0,0 +1,6 @@
+#include "mx_aastep.glsl"
+
+void mx_splitlr_vector4(vec4 valuel, vec4 valuer, float center, vec2 texcoord, out vec4 result)
+{
+ result = mix(valuel, valuer, mx_aastep(center, texcoord.x));
+}
diff --git a/templates/stdlib/genglsl/mx_splittb_float.glsl b/templates/stdlib/genglsl/mx_splittb_float.glsl
new file mode 100644
index 0000000000..31b49927bb
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_splittb_float.glsl
@@ -0,0 +1,6 @@
+#include "mx_aastep.glsl"
+
+void mx_splittb_float(float valuet, float valueb, float center, vec2 texcoord, out float result)
+{
+ result = mix(valuet, valueb, mx_aastep(center, texcoord.y));
+}
diff --git a/templates/stdlib/genglsl/mx_splittb_vector2.glsl b/templates/stdlib/genglsl/mx_splittb_vector2.glsl
new file mode 100644
index 0000000000..12b0bcf3d8
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_splittb_vector2.glsl
@@ -0,0 +1,6 @@
+#include "mx_aastep.glsl"
+
+void mx_splittb_vector2(vec2 valuet, vec2 valueb, float center, vec2 texcoord, out vec2 result)
+{
+ result = mix(valuet, valueb, mx_aastep(center, texcoord.y));
+}
diff --git a/templates/stdlib/genglsl/mx_splittb_vector3.glsl b/templates/stdlib/genglsl/mx_splittb_vector3.glsl
new file mode 100644
index 0000000000..ff468b3d7e
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_splittb_vector3.glsl
@@ -0,0 +1,6 @@
+#include "mx_aastep.glsl"
+
+void mx_splittb_vector3(vec3 valuet, vec3 valueb, float center, vec2 texcoord, out vec3 result)
+{
+ result = mix(valuet, valueb, mx_aastep(center, texcoord.y));
+}
diff --git a/templates/stdlib/genglsl/mx_splittb_vector4.glsl b/templates/stdlib/genglsl/mx_splittb_vector4.glsl
new file mode 100644
index 0000000000..97fa787af4
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_splittb_vector4.glsl
@@ -0,0 +1,6 @@
+#include "mx_aastep.glsl"
+
+void mx_splittb_vector4(vec4 valuet, vec4 valueb, float center, vec2 texcoord, out vec4 result)
+{
+ result = mix(valuet, valueb, mx_aastep(center, texcoord.y));
+}
diff --git a/templates/stdlib/genglsl/mx_surface_unlit.glsl b/templates/stdlib/genglsl/mx_surface_unlit.glsl
new file mode 100644
index 0000000000..6adeee6c80
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_surface_unlit.glsl
@@ -0,0 +1,6 @@
+
+void mx_surface_unlit(float emission, vec3 emission_color, float transmission, vec3 transmission_color, float opacity, out surfaceshader result)
+{
+ result.color = emission * emission_color * opacity;
+ result.transparency = mix(vec3(1.0), transmission * transmission_color, opacity);
+}
diff --git a/templates/stdlib/genglsl/mx_transformmatrix_vector2M3.glsl b/templates/stdlib/genglsl/mx_transformmatrix_vector2M3.glsl
new file mode 100644
index 0000000000..2afc0248f0
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_transformmatrix_vector2M3.glsl
@@ -0,0 +1,5 @@
+void mx_transformmatrix_vector2M3(vec2 val, mat3 transform, out vec2 result)
+{
+ vec3 res = transform * vec3(val, 1.0);
+ result = res.xy;
+}
diff --git a/templates/stdlib/genglsl/mx_transformmatrix_vector3M4.glsl b/templates/stdlib/genglsl/mx_transformmatrix_vector3M4.glsl
new file mode 100644
index 0000000000..2e16244268
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_transformmatrix_vector3M4.glsl
@@ -0,0 +1,5 @@
+void mx_transformmatrix_vector3M4(vec3 val, mat4 transform, out vec3 result)
+{
+ vec4 res = transform * vec4(val, 1.0);
+ result = res.xyz;
+}
diff --git a/templates/stdlib/genglsl/mx_unpremult_color4.glsl b/templates/stdlib/genglsl/mx_unpremult_color4.glsl
new file mode 100644
index 0000000000..c4503b4e05
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_unpremult_color4.glsl
@@ -0,0 +1,4 @@
+void mx_unpremult_color4(vec4 _in, out vec4 result)
+{
+ result = vec4(_in.rgb / _in.a, _in.a);
+}
diff --git a/templates/stdlib/genglsl/mx_worleynoise2d_float.glsl b/templates/stdlib/genglsl/mx_worleynoise2d_float.glsl
new file mode 100644
index 0000000000..a69bc80b0c
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_worleynoise2d_float.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise2d_float(vec2 texcoord, float jitter, int style, out float result)
+{
+ result = mx_worley_noise_float(texcoord, jitter, style, 0);
+}
diff --git a/templates/stdlib/genglsl/mx_worleynoise2d_vector2.glsl b/templates/stdlib/genglsl/mx_worleynoise2d_vector2.glsl
new file mode 100644
index 0000000000..fca38efbde
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_worleynoise2d_vector2.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise2d_vector2(vec2 texcoord, float jitter, int style, out vec2 result)
+{
+ result = mx_worley_noise_vec2(texcoord, jitter, style, 0);
+}
diff --git a/templates/stdlib/genglsl/mx_worleynoise2d_vector3.glsl b/templates/stdlib/genglsl/mx_worleynoise2d_vector3.glsl
new file mode 100644
index 0000000000..2aff692834
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_worleynoise2d_vector3.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise2d_vector3(vec2 texcoord, float jitter, int style, out vec3 result)
+{
+ result = mx_worley_noise_vec3(texcoord, jitter, style, 0);
+}
diff --git a/templates/stdlib/genglsl/mx_worleynoise3d_float.glsl b/templates/stdlib/genglsl/mx_worleynoise3d_float.glsl
new file mode 100644
index 0000000000..ad43dee954
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_worleynoise3d_float.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise3d_float(vec3 position, float jitter, int style, out float result)
+{
+ result = mx_worley_noise_float(position, jitter, style, 0);
+}
diff --git a/templates/stdlib/genglsl/mx_worleynoise3d_vector2.glsl b/templates/stdlib/genglsl/mx_worleynoise3d_vector2.glsl
new file mode 100644
index 0000000000..c124d3a3eb
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_worleynoise3d_vector2.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise3d_vector2(vec3 position, float jitter, int style, out vec2 result)
+{
+ result = mx_worley_noise_vec2(position, jitter, style, 0);
+}
diff --git a/templates/stdlib/genglsl/mx_worleynoise3d_vector3.glsl b/templates/stdlib/genglsl/mx_worleynoise3d_vector3.glsl
new file mode 100644
index 0000000000..b96086269c
--- /dev/null
+++ b/templates/stdlib/genglsl/mx_worleynoise3d_vector3.glsl
@@ -0,0 +1,6 @@
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise3d_vector3(vec3 position, float jitter, int style, out vec3 result)
+{
+ result = mx_worley_noise_vec3(position, jitter, style, 0);
+}
diff --git a/templates/stdlib/genglsl/stdlib_genglsl_impl.mtlx b/templates/stdlib/genglsl/stdlib_genglsl_impl.mtlx
new file mode 100644
index 0000000000..8958a8a520
--- /dev/null
+++ b/templates/stdlib/genglsl/stdlib_genglsl_impl.mtlx
@@ -0,0 +1,760 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/stdlib/genmdl/stdlib_genmdl_impl.mtlx b/templates/stdlib/genmdl/stdlib_genmdl_impl.mtlx
new file mode 100644
index 0000000000..28e2b35465
--- /dev/null
+++ b/templates/stdlib/genmdl/stdlib_genmdl_impl.mtlx
@@ -0,0 +1,773 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/stdlib/genmsl/lib/mx_math.metal b/templates/stdlib/genmsl/lib/mx_math.metal
new file mode 100644
index 0000000000..1c5c6a2785
--- /dev/null
+++ b/templates/stdlib/genmsl/lib/mx_math.metal
@@ -0,0 +1,134 @@
+#define M_FLOAT_EPS 1e-8
+
+#define mx_sin metal::sin
+#define mx_cos metal::cos
+#define mx_tan metal::tan
+#define mx_asin metal::asin
+#define mx_acos metal::acos
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+float mx_inversesqrt(float x)
+{
+ return metal::rsqrt(x);
+}
+
+template
+T1 mx_mod(T1 x, T2 y)
+{
+ return x - y * floor(x/y);
+}
+
+float3x3 mx_inverse(float3x3 m)
+{
+ float n11 = m[0][0], n12 = m[1][0], n13 = m[2][0];
+ float n21 = m[0][1], n22 = m[1][1], n23 = m[2][1];
+ float n31 = m[0][2], n32 = m[1][2], n33 = m[2][2];
+
+ float det = metal::determinant(m);
+ float idet = 1.0f / det;
+
+ float3x3 ret;
+
+ ret[0][0] = idet * (n22 * n33 - n32 * n23);
+ ret[1][0] = idet * (n32 * n13 - n12 * n33);
+ ret[2][0] = idet * (n12 * n23 - n22 * n13);
+
+ ret[0][1] = idet * (n31 * n23 - n21 * n33);
+ ret[1][1] = idet * (n11 * n33 - n31 * n13);
+ ret[2][1] = idet * (n21 * n13 - n11 * n23);
+
+ ret[0][2] = idet * (n21 * n32 - n31 * n22);
+ ret[1][2] = idet * (n31 * n12 - n11 * n32);
+ ret[2][2] = idet * (n11 * n22 - n21 * n12);
+
+ return ret;
+}
+
+float4x4 mx_inverse(float4x4 m)
+{
+ float n11 = m[0][0], n12 = m[1][0], n13 = m[2][0], n14 = m[3][0];
+ float n21 = m[0][1], n22 = m[1][1], n23 = m[2][1], n24 = m[3][1];
+ float n31 = m[0][2], n32 = m[1][2], n33 = m[2][2], n34 = m[3][2];
+ float n41 = m[0][3], n42 = m[1][3], n43 = m[2][3], n44 = m[3][3];
+
+ float t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44;
+ float t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44;
+ float t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44;
+ float t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
+
+ float det = metal::determinant(m);
+ float idet = 1.0f / det;
+
+ float4x4 ret;
+
+ ret[0][0] = t11 * idet;
+ ret[0][1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * idet;
+ ret[0][2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * idet;
+ ret[0][3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * idet;
+
+ ret[1][0] = t12 * idet;
+ ret[1][1] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * idet;
+ ret[1][2] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * idet;
+ ret[1][3] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * idet;
+
+ ret[2][0] = t13 * idet;
+ ret[2][1] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * idet;
+ ret[2][2] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * idet;
+ ret[2][3] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * idet;
+
+ ret[3][0] = t14 * idet;
+ ret[3][1] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * idet;
+ ret[3][2] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * idet;
+ ret[3][3] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * idet;
+
+ return ret;
+}
+
+float mx_atan(float y_over_x)
+{
+ return metal::atan(y_over_x);
+}
+
+float mx_atan(float y, float x)
+{
+ return metal::atan2(y, x);
+}
+
+vec2 mx_atan(vec2 y, vec2 x)
+{
+ return metal::atan2(y, x);
+}
+
+vec3 mx_atan(vec3 y, vec3 x)
+{
+ return metal::atan2(y, x);
+}
+
+vec4 mx_atan(vec4 y, vec4 x)
+{
+ return metal::atan2(y, x);
+}
+
+float mx_radians(float degree)
+{
+ return (degree * M_PI_F / 180.0f);
+}
+
+vec2 mx_radians(vec2 degree)
+{
+ return (degree * M_PI_F / 180.0f);
+}
diff --git a/templates/stdlib/genmsl/lib/mx_matscalaroperators.metal b/templates/stdlib/genmsl/lib/mx_matscalaroperators.metal
new file mode 100644
index 0000000000..2b32a45762
--- /dev/null
+++ b/templates/stdlib/genmsl/lib/mx_matscalaroperators.metal
@@ -0,0 +1,55 @@
+float3x3 operator+(float3x3 a, float b)
+{
+ return a + float3x3(b,b,b,b,b,b,b,b,b);
+}
+
+float4x4 operator+(float4x4 a, float b)
+{
+ return a + float4x4(b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b);
+}
+
+float3x3 operator-(float3x3 a, float b)
+{
+ return a - float3x3(b,b,b,b,b,b,b,b,b);
+}
+
+float4x4 operator-(float4x4 a, float b)
+{
+ return a - float4x4(b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b);
+}
+
+float3x3 operator/(float3x3 a, float3x3 b)
+{
+ for(int i = 0; i < 3; ++i)
+ for(int j = 0; j < 3; ++j)
+ a[i][j] /= b[i][j];
+
+ return a;
+}
+
+float4x4 operator/(float4x4 a, float4x4 b)
+{
+ for(int i = 0; i < 4; ++i)
+ for(int j = 0; j < 4; ++j)
+ a[i][j] /= b[i][j];
+
+ return a;
+}
+
+float3x3 operator/(float3x3 a, float b)
+{
+ for(int i = 0; i < 3; ++i)
+ for(int j = 0; j < 3; ++j)
+ a[i][j] /= b;
+
+ return a;
+}
+
+float4x4 operator/(float4x4 a, float b)
+{
+ for(int i = 0; i < 4; ++i)
+ for(int j = 0; j < 4; ++j)
+ a[i][j] /= b;
+
+ return a;
+}
diff --git a/templates/stdlib/genmsl/lib/mx_texture.metal b/templates/stdlib/genmsl/lib/mx_texture.metal
new file mode 100644
index 0000000000..eb3234584d
--- /dev/null
+++ b/templates/stdlib/genmsl/lib/mx_texture.metal
@@ -0,0 +1,30 @@
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+
+ // needed for Storm
+ int get_width() { return tex.get_width(); }
+ int get_height() { return tex.get_height(); }
+ int get_num_mip_levels() { return tex.get_num_mip_levels(); }
+};
+
+float4 texture(MetalTexture mtlTex, float2 uv)
+{
+ return mtlTex.tex.sample(mtlTex.s, uv);
+}
+
+float4 textureLod(MetalTexture mtlTex, float2 uv, float lod)
+{
+ return mtlTex.tex.sample(mtlTex.s, uv, level(lod));
+}
+
+float4 textureGrad(MetalTexture mtlTex, float2 uv, float2 dx, float2 dy)
+{
+ return mtlTex.tex.sample(mtlTex.s, uv, gradient2d(dx, dy));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.tex.get_width(), mtlTex.tex.get_height());
+}
diff --git a/templates/stdlib/genmsl/stdlib_genmsl_impl.mtlx b/templates/stdlib/genmsl/stdlib_genmsl_impl.mtlx
new file mode 100644
index 0000000000..4daa0b8405
--- /dev/null
+++ b/templates/stdlib/genmsl/stdlib_genmsl_impl.mtlx
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/stdlib/genosl/include/color4.h b/templates/stdlib/genosl/include/color4.h
new file mode 100644
index 0000000000..8fbdca46a2
--- /dev/null
+++ b/templates/stdlib/genosl/include/color4.h
@@ -0,0 +1,332 @@
+// Copyright Contributors to the Open Shading Language project.
+// SPDX-License-Identifier: BSD-3-Clause
+// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
+
+#pragma once
+#define COLOR4_H
+
+
+// color4 is a color + alpha
+struct color4
+{
+ color rgb;
+ float a;
+};
+
+
+
+//
+// For color4, define math operators to match color
+//
+
+color4 __operator__neg__(color4 a)
+{
+ return color4(-a.rgb, -a.a);
+}
+
+color4 __operator__add__(color4 a, color4 b)
+{
+ return color4(a.rgb + b.rgb, a.a + b.a);
+}
+
+color4 __operator__add__(color4 a, int b)
+{
+ return a + color4(color(b), b);
+}
+
+color4 __operator__add__(color4 a, float b)
+{
+ return a + color4(color(b), b);
+}
+
+color4 __operator__add__(int a, color4 b)
+{
+ return color4(color(a), a) + b;
+}
+
+color4 __operator__add__(float a, color4 b)
+{
+ return color4(color(a), a) + b;
+}
+
+color4 __operator__sub__(color4 a, color4 b)
+{
+ return color4(a.rgb - b.rgb, a.a - b.a);
+}
+
+color4 __operator__sub__(color4 a, int b)
+{
+ return a - color4(color(b), b);
+}
+
+color4 __operator__sub__(color4 a, float b)
+{
+ return a - color4(color(b), b);
+}
+
+color4 __operator__sub__(int a, color4 b)
+{
+ return color4(color(a), a) - b;
+}
+
+color4 __operator__sub__(float a, color4 b)
+{
+ return color4(color(a), a) - b;
+}
+
+color4 __operator__mul__(color4 a, color4 b)
+{
+ return color4(a.rgb * b.rgb, a.a * b.a);
+}
+
+color4 __operator__mul__(color4 a, int b)
+{
+ return a * color4(color(b), b);
+}
+
+color4 __operator__mul__(color4 a, float b)
+{
+ return a * color4(color(b), b);
+}
+
+color4 __operator__mul__(int a, color4 b)
+{
+ return color4(color(a), a) * b;
+}
+
+color4 __operator__mul__(float a, color4 b)
+{
+ return color4(color(a), a) * b;
+}
+
+color4 __operator__div__(color4 a, color4 b)
+{
+ return color4(a.rgb / b.rgb, a.a / b.a);
+}
+
+color4 __operator__div__(color4 a, int b)
+{
+ float b_inv = 1.0 / float(b);
+ return a * color4(color(b_inv), b_inv);
+}
+
+color4 __operator__div__(color4 a, float b)
+{
+ float b_inv = 1.0 / b;
+ return a * color4(color(b_inv), b_inv);
+}
+
+color4 __operator_div__(int a, color4 b)
+{
+ return color4(color(a), a) / b;
+}
+
+color4 __operator__div__(float a, color4 b)
+{
+ return color4(color(a), a) / b;
+}
+
+int __operator__eq__(color4 a, color4 b)
+{
+ return (a.rgb == b.rgb) && (a.a == b.a);
+}
+
+int __operator__neq__(color4 a, color4 b)
+{
+ return (a.rgb != b.rgb) || (a.a != b.a);
+}
+
+
+
+//
+// For color4, define most of the stdosl functions to match color
+//
+
+color4 abs(color4 a)
+{
+ return color4(abs(a.rgb), abs(a.a));
+}
+
+color4 ceil(color4 a)
+{
+ return color4(ceil(a.rgb), ceil(a.a));
+}
+
+color4 round(color4 a)
+{
+ return color4(round(a.rgb), round(a.a));
+}
+
+color4 floor(color4 a)
+{
+ return color4(floor(a.rgb), floor(a.a));
+}
+
+color4 sqrt(color4 a)
+{
+ return color4(sqrt(a.rgb), sqrt(a.a));
+}
+
+color4 exp(color4 a)
+{
+ return color4(exp(a.rgb), exp(a.a));
+}
+
+color4 log(color4 a)
+{
+ return color4(log(a.rgb), log(a.a));
+}
+
+color4 log2(color4 a)
+{
+ return color4(log2(a.rgb), log2(a.a));
+}
+
+color4 mix(color4 a, color4 b, float x )
+{
+ return color4(mix(a.rgb, b.rgb, x),
+ mix(a.a, b.a, x));
+}
+
+color4 mix(color4 a, color4 b, color4 x )
+{
+ return color4(mix(a.rgb, b.rgb, x.rgb),
+ mix(a.a, b.a, x.a));
+}
+
+color4 smoothstep(color4 edge0, color4 edge1, color4 c)
+{
+ return color4(smoothstep(edge0.rgb, edge1.rgb, c.rgb),
+ smoothstep(edge0.a, edge1.a, c.a));
+}
+
+color4 smoothstep(float edge0, float edge1, color4 c)
+{
+ return smoothstep(color4(color(edge0), edge0), color4(color(edge1), edge1), c);
+}
+
+color4 clamp(color4 c, color4 minval, color4 maxval)
+{
+ return color4(clamp(c.rgb, minval.rgb, maxval.rgb),
+ clamp(c.a, minval.a, maxval.a));
+}
+
+color4 clamp(color4 c, float minval, float maxval)
+{
+ return clamp(c, color4(color(minval), minval), color4(color(maxval), maxval));
+}
+
+color4 max(color4 a, color4 b)
+{
+ return color4(max(a.rgb, b.rgb),
+ max(a.a, b.a));
+}
+
+color4 max(color4 a, float b)
+{
+ return color4(max(a.rgb, b),
+ max(a.a, b));
+}
+
+color4 min(color4 a, color4 b)
+{
+ return color4(min(a.rgb, b.rgb),
+ min(a.a, b.a));
+}
+
+color4 min(color4 a, float b)
+{
+ return color4(min(a.rgb, b),
+ min(a.a, b));
+}
+
+color4 mod(color4 a, color4 b)
+{
+ return color4(mod(a.rgb, b.rgb),
+ mod(a.a, b.a));
+}
+
+color4 mod(color4 a, float b)
+{
+ return mod(a, color4(color(b), b));
+}
+
+color4 fmod(color4 a, color4 b)
+{
+ return color4(fmod(a.rgb, b.rgb),
+ fmod(a.a, b.a));
+}
+
+color4 fmod(color4 a, int b)
+{
+ return fmod(a, color4(color(b), b));
+}
+
+color4 fmod(color4 a, float b)
+{
+ return fmod(a, color4(color(b), b));
+}
+
+color4 pow(color4 base, color4 power)
+{
+ return color4(pow(base.rgb, power.rgb), pow(base.a, power.a));
+}
+
+color4 pow(color4 base, float power)
+{
+ return pow(base, color4(color(power), power));
+}
+
+color4 sign(color4 a)
+{
+ return color4(sign(a.rgb),
+ sign(a.a));
+}
+
+color4 sin(color4 a)
+{
+ return color4(sin(a.rgb),
+ sin(a.a));
+}
+
+color4 cos(color4 a)
+{
+ return color4(cos(a.rgb),
+ cos(a.a));
+}
+
+color4 tan(color4 a)
+{
+ return color4(tan(a.rgb),
+ tan(a.a));
+}
+
+color4 asin(color4 a)
+{
+ return color4(asin(a.rgb),
+ asin(a.a));
+}
+
+color4 acos(color4 a)
+{
+ return color4(acos(a.rgb),
+ acos(a.a));
+}
+
+color4 atan2(color4 a, float f)
+{
+ return color4(atan2(a.rgb, f),
+ atan2(a.a, f));
+}
+
+color4 atan2(color4 a, color4 b)
+{
+ return color4(atan2(a.rgb, b.rgb),
+ atan2(a.a, b.a));
+}
+
+
+color4 transformc (string fromspace, string tospace, color4 C)
+{
+ return color4 (transformc (fromspace, tospace, C.rgb), C.a);
+}
diff --git a/templates/stdlib/genosl/include/matrix33.h b/templates/stdlib/genosl/include/matrix33.h
new file mode 100644
index 0000000000..e14dc28f44
--- /dev/null
+++ b/templates/stdlib/genosl/include/matrix33.h
@@ -0,0 +1,161 @@
+// Copyright Contributors to the Open Shading Language project.
+// SPDX-License-Identifier: BSD-3-Clause
+// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
+
+
+#pragma once
+#define MATRIX33_H
+
+
+struct matrix33
+{
+ matrix m;
+};
+
+int isValidAs33(matrix m44)
+{
+ return m44[0][3] == 0 &&
+ m44[1][3] == 0 &&
+ m44[2][3] == 0 &&
+ m44[3][0] == 0 &&
+ m44[3][1] == 0 &&
+ m44[3][2] == 0 &&
+ m44[3][3] == 1;
+}
+
+matrix matrix33To44 (matrix33 m33)
+{
+ return m33.m;
+}
+
+// Convert an arbitrary m44 to m33 by removing the translation
+//QUESTION: should we check if it's valid to represent the 4x4 as a 3x3?
+matrix33 matrix44To33 (matrix m44)
+{
+ matrix33 m33;
+ m33.m = m44;
+ m33.m[0][3] = 0;
+ m33.m[1][3] = 0;
+ m33.m[2][3] = 0;
+ m33.m[3][0] = 0;
+ m33.m[3][1] = 0;
+ m33.m[3][2] = 0;
+ m33.m[3][3] = 1;
+
+ return m33;
+}
+
+matrix33 __operator__neg__(matrix33 a)
+{
+ matrix33 m33;
+ m33.m = -a.m;
+ return m33;
+}
+
+
+matrix33 __operator__mul__(int a, matrix33 b)
+{
+ matrix33 m33;
+ m33.m = a * b.m;
+ return m33;
+}
+
+matrix33 __operator__mul__(float a, matrix33 b)
+{
+ matrix33 m33;
+ m33.m = a * b.m;
+ return m33;
+}
+
+matrix33 __operator__mul__(matrix33 a, int b)
+{
+ matrix33 m33;
+ m33.m = a.m * b;
+ return m33;
+}
+
+matrix33 __operator__mul__(matrix33 a, float b)
+{
+ matrix33 m33;
+ m33.m = a.m * b;
+ return m33;
+}
+
+matrix33 __operator__mul__(matrix33 a, matrix33 b)
+{
+ matrix33 m33;
+ m33.m = a.m * b.m;
+ return m33;
+}
+
+matrix33 __operator__div__(int a, matrix33 b)
+{
+ matrix33 m33;
+ m33.m = a / b.m;
+ return m33;
+}
+
+matrix33 __operator__div__(float a, matrix33 b)
+{
+ matrix33 m33;
+ m33.m = a / b.m;
+ return m33;
+}
+
+matrix33 __operator__div__(matrix33 a, int b)
+{
+ matrix33 m33;
+ m33.m = a.m / b;
+ return m33;
+}
+
+matrix33 __operator__div__(matrix33 a, float b)
+{
+ matrix33 m33;
+ m33.m = a.m / b;
+ return m33;
+}
+
+matrix33 __operator__div__(matrix33 a, matrix33 b)
+{
+ matrix33 m33;
+ m33.m = a.m / b.m;
+ return m33;
+}
+
+int __operator__eq__(matrix33 a, matrix33 b)
+{
+ return a.m == b.m;
+}
+
+int __operator__ne__(matrix33 a, matrix33 b)
+{
+ return a.m != b.m;
+}
+
+float determinant (matrix33 a)
+{
+ return determinant(a.m);
+}
+
+matrix33 transpose(matrix33 a)
+{
+ matrix33 m33;
+ m33.m = transpose(a.m);
+ return m33;
+}
+
+point transform(matrix33 a, point b)
+{
+ return transform(a.m, b);
+}
+
+vector transform(matrix33 a, vector b)
+{
+ return transform(a.m, b);
+}
+
+normal transform(matrix33 a, normal b)
+{
+ return transform(a.m, b);
+}
diff --git a/templates/stdlib/genosl/include/mx_funcs.h b/templates/stdlib/genosl/include/mx_funcs.h
new file mode 100644
index 0000000000..e1c4e11e15
--- /dev/null
+++ b/templates/stdlib/genosl/include/mx_funcs.h
@@ -0,0 +1,724 @@
+// Open Shading Language : Copyright (c) 2009-2017 Sony Pictures Imageworks Inc., et al.
+// https://github.com/imageworks/OpenShadingLanguage/blob/master/LICENSE
+//
+// MaterialX specification (c) 2017 Lucasfilm Ltd.
+// http://www.materialx.org/
+
+#pragma once
+
+#include "color4.h"
+#include "vector2.h"
+#include "vector4.h"
+#include "matrix33.h"
+
+//
+// Support functions for OSL implementations of the MaterialX nodes.
+//
+
+float mx_ternary(int expr, float v1, float v2) { if (expr) return v1; else return v2; }
+int mx_ternary(int expr, int v1, int v2) { if (expr) return v1; else return v2; }
+color mx_ternary(int expr, color v1, color v2) { if (expr) return v1; else return v2; }
+color4 mx_ternary(int expr, color4 v1, color4 v2) { if (expr) return v1; else return v2; }
+vector mx_ternary(int expr, vector v1, vector v2) { if (expr) return v1; else return v2; }
+vector2 mx_ternary(int expr, vector2 v1, vector2 v2) { if (expr) return v1; else return v2; }
+vector4 mx_ternary(int expr, vector4 v1, vector4 v2) { if (expr) return v1; else return v2; }
+matrix mx_ternary(int expr, matrix v1, matrix v2) { if (expr) return v1; else return v2; }
+matrix33 mx_ternary(int expr, matrix33 v1, matrix33 v2) { if (expr) return v1; else return v2; }
+
+
+matrix33 mx_add(matrix33 a, matrix33 b)
+{
+ return matrix33(matrix(
+ a.m[0][0]+b.m[0][0], a.m[0][1]+b.m[0][1], a.m[0][2]+b.m[0][2], 0.0,
+ a.m[1][0]+b.m[1][0], a.m[1][1]+b.m[1][1], a.m[1][2]+b.m[1][2], 0.0,
+ a.m[2][0]+b.m[2][0], a.m[2][1]+b.m[2][1], a.m[2][2]+b.m[2][2], 0.0,
+ 0.0, 0.0, 0.0, 1.0));
+}
+
+matrix33 mx_add(matrix33 a, float b)
+{
+ return matrix33(matrix(
+ a.m[0][0]+b, a.m[0][1]+b, a.m[0][2]+b, 0.0,
+ a.m[1][0]+b, a.m[1][1]+b, a.m[1][2]+b, 0.0,
+ a.m[2][0]+b, a.m[2][1]+b, a.m[2][2]+b, 0.0,
+ 0.0, 0.0, 0.0, 1.0));
+}
+
+matrix mx_add(matrix a, matrix b)
+{
+ return matrix(
+ a[0][0]+b[0][0], a[0][1]+b[0][1], a[0][2]+b[0][2], a[0][3]+b[0][3],
+ a[1][0]+b[1][0], a[1][1]+b[1][1], a[1][2]+b[1][2], a[1][3]+b[1][3],
+ a[2][0]+b[2][0], a[2][1]+b[2][1], a[2][2]+b[2][2], a[2][3]+b[2][3],
+ a[3][0]+b[3][0], a[3][1]+b[3][1], a[3][2]+b[3][2], a[3][3]+b[3][3]);
+}
+
+matrix mx_add(matrix a, float b)
+{
+ return matrix(
+ a[0][0]+b, a[0][1]+b, a[0][2]+b, a[0][3]+b,
+ a[1][0]+b, a[1][1]+b, a[1][2]+b, a[1][3]+b,
+ a[2][0]+b, a[2][1]+b, a[2][2]+b, a[2][3]+b,
+ a[3][0]+b, a[3][1]+b, a[3][2]+b, a[3][3]+b);
+}
+
+
+matrix33 mx_subtract(matrix33 a, matrix33 b)
+{
+ return matrix33(matrix(
+ a.m[0][0]-b.m[0][0], a.m[0][1]-b.m[0][1], a.m[0][2]-b.m[0][2], 0.0,
+ a.m[1][0]-b.m[1][0], a.m[1][1]-b.m[1][1], a.m[1][2]-b.m[1][2], 0.0,
+ a.m[2][0]-b.m[2][0], a.m[2][1]-b.m[2][1], a.m[2][2]-b.m[2][2], 0.0,
+ 0.0, 0.0, 0.0, 1.0));
+}
+
+matrix33 mx_subtract(matrix33 a, float b)
+{
+ return matrix33(matrix(
+ a.m[0][0]-b, a.m[0][1]-b, a.m[0][2]-b, 0.0,
+ a.m[1][0]-b, a.m[1][1]-b, a.m[1][2]-b, 0.0,
+ a.m[2][0]-b, a.m[2][1]-b, a.m[2][2]-b, 0.0,
+ 0.0, 0.0, 0.0, 1.0));
+}
+
+matrix mx_subtract(matrix a, matrix b)
+{
+ return matrix(
+ a[0][0]-b[0][0], a[0][1]-b[0][1], a[0][2]-b[0][2], a[0][3]-b[0][3],
+ a[1][0]-b[1][0], a[1][1]-b[1][1], a[1][2]-b[1][2], a[1][3]-b[1][3],
+ a[2][0]-b[2][0], a[2][1]-b[2][1], a[2][2]-b[2][2], a[2][3]-b[2][3],
+ a[3][0]-b[3][0], a[3][1]-b[3][1], a[3][2]-b[3][2], a[3][3]-b[3][3]);
+}
+
+matrix mx_subtract(matrix a, float b)
+{
+ return matrix(
+ a[0][0]-b, a[0][1]-b, a[0][2]-b, a[0][3]-b,
+ a[1][0]-b, a[1][1]-b, a[1][2]-b, a[1][3]-b,
+ a[2][0]-b, a[2][1]-b, a[2][2]-b, a[2][3]-b,
+ a[3][0]-b, a[3][1]-b, a[3][2]-b, a[3][3]-b);
+}
+
+
+float mx_extract(color in, int index)
+{
+ return in[index];
+}
+
+float mx_extract(color4 in, int index)
+{
+ if (index == 0) return in.rgb.r;
+ else if (index == 1) return in.rgb.g;
+ else if (index == 2) return in.rgb.b;
+ else return in.a;
+}
+
+float mx_extract(vector2 in, int index)
+{
+ if (index == 0) return in.x;
+ else return in.y;
+}
+
+float mx_extract(vector in, int index)
+{
+ return in[index];
+}
+
+float mx_extract(vector4 in, int index)
+{
+ if (index == 0) return in.x;
+ else if (index == 1) return in.y;
+ else if (index == 2) return in.z;
+ else return in.w;
+}
+
+
+float mx_remap(float in, float inLow, float inHigh, float outLow, float outHigh, int doClamp)
+{
+ float x = (in - inLow)/(inHigh-inLow);
+ if (doClamp == 1) {
+ x = clamp(x, 0, 1);
+ }
+ return outLow + (outHigh - outLow) * x;
+}
+
+color mx_remap(color in, color inLow, color inHigh, color outLow, color outHigh, int doClamp)
+{
+ color x = (in - inLow) / (inHigh - inLow);
+ if (doClamp == 1) {
+ x = clamp(x, 0, 1);
+ }
+ return outLow + (outHigh - outLow) * x;
+}
+
+color mx_remap(color in, float inLow, float inHigh, float outLow, float outHigh, int doClamp)
+{
+ color x = (in - inLow) / (inHigh - inLow);
+ if (doClamp == 1) {
+ x = clamp(x, 0, 1);
+ }
+ return outLow + (outHigh - outLow) * x;
+}
+
+color4 mx_remap(color4 c, color4 inLow, color4 inHigh, color4 outLow, color4 outHigh, int doClamp)
+{
+ return color4(mx_remap(c.rgb, inLow.rgb, inHigh.rgb, outLow.rgb, outHigh.rgb, doClamp),
+ mx_remap(c.a, inLow.a, inHigh.a, outLow.a, outHigh.a, doClamp));
+}
+
+color4 mx_remap(color4 c, float inLow, float inHigh, float outLow, float outHigh, int doClamp)
+{
+ color4 c4_inLow = color4(color(inLow), inLow);
+ color4 c4_inHigh = color4(color(inHigh), inHigh);
+ color4 c4_outLow = color4(color(outLow), outLow);
+ color4 c4_outHigh = color4(color(outHigh), outHigh);
+ return mx_remap(c, c4_inLow, c4_inHigh, c4_outLow, c4_outHigh, doClamp);
+}
+
+vector2 mx_remap(vector2 in, vector2 inLow, vector2 inHigh, vector2 outLow, vector2 outHigh, int doClamp)
+{
+ return vector2(mx_remap(in.x, inLow.x, inHigh.x, outLow.x, outHigh.x, doClamp),
+ mx_remap(in.y, inLow.y, inHigh.y, outLow.y, outHigh.y, doClamp));
+}
+
+vector2 mx_remap(vector2 in, float inLow, float inHigh, float outLow, float outHigh, int doClamp)
+{
+ return vector2(mx_remap(in.x, inLow, inHigh, outLow, outHigh, doClamp),
+ mx_remap(in.y, inLow, inHigh, outLow, outHigh, doClamp));
+}
+
+vector4 mx_remap(vector4 in, vector4 inLow, vector4 inHigh, vector4 outLow, vector4 outHigh, int doClamp)
+{
+ return vector4(mx_remap(in.x, inLow.x, inHigh.x, outLow.x, outHigh.x, doClamp),
+ mx_remap(in.y, inLow.y, inHigh.y, outLow.y, outHigh.y, doClamp),
+ mx_remap(in.z, inLow.z, inHigh.z, outLow.z, outHigh.z, doClamp),
+ mx_remap(in.w, inLow.w, inHigh.w, outLow.w, outHigh.w, doClamp));
+}
+
+vector4 mx_remap(vector4 in, float inLow, float inHigh, float outLow, float outHigh, int doClamp)
+{
+ return vector4(mx_remap(in.x, inLow, inHigh, outLow, outHigh, doClamp),
+ mx_remap(in.y, inLow, inHigh, outLow, outHigh, doClamp),
+ mx_remap(in.z, inLow, inHigh, outLow, outHigh, doClamp),
+ mx_remap(in.w, inLow, inHigh, outLow, outHigh, doClamp));
+}
+
+
+float mx_contrast(float in, float amount, float pivot)
+{
+ float out = in - pivot;
+ out *= amount;
+ out += pivot;
+ return out;
+}
+
+color mx_contrast(color in, color amount, color pivot)
+{
+ color out = in - pivot;
+ out *= amount;
+ out += pivot;
+ return out;
+}
+
+color mx_contrast(color in, float amount, float pivot)
+{
+ color out = in - pivot;
+ out *= amount;
+ out += pivot;
+ return out;
+}
+
+color4 mx_contrast(color4 c, color4 amount, color4 pivot)
+{
+ return color4(mx_contrast(c.rgb, amount.rgb, pivot.rgb),
+ mx_contrast(c.a, amount.a, pivot.a));
+}
+
+color4 mx_contrast(color4 c, float amount, float pivot)
+{
+ return mx_contrast(c, color4(color(amount), amount), color4(color(pivot), pivot));
+}
+
+vector2 mx_contrast(vector2 in, vector2 amount, vector2 pivot)
+{
+ return vector2 (mx_contrast(in.x, amount.x, pivot.x),
+ mx_contrast(in.y, amount.y, pivot.y));
+}
+
+vector2 mx_contrast(vector2 in, float amount, float pivot)
+{
+ return mx_contrast(in, vector2(amount, amount), vector2(pivot, pivot));
+}
+
+vector4 mx_contrast(vector4 in, vector4 amount, vector4 pivot)
+{
+ return vector4(mx_contrast(in.x, amount.x, pivot.x),
+ mx_contrast(in.y, amount.y, pivot.y),
+ mx_contrast(in.z, amount.z, pivot.z),
+ mx_contrast(in.w, amount.w, pivot.w));
+}
+
+vector4 mx_contrast(vector4 in, float amount, float pivot)
+{
+ return vector4(mx_contrast(in.x, amount, pivot),
+ mx_contrast(in.y, amount, pivot),
+ mx_contrast(in.z, amount, pivot),
+ mx_contrast(in.w, amount, pivot));
+}
+
+
+vector2 mx_noise(string noisetype, float x, float y)
+{
+ color cnoise = (color) noise(noisetype, x, y);
+ return vector2 (cnoise[0], cnoise[1]);
+}
+
+color4 mx_noise(string noisetype, float x, float y)
+{
+ color cnoise = (color) noise(noisetype, x, y);
+ float fnoise = (float) noise(noisetype, x + 19, y + 73);
+ return color4 (cnoise, fnoise);
+}
+
+vector4 mx_noise(string noisetype, float x, float y)
+{
+ color cnoise = (color) noise(noisetype, x, y);
+ float fnoise = (float) noise(noisetype, x + 19, y + 73);
+ return vector4 (cnoise[0], cnoise[1], cnoise[2], fnoise);
+}
+
+vector2 mx_noise(string noisetype, point position)
+{
+ color cnoise = (color) noise(noisetype, position);
+ return vector2 (cnoise[0], cnoise[1]);
+}
+
+color4 mx_noise(string noisetype, point position)
+{
+ color cnoise = (color) noise(noisetype, position);
+ float fnoise = (float) noise(noisetype, position+vector(19,73,29));
+ return color4 (cnoise, fnoise);
+}
+
+vector4 mx_noise(string noisetype, point position)
+{
+ color cnoise = (color) noise(noisetype, position);
+ float fnoise = (float) noise(noisetype, position+vector(19,73,29));
+ return vector4 (cnoise[0], cnoise[1], cnoise[2], fnoise);
+}
+
+float mx_fbm(float x, float y, int octaves, float lacunarity, float diminish, string noisetype)
+{
+ float out = 0;
+ float amp = 1.0;
+ float xx = x;
+ float yy = y;
+
+ for (int i = 0; i < octaves; i += 1) {
+ out += amp * noise(noisetype, xx, yy);
+ amp *= diminish;
+ xx *= lacunarity;
+ yy *= lacunarity;
+ }
+ return out;
+}
+
+color mx_fbm(float x, float y, int octaves, float lacunarity, float diminish, string noisetype)
+{
+ color out = 0;
+ float amp = 1.0;
+ float xx = x;
+ float yy = y;
+
+ for (int i = 0; i < octaves; i += 1) {
+ out += amp * (color)noise(noisetype, xx, yy);
+ amp *= diminish;
+ xx *= lacunarity;
+ yy *= lacunarity;
+ }
+ return out;
+}
+
+vector2 mx_fbm(float x, float y, int octaves, float lacunarity, float diminish, string noisetype)
+{
+ return vector2((float) mx_fbm(x, y, octaves, lacunarity, diminish, noisetype),
+ (float) mx_fbm(x+19, y+193, octaves, lacunarity, diminish, noisetype));
+}
+
+color4 mx_fbm(float x, float y, int octaves, float lacunarity, float diminish, string noisetype)
+{
+ color c = (color) mx_fbm(x, y, octaves, lacunarity, diminish, noisetype);
+ float f = (float) mx_fbm(x+19, y+193, octaves, lacunarity, diminish, noisetype);
+ return color4 (c, f);
+}
+
+vector4 mx_fbm(float x, float y, int octaves, float lacunarity, float diminish, string noisetype)
+{
+ color c = (color) mx_fbm(x, y, octaves, lacunarity, diminish, noisetype);
+ float f = (float) mx_fbm(x+19, y+193, octaves, lacunarity, diminish, noisetype);
+ return vector4 (c[0], c[1], c[2], f);
+}
+
+float mx_fbm(point position, int octaves, float lacunarity, float diminish, string noisetype)
+{
+ float out = 0;
+ float amp = 1.0;
+ point p = position;
+
+ for (int i = 0; i < octaves; i += 1) {
+ out += amp * noise(noisetype, p);
+ amp *= diminish;
+ p *= lacunarity;
+ }
+ return out;
+}
+
+color mx_fbm(point position, int octaves, float lacunarity, float diminish, string noisetype)
+{
+ color out = 0;
+ float amp = 1.0;
+ point p = position;
+
+ for (int i = 0; i < octaves; i += 1) {
+ out += amp * (color)noise(noisetype, p);
+ amp *= diminish;
+ p *= lacunarity;
+ }
+ return out;
+}
+
+vector2 mx_fbm(point position, int octaves, float lacunarity, float diminish, string noisetype)
+{
+ return vector2((float) mx_fbm(position, octaves, lacunarity, diminish, noisetype),
+ (float) mx_fbm(position+point(19, 193, 17), octaves, lacunarity, diminish, noisetype));
+}
+
+color4 mx_fbm(point position, int octaves, float lacunarity, float diminish, string noisetype)
+{
+ color c = (color) mx_fbm(position, octaves, lacunarity, diminish, noisetype);
+ float f = (float) mx_fbm(position+point(19, 193, 17), octaves, lacunarity, diminish, noisetype);
+ return color4 (c, f);
+}
+
+vector4 mx_fbm(point position, int octaves, float lacunarity, float diminish, string noisetype)
+{
+ color c = (color) mx_fbm(position, octaves, lacunarity, diminish, noisetype);
+ float f = (float) mx_fbm(position+point(19, 193, 17), octaves, lacunarity, diminish, noisetype);
+ return vector4 (c[0], c[1], c[2], f);
+}
+
+vector2 mx_worley_cell_position(int x, int y, int xoff, int yoff, float jitter)
+{
+ vector tmp = cellnoise(x+xoff, y+yoff);
+ vector2 off = vector2(tmp.x, tmp.y);
+ off -= 0.5;
+ off *= jitter;
+ off += 0.5;
+ return vector2(x, y) + off;
+}
+
+vector mx_worley_cell_position(int x, int y, int z, int xoff, int yoff, int zoff, float jitter)
+{
+ vector off = cellnoise(vector(x+xoff, y+yoff, z+zoff));
+ off -= 0.5;
+ off *= jitter;
+ off += 0.5;
+ return vector(x,y,z) + off;
+}
+
+float mx_worley_distance(vector2 p, int x, int y, int X, int Y, float jitter, int metric)
+{
+ vector2 cellpos = mx_worley_cell_position(x,y,X,Y,jitter);
+ vector2 diff = cellpos - p;
+
+ if (metric == 2)
+ return abs(diff.x) + abs(diff.y); // Manhattan distance
+ if (metric == 3)
+ return max(abs(diff.x), abs(diff.y)); // Chebyshev distance
+ return diff.x*diff.x + diff.y*diff.y; // Euclidean or distance^2
+}
+
+float mx_worley_distance(vector p, int x, int y, int z, int X, int Y, int Z, float jitter, int metric)
+{
+ vector cellpos = mx_worley_cell_position(x,y,z,X,Y,Z,jitter);
+ vector diff = cellpos - p;
+ if (metric == 2)
+ return abs(diff[0]) + abs(diff[1]); // Manhattan distance
+ if (metric == 3)
+ return max(abs(diff[0]), abs(diff[1])); // Chebyshev distance
+ return dot(diff, diff); // Eucldean or distance^2
+}
+
+void mx_sort_distance(float dist, output vector2 result)
+{
+ if (dist < result.x)
+ {
+ result.y = result.x;
+ result.x = dist;
+ }
+ else if (dist < result.y)
+ {
+ result.y = dist;
+ }
+}
+
+void mx_sort_distance(float dist, output vector result)
+{
+ if (dist < result[0])
+ {
+ result[2] = result[1];
+ result[1] = result[0];
+ result[0] = dist;
+ }
+ else if (dist < result[1])
+ {
+ result[2] = result[1];
+ result[1] = dist;
+ }
+ else if (dist < result[2])
+ {
+ result[2] = dist;
+ }
+}
+
+// return floor as well as the fractional remainder
+float mx_floorfrac(float x, output int i)
+{
+ i = (int)floor(x);
+ return x - float(i);
+}
+
+float mx_worley_noise_float(vector2 p, float jitter, int style, int metric)
+{
+ int X, Y;
+ float sqdist = 1e6;
+ vector2 localpos = vector2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
+ vector2 minpos = vector2(0.0, 0.0);
+
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
+ vector2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
+ if (dist < sqdist)
+ {
+ sqdist = dist;
+ minpos = cellpos;
+ }
+ }
+ }
+ if (style == 1)
+ {
+ vector2 tmpP = minpos + p;
+ return cellnoise(tmpP.x, tmpP.y);
+ }
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
+
+vector2 mx_worley_noise_vector2(vector2 p, float jitter, int style, int metric)
+{
+ int X, Y;
+ vector2 sqdist = vector2(1e6, 1e6);
+ vector2 localpos = vector2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
+ vector2 minpos = vector2(0.0, 0.0);
+
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
+ vector2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
+ if (dist < sqdist.x)
+ {
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ minpos = cellpos;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.y = dist;
+ }
+ }
+ }
+
+ if (style == 1)
+ {
+ vector2 tmpP = minpos + p;
+ vector tmp = cellnoise(tmpP.x, tmpP.y);
+ return vector2(tmp.x, tmp.y);
+ }
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
+
+vector mx_worley_noise_vector3(vector2 p, float jitter, int style, int metric)
+{
+ int X, Y;
+ vector sqdist = vector(1e6, 1e6, 1e6);
+ vector2 localpos = vector2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
+ vector2 minpos = vector2(0.0, 0.0);
+
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
+ vector2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
+ if (dist < sqdist.x)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ minpos = cellpos;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = dist;
+ }
+ else if (dist < sqdist.z)
+ {
+ sqdist.z = dist;
+ }
+ }
+ }
+ if (style == 1)
+ {
+ vector2 tmpP = minpos + p;
+ return cellnoise(tmpP.x, tmpP.y);
+ }
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
+
+float mx_worley_noise_float(vector p, float jitter, int style, int metric)
+{
+ int X, Y, Z;
+ vector seed = p;
+ float sqdist = 1e6;
+ vector localpos = vector(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
+ vector minpos = vector(0.0, 0.0, 0.0);
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ for (int z = -1; z <= 1; ++z)
+ {
+ float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
+ vector cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
+ if(dist < sqdist)
+ {
+ sqdist = dist;
+ minpos = cellpos;
+ }
+ }
+ }
+ }
+ if (style == 1)
+ return cellnoise(minpos + p);
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
+
+vector2 mx_worley_noise_vector2(vector p, float jitter, int style, int metric)
+{
+ int X, Y, Z;
+ vector2 sqdist = vector2(1e6, 1e6);
+ vector localpos = vector(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
+ vector minpos = vector(0.0, 0.0, 0.0);
+
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ for (int z = -1; z <= 1; ++z)
+ {
+ float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
+ vector cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
+ if (dist < sqdist.x)
+ {
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ minpos = cellpos;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.y = dist;
+ }
+ }
+ }
+ }
+ if (style == 1)
+ {
+ vector tmp = cellnoise(minpos + p);
+ return vector2(tmp.x,tmp.y);
+ }
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
+
+vector mx_worley_noise_vector3(vector p, float jitter, int style, int metric)
+{
+ int X, Y, Z;
+ vector sqdist = 1e6;
+ vector localpos = vector(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
+ vector minpos = vector(0.0, 0.0, 0.0);
+
+ for (int x = -1; x <= 1; ++x)
+ {
+ for (int y = -1; y <= 1; ++y)
+ {
+ for (int z = -1; z <= 1; ++z)
+ {
+ float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
+ vector cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
+ if (dist < sqdist.x)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ minpos = cellpos;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = dist;
+ }
+ else if (dist < sqdist.z)
+ {
+ sqdist.z = dist;
+ }
+ }
+ }
+ }
+ if (style == 1)
+ return cellnoise(minpos + p);
+ else
+ {
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+ }
+}
diff --git a/templates/stdlib/genosl/include/vector2.h b/templates/stdlib/genosl/include/vector2.h
new file mode 100644
index 0000000000..54a7db01a7
--- /dev/null
+++ b/templates/stdlib/genosl/include/vector2.h
@@ -0,0 +1,333 @@
+// Copyright Contributors to the Open Shading Language project.
+// SPDX-License-Identifier: BSD-3-Clause
+// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
+
+#pragma once
+#define VECTOR2_H
+
+// vector2 is a 2D vector
+struct vector2
+{
+ float x;
+ float y;
+};
+
+
+
+//
+// For vector2, define math operators to match vector
+//
+
+vector2 __operator__neg__(vector2 a)
+{
+ return vector2(-a.x, -a.y);
+}
+
+vector2 __operator__add__(vector2 a, vector2 b)
+{
+ return vector2(a.x + b.x, a.y + b.y);
+}
+
+vector2 __operator__add__(vector2 a, int b)
+{
+ return a + vector2(b, b);
+}
+
+vector2 __operator__add__(vector2 a, float b)
+{
+ return a + vector2(b, b);
+}
+
+vector2 __operator__add__(int a, vector2 b)
+{
+ return vector2(a, a) + b;
+}
+
+vector2 __operator__add__(float a, vector2 b)
+{
+ return vector2(a, a) + b;
+}
+
+vector2 __operator__sub__(vector2 a, vector2 b)
+{
+ return vector2(a.x - b.x, a.y - b.y);
+}
+
+vector2 __operator__sub__(vector2 a, int b)
+{
+ return a - vector2(b, b);
+}
+
+vector2 __operator__sub__(vector2 a, float b)
+{
+ return a - vector2(b, b);
+}
+
+vector2 __operator__sub__(int a, vector2 b)
+{
+ return vector2(a, a) - b;
+}
+
+vector2 __operator__sub__(float a, vector2 b)
+{
+ return vector2(a, a) - b;
+}
+
+vector2 __operator__mul__(vector2 a, vector2 b)
+{
+ return vector2(a.x * b.x, a.y * b.y);
+}
+
+vector2 __operator__mul__(vector2 a, int b)
+{
+ return a * vector2(b, b);
+}
+
+vector2 __operator__mul__(vector2 a, float b)
+{
+ return a * vector2(b, b);
+}
+
+vector2 __operator__mul__(int a, vector2 b)
+{
+ return b * vector2(a, a);
+}
+
+vector2 __operator__mul__(float a, vector2 b)
+{
+ return b * vector2(a, a);
+}
+
+vector2 __operator__div__(vector2 a, vector2 b)
+{
+ return vector2(a.x / b.x, a.y / b.y);
+}
+
+vector2 __operator__div__(vector2 a, int b)
+{
+ float b_inv = 1.0 / float(b);
+ return a * vector2(b_inv, b_inv);
+}
+
+vector2 __operator__div__(vector2 a, float b)
+{
+ float b_inv = 1.0 / b;
+ return a * vector2(b_inv, b_inv);
+}
+
+vector2 __operator__div__(int a, vector2 b)
+{
+ return vector2(a, a) / b;
+}
+
+vector2 __operator__div__(float a, vector2 b)
+{
+ return vector2(a, a) / b;
+}
+
+int __operator__eq__(vector2 a, vector2 b)
+{
+ return (a.x == b.x) && (a.y == b.y);
+}
+
+int __operator__neq__(vector2 a, vector2 b)
+{
+ return (a.x != b.x) || (a.y != b.y);
+}
+
+
+
+
+//
+// For vector2, define most of the stdosl functions to match vector
+//
+
+vector2 abs(vector2 a)
+{
+ return vector2 (abs(a.x), abs(a.y));
+}
+
+vector2 ceil(vector2 a)
+{
+ return vector2 (ceil(a.x), ceil(a.y));
+}
+
+vector2 round(vector2 a)
+{
+ return vector2 (round(a.x), round(a.y));
+}
+
+vector2 floor(vector2 a)
+{
+ return vector2 (floor(a.x), floor(a.y));
+}
+
+vector2 sqrt(vector2 a)
+{
+ return vector2 (sqrt(a.x), sqrt(a.y));
+}
+
+vector2 exp(vector2 a)
+{
+ return vector2 (exp(a.x), exp(a.y));
+}
+
+vector2 log(vector2 a)
+{
+ return vector2 (log(a.x), log(a.y));
+}
+
+vector2 log2(vector2 a)
+{
+ return vector2 (log2(a.x), log2(a.y));
+}
+
+vector2 mix(vector2 a, vector2 b, float x )
+{
+ return vector2 (mix(a.x, b.x, x), mix(a.y, b.y, x));
+}
+
+vector2 mix(vector2 a, vector2 b, vector2 x )
+{
+ return vector2 (mix(a.x, b.x, x.x), mix(a.y, b.y, x.y));
+}
+
+float dot(vector2 a, vector2 b)
+{
+ return (a.x * b.x + a.y * b.y);
+}
+
+float length (vector2 a)
+{
+ return hypot (a.x, a.y);
+}
+
+vector2 smoothstep(vector2 low, vector2 high, vector2 in)
+{
+ return vector2 (smoothstep(low.x, high.x, in.x),
+ smoothstep(low.y, high.y, in.y));
+}
+
+vector2 smoothstep(float low, float high, vector2 in)
+{
+ return vector2 (smoothstep(low, high, in.x),
+ smoothstep(low, high, in.y));
+}
+
+vector2 clamp(vector2 in, vector2 low, vector2 high)
+{
+ return vector2 (clamp(in.x, low.x, high.x),
+ clamp(in.y, low.y, high.y));
+}
+
+vector2 clamp(vector2 in, float low, float high)
+{
+ return clamp(in, vector2(low, low), vector2(high, high));
+}
+
+vector2 max(vector2 a, vector2 b)
+{
+ return vector2 (max(a.x, b.x),
+ max(a.y, b.y));
+}
+
+vector2 min(vector2 a, vector2 b)
+{
+ return vector2 (min(a.x, b.x),
+ min(a.y, b.y));
+}
+
+vector2 min(vector2 a, float b)
+{
+ return min(a, vector2(b, b));
+}
+
+vector2 max(vector2 a, float b)
+{
+ return max(a, vector2(b, b));
+}
+
+vector2 normalize(vector2 a)
+{
+ return a / length(a);
+}
+
+vector2 mod(vector2 a, vector2 b)
+{
+ return vector2(mod(a.x, b.x),
+ mod(a.y, b.y));
+}
+
+vector2 mod(vector2 a, float b)
+{
+ return mod(a, vector2(b, b));
+}
+
+vector2 fmod(vector2 a, vector2 b)
+{
+ return vector2 (fmod(a.x, b.x),
+ fmod(a.y, b.y));
+}
+
+vector2 fmod(vector2 a, float b)
+{
+ return fmod(a, vector2(b, b));
+}
+
+vector2 pow(vector2 in, vector2 amount)
+{
+ return vector2(pow(in.x, amount.x), pow(in.y, amount.y));
+}
+
+vector2 pow(vector2 in, float amount)
+{
+ return pow(in, vector2(amount, amount));
+}
+
+vector2 sign(vector2 a)
+{
+ return vector2(sign(a.x),
+ sign(a.y));
+}
+
+vector2 sin(vector2 a)
+{
+ return vector2(sin(a.x),
+ sin(a.y));
+}
+
+vector2 cos(vector2 a)
+{
+ return vector2(cos(a.x),
+ cos(a.y));
+}
+
+vector2 tan(vector2 a)
+{
+ return vector2(tan(a.x),
+ tan(a.y));
+}
+
+vector2 asin(vector2 a)
+{
+ return vector2(asin(a.x),
+ asin(a.y));
+}
+
+vector2 acos(vector2 a)
+{
+ return vector2(acos(a.x),
+ acos(a.y));
+}
+
+vector2 atan2(vector2 a, float f)
+{
+ return vector2(atan2(a.x, f),
+ atan2(a.y, f));
+}
+
+vector2 atan2(vector2 a, vector2 b)
+{
+ return vector2(atan2(a.x, b.x),
+ atan2(a.y, b.y));
+}
diff --git a/templates/stdlib/genosl/include/vector4.h b/templates/stdlib/genosl/include/vector4.h
new file mode 100644
index 0000000000..2ad650a501
--- /dev/null
+++ b/templates/stdlib/genosl/include/vector4.h
@@ -0,0 +1,424 @@
+// Copyright Contributors to the Open Shading Language project.
+// SPDX-License-Identifier: BSD-3-Clause
+// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
+
+#pragma once
+#define VECTOR4_H
+
+
+// vector4 is a 4D vector
+struct vector4
+{
+ float x;
+ float y;
+ float z;
+ float w;
+};
+
+
+
+//
+// For vector4, define math operators to match vector
+//
+
+vector4 __operator__neg__(vector4 a)
+{
+ return vector4(-a.x, -a.y, -a.z, -a.w);
+}
+
+vector4 __operator__add__(vector4 a, vector4 b)
+{
+ return vector4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
+}
+
+vector4 __operator__add__(vector4 a, int b)
+{
+ return a + vector4(b, b, b, b);
+}
+
+vector4 __operator__add__(vector4 a, float b)
+{
+ return a + vector4(b, b, b, b);
+}
+
+vector4 __operator__add__(int a, vector4 b)
+{
+ return vector4(a, a, a, a) + b;
+}
+
+vector4 __operator__add__(float a, vector4 b)
+{
+ return vector4(a, a, a, a) + b;
+}
+
+vector4 __operator__sub__(vector4 a, vector4 b)
+{
+ return vector4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
+}
+
+vector4 __operator__sub__(vector4 a, int b)
+{
+ return a - vector4(b, b, b, b);
+}
+
+vector4 __operator__sub__(vector4 a, float b)
+{
+ return a - vector4(b, b, b, b);
+}
+
+vector4 __operator__sub__(int a, vector4 b)
+{
+ return vector4(a, a, a, a) - b;
+}
+
+vector4 __operator__sub__(float a, vector4 b)
+{
+ return vector4(a, a, a, a) - b;
+}
+
+vector4 __operator__mul__(vector4 a, vector4 b)
+{
+ return vector4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
+}
+
+vector4 __operator__mul__(vector4 a, int b)
+{
+ return a * vector4(b, b, b, b);
+}
+
+vector4 __operator__mul__(vector4 a, float b)
+{
+ return a * vector4(b, b, b, b);
+}
+
+vector4 __operator__mul__(int a, vector4 b)
+{
+ return vector4(a, a, a, a) * b;
+}
+
+vector4 __operator__mul__(float a, vector4 b)
+{
+ return vector4(a, a, a, a) * b;
+}
+
+vector4 __operator__div__(vector4 a, vector4 b)
+{
+ return vector4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
+}
+
+vector4 __operator__div__(vector4 a, int b)
+{
+ float b_inv = 1.0 / float(b);
+ return a * vector4(b_inv, b_inv, b_inv, b_inv);
+}
+
+vector4 __operator__div__(vector4 a, float b)
+{
+ float b_inv = 1.0 / b;
+ return a * vector4(b_inv, b_inv, b_inv, b_inv);
+}
+
+vector4 __operator__div__(int a, vector4 b)
+{
+ return vector4(a, a, a, a) / b;
+}
+
+vector4 __operator__div__(float a, vector4 b)
+{
+ return vector4(a, a, a, a) / b;
+}
+
+int __operator__eq__(vector4 a, vector4 b)
+{
+ return (a.x == b.x) && (a.y == b.y) && (a.z == b.z) && (a.w == b.w);
+}
+
+int __operator__neq__(vector4 a, vector4 b)
+{
+ return (a.x != b.x) || (a.y != b.y) || (a.z != b.z) || (a.w != b.w);
+}
+
+
+
+
+//
+// For vector4, define most of the stdosl functions to match vector
+//
+
+vector4 abs(vector4 in)
+{
+ return vector4 (abs(in.x),
+ abs(in.y),
+ abs(in.z),
+ abs(in.w));
+}
+
+vector4 ceil(vector4 in)
+{
+ return vector4 (ceil(in.x),
+ ceil(in.y),
+ ceil(in.z),
+ ceil(in.w));
+}
+
+vector4 round(vector4 in)
+{
+ return vector4 (round(in.x),
+ round(in.y),
+ round(in.z),
+ round(in.w));
+}
+
+vector4 floor(vector4 in)
+{
+ return vector4 (floor(in.x),
+ floor(in.y),
+ floor(in.z),
+ floor(in.w));
+}
+
+vector4 sqrt(vector4 in)
+{
+ return vector4 (sqrt(in.x),
+ sqrt(in.y),
+ sqrt(in.z),
+ sqrt(in.w));
+}
+
+vector4 exp(vector4 in)
+{
+ return vector4 (exp(in.x),
+ exp(in.y),
+ exp(in.z),
+ exp(in.w));
+}
+
+vector4 log(vector4 in)
+{
+ return vector4 (log(in.x),
+ log(in.y),
+ log(in.z),
+ log(in.w));
+}
+
+vector4 log2(vector4 in)
+{
+ return vector4 (log2(in.x),
+ log2(in.y),
+ log2(in.z),
+ log2(in.w));
+}
+
+vector4 mix(vector4 value1, vector4 value2, float x )
+{
+ return vector4 (mix( value1.x, value2.x, x),
+ mix( value1.y, value2.y, x),
+ mix( value1.z, value2.z, x),
+ mix( value1.w, value2.w, x));
+}
+
+vector4 mix(vector4 value1, vector4 value2, vector4 x )
+{
+ return vector4 (mix( value1.x, value2.x, x.x),
+ mix( value1.y, value2.y, x.y),
+ mix( value1.z, value2.z, x.z),
+ mix( value1.w, value2.w, x.w));
+}
+
+vector vec4ToVec3(vector4 v)
+{
+ return vector(v.x, v.y, v.z) / v.w;
+}
+
+float dot(vector4 a, vector4 b)
+{
+ return ((a.x * b.x) + (a.y * b.y) + (a.z * b.z) + (a.w * b.w));
+}
+
+float length (vector4 a)
+{
+ return sqrt (a.x*a.x + a.y*a.y + a.z*a.z + a.w*a.w);
+}
+
+vector4 smoothstep(vector4 low, vector4 high, vector4 in)
+{
+ return vector4 (smoothstep(low.x, high.x, in.x),
+ smoothstep(low.y, high.y, in.y),
+ smoothstep(low.z, high.z, in.z),
+ smoothstep(low.w, high.w, in.w));
+}
+
+vector4 smoothstep(float low, float high, vector4 in)
+{
+ return vector4 (smoothstep(low, high, in.x),
+ smoothstep(low, high, in.y),
+ smoothstep(low, high, in.z),
+ smoothstep(low, high, in.w));
+}
+
+vector4 clamp(vector4 in, vector4 low, vector4 high)
+{
+ return vector4 (clamp(in.x, low.x, high.x),
+ clamp(in.y, low.y, high.y),
+ clamp(in.z, low.z, high.z),
+ clamp(in.w, low.w, high.w));
+}
+
+vector4 clamp(vector4 in, float low, float high)
+{
+ return vector4 (clamp(in.x, low, high),
+ clamp(in.y, low, high),
+ clamp(in.z, low, high),
+ clamp(in.w, low, high));
+}
+
+vector4 max(vector4 a, vector4 b)
+{
+ return vector4 (max(a.x, b.x),
+ max(a.y, b.y),
+ max(a.z, b.z),
+ max(a.w, b.w));
+}
+
+vector4 max(vector4 a, float b)
+{
+ return max(a, vector4(b, b, b, b));
+}
+
+vector4 min(vector4 a, vector4 b)
+{
+ return vector4 (min(a.x, b.x),
+ min(a.y, b.y),
+ min(a.z, b.z),
+ min(a.w, b.w));
+}
+
+vector4 min(vector4 a, float b)
+{
+ return min(a, vector4(b, b, b, b));
+}
+
+vector4 normalize(vector4 a)
+{
+ return a / length(a);
+}
+
+vector4 mod(vector4 a, vector4 b)
+{
+ return vector4(mod(a.x, b.x),
+ mod(a.y, b.y),
+ mod(a.z, b.z),
+ mod(a.w, b.w));
+}
+
+vector4 mod(vector4 a, float b)
+{
+ return mod(a, vector4(b, b, b, b));
+}
+
+vector4 fmod(vector4 a, vector4 b)
+{
+ return vector4 (fmod(a.x, b.x),
+ fmod(a.y, b.y),
+ fmod(a.z, b.z),
+ fmod(a.w, b.w));
+}
+
+vector4 fmod(vector4 a, float b)
+{
+ return fmod(a, vector4(b, b, b, b));
+}
+
+vector4 pow(vector4 in, vector4 amount)
+{
+ return vector4 (pow(in.x, amount.x),
+ pow(in.y, amount.y),
+ pow(in.z, amount.z),
+ pow(in.w, amount.w));
+}
+
+vector4 pow(vector4 in, float amount)
+{
+ return vector4 (pow(in.x, amount),
+ pow(in.y, amount),
+ pow(in.z, amount),
+ pow(in.w, amount));
+}
+
+vector4 sign(vector4 a)
+{
+ return vector4(sign(a.x),
+ sign(a.y),
+ sign(a.z),
+ sign(a.w));
+}
+
+vector4 sin(vector4 a)
+{
+ return vector4(sin(a.x),
+ sin(a.y),
+ sin(a.z),
+ sin(a.w));
+}
+
+vector4 cos(vector4 a)
+{
+ return vector4(cos(a.x),
+ cos(a.y),
+ cos(a.z),
+ cos(a.w));
+}
+
+vector4 tan(vector4 a)
+{
+ return vector4(tan(a.x),
+ tan(a.y),
+ tan(a.z),
+ tan(a.w));
+}
+
+vector4 asin(vector4 a)
+{
+ return vector4(asin(a.x),
+ asin(a.y),
+ asin(a.z),
+ asin(a.w));
+}
+
+vector4 acos(vector4 a)
+{
+ return vector4(acos(a.x),
+ acos(a.y),
+ acos(a.z),
+ acos(a.w));
+}
+
+vector4 atan2(vector4 a, float f)
+{
+ return vector4(atan2(a.x, f),
+ atan2(a.y, f),
+ atan2(a.z, f),
+ atan2(a.w, f));
+}
+
+vector4 atan2(vector4 a, vector4 b)
+{
+ return vector4(atan2(a.x, b.x),
+ atan2(a.y, b.y),
+ atan2(a.z, b.z),
+ atan2(a.w, b.w));
+}
+
+
+vector4 transform (matrix M, vector4 p)
+{
+ return vector4 (M[0][0]*p.x + M[1][0]*p.y + M[2][0]*p.z + M[3][0]*p.w,
+ M[0][1]*p.x + M[1][1]*p.y + M[2][1]*p.z + M[3][1]*p.w,
+ M[0][2]*p.x + M[1][2]*p.y + M[2][2]*p.z + M[3][2]*p.w,
+ M[0][3]*p.x + M[1][3]*p.y + M[2][3]*p.z + M[3][3]*p.w);
+}
+
+vector4 transform (string fromspace, string tospace, vector4 p)
+{
+ return transform (matrix(fromspace,tospace), p);
+}
diff --git a/templates/stdlib/genosl/lib/mx_transform_uv.osl b/templates/stdlib/genosl/lib/mx_transform_uv.osl
new file mode 100644
index 0000000000..3be9c308c0
--- /dev/null
+++ b/templates/stdlib/genosl/lib/mx_transform_uv.osl
@@ -0,0 +1,4 @@
+vector2 mx_transform_uv(vector2 texcoord)
+{
+ return texcoord;
+}
diff --git a/templates/stdlib/genosl/lib/mx_transform_uv_vflip.osl b/templates/stdlib/genosl/lib/mx_transform_uv_vflip.osl
new file mode 100644
index 0000000000..75f38902b5
--- /dev/null
+++ b/templates/stdlib/genosl/lib/mx_transform_uv_vflip.osl
@@ -0,0 +1,4 @@
+vector2 mx_transform_uv(vector2 texcoord)
+{
+ return vector2(texcoord.x, 1.0 - texcoord.y);
+}
diff --git a/templates/stdlib/genosl/lib/vector4_extra_ops.osl b/templates/stdlib/genosl/lib/vector4_extra_ops.osl
new file mode 100644
index 0000000000..8bacf20d2d
--- /dev/null
+++ b/templates/stdlib/genosl/lib/vector4_extra_ops.osl
@@ -0,0 +1,42 @@
+// Adds some syntactic sugar allowing mixing vector4 and color4 as
+// arguments of some binary operators used by OCIO transform code.
+
+vector4 __operator__mul__(matrix m, vector4 v)
+{
+ return transform(m, v);
+}
+
+vector4 __operator__mul__(color4 c, vector4 v)
+{
+ return vector4(c.rgb.r, c.rgb.g, c.rgb.b, c.a) * v;
+}
+
+vector4 __operator__mul__(vector4 v, color4 c)
+{
+ return c * v;
+}
+
+vector4 __operator__sub__(color4 c, vector4 v)
+{
+ return vector4(c.rgb.r, c.rgb.g, c.rgb.b, c.a) - v;
+}
+
+vector4 __operator__add__(vector4 v, color4 c)
+{
+ return v + vector4(c.rgb.r, c.rgb.g, c.rgb.b, c.a);
+}
+
+vector4 __operator__add__(color4 c, vector4 v)
+{
+ return v + c;
+}
+
+vector4 pow(color4 c, vector4 v)
+{
+ return pow(vector4(c.rgb.r, c.rgb.g, c.rgb.b, c.a), v);
+}
+
+vector4 max(vector4 v, color4 c)
+{
+ return max(v, vector4(c.rgb.r, c.rgb.g, c.rgb.b, c.a));
+}
diff --git a/templates/stdlib/genosl/mx_burn_color3.osl b/templates/stdlib/genosl/mx_burn_color3.osl
new file mode 100644
index 0000000000..5c8d7a3d47
--- /dev/null
+++ b/templates/stdlib/genosl/mx_burn_color3.osl
@@ -0,0 +1,8 @@
+#include "mx_burn_float.osl"
+
+void mx_burn_color3(color fg, color bg, float mix, output color result)
+{
+ mx_burn_float(fg[0], bg[0], mix, result[0]);
+ mx_burn_float(fg[1], bg[1], mix, result[1]);
+ mx_burn_float(fg[2], bg[2], mix, result[2]);
+}
diff --git a/templates/stdlib/genosl/mx_burn_color4.osl b/templates/stdlib/genosl/mx_burn_color4.osl
new file mode 100644
index 0000000000..54f40ec9c7
--- /dev/null
+++ b/templates/stdlib/genosl/mx_burn_color4.osl
@@ -0,0 +1,9 @@
+#include "mx_burn_float.osl"
+
+void mx_burn_color4(color4 fg, color4 bg, float mix, output color4 result)
+{
+ mx_burn_float(fg.rgb[0], bg.rgb[0], mix, result.rgb[0]);
+ mx_burn_float(fg.rgb[1], bg.rgb[1], mix, result.rgb[1]);
+ mx_burn_float(fg.rgb[2], bg.rgb[2], mix, result.rgb[2]);
+ mx_burn_float(fg.a, bg.a, mix, result.a);
+}
diff --git a/templates/stdlib/genosl/mx_burn_float.osl b/templates/stdlib/genosl/mx_burn_float.osl
new file mode 100644
index 0000000000..9f17b7251a
--- /dev/null
+++ b/templates/stdlib/genosl/mx_burn_float.osl
@@ -0,0 +1,9 @@
+void mx_burn_float(float fg, float bg, float mix, output float result)
+{
+ if (abs(fg) < M_FLOAT_EPS)
+ {
+ result = 0.0;
+ return;
+ }
+ result = mix*(1.0 - ((1.0 - bg) / fg)) + ((1.0-mix)*bg);
+}
diff --git a/templates/stdlib/genosl/mx_cellnoise2d_float.osl b/templates/stdlib/genosl/mx_cellnoise2d_float.osl
new file mode 100644
index 0000000000..699f088894
--- /dev/null
+++ b/templates/stdlib/genosl/mx_cellnoise2d_float.osl
@@ -0,0 +1,4 @@
+void mx_cellnoise2d_float(vector2 texcoord, output float result)
+{
+ result = cellnoise(texcoord.x, texcoord.y);
+}
diff --git a/templates/stdlib/genosl/mx_cellnoise3d_float.osl b/templates/stdlib/genosl/mx_cellnoise3d_float.osl
new file mode 100644
index 0000000000..a40b612611
--- /dev/null
+++ b/templates/stdlib/genosl/mx_cellnoise3d_float.osl
@@ -0,0 +1,4 @@
+void mx_cellnoise3d_float(vector position, output float result)
+{
+ result = cellnoise(position);
+}
diff --git a/templates/stdlib/genosl/mx_creatematrix.osl b/templates/stdlib/genosl/mx_creatematrix.osl
new file mode 100644
index 0000000000..3e567d3b5b
--- /dev/null
+++ b/templates/stdlib/genosl/mx_creatematrix.osl
@@ -0,0 +1,23 @@
+void mx_creatematrix_vector3_matrix33(vector in1, vector in2, vector in3, output matrix result)
+{
+ result = matrix(in1.x, in1.y, in1.z, 0.0,
+ in2.x, in2.y, in2.z, 0.0,
+ in3.x, in3.y, in3.z, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+}
+
+void mx_creatematrix_vector3_matrix44(vector in1, vector in2, vector in3, vector in4, output matrix result)
+{
+ result = matrix(in1.x, in1.y, in1.z, 0.0,
+ in2.x, in2.y, in2.z, 0.0,
+ in3.x, in3.y, in3.z, 0.0,
+ in4.x, in4.y, in4.z, 1.0);
+}
+
+void mx_creatematrix_vector4_matrix44(vector4 in1, vector4 in2, vector4 in3, vector4 in4, output matrix result)
+{
+ result = matrix(in1.x, in1.y, in1.z, in1.w,
+ in2.x, in2.y, in2.z, in2.w,
+ in3.x, in3.y, in3.z, in3.w,
+ in4.x, in4.y, in4.z, in4.w);
+}
diff --git a/templates/stdlib/genosl/mx_disjointover_color4.osl b/templates/stdlib/genosl/mx_disjointover_color4.osl
new file mode 100644
index 0000000000..094e1f505b
--- /dev/null
+++ b/templates/stdlib/genosl/mx_disjointover_color4.osl
@@ -0,0 +1,25 @@
+void mx_disjointover_color4(color4 fg, color4 bg, float mix, output color4 result)
+{
+ float summedAlpha = fg.a + bg.a;
+
+ if (summedAlpha <= 1)
+ {
+ result.rgb = fg.rgb + bg.rgb;
+ }
+ else
+ {
+ if (abs(bg.a) < M_FLOAT_EPS)
+ {
+ result.rgb = 0.0;
+ }
+ else
+ {
+ float x = (1 - fg.a) / bg.a;
+ result.rgb = fg.rgb + bg.rgb * x;
+ }
+ }
+ result.a = min(summedAlpha, 1.0);
+
+ result.rgb = result.rgb * mix + (1.0 - mix) * bg.rgb;
+ result.a = result.a * mix + (1.0 - mix) * bg.a;
+}
diff --git a/templates/stdlib/genosl/mx_dodge_color3.osl b/templates/stdlib/genosl/mx_dodge_color3.osl
new file mode 100644
index 0000000000..3ebbac0a61
--- /dev/null
+++ b/templates/stdlib/genosl/mx_dodge_color3.osl
@@ -0,0 +1,8 @@
+#include "mx_dodge_float.osl"
+
+void mx_dodge_color3(color fg, color bg, float mix, output color result)
+{
+ mx_dodge_float(fg[0], bg[0], mix, result[0]);
+ mx_dodge_float(fg[1], bg[1], mix, result[1]);
+ mx_dodge_float(fg[2], bg[2], mix, result[2]);
+}
diff --git a/templates/stdlib/genosl/mx_dodge_color4.osl b/templates/stdlib/genosl/mx_dodge_color4.osl
new file mode 100644
index 0000000000..0ad4a2b142
--- /dev/null
+++ b/templates/stdlib/genosl/mx_dodge_color4.osl
@@ -0,0 +1,9 @@
+#include "mx_dodge_float.osl"
+
+void mx_dodge_color4(color4 fg , color4 bg , float mix , output color4 result)
+{
+ mx_dodge_float(fg.rgb[0], bg.rgb[0], mix, result.rgb[0]);
+ mx_dodge_float(fg.rgb[1], bg.rgb[1], mix, result.rgb[1]);
+ mx_dodge_float(fg.rgb[2], bg.rgb[2], mix, result.rgb[2]);
+ mx_dodge_float(fg.a, bg.a, mix, result.a);
+}
diff --git a/templates/stdlib/genosl/mx_dodge_float.osl b/templates/stdlib/genosl/mx_dodge_float.osl
new file mode 100644
index 0000000000..a5c869a80c
--- /dev/null
+++ b/templates/stdlib/genosl/mx_dodge_float.osl
@@ -0,0 +1,9 @@
+void mx_dodge_float(float fg, float bg, float mix, output float out)
+{
+ if (abs(1.0 - fg) < M_FLOAT_EPS)
+ {
+ out = 0.0;
+ return;
+ }
+ out = mix*(bg / (1.0 - fg)) + ((1.0-mix)*bg);
+}
diff --git a/templates/stdlib/genosl/mx_fractal2d_float.osl b/templates/stdlib/genosl/mx_fractal2d_float.osl
new file mode 100644
index 0000000000..bb2148437e
--- /dev/null
+++ b/templates/stdlib/genosl/mx_fractal2d_float.osl
@@ -0,0 +1,5 @@
+void mx_fractal2d_float(float amplitude, int octaves, float lacunarity, float diminish, vector2 texcoord, output float result)
+{
+ float f = mx_fbm(texcoord.x, texcoord.y, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
diff --git a/templates/stdlib/genosl/mx_fractal2d_vector2.osl b/templates/stdlib/genosl/mx_fractal2d_vector2.osl
new file mode 100644
index 0000000000..c7045d0723
--- /dev/null
+++ b/templates/stdlib/genosl/mx_fractal2d_vector2.osl
@@ -0,0 +1,5 @@
+void mx_fractal2d_vector2(vector2 amplitude, int octaves, float lacunarity, float diminish, vector2 texcoord, output vector2 result)
+{
+ vector2 f = mx_fbm(texcoord.x, texcoord.y, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
diff --git a/templates/stdlib/genosl/mx_fractal2d_vector3.osl b/templates/stdlib/genosl/mx_fractal2d_vector3.osl
new file mode 100644
index 0000000000..49c217e3e9
--- /dev/null
+++ b/templates/stdlib/genosl/mx_fractal2d_vector3.osl
@@ -0,0 +1,5 @@
+void mx_fractal2d_vector3(vector amplitude, int octaves, float lacunarity, float diminish, vector2 texcoord, output vector result)
+{
+ vector f = mx_fbm(texcoord.x, texcoord.y, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
diff --git a/templates/stdlib/genosl/mx_fractal2d_vector4.osl b/templates/stdlib/genosl/mx_fractal2d_vector4.osl
new file mode 100644
index 0000000000..d8e78195f3
--- /dev/null
+++ b/templates/stdlib/genosl/mx_fractal2d_vector4.osl
@@ -0,0 +1,5 @@
+void mx_fractal2d_vector4(vector4 amplitude, int octaves, float lacunarity, float diminish, vector2 texcoord, output vector4 result)
+{
+ vector4 f = mx_fbm(texcoord.x, texcoord.y, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
diff --git a/templates/stdlib/genosl/mx_fractal3d_float.osl b/templates/stdlib/genosl/mx_fractal3d_float.osl
new file mode 100644
index 0000000000..383daafe62
--- /dev/null
+++ b/templates/stdlib/genosl/mx_fractal3d_float.osl
@@ -0,0 +1,5 @@
+void mx_fractal3d_float(float amplitude, int octaves, float lacunarity, float diminish, vector position, output float result)
+{
+ float f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
diff --git a/templates/stdlib/genosl/mx_fractal3d_vector2.osl b/templates/stdlib/genosl/mx_fractal3d_vector2.osl
new file mode 100644
index 0000000000..38b45fde52
--- /dev/null
+++ b/templates/stdlib/genosl/mx_fractal3d_vector2.osl
@@ -0,0 +1,5 @@
+void mx_fractal3d_vector2(vector2 amplitude, int octaves, float lacunarity, float diminish, vector position, output vector2 result)
+{
+ vector2 f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
diff --git a/templates/stdlib/genosl/mx_fractal3d_vector3.osl b/templates/stdlib/genosl/mx_fractal3d_vector3.osl
new file mode 100644
index 0000000000..dad044b091
--- /dev/null
+++ b/templates/stdlib/genosl/mx_fractal3d_vector3.osl
@@ -0,0 +1,5 @@
+void mx_fractal3d_vector3(vector amplitude, int octaves, float lacunarity, float diminish, vector position, output vector result)
+{
+ vector f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
diff --git a/templates/stdlib/genosl/mx_fractal3d_vector4.osl b/templates/stdlib/genosl/mx_fractal3d_vector4.osl
new file mode 100644
index 0000000000..33ac5c0e8e
--- /dev/null
+++ b/templates/stdlib/genosl/mx_fractal3d_vector4.osl
@@ -0,0 +1,5 @@
+void mx_fractal3d_vector4(vector4 amplitude, int octaves, float lacunarity, float diminish, vector position, output vector4 result)
+{
+ vector4 f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
diff --git a/templates/stdlib/genosl/mx_frame_float.osl b/templates/stdlib/genosl/mx_frame_float.osl
new file mode 100644
index 0000000000..ab05828131
--- /dev/null
+++ b/templates/stdlib/genosl/mx_frame_float.osl
@@ -0,0 +1,6 @@
+void mx_frame_float(output float result)
+{
+ // Use the standard default value if the attribute is not present.
+ result = 1.0;
+ getattribute("frame", result);
+}
diff --git a/templates/stdlib/genosl/mx_geomcolor_color3.osl b/templates/stdlib/genosl/mx_geomcolor_color3.osl
new file mode 100644
index 0000000000..bb94651b2f
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geomcolor_color3.osl
@@ -0,0 +1,4 @@
+void mx_geomcolor_color3(int index, output color result)
+{
+ getattribute("color", result);
+}
diff --git a/templates/stdlib/genosl/mx_geomcolor_color4.osl b/templates/stdlib/genosl/mx_geomcolor_color4.osl
new file mode 100644
index 0000000000..b501554044
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geomcolor_color4.osl
@@ -0,0 +1,9 @@
+void mx_geomcolor_color4(int index, output color4 result)
+{
+ float value[4];
+ getattribute("color", value);
+ result.rgb[0] = value[0];
+ result.rgb[1] = value[1];
+ result.rgb[2] = value[2];
+ result.a = value[3];
+}
diff --git a/templates/stdlib/genosl/mx_geomcolor_float.osl b/templates/stdlib/genosl/mx_geomcolor_float.osl
new file mode 100644
index 0000000000..fa9eb99445
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geomcolor_float.osl
@@ -0,0 +1,4 @@
+void mx_geomcolor_float(int index, output float result)
+{
+ getattribute("color", result);
+}
diff --git a/templates/stdlib/genosl/mx_geompropvalue_boolean.osl b/templates/stdlib/genosl/mx_geompropvalue_boolean.osl
new file mode 100644
index 0000000000..5a433247de
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geompropvalue_boolean.osl
@@ -0,0 +1,5 @@
+void mx_geompropvalue_boolean(string geomprop, int defaultVal, output int out)
+{
+ if (getattribute(geomprop, out) == 0)
+ out = defaultVal;
+}
diff --git a/templates/stdlib/genosl/mx_geompropvalue_color3.osl b/templates/stdlib/genosl/mx_geompropvalue_color3.osl
new file mode 100644
index 0000000000..b71333c047
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geompropvalue_color3.osl
@@ -0,0 +1,5 @@
+void mx_geompropvalue_color(string geomprop, color defaultVal, output color out)
+{
+ if (getattribute(geomprop, out) == 0)
+ out = defaultVal;
+}
diff --git a/templates/stdlib/genosl/mx_geompropvalue_color4.osl b/templates/stdlib/genosl/mx_geompropvalue_color4.osl
new file mode 100644
index 0000000000..92ccad48d3
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geompropvalue_color4.osl
@@ -0,0 +1,16 @@
+void mx_geompropvalue_color4(string geomprop, color4 defaultVal, output color4 out)
+{
+ float value[4];
+ if (getattribute(geomprop, value) == 0)
+ {
+ out.rgb = defaultVal.rgb;
+ out.a = defaultVal.a;
+ }
+ else
+ {
+ out.rgb[0] = value[0];
+ out.rgb[1] = value[1];
+ out.rgb[2] = value[2];
+ out.a = value[3];
+ }
+}
diff --git a/templates/stdlib/genosl/mx_geompropvalue_float.osl b/templates/stdlib/genosl/mx_geompropvalue_float.osl
new file mode 100644
index 0000000000..7209b05d98
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geompropvalue_float.osl
@@ -0,0 +1,7 @@
+void mx_geompropvalue_float(string geomprop, float defaultVal, output float result)
+{
+ if (getattribute(geomprop, result) == 0)
+ {
+ result = defaultVal;
+ }
+}
diff --git a/templates/stdlib/genosl/mx_geompropvalue_integer.osl b/templates/stdlib/genosl/mx_geompropvalue_integer.osl
new file mode 100644
index 0000000000..719f2e2334
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geompropvalue_integer.osl
@@ -0,0 +1,5 @@
+void mx_geompropvalue_integer(string geomprop, int defaultVal, output int out)
+{
+ if (getattribute(geomprop, out) == 0)
+ out = defaultVal;
+}
diff --git a/templates/stdlib/genosl/mx_geompropvalue_string.osl b/templates/stdlib/genosl/mx_geompropvalue_string.osl
new file mode 100644
index 0000000000..cd81feba93
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geompropvalue_string.osl
@@ -0,0 +1,5 @@
+void mx_geompropvalue_string(string geomprop, string defaultVal, output string out)
+{
+ if (getattribute(geomprop, out) == 0)
+ out = defaultVal;
+}
diff --git a/templates/stdlib/genosl/mx_geompropvalue_vector2.osl b/templates/stdlib/genosl/mx_geompropvalue_vector2.osl
new file mode 100644
index 0000000000..349d33c8ad
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geompropvalue_vector2.osl
@@ -0,0 +1,13 @@
+void mx_geompropvalue_vector2(string geomprop, vector2 defaultVal, output vector2 out)
+{
+ float value[2];
+ if (getattribute(geomprop, value) == 0)
+ {
+ out = defaultVal;
+ }
+ else
+ {
+ out.x = value[0];
+ out.y = value[1];
+ }
+}
diff --git a/templates/stdlib/genosl/mx_geompropvalue_vector3.osl b/templates/stdlib/genosl/mx_geompropvalue_vector3.osl
new file mode 100644
index 0000000000..00fa534f78
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geompropvalue_vector3.osl
@@ -0,0 +1,5 @@
+void mx_geompropvalue_vector(string geomprop, vector defaultVal, output vector out)
+{
+ if (getattribute(geomprop, out) == 0)
+ out = defaultVal;
+}
diff --git a/templates/stdlib/genosl/mx_geompropvalue_vector4.osl b/templates/stdlib/genosl/mx_geompropvalue_vector4.osl
new file mode 100644
index 0000000000..0fc684604f
--- /dev/null
+++ b/templates/stdlib/genosl/mx_geompropvalue_vector4.osl
@@ -0,0 +1,15 @@
+void mx_geompropvalue_vector4(string geomprop, vector4 defaultVal, output vector4 out)
+{
+ float value[4];
+ if (getattribute(geomprop, value) == 0)
+ {
+ out = defaultVal;
+ }
+ else
+ {
+ out.x = value[0];
+ out.y = value[1];
+ out.z = value[2];
+ out.w = value[3];
+ }
+}
diff --git a/templates/stdlib/genosl/mx_heighttonormal_vector3.osl b/templates/stdlib/genosl/mx_heighttonormal_vector3.osl
new file mode 100644
index 0000000000..da28638ddf
--- /dev/null
+++ b/templates/stdlib/genosl/mx_heighttonormal_vector3.osl
@@ -0,0 +1,28 @@
+void mx_heighttonormal_vector3(float height, float scale, vector2 texcoord, output vector result)
+{
+ // Scale factor for parity with traditional Sobel filtering.
+ float SOBEL_SCALE_FACTOR = 1.0 / 16.0;
+
+ // Compute screen-space gradients of the heightfield and texture coordinates.
+ vector2 dHdS = vector2(Dx(height), Dy(height)) * scale * SOBEL_SCALE_FACTOR;
+ vector2 dUdS = vector2(Dx(texcoord.x), Dy(texcoord.x));
+ vector2 dVdS = vector2(Dx(texcoord.y), Dy(texcoord.y));
+
+ // Construct a screen-space tangent frame.
+ vector tangent = vector(dUdS.x, dVdS.x, dHdS.x);
+ vector bitangent = vector(dUdS.y, dVdS.y, dHdS.y);
+ vector n = cross(tangent, bitangent);
+
+ // Handle invalid and mirrored texture coordinates.
+ if (dot(n, n) < M_FLOAT_EPS * M_FLOAT_EPS)
+ {
+ n = vector(0, 0, 1);
+ }
+ else if (n[2] < 0.0)
+ {
+ n *= -1.0;
+ }
+
+ // Normalize and encode the results.
+ result = normalize(n) * 0.5 + 0.5;
+}
diff --git a/templates/stdlib/genosl/mx_hsvtorgb_color3.osl b/templates/stdlib/genosl/mx_hsvtorgb_color3.osl
new file mode 100644
index 0000000000..4b3e83b62f
--- /dev/null
+++ b/templates/stdlib/genosl/mx_hsvtorgb_color3.osl
@@ -0,0 +1,4 @@
+void mx_hsvtorgb_color3(vector _in, output vector result)
+{
+ result = transformc("hsv","rgb", _in);
+}
diff --git a/templates/stdlib/genosl/mx_hsvtorgb_color4.osl b/templates/stdlib/genosl/mx_hsvtorgb_color4.osl
new file mode 100644
index 0000000000..60e76c011e
--- /dev/null
+++ b/templates/stdlib/genosl/mx_hsvtorgb_color4.osl
@@ -0,0 +1,4 @@
+void mx_hsvtorgb_color4(color4 _in, output color4 result)
+{
+ result = color4(transformc("hsv","rgb", _in.rgb), 1.0);
+}
diff --git a/templates/stdlib/genosl/mx_image_color3.osl b/templates/stdlib/genosl/mx_image_color3.osl
new file mode 100644
index 0000000000..e17db18de4
--- /dev/null
+++ b/templates/stdlib/genosl/mx_image_color3.osl
@@ -0,0 +1,23 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_color3(textureresource file, string layer, color default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output color out)
+{
+ if (file.filename == "" ||
+ (uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
+ (vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
+ {
+ out = default_value;
+ return;
+ }
+
+ color missingColor = default_value;
+ vector2 st = mx_transform_uv(texcoord);
+ out = texture(file.filename, st.x, st.y,
+ "subimage", layer, "interp", filtertype,
+ "missingcolor", missingColor,
+ "swrap", uaddressmode, "twrap", vaddressmode
+#if OSL_VERSION_MAJOR >= 1 && OSL_VERSION_MINOR >= 14
+ , "colorspace", file.colorspace
+#endif
+ );
+}
diff --git a/templates/stdlib/genosl/mx_image_color4.osl b/templates/stdlib/genosl/mx_image_color4.osl
new file mode 100644
index 0000000000..40bfa56c0a
--- /dev/null
+++ b/templates/stdlib/genosl/mx_image_color4.osl
@@ -0,0 +1,27 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_color4(textureresource file, string layer, color4 default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output color4 out)
+{
+ if (file.filename == "" ||
+ (uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
+ (vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
+ {
+ out = default_value;
+ return;
+ }
+
+ color missingColor = default_value.rgb;
+ float missingAlpha = default_value.a;
+ vector2 st = mx_transform_uv(texcoord);
+ float alpha;
+ color rgb = texture(file.filename, st.x, st.y, "alpha", alpha,
+ "subimage", layer, "interp", filtertype,
+ "missingcolor", missingColor, "missingalpha", missingAlpha,
+ "swrap", uaddressmode, "twrap", vaddressmode
+#if OSL_VERSION_MAJOR >= 1 && OSL_VERSION_MINOR >= 14
+ , "colorspace", file.colorspace
+#endif
+ );
+
+ out = color4(rgb, alpha);
+}
diff --git a/templates/stdlib/genosl/mx_image_float.osl b/templates/stdlib/genosl/mx_image_float.osl
new file mode 100644
index 0000000000..2cdbb9fb27
--- /dev/null
+++ b/templates/stdlib/genosl/mx_image_float.osl
@@ -0,0 +1,20 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_float(textureresource file, string layer, float default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output float out)
+{
+ if (file.filename == "" ||
+ (uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
+ (vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
+ {
+ out = default_value;
+ return;
+ }
+
+ color missingColor = color(default_value);
+ vector2 st = mx_transform_uv(texcoord);
+ color rgb = texture(file.filename, st.x, st.y,
+ "subimage", layer, "interp", filtertype,
+ "missingcolor", missingColor,
+ "swrap", uaddressmode, "twrap", vaddressmode);
+ out = rgb[0];
+}
diff --git a/templates/stdlib/genosl/mx_image_vector2.osl b/templates/stdlib/genosl/mx_image_vector2.osl
new file mode 100644
index 0000000000..67c1ae270b
--- /dev/null
+++ b/templates/stdlib/genosl/mx_image_vector2.osl
@@ -0,0 +1,21 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_vector2(textureresource file, string layer, vector2 default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output vector2 out)
+{
+ if (file.filename == "" ||
+ (uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
+ (vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
+ {
+ out = default_value;
+ return;
+ }
+
+ color missingColor = color(default_value.x, default_value.y, 0.0);
+ vector2 st = mx_transform_uv(texcoord);
+ color rgb = texture(file.filename, st.x, st.y,
+ "subimage", layer, "interp", filtertype,
+ "missingcolor", missingColor,
+ "swrap", uaddressmode, "twrap", vaddressmode);
+ out.x = rgb[0];
+ out.y = rgb[1];
+}
diff --git a/templates/stdlib/genosl/mx_image_vector3.osl b/templates/stdlib/genosl/mx_image_vector3.osl
new file mode 100644
index 0000000000..63f363e957
--- /dev/null
+++ b/templates/stdlib/genosl/mx_image_vector3.osl
@@ -0,0 +1,19 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_vector3(textureresource file, string layer, vector default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output vector out)
+{
+ if (file.filename == "" ||
+ (uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
+ (vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
+ {
+ out = default_value;
+ return;
+ }
+
+ color missingColor = default_value;
+ vector2 st = mx_transform_uv(texcoord);
+ out = texture(file.filename, st.x, st.y,
+ "subimage", layer, "interp", filtertype,
+ "missingcolor", missingColor,
+ "swrap", uaddressmode, "twrap", vaddressmode);
+}
diff --git a/templates/stdlib/genosl/mx_image_vector4.osl b/templates/stdlib/genosl/mx_image_vector4.osl
new file mode 100644
index 0000000000..eba820a241
--- /dev/null
+++ b/templates/stdlib/genosl/mx_image_vector4.osl
@@ -0,0 +1,23 @@
+#include "lib/$fileTransformUv"
+
+void mx_image_vector4(textureresource file, string layer, vector4 default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output vector4 out)
+{
+ if (file.filename == "" ||
+ (uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
+ (vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
+ {
+ out = default_value;
+ return;
+ }
+
+ color missingColor = color(default_value.x, default_value.y, default_value.z);
+ float missingAlpha = default_value.w;
+ vector2 st = mx_transform_uv(texcoord);
+ float alpha;
+ color rgb = texture(file.filename, st.x, st.y, "alpha", alpha,
+ "subimage", layer, "interp", filtertype,
+ "missingcolor", missingColor, "missingalpha", missingAlpha,
+ "swrap", uaddressmode, "twrap", vaddressmode);
+
+ out = vector4(rgb[0], rgb[1], rgb[2], alpha);
+}
diff --git a/templates/stdlib/genosl/mx_luminance_color3.osl b/templates/stdlib/genosl/mx_luminance_color3.osl
new file mode 100644
index 0000000000..8db9712a34
--- /dev/null
+++ b/templates/stdlib/genosl/mx_luminance_color3.osl
@@ -0,0 +1,4 @@
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
diff --git a/templates/stdlib/genosl/mx_luminance_color4.osl b/templates/stdlib/genosl/mx_luminance_color4.osl
new file mode 100644
index 0000000000..bee2298b7a
--- /dev/null
+++ b/templates/stdlib/genosl/mx_luminance_color4.osl
@@ -0,0 +1,4 @@
+void mx_luminance_color4(color4 in, color lumacoeffs, output color4 result)
+{
+ result = color4(dot(in.rgb, lumacoeffs), in.a);
+}
diff --git a/templates/stdlib/genosl/mx_mix_surfaceshader.osl b/templates/stdlib/genosl/mx_mix_surfaceshader.osl
new file mode 100644
index 0000000000..f6a9ea67ae
--- /dev/null
+++ b/templates/stdlib/genosl/mx_mix_surfaceshader.osl
@@ -0,0 +1,6 @@
+void mx_mix_surfaceshader(surfaceshader fg, surfaceshader bg, float w, output surfaceshader result)
+{
+ result.bsdf = mix(bg.bsdf, fg.bsdf, w);
+ result.edf = mix(bg.edf, fg.edf, w);
+ result.opacity = mix(bg.opacity, fg.opacity, w);
+}
diff --git a/templates/stdlib/genosl/mx_noise2d_float.osl b/templates/stdlib/genosl/mx_noise2d_float.osl
new file mode 100644
index 0000000000..2af1bd421e
--- /dev/null
+++ b/templates/stdlib/genosl/mx_noise2d_float.osl
@@ -0,0 +1,5 @@
+void mx_noise2d_float(float amplitude, float pivot, vector2 texcoord, output float result)
+{
+ float value = noise("snoise", texcoord.x, texcoord.y);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genosl/mx_noise2d_vector2.osl b/templates/stdlib/genosl/mx_noise2d_vector2.osl
new file mode 100644
index 0000000000..1dff4f3925
--- /dev/null
+++ b/templates/stdlib/genosl/mx_noise2d_vector2.osl
@@ -0,0 +1,5 @@
+void mx_noise2d_vector2(vector2 amplitude, float pivot, vector2 texcoord, output vector2 result)
+{
+ vector2 value = mx_noise("snoise", texcoord.x, texcoord.y);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genosl/mx_noise2d_vector3.osl b/templates/stdlib/genosl/mx_noise2d_vector3.osl
new file mode 100644
index 0000000000..06d10cc262
--- /dev/null
+++ b/templates/stdlib/genosl/mx_noise2d_vector3.osl
@@ -0,0 +1,5 @@
+void mx_noise2d_vector3(vector amplitude, float pivot, vector2 texcoord, output vector result)
+{
+ vector value = noise("snoise", texcoord.x, texcoord.y);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genosl/mx_noise2d_vector4.osl b/templates/stdlib/genosl/mx_noise2d_vector4.osl
new file mode 100644
index 0000000000..6aac0ea690
--- /dev/null
+++ b/templates/stdlib/genosl/mx_noise2d_vector4.osl
@@ -0,0 +1,5 @@
+void mx_noise2d_vector4(vector4 amplitude, float pivot, vector2 texcoord, output vector4 result)
+{
+ vector4 value = mx_noise("snoise", texcoord.x, texcoord.y);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genosl/mx_noise3d_float.osl b/templates/stdlib/genosl/mx_noise3d_float.osl
new file mode 100644
index 0000000000..23c5985098
--- /dev/null
+++ b/templates/stdlib/genosl/mx_noise3d_float.osl
@@ -0,0 +1,5 @@
+void mx_noise3d_float(float amplitude, float pivot, vector position, output float result)
+{
+ float value = noise("snoise", position);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genosl/mx_noise3d_vector2.osl b/templates/stdlib/genosl/mx_noise3d_vector2.osl
new file mode 100644
index 0000000000..216502aa43
--- /dev/null
+++ b/templates/stdlib/genosl/mx_noise3d_vector2.osl
@@ -0,0 +1,5 @@
+void mx_noise3d_vector2(vector2 amplitude, float pivot, vector position, output vector2 result)
+{
+ vector2 value = mx_noise("snoise", position);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genosl/mx_noise3d_vector3.osl b/templates/stdlib/genosl/mx_noise3d_vector3.osl
new file mode 100644
index 0000000000..cc6b2410ed
--- /dev/null
+++ b/templates/stdlib/genosl/mx_noise3d_vector3.osl
@@ -0,0 +1,5 @@
+void mx_noise3d_vector3(vector amplitude, float pivot, vector position, output vector result)
+{
+ vector value = noise("snoise", position);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genosl/mx_noise3d_vector4.osl b/templates/stdlib/genosl/mx_noise3d_vector4.osl
new file mode 100644
index 0000000000..10c96b4bac
--- /dev/null
+++ b/templates/stdlib/genosl/mx_noise3d_vector4.osl
@@ -0,0 +1,5 @@
+void mx_noise3d_vector4(vector4 amplitude, float pivot, vector position, output vector4 result)
+{
+ vector4 value = mx_noise("snoise", position);
+ result = value * amplitude + pivot;
+}
diff --git a/templates/stdlib/genosl/mx_normalmap.osl b/templates/stdlib/genosl/mx_normalmap.osl
new file mode 100644
index 0000000000..1057eb61c7
--- /dev/null
+++ b/templates/stdlib/genosl/mx_normalmap.osl
@@ -0,0 +1,24 @@
+void mx_normalmap_vector2(vector value, vector2 normal_scale, vector N, vector T, vector B, output vector result)
+{
+ if (value == vector(0.0))
+ {
+ result = N;
+ }
+ else
+ {
+ // The OSL backend uses dPdu and dPdv for tangents and bitangents, but these vectors are not
+ // guaranteed to be orthonormal.
+ //
+ // Orthogonalize the tangent frame using Gram-Schmidt, unlike in the other backends.
+ //
+ vector v = value * 2.0 - 1.0;
+ vector Tn = normalize(T - dot(T, N) * N);
+ vector Bn = normalize(B - dot(B, N) * N - dot(B, Tn) * Tn);
+ result = normalize(Tn * v[0] * normal_scale.x + Bn * v[1] * normal_scale.y + N * v[2]);
+ }
+}
+
+void mx_normalmap_float(vector value, float normal_scale, vector N, vector T, vector B, output vector result)
+{
+ mx_normalmap_vector2(value, vector2(normal_scale, normal_scale), N, T, B, result);
+}
diff --git a/templates/stdlib/genosl/mx_premult_color4.osl b/templates/stdlib/genosl/mx_premult_color4.osl
new file mode 100644
index 0000000000..8e57e13926
--- /dev/null
+++ b/templates/stdlib/genosl/mx_premult_color4.osl
@@ -0,0 +1,4 @@
+void mx_premult_color4(color4 in, output color4 result)
+{
+ result = color4(in.rgb * in.a, in.a);
+}
diff --git a/templates/stdlib/genosl/mx_rgbtohsv_color3.osl b/templates/stdlib/genosl/mx_rgbtohsv_color3.osl
new file mode 100644
index 0000000000..7315ead9b9
--- /dev/null
+++ b/templates/stdlib/genosl/mx_rgbtohsv_color3.osl
@@ -0,0 +1,4 @@
+void mx_rgbtohsv_color3(vector _in, output vector result)
+{
+ result = transformc("rgb","hsv", _in);
+}
diff --git a/templates/stdlib/genosl/mx_rgbtohsv_color4.osl b/templates/stdlib/genosl/mx_rgbtohsv_color4.osl
new file mode 100644
index 0000000000..92edec0e73
--- /dev/null
+++ b/templates/stdlib/genosl/mx_rgbtohsv_color4.osl
@@ -0,0 +1,4 @@
+void mx_rgbtohsv_color4(color4 _in, output color4 result)
+{
+ result = color4(transformc("rgb","hsv", _in.rgb), 1.0);
+}
diff --git a/templates/stdlib/genosl/mx_rotate_vector2.osl b/templates/stdlib/genosl/mx_rotate_vector2.osl
new file mode 100644
index 0000000000..7ac3dbfa57
--- /dev/null
+++ b/templates/stdlib/genosl/mx_rotate_vector2.osl
@@ -0,0 +1,7 @@
+void mx_rotate_vector2(vector2 _in, float amount, output vector2 result)
+{
+ float rotationRadians = radians(amount);
+ float sa = sin(rotationRadians);
+ float ca = cos(rotationRadians);
+ result = vector2(ca*_in.x + sa*_in.y, -sa*_in.x + ca*_in.y);
+}
diff --git a/templates/stdlib/genosl/mx_rotate_vector3.osl b/templates/stdlib/genosl/mx_rotate_vector3.osl
new file mode 100644
index 0000000000..d036fe655e
--- /dev/null
+++ b/templates/stdlib/genosl/mx_rotate_vector3.osl
@@ -0,0 +1,20 @@
+matrix rotationMatrix(vector axis, float angle)
+{
+ vector nAxis = normalize(axis);
+ float s = sin(angle);
+ float c = cos(angle);
+ float oc = 1.0 - c;
+
+ return matrix(oc * nAxis[0] * nAxis[0] + c, oc * nAxis[0] * nAxis[1] - nAxis[2] * s, oc * nAxis[2] * nAxis[0] + nAxis[1] * s, 0.0,
+ oc * nAxis[0] * nAxis[1] + nAxis[2] * s, oc * nAxis[1] * nAxis[1] + c, oc * nAxis[1] * nAxis[2] - nAxis[0] * s, 0.0,
+ oc * nAxis[2] * nAxis[0] - nAxis[1] * s, oc * nAxis[1] * nAxis[2] + nAxis[0] * s, oc * nAxis[2] * nAxis[2] + c, 0.0,
+ 0.0, 0.0, 0.0, 1.0);
+}
+
+void mx_rotate_vector3(vector _in, float amount, vector axis, output vector result)
+{
+ float rotationRadians = radians(amount);
+ matrix m = rotationMatrix(axis, rotationRadians);
+ vector4 trans = transform(m, vector4(_in[0], _in[1], _in[2], 1.0));
+ result = vector(trans.x, trans.y, trans.z);
+}
diff --git a/templates/stdlib/genosl/mx_surface_unlit.osl b/templates/stdlib/genosl/mx_surface_unlit.osl
new file mode 100644
index 0000000000..b870ef2b21
--- /dev/null
+++ b/templates/stdlib/genosl/mx_surface_unlit.osl
@@ -0,0 +1,7 @@
+void mx_surface_unlit(float emission_weight, color emission_color, float transmission_weight, color transmission_color, float opacity, output surfaceshader result)
+{
+ float trans = clamp(transmission_weight, 0.0, 1.0);
+ result.bsdf = trans * transmission_color * transparent();
+ result.edf = (1.0 - trans) * emission_weight * emission_color * emission();
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
diff --git a/templates/stdlib/genosl/mx_surfacematerial.osl b/templates/stdlib/genosl/mx_surfacematerial.osl
new file mode 100644
index 0000000000..ca68960e76
--- /dev/null
+++ b/templates/stdlib/genosl/mx_surfacematerial.osl
@@ -0,0 +1,5 @@
+void mx_surfacematerial(surfaceshader surface, surfaceshader back, displacementshader disp, output MATERIAL result)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ result = (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
diff --git a/templates/stdlib/genosl/mx_time_float.osl b/templates/stdlib/genosl/mx_time_float.osl
new file mode 100644
index 0000000000..511cc18f93
--- /dev/null
+++ b/templates/stdlib/genosl/mx_time_float.osl
@@ -0,0 +1,6 @@
+void mx_time_float(float fps, output float result)
+{
+ // Use the standard default value if the attribute is not present.
+ result = 0.0;
+ getattribute("time", result);
+}
diff --git a/templates/stdlib/genosl/mx_transformmatrix_vector2M3.osl b/templates/stdlib/genosl/mx_transformmatrix_vector2M3.osl
new file mode 100644
index 0000000000..fe1937b80b
--- /dev/null
+++ b/templates/stdlib/genosl/mx_transformmatrix_vector2M3.osl
@@ -0,0 +1,6 @@
+void mx_transformmatrix_vector2M3(vector2 val, matrix m, output vector2 result)
+{
+ point res = transform(m, point(val.x, val.y, 1.0));
+ result.x = res[0];
+ result.y = res[1];
+}
diff --git a/templates/stdlib/genosl/mx_unpremult_color4.osl b/templates/stdlib/genosl/mx_unpremult_color4.osl
new file mode 100644
index 0000000000..55d074c76e
--- /dev/null
+++ b/templates/stdlib/genosl/mx_unpremult_color4.osl
@@ -0,0 +1,4 @@
+void mx_unpremult_color4(color4 in, output color4 result)
+{
+ result = color4(in.rgb / in.a, in.a);
+}
diff --git a/templates/stdlib/genosl/mx_worleynoise2d_float.osl b/templates/stdlib/genosl/mx_worleynoise2d_float.osl
new file mode 100644
index 0000000000..1b98e316af
--- /dev/null
+++ b/templates/stdlib/genosl/mx_worleynoise2d_float.osl
@@ -0,0 +1,4 @@
+void mx_worleynoise2d_float(vector2 texcoord, float jitter, int style, output float result)
+{
+ result = mx_worley_noise_float(texcoord, jitter, style, 0);
+}
diff --git a/templates/stdlib/genosl/mx_worleynoise2d_vector2.osl b/templates/stdlib/genosl/mx_worleynoise2d_vector2.osl
new file mode 100644
index 0000000000..6d6be62d22
--- /dev/null
+++ b/templates/stdlib/genosl/mx_worleynoise2d_vector2.osl
@@ -0,0 +1,4 @@
+void mx_worleynoise2d_vector2(vector2 texcoord, float jitter, int style, output vector2 result)
+{
+ result = mx_worley_noise_vector2(texcoord, jitter, style, 0);
+}
diff --git a/templates/stdlib/genosl/mx_worleynoise2d_vector3.osl b/templates/stdlib/genosl/mx_worleynoise2d_vector3.osl
new file mode 100644
index 0000000000..00ae3a5eae
--- /dev/null
+++ b/templates/stdlib/genosl/mx_worleynoise2d_vector3.osl
@@ -0,0 +1,4 @@
+void mx_worleynoise2d_vector3(vector2 texcoord, float jitter, int style, output vector result)
+{
+ result = mx_worley_noise_vector3(texcoord, jitter, style, 0);
+}
diff --git a/templates/stdlib/genosl/mx_worleynoise3d_float.osl b/templates/stdlib/genosl/mx_worleynoise3d_float.osl
new file mode 100644
index 0000000000..0386075740
--- /dev/null
+++ b/templates/stdlib/genosl/mx_worleynoise3d_float.osl
@@ -0,0 +1,4 @@
+void mx_worleynoise3d_float(vector position, float jitter, int style, output float result)
+{
+ result = mx_worley_noise_float(position, jitter, style, 0);
+}
diff --git a/templates/stdlib/genosl/mx_worleynoise3d_vector2.osl b/templates/stdlib/genosl/mx_worleynoise3d_vector2.osl
new file mode 100644
index 0000000000..314dde0d45
--- /dev/null
+++ b/templates/stdlib/genosl/mx_worleynoise3d_vector2.osl
@@ -0,0 +1,4 @@
+void mx_worleynoise3d_vector2(vector position, float jitter, int style, output vector2 result)
+{
+ result = mx_worley_noise_vector2(position, jitter, style, 0);
+}
diff --git a/templates/stdlib/genosl/mx_worleynoise3d_vector3.osl b/templates/stdlib/genosl/mx_worleynoise3d_vector3.osl
new file mode 100644
index 0000000000..5cfce1904d
--- /dev/null
+++ b/templates/stdlib/genosl/mx_worleynoise3d_vector3.osl
@@ -0,0 +1,4 @@
+void mx_worleynoise3d_vector3(vector position, float jitter, int style, output vector result)
+{
+ result = mx_worley_noise_vector3(position, jitter, style, 0);
+}
diff --git a/templates/stdlib/genosl/stdlib_genosl_impl.mtlx b/templates/stdlib/genosl/stdlib_genosl_impl.mtlx
new file mode 100644
index 0000000000..1141db7b65
--- /dev/null
+++ b/templates/stdlib/genosl/stdlib_genosl_impl.mtlx
@@ -0,0 +1,750 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/stdlib/stdlib_defs.mtlx b/templates/stdlib/stdlib_defs.mtlx
new file mode 100644
index 0000000000..fcf27d8eed
--- /dev/null
+++ b/templates/stdlib/stdlib_defs.mtlx
@@ -0,0 +1,2393 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/stdlib/stdlib_ng.mtlx b/templates/stdlib/stdlib_ng.mtlx
new file mode 100644
index 0000000000..36ce3ef35f
--- /dev/null
+++ b/templates/stdlib/stdlib_ng.mtlx
@@ -0,0 +1,4986 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/targets/essl.mtlx b/templates/targets/essl.mtlx
new file mode 100644
index 0000000000..03a1b5bc88
--- /dev/null
+++ b/templates/targets/essl.mtlx
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/targets/genglsl.mtlx b/templates/targets/genglsl.mtlx
new file mode 100644
index 0000000000..820a43c248
--- /dev/null
+++ b/templates/targets/genglsl.mtlx
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/targets/genmdl.mtlx b/templates/targets/genmdl.mtlx
new file mode 100644
index 0000000000..91ac5ff01c
--- /dev/null
+++ b/templates/targets/genmdl.mtlx
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/targets/genmsl.mtlx b/templates/targets/genmsl.mtlx
new file mode 100644
index 0000000000..0ad09cc142
--- /dev/null
+++ b/templates/targets/genmsl.mtlx
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/targets/genosl.mtlx b/templates/targets/genosl.mtlx
new file mode 100644
index 0000000000..cae914fb1b
--- /dev/null
+++ b/templates/targets/genosl.mtlx
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+