diff --git a/Geometry/boombox.glb b/Geometry/boombox.glb
new file mode 100644
index 0000000000..fea645856e
Binary files /dev/null and b/Geometry/boombox.glb differ
diff --git a/Geometry/chess_set.glb b/Geometry/chess_set.glb
new file mode 100644
index 0000000000..c3b1770bd1
Binary files /dev/null and b/Geometry/chess_set.glb differ
diff --git a/Geometry/shaderball.glb b/Geometry/shaderball.glb
new file mode 100644
index 0000000000..ce6d5a3cf7
Binary files /dev/null and b/Geometry/shaderball.glb differ
diff --git a/Images/brass_color.jpg b/Images/brass_color.jpg
new file mode 100644
index 0000000000..047f1619bc
Binary files /dev/null and b/Images/brass_color.jpg differ
diff --git a/Images/brass_roughness.jpg b/Images/brass_roughness.jpg
new file mode 100644
index 0000000000..2f0054013d
Binary files /dev/null and b/Images/brass_roughness.jpg differ
diff --git a/Images/brick_base_gray.jpg b/Images/brick_base_gray.jpg
new file mode 100644
index 0000000000..0b77aff18b
Binary files /dev/null and b/Images/brick_base_gray.jpg differ
diff --git a/Images/brick_dirt_mask.jpg b/Images/brick_dirt_mask.jpg
new file mode 100644
index 0000000000..b3745644ad
Binary files /dev/null and b/Images/brick_dirt_mask.jpg differ
diff --git a/Images/brick_mask.jpg b/Images/brick_mask.jpg
new file mode 100644
index 0000000000..66d5439c41
Binary files /dev/null and b/Images/brick_mask.jpg differ
diff --git a/Images/brick_normal.jpg b/Images/brick_normal.jpg
new file mode 100644
index 0000000000..0f672d823a
Binary files /dev/null and b/Images/brick_normal.jpg differ
diff --git a/Images/brick_roughness.jpg b/Images/brick_roughness.jpg
new file mode 100644
index 0000000000..bcf492036c
Binary files /dev/null and b/Images/brick_roughness.jpg differ
diff --git a/Images/brick_variation_mask.jpg b/Images/brick_variation_mask.jpg
new file mode 100644
index 0000000000..3ad359b95e
Binary files /dev/null and b/Images/brick_variation_mask.jpg differ
diff --git a/Images/cloth.bmp b/Images/cloth.bmp
new file mode 100644
index 0000000000..b6191de952
Binary files /dev/null and b/Images/cloth.bmp differ
diff --git a/Images/cloth.gif b/Images/cloth.gif
new file mode 100644
index 0000000000..d9cd2b48bc
Binary files /dev/null and b/Images/cloth.gif differ
diff --git a/Images/cloth.jpg b/Images/cloth.jpg
new file mode 100644
index 0000000000..53f55c3927
Binary files /dev/null and b/Images/cloth.jpg differ
diff --git a/Images/cloth.png b/Images/cloth.png
new file mode 100644
index 0000000000..c4e3459b66
Binary files /dev/null and b/Images/cloth.png differ
diff --git a/Images/cloth.tga b/Images/cloth.tga
new file mode 100644
index 0000000000..bd38dd95b6
Binary files /dev/null and b/Images/cloth.tga differ
diff --git a/Images/greysphere_calibration.png b/Images/greysphere_calibration.png
new file mode 100644
index 0000000000..76bff27d65
Binary files /dev/null and b/Images/greysphere_calibration.png differ
diff --git a/Images/grid.png b/Images/grid.png
new file mode 100644
index 0000000000..9af2fba96f
Binary files /dev/null and b/Images/grid.png differ
diff --git a/Images/marble.png b/Images/marble.png
new file mode 100644
index 0000000000..f3c58d2707
Binary files /dev/null and b/Images/marble.png differ
diff --git a/Images/mesh_wire_norm.png b/Images/mesh_wire_norm.png
new file mode 100644
index 0000000000..75dc2ccfc2
Binary files /dev/null and b/Images/mesh_wire_norm.png differ
diff --git a/Images/plain_heightmap.png b/Images/plain_heightmap.png
new file mode 100644
index 0000000000..763685fb66
Binary files /dev/null and b/Images/plain_heightmap.png differ
diff --git a/Images/wood_color.jpg b/Images/wood_color.jpg
new file mode 100644
index 0000000000..c61b13a0fd
Binary files /dev/null and b/Images/wood_color.jpg differ
diff --git a/Images/wood_roughness.jpg b/Images/wood_roughness.jpg
new file mode 100644
index 0000000000..af292bdd36
Binary files /dev/null and b/Images/wood_roughness.jpg differ
diff --git a/JsMaterialXGenShader.data b/JsMaterialXGenShader.data
new file mode 100644
index 0000000000..8eef09d63c
--- /dev/null
+++ b/JsMaterialXGenShader.data
@@ -0,0 +1,21074 @@
+install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
+ DESTINATION "${MATERIALX_INSTALL_STDLIB_PATH}" MESSAGE_NEVER
+ PATTERN "CMakeLists.txt" EXCLUDE
+ PATTERN "pbrlib_genosl_impl.*" EXCLUDE)
+
+if (MATERIALX_OSL_LEGACY_CLOSURES)
+ set(PBRLIB_SUFFIX "legacy")
+else()
+ set(PBRLIB_SUFFIX "mtlx")
+endif()
+
+install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pbrlib/genosl/pbrlib_genosl_impl.${PBRLIB_SUFFIX}"
+ DESTINATION "${MATERIALX_INSTALL_STDLIB_PATH}/pbrlib/genosl/" RENAME pbrlib_genosl_impl.mtlx)
+
+install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
+ DESTINATION "python/MaterialX/${MATERIALX_INSTALL_STDLIB_PATH}"
+ PATTERN "CMakeLists.txt" EXCLUDE
+ PATTERN "pbrlib_genosl_impl.*" EXCLUDE)
+install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pbrlib/genosl/pbrlib_genosl_impl.${PBRLIB_SUFFIX}"
+ DESTINATION "python/MaterialX/${MATERIALX_INSTALL_STDLIB_PATH}/pbrlib/genosl/" RENAME pbrlib_genosl_impl.mtlx)
+# MaterialX Data Libraries
+
+This folder contains the standard data libraries for MaterialX, providing declarations and graph definitions for the MaterialX nodes, and source code for all supported shader generators.
+
+## Standard Pattern Library
+- [stdlib](stdlib)
+ - [stdlib_defs.mtlx](stdlib/stdlib_defs.mtlx) : Nodedef declarations.
+ - [stdlib_ng.mtlx](stdlib/stdlib_ng.mtlx) : Nodegraph definitions.
+ - [genglsl](stdlib/genglsl): GLSL language support.
+ - [lib](stdlib/genglsl/lib) : Shader utility files.
+ - [stdlib_genglsl_impl.mtlx](stdlib/genglsl/stdlib_genglsl_impl.mtlx) : Mapping from declarations to implementations.
+ - [genosl](stdlib/genosl): OSL language support.
+ - [lib](stdlib/genosl/lib) : Shader utility files.
+ - [stdlib_genosl_impl.mtlx](stdlib/genosl/stdlib_genosl_impl.mtlx) : Mapping from declarations to implementations.
+ - [genmdl](stdlib/genmdl): MDL language support.
+ - [stdlib_genmdl_impl.mtlx](stdlib/genmdl/stdlib_genmdl_impl.mtlx) : Mapping from declarations to implementations.
+ - Additional MaterialX support libraries for MDL are located in the [source/MaterialXGenMdl/mdl/materialx](../source/MaterialXGenMdl/mdl/materialx) package folder
+ - [genmsl](stdlib/genmsl): MSL language support.
+ - [lib](stdlib/genmsl/lib) : Shader utility files.
+ - [stdlib_genmsl_impl.mtlx](stdlib/genmsl/stdlib_genmsl_impl.mtlx) : Mapping from declarations to implementations.
+
+## Physically Based Shading Library
+- [pbrlib](pbrlib)
+ - [pbrlib_defs.mtlx](pbrlib/pbrlib_defs.mtlx) : Nodedef declarations.
+ - [pbrlib_ng.mtlx](pbrlib/pbrlib_ng.mtlx) : Nodegraph definitions.
+ - [genglsl](pbrlib/genglsl) : GLSL language support
+ - [lib](pbrlib/genglsl/lib) : Shader utility files.
+ - [pbrlib_genglsl_impl.mtlx](pbrlib/genglsl/pbrlib_genglsl_impl.mtlx) : Mapping from declarations to implementations.
+ - [genosl](pbrlib/genosl) : OSL language support
+ - [lib](pbrlib/genosl/lib) : Shader utility files.
+ - [pbrlib_genosl_impl.mtlx](pbrlib/genosl/pbrlib_genosl_impl.mtlx) : Mapping from declarations to implementations.
+ - [genmdl](pbrlib/genmdl) : MDL language support
+ - [pbrlib_genmdl_impl.mtlx](pbrlib/genmdl/pbrlib_genmdl_impl.mtlx) : Mapping from declarations to implementations.
+ - [genmsl](pbrlib/genmsl) : MSL language support
+ - [pbrlib_genmsl_impl.mtlx](pbrlib/genmsl/pbrlib_genmsl_impl.mtlx) : Mapping from declarations to implementations.
+
+## BxDF Graph Library
+- [bxdf](bxdf)
+ - [standard_surface.mtlx](bxdf/standard_surface.mtlx) : Graph definition of the [Autodesk Standard Surface](https://autodesk.github.io/standard-surface/) shading model.
+ - [gltf_pbr.mtlx](bxdf/gltf_pbr.mtlx) : Graph definition of the [glTF PBR](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#appendix-b-brdf-implementation) shading model.
+ - [usd_preview_surface.mtlx](bxdf/usd_preview_surface.mtlx) : Graph definition of the [UsdPreviewSurface](https://openusd.org/release/spec_usdpreviewsurface.html) shading model.
+ - [lama](bxdf/lama) : Graph definitions of the [MaterialX Lama](https://rmanwiki.pixar.com/display/REN24/MaterialX+Lama) node set.
+
+## Color Management Library
+- MaterialX shader generation natively supports a small set of common spaces for input colors, with all color transforms implemented as language-independent MaterialX graphs.The canonical definitions of these color transforms may be found in the OpenColorIO configuration for [ACES 1.2](https://github.com/colour-science/OpenColorIO-Configs/tree/feature/aces-1.2-config/aces_1.2).
+ - lin_rec709
+ - g18_rec709
+ - g22_rec709
+ - rec709_display
+ - acescg (lin_ap1)
+ - g22_ap1
+ - srgb_texture
+ - lin_adobergb
+ - adobergb
+ - srgb_displayp3
+ - lin_displayp3
+- [cmlib](cmlib)
+ - [cmlib_defs.mtlx](cmlib/cmlib_defs.mtlx) : Nodedef declarations.
+ - [cmlib_ng.mtlx](cmlib/cmlib_ng.mtlx) : Nodegraph definitions.
+
+## Target Definitions
+- Each target implementation requires a target definition for declaration / implementation correspondence to work.
+- The [targets](targets) folder contains definition files for the following core targets:
+ - GLSL : `genglsl`
+ - OSL : `genosl`
+ - MDL : `genmdl`
+ - MSL : `genmsl`
+- Any additional target files should be added under this folder and loaded in as required.
+
+### Target Support
+- GLSL target support is for version 4.0 or higher.
+- OSL target support is for version 1.9.10 or higher.
+- MDL target support is for version 1.7.
+- Basic GLSL and MSL `lightshader` node definitions and implementations are provided for the following light types:
+ - point, directional, spot
+- Shader generation does not currently support:
+ - `ambientocclusion` node.
+ - `arrayappend` node.
+ - `curveadjust` node.
+ - `displacementshader` and `volumeshader` nodes for hardware shading targets (GLSL, MSL).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void mx_fractal3d_fa_color3(float amplitude, int octaves, float lacunarity, float diminish, vector position, output color result)
+{
+ color f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
+void mx_noise2d_fa_vector3(float amplitude, float pivot, vector2 texcoord, output vector result)
+{
+ vector value = noise("snoise", texcoord.x, texcoord.y);
+ result = value * amplitude + pivot;
+}
+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;
+}
+void mx_worleynoise2d_vector2(vector2 texcoord, float jitter, output vector2 result)
+{
+ result = mx_worley_noise_vector2(texcoord, jitter, 0);
+}
+void mx_noise2d_fa_color3(float amplitude, float pivot, vector2 texcoord, output color result)
+{
+ color value = noise("snoise", texcoord.x, texcoord.y);
+ result = value * amplitude + pivot;
+}
+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;
+}
+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];
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+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];
+ }
+}
+void mx_noise3d_fa_vector4(float amplitude, float pivot, vector position, output vector4 result)
+{
+ vector4 value = mx_noise("snoise", position);
+ result = value * amplitude + pivot;
+}
+void mx_noise2d_color3(vector amplitude, float pivot, vector2 texcoord, output color result)
+{
+ color value = noise("snoise", texcoord.x, texcoord.y);
+ result = value * amplitude + pivot;
+}
+#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,
+ "missingcolor", missingColor, "missingalpha", missingAlpha, "swrap", uaddressmode, "twrap", vaddressmode $extraTextureLookupArguments );
+
+ out = color4(rgb, alpha);
+}
+void mx_fractal3d_fa_vector4(float amplitude, int octaves, float lacunarity, float diminish, vector position, output vector4 result)
+{
+ vector4 f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
+#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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode $extraTextureLookupArguments);
+}
+
+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];
+ }
+}
+void mx_worleynoise2d_vector3(vector2 texcoord, float jitter, output vector result)
+{
+ result = mx_worley_noise_vector3(texcoord, jitter, 0);
+}
+void mx_worleynoise2d_float(vector2 texcoord, float jitter, output float result)
+{
+ result = mx_worley_noise_float(texcoord, jitter, 0);
+}
+void mx_geompropvalue_vector(string geomprop, vector defaultVal, output vector out)
+{
+ if (getattribute(geomprop, out) == 0)
+ out = defaultVal;
+}
+void mx_worleynoise3d_vector2(vector position, float jitter, output vector2 result)
+{
+ result = mx_worley_noise_vector2(position, jitter, 0);
+}
+void mx_noise3d_float(float amplitude, float pivot, vector position, output float result)
+{
+ float value = noise("snoise", position);
+ result = value * amplitude + pivot;
+}
+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);
+}
+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;
+}
+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);
+}
+void mx_heighttonormal_vector3(float in, float scale, output vector result)
+{
+ point htP = P + normalize(N) * in * scale;
+ result = normalize(calculatenormal(htP));
+}
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+void mx_hsvtorgb_color3(vector _in, output vector result)
+{
+ result = transformc("hsv","rgb", _in);
+}
+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;
+}
+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;
+}
+void mx_geomcolor_float(int index, output float result)
+{
+ getattribute("color", result);
+}
+void mx_noise3d_fa_color3(float amplitude, float pivot, vector position, output color result)
+{
+ color value = noise("snoise", position);
+ result = value * amplitude + pivot;
+}
+void mx_luminance_color4(color4 in, color lumacoeffs, output color4 result)
+{
+ result = color4(dot(in.rgb, lumacoeffs), in.a);
+}
+#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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+ out = rgb[0];
+}
+void mx_fractal3d_color4(vector4 amplitude, int octaves, float lacunarity, float diminish, vector position, output color4 result)
+{
+ color4 f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
+ result = f * color4(color(amplitude.x, amplitude.y, amplitude.z), amplitude.w);
+}
+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;
+}
+void mx_fractal3d_fa_vector2(float amplitude, int octaves, float lacunarity, float diminish, vector position, output vector2 result)
+{
+ vector2 f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
+void mx_fractal3d_color3(vector amplitude, int octaves, float lacunarity, float diminish, vector position, output color result)
+{
+ color f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
+void mx_normalmap(vector value, string map_space, float normal_scale, vector N, vector U, output vector result)
+{
+ // Tangent space
+ if (map_space == "tangent")
+ {
+ vector v = value * 2.0 - 1.0;
+ vector T = normalize(U - dot(U, N) * N);
+ vector B = normalize(cross(N, T));
+ result = normalize(T * v[0] * normal_scale + B * v[1] * normal_scale + N * v[2]);
+ }
+ // Object space
+ else
+ {
+ vector n = value * 2.0 - 1.0;
+ result = normalize(n);
+ }
+}
+void mx_noise3d_fa_vector2(float amplitude, float pivot, vector position, output vector2 result)
+{
+ vector2 value = mx_noise("snoise", position);
+ result = value * amplitude + pivot;
+}
+#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,
+ "missingcolor", missingColor, "missingalpha", missingAlpha, "swrap", uaddressmode, "twrap", vaddressmode);
+
+ out = vector4(rgb[0], rgb[1], rgb[2], alpha);
+}
+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);
+}
+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);
+}
+void mx_fractal3d_fa_vector3(float amplitude, int octaves, float lacunarity, float diminish, vector position, output vector result)
+{
+ vector f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
+void mx_premult_color4(color4 in, output color4 result)
+{
+ result = color4(in.rgb * in.a, in.a);
+}
+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;
+}
+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);
+}
+void mx_noise3d_vector3(vector amplitude, float pivot, vector position, output vector result)
+{
+ vector value = noise("snoise", position);
+ result = value * amplitude + pivot;
+}
+void mx_geompropvalue_float(string geomprop, float defaultVal, output float result)
+{
+ if (getattribute(geomprop, result) == 0)
+ {
+ result = defaultVal;
+ }
+}
+void mx_cellnoise2d_float(vector2 texcoord, output float result)
+{
+ result = cellnoise(texcoord.x, texcoord.y);
+}
+void mx_noise3d_vector2(vector2 amplitude, float pivot, vector position, output vector2 result)
+{
+ vector2 value = mx_noise("snoise", position);
+ result = value * amplitude + pivot;
+}
+#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);
+}
+void mx_noise3d_color4(vector4 amplitude, float pivot, vector position, output color4 result)
+{
+ color4 value = mx_noise("snoise", position);
+ result = value * color4(color(amplitude.x, amplitude.y, amplitude.z), amplitude.w) + pivot;
+}
+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];
+}
+void mx_time_float(float fps, output float result)
+{
+ float frame;
+ getattribute("frame", frame);
+ result = frame / fps;
+}
+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;
+}
+void mx_noise3d_fa_color4(float amplitude, float pivot, vector position, output color4 result)
+{
+ color4 value = mx_noise("snoise", position);
+ result = value * amplitude + pivot;
+}
+void mx_frame_float(output float result)
+{
+ getattribute("frame", result);
+}
+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;
+}
+void mx_rgbtohsv_color3(vector _in, output vector result)
+{
+ result = transformc("rgb","hsv", _in);
+}
+void mx_fractal3d_fa_color4(float amplitude, int octaves, float lacunarity, float diminish, vector position, output color4 result)
+{
+ color4 f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
+ result = f * amplitude;
+}
+void mx_worleynoise3d_float(vector position, float jitter, output float result)
+{
+ result = mx_worley_noise_float(position, jitter, 0);
+}
+void mx_noise2d_fa_vector4(float amplitude, float pivot, vector2 texcoord, output vector4 result)
+{
+ vector4 value = mx_noise("snoise", texcoord.x, texcoord.y);
+ result = value * amplitude + pivot;
+}
+#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]);
+}
+void mx_worleynoise3d_vector3(vector position, float jitter, output vector result)
+{
+ result = mx_worley_noise_vector3(position, jitter, 0);
+}
+void mx_geomcolor_color3(int index, output color result)
+{
+ getattribute("color", result);
+}
+void mx_noise2d_fa_color4(float amplitude, float pivot, vector2 texcoord, output color4 result)
+{
+ color4 value = mx_noise("snoise", texcoord.x, texcoord.y);
+ result = value * amplitude + pivot;
+}
+void mx_hsvtorgb_color4(color4 _in, output color4 result)
+{
+ result = color4(transformc("hsv","rgb", _in.rgb), 1.0);
+}
+void mx_noise2d_color4(vector4 amplitude, float pivot, vector2 texcoord, output color4 result)
+{
+ color4 value = mx_noise("snoise", texcoord.x, texcoord.y);
+ result = value * color4(color(amplitude.x, amplitude.y, amplitude.z), amplitude.w) + pivot;
+}
+void mx_geompropvalue_integer(string geomprop, int defaultVal, output int out)
+{
+ if (getattribute(geomprop, out) == 0)
+ out = defaultVal;
+}
+void mx_noise3d_fa_vector3(float amplitude, float pivot, vector position, output vector result)
+{
+ vector value = noise("snoise", position);
+ result = value * amplitude + pivot;
+}
+float overlay(float fg, float bg)
+{
+ return (fg < 0.5) ? (2 * fg * bg) : (1 - (1 - fg) * (1 - bg));
+}
+
+color overlay(color fg, color bg)
+{
+ return color(overlay(fg[0], bg[0]),
+ overlay(fg[1], bg[1]),
+ overlay(fg[2], bg[2]));
+}
+
+void mx_overlay_color3(color fg, color bg, float mix, output color out)
+{
+ out = mix * overlay(fg, bg) + (1-mix) * bg;
+}
+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);
+}
+void mx_ambientocclusion_float(float coneangle, float maxdistance, output float result)
+{
+ // This node is a stub and does not currently operate to specification
+ result = 0;
+}
+void mx_noise3d_vector4(vector4 amplitude, float pivot, vector position, output vector4 result)
+{
+ vector4 value = mx_noise("snoise", position);
+ result = value * amplitude + pivot;
+}
+void mx_geompropvalue_color(string geomprop, color defaultVal, output color out)
+{
+ if (getattribute(geomprop, out) == 0)
+ out = defaultVal;
+}
+void mx_unpremult_color4(color4 in, output color4 result)
+{
+ result = color4(in.rgb / in.a, in.a);
+}
+void mx_geompropvalue_string(string geomprop, string defaultVal, output string out)
+{
+ if (getattribute(geomprop, out) == 0)
+ out = defaultVal;
+}
+#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]);
+}
+#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);
+}
+float overlay(float fg, float bg)
+{
+ return (fg < 0.5) ? (2 * fg * bg) : (1 - (1 - fg) * (1 - bg));
+}
+
+color overlay(color fg, color bg)
+{
+ return color(overlay(fg[0], bg[0]),
+ overlay(fg[1], bg[1]),
+ overlay(fg[2], bg[2]));
+}
+
+color4 overlay(color4 fg, color4 bg)
+{
+ return color4(overlay(fg.rgb, bg.rgb),
+ overlay(fg.a, bg.a));
+}
+
+void mx_overlay_color4(color4 fg, color4 bg, float mix, output color4 out)
+{
+ out = mix * overlay(fg, bg) + (1-mix) * bg;
+}
+#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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+ out.x = rgb[0];
+ out.y = rgb[1];
+}
+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];
+}
+void mx_noise2d_fa_vector2(float amplitude, float pivot, vector2 texcoord, output vector2 result)
+{
+ vector2 value = mx_noise("snoise", texcoord.x, texcoord.y);
+ result = value * amplitude + pivot;
+}
+void mx_cellnoise3d_float(vector position, output float result)
+{
+ result = cellnoise(position);
+}
+#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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+}
+void mx_noise3d_color3(vector amplitude, float pivot, vector position, output color result)
+{
+ color value = noise("snoise", position);
+ result = value * amplitude + pivot;
+}
+void mx_geompropvalue_boolean(string geomprop, int defaultVal, output int out)
+{
+ if (getattribute(geomprop, out) == 0)
+ out = defaultVal;
+}
+void mx_rgbtohsv_color4(color4 _in, output color4 result)
+{
+ result = color4(transformc("rgb","hsv", _in.rgb), 1.0);
+}
+// Restrict to 7x7 kernel size for performance reasons
+#define MX_MAX_SAMPLE_COUNT 49
+// Size of all weights for all levels (including level 1)
+#define MX_WEIGHT_ARRAY_SIZE 84
+
+//
+// Function to compute the sample size relative to a texture coordinate
+//
+vector2 mx_compute_sample_size_uv(vector2 uv, float filterSize, float filterOffset)
+{
+ vector derivUVx = Dx(vector(uv.x, uv.y, 0.0)) * 0.5;
+ vector derivUVy = Dy(vector(uv.x, uv.y, 0.0)) * 0.5;
+ float derivX = abs(derivUVx[0]) + abs(derivUVy[0]);
+ float derivY = abs(derivUVx[1]) + abs(derivUVy[1]);
+ float sampleSizeU = filterSize * derivX + filterOffset;
+ if (sampleSizeU < 1.0E-05)
+ sampleSizeU = 1.0E-05;
+ float sampleSizeV = filterSize * derivY + filterOffset;
+ if (sampleSizeV < 1.0E-05)
+ sampleSizeV = 1.0E-05;
+ return vector2(sampleSizeU, sampleSizeV);
+}
+
+// Kernel weights for box filter
+void mx_get_box_weights(output float W[MX_MAX_SAMPLE_COUNT], int filterSize)
+{
+ int sampleCount = filterSize*filterSize;
+ float value = 1.0 / float(sampleCount);
+ for (int i=0; i= 7)
+ {
+ W[0] = 0.000036; W[1] = 0.000363; W[2] = 0.001446; W[3] = 0.002291; W[4] = 0.001446; W[5] = 0.000363; W[6] = 0.000036;
+ W[7] = 0.000363; W[8] = 0.003676; W[9] = 0.014662; W[10] = 0.023226; W[11] = 0.014662; W[12] = 0.003676; W[13] = 0.000363;
+ W[14] = 0.001446; W[15] = 0.014662; W[16] = 0.058488; W[17] = 0.092651; W[18] = 0.058488; W[19] = 0.014662; W[20] = 0.001446;
+ W[21] = 0.002291; W[22] = 0.023226; W[23] = 0.092651; W[24] = 0.146768; W[25] = 0.092651; W[26] = 0.023226; W[27] = 0.002291;
+ W[28] = 0.001446; W[29] = 0.014662; W[30] = 0.058488; W[31] = 0.092651; W[32] = 0.058488; W[33] = 0.014662; W[34] = 0.001446;
+ W[35] = 0.000363; W[36] = 0.003676; W[37] = 0.014662; W[38] = 0.023226; W[39] = 0.014662; W[40] = 0.003676; W[41] = 0.000363;
+ W[42] = 0.000036; W[43] = 0.000363; W[44] = 0.001446; W[45] = 0.002291; W[46] = 0.001446; W[47] = 0.000363; W[48] = 0.000036;
+ }
+ else if (filterSize >= 5)
+ {
+ W[0] = 0.003765; W[1] = 0.015019; W[2] = 0.023792; W[3] = 0.015019; W[4] = 0.003765;
+ W[5] = 0.015019; W[6] = 0.059912; W[7] = 0.094907; W[8] = 0.059912; W[9] = 0.015019;
+ W[10] = 0.023792; W[11] = 0.094907; W[12] = 0.150342; W[13] = 0.094907; W[14] = 0.023792;
+ W[15] = 0.015019; W[16] = 0.059912; W[17] = 0.094907; W[18] = 0.059912; W[19] = 0.015019;
+ W[20] = 0.003765; W[21] = 0.015019; W[22] = 0.023792; W[23] = 0.015019; W[24] = 0.003765;
+ }
+ else if (filterSize >= 3)
+ {
+ W[0] = 0.0625; W[1] = 0.125; W[2] = 0.0625;
+ W[3] = 0.125; W[4] = 0.25; W[5] = 0.125;
+ W[6] = 0.0625; W[7] = 0.125; W[8] = 0.0625;
+ }
+ else
+ {
+ W[0] = 1.0;
+ }
+}
+
+//
+// Apply filter for float samples S, using weights W.
+// sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 }
+//
+float mx_convolution_float(float S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
+{
+ float result = 0.0;
+ for (int i = 0; i < sampleCount; i++)
+ {
+ result += S[i]*W[i+offset];
+ }
+ return result;
+}
+
+//
+// Apply filter for vector2 samples S, using weights W.
+// sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 }
+//
+vector2 mx_convolution_vector2(vector2 S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
+{
+ vector2 result = vector2(0.0, 0.0);
+ for (int i=0; i 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv);
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+#include "lib/mx_noise.glsl"
+
+void mx_noise3d_fa_vector2(float amplitude, float pivot, vec3 position, out vec2 result)
+{
+ vec3 value = mx_perlin_noise_vec3(position);
+ result = value.xy * amplitude + pivot;
+}
+#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;
+}
+#include "mx_smoothstep_float.glsl"
+
+void mx_smoothstep_vec3FA(vec3 val, float low, float high, out vec3 result)
+{
+ mx_smoothstep_float(val.x, low, high, result.x);
+ mx_smoothstep_float(val.y, low, high, result.y);
+ mx_smoothstep_float(val.z, low, high, result.z);
+}
+void mx_ramplr_vector4(vec4 valuel, vec4 valuer, vec2 texcoord, out vec4 result)
+{
+ result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
+}
+#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;
+}
+#include "mx_dodge_float.glsl"
+
+void mx_dodge_color3(vec3 fg, vec3 bg, float mixval, out vec3 result)
+{
+ mx_dodge_float(fg.x, bg.x, mixval, result.x);
+ mx_dodge_float(fg.y, bg.y, mixval, result.y);
+ mx_dodge_float(fg.z, bg.z, mixval, result.z);
+}
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise2d_float(vec2 texcoord, float jitter, out float result)
+{
+ result = mx_worley_noise_float(texcoord, jitter, 0);
+}
+#include "lib/mx_noise.glsl"
+
+void mx_fractal3d_fa_vector2(float amplitude, int octaves, float lacunarity, float diminish, vec3 position, out vec2 result)
+{
+ vec2 value = mx_fractal_noise_vec2(position, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
+#include "mx_dodge_float.glsl"
+
+void mx_dodge_color4(vec4 fg , vec4 bg , float mixval, out vec4 result)
+{
+ mx_dodge_float(fg.x, bg.x, mixval, result.x);
+ mx_dodge_float(fg.y, bg.y, mixval, result.y);
+ mx_dodge_float(fg.z, bg.z, mixval, result.z);
+ mx_dodge_float(fg.w, bg.w, mixval, result.w);
+}
+#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_fractal_noise_vec2(position, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
+#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;
+}
+#include "lib/$fileTransformUv"
+
+void mx_image_color3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+#include "lib/$fileTransformUv"
+
+void mx_image_float(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+#include "mx_smoothstep_float.glsl"
+
+void mx_smoothstep_vec4(vec4 val, vec4 low, vec4 high, out vec4 result)
+{
+ mx_smoothstep_float(val.x, low.x, high.x, result.x);
+ mx_smoothstep_float(val.y, low.y, high.y, result.y);
+ mx_smoothstep_float(val.z, low.z, high.z, result.z);
+ mx_smoothstep_float(val.w, low.w, high.w, result.w);
+}
+void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, out vec3 result)
+{
+ // Decode the normal map.
+ value = (value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+}
+#include "lib/mx_hsv.glsl"
+
+void mx_hsvtorgb_color3(vec3 _in, out vec3 result)
+{
+ result = mx_hsvtorgb(_in);
+}
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise3d_vector2(vec3 position, float jitter, out vec2 result)
+{
+ result = mx_worley_noise_vec2(position, jitter, 0);
+}
+float mx_aastep(float threshold, float value)
+{
+ float afwidth = length(vec2(dFdx(value), dFdy(value))) * 0.70710678118654757;
+ return smoothstep(threshold-afwidth, threshold+afwidth, value);
+}
+#include "mx_smoothstep_float.glsl"
+
+void mx_smoothstep_vec3(vec3 val, vec3 low, vec3 high, out vec3 result)
+{
+ mx_smoothstep_float(val.x, low.x, high.x, result.x);
+ mx_smoothstep_float(val.y, low.y, high.y, result.y);
+ mx_smoothstep_float(val.z, low.z, high.z, result.z);
+}
+#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;
+}
+void mx_ramptb_vector3(vec3 valuet, vec3 valueb, vec2 texcoord, out vec3 result)
+{
+ result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) );
+}
+#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_fractal_noise_vec3(position, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
+#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));
+}
+#include "mx_smoothstep_float.glsl"
+
+void mx_smoothstep_vec2FA(vec2 val, float low, float high, out vec2 result)
+{
+ mx_smoothstep_float(val.x, low, high, result.x);
+ mx_smoothstep_float(val.y, low, high, result.y);
+}
+#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_fractal_noise_vec4(position, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
+#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;
+}
+#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;
+}
+#include "mx_smoothstep_float.glsl"
+
+void mx_smoothstep_vec2(vec2 val, vec2 low, vec2 high, out vec2 result)
+{
+ mx_smoothstep_float(val.x, low.x, high.x, result.x);
+ mx_smoothstep_float(val.y, low.y, high.y, result.y);
+}
+void mx_ramptb_float(float valuet, float valueb, vec2 texcoord, out float result)
+{
+ result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) );
+}
+#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));
+}
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise2d_vector2(vec2 texcoord, float jitter, out vec2 result)
+{
+ result = mx_worley_noise_vec2(texcoord, jitter, 0);
+}
+void mx_ramplr_vector3(vec3 valuel, vec3 valuer, vec2 texcoord, out vec3 result)
+{
+ result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#include "lib/mx_hsv.glsl"
+
+void mx_rgbtohsv_color4(vec4 _in, out vec4 result)
+{
+ result = vec4(mx_rgbtohsv(_in.rgb), 1.0);
+}
+float mx_overlay(float fg, float bg)
+{
+ return (fg < 0.5) ? (2.0 * fg * bg) : (1.0 - (1.0 - fg) * (1.0 - bg));
+}
+
+vec2 mx_overlay(vec2 fg, vec2 bg)
+{
+ return vec2(mx_overlay(fg.r, bg.r),
+ mx_overlay(fg.g, bg.g));
+}
+
+vec3 mx_overlay(vec3 fg, vec3 bg)
+{
+ return vec3(mx_overlay(fg.r, bg.r),
+ mx_overlay(fg.g, bg.g),
+ mx_overlay(fg.b, bg.b));
+}
+
+vec4 mx_overlay(vec4 fg, vec4 bg)
+{
+ return vec4(mx_overlay(fg.r, bg.r),
+ mx_overlay(fg.g, bg.g),
+ mx_overlay(fg.b, bg.b),
+ mx_overlay(fg.a, bg.a));
+}
+#include "mx_burn_float.glsl"
+
+void mx_burn_color3(vec3 fg, vec3 bg, float mixval, out vec3 result)
+{
+ mx_burn_float(fg.x, bg.x, mixval, result.x);
+ mx_burn_float(fg.y, bg.y, mixval, result.y);
+ mx_burn_float(fg.z, bg.z, mixval, result.z);
+}
+void mx_transformmatrix_vector2M3(vec2 val, mat3 transform, out vec2 result)
+{
+ vec3 res = transform * vec3(val, 1.0);
+ result = res.xy;
+}
+void mx_transformmatrix_vector3M4(vec3 val, mat4 transform, out vec3 result)
+{
+ vec4 res = transform * vec4(val, 1.0);
+ result = res.xyz;
+}
+#include "lib/mx_noise.glsl"
+
+void mx_fractal3d_fa_vector4(float amplitude, int octaves, float lacunarity, float diminish, vec3 position, out vec4 result)
+{
+ vec4 value = mx_fractal_noise_vec4(position, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
+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);
+}
+#include "lib/$fileTransformUv"
+
+void mx_image_vector3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+void mx_rotate_vector2(vec2 _in, float amount, out vec2 result)
+{
+ float rotationRadians = radians(amount);
+ float sa = sin(rotationRadians);
+ float ca = cos(rotationRadians);
+ result = vec2(ca*_in.x + sa*_in.y, -sa*_in.x + ca*_in.y);
+}
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise3d_vector3(vec3 position, float jitter, out vec3 result)
+{
+ result = mx_worley_noise_vec3(position, jitter, 0);
+}
+#include "lib/mx_noise.glsl"
+
+void mx_noise3d_fa_vector3(float amplitude, float pivot, vec3 position, out vec3 result)
+{
+ vec3 value = mx_perlin_noise_vec3(position);
+ result = value * amplitude + pivot;
+}
+#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));
+}
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+void mx_ramplr_vector2(vec2 valuel, vec2 valuer, vec2 texcoord, out vec2 result)
+{
+ result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
+}
+#include "lib/mx_noise.glsl"
+
+void mx_fractal3d_fa_vector3(float amplitude, int octaves, float lacunarity, float diminish, vec3 position, out vec3 result)
+{
+ vec3 value = mx_fractal_noise_vec3(position, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
+#include "lib/mx_noise.glsl"
+
+void mx_noise2d_fa_vector3(float amplitude, float pivot, vec2 texcoord, out vec3 result)
+{
+ vec3 value = mx_perlin_noise_vec3(texcoord);
+ result = value * amplitude + pivot;
+}
+#include "mx_overlay.glsl"
+
+void mx_overlay_color4(vec4 fg, vec4 bg, float mix, out vec4 result)
+{
+ result = mix * mx_overlay(fg, bg) + (1.0-mix) * bg;
+}
+#include "mx_smoothstep_float.glsl"
+
+void mx_smoothstep_vec4FA(vec4 val, float low, float high, out vec4 result)
+{
+ mx_smoothstep_float(val.x, low, high, result.x);
+ mx_smoothstep_float(val.y, low, high, result.y);
+ mx_smoothstep_float(val.z, low, high, result.z);
+ mx_smoothstep_float(val.w, low, high, result.w);
+}
+void mx_ramptb_vector4(vec4 valuet, vec4 valueb, vec2 texcoord, out vec4 result)
+{
+ result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) );
+}
+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;
+}
+#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));
+}
+#include "mx_burn_float.glsl"
+
+void mx_burn_color4(vec4 fg, vec4 bg, float mixval, out vec4 result)
+{
+ mx_burn_float(fg.x, bg.x, mixval, result.x);
+ mx_burn_float(fg.y, bg.y, mixval, result.y);
+ mx_burn_float(fg.z, bg.z, mixval, result.z);
+ mx_burn_float(fg.w, bg.w, mixval, result.w);
+}
+#include "lib/$fileTransformUv"
+
+void mx_image_color4(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv);
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+#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;
+}
+#include "lib/mx_noise.glsl"
+
+void mx_noise2d_fa_vector2(float amplitude, float pivot, vec2 texcoord, out vec2 result)
+{
+ vec3 value = mx_perlin_noise_vec3(texcoord);
+ result = value.xy * amplitude + pivot;
+}
+#include "lib/$fileTransformUv"
+
+void mx_image_vector2(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rg;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+void mx_smoothstep_float(float val, float low, float high, out float result)
+{
+ if (val <= low)
+ result = 0.0;
+ else if (val >= high)
+ result = 1.0;
+ else
+ result = smoothstep(low, high, val);
+}
+#include "lib/mx_noise.glsl"
+
+void mx_noise2d_fa_vector4(float 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;
+}
+void mx_premult_color4(vec4 _in, out vec4 result)
+{
+ result = vec4(_in.rgb * _in.a, _in.a);
+}
+#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_fractal_noise_float(position, octaves, lacunarity, diminish);
+ result = value * amplitude;
+}
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise2d_vector3(vec2 texcoord, float jitter, out vec3 result)
+{
+ result = mx_worley_noise_vec3(texcoord, jitter, 0);
+}
+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);
+}
+#include "lib/mx_noise.glsl"
+
+void mx_noise3d_fa_vector4(float 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;
+}
+#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));
+}
+void mx_ramplr_float(float valuel, float valuer, vec2 texcoord, out float result)
+{
+ result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
+}
+#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;
+}
+#include "lib/mx_noise.glsl"
+
+void mx_cellnoise2d_float(vec2 texcoord, out float result)
+{
+ result = mx_cell_noise_float(texcoord);
+}
+#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));
+}
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+#include "lib/mx_hsv.glsl"
+
+void mx_rgbtohsv_color3(vec3 _in, out vec3 result)
+{
+ result = mx_rgbtohsv(_in);
+}
+#include "lib/mx_hsv.glsl"
+
+void mx_hsvtorgb_color4(vec4 _in, out vec4 result)
+{
+ result = vec4(mx_hsvtorgb(_in.rgb), 1.0);
+}
+#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));
+}
+#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));
+}
+#include "mx_overlay.glsl"
+
+void mx_overlay_color3(vec3 fg, vec3 bg, float mix, out vec3 result)
+{
+ result = mix * mx_overlay(fg, bg) + (1.0-mix) * bg;
+}
+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);
+}
+#include "lib/mx_noise.glsl"
+
+void mx_worleynoise3d_float(vec3 position, float jitter, out float result)
+{
+ result = mx_worley_noise_float(position, jitter, 0);
+}
+#include "lib/mx_noise.glsl"
+
+void mx_cellnoise3d_float(vec3 position, out float result)
+{
+ result = mx_cell_noise_float(position);
+}
+void mx_luminance_color4(vec4 _in, vec3 lumacoeffs, out vec4 result)
+{
+ result = vec4(vec3(dot(_in.rgb, lumacoeffs)), _in.a);
+}
+/*
+Color transform functions.
+
+These funcions 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);
+}
+// Restrict to 7x7 kernel size for performance reasons
+#define MX_MAX_SAMPLE_COUNT 49
+// Size of all weights for all levels (including level 1)
+#define MX_WEIGHT_ARRAY_SIZE 84
+
+//
+// Function to compute the sample size relative to a texture coordinate
+//
+vec2 mx_compute_sample_size_uv(vec2 uv, float filterSize, float filterOffset)
+{
+ vec2 derivUVx = dFdx(uv) * 0.5f;
+ vec2 derivUVy = dFdy(uv) * 0.5f;
+ float derivX = abs(derivUVx.x) + abs(derivUVy.x);
+ float derivY = abs(derivUVx.y) + abs(derivUVy.y);
+ float sampleSizeU = 2.0f * filterSize * derivX + filterOffset;
+ if (sampleSizeU < 1.0E-05f)
+ sampleSizeU = 1.0E-05f;
+ float sampleSizeV = 2.0f * filterSize * derivY + filterOffset;
+ if (sampleSizeV < 1.0E-05f)
+ sampleSizeV = 1.0E-05f;
+ return vec2(sampleSizeU, sampleSizeV);
+}
+
+//
+// Compute a normal mapped to 0..1 space based on a set of input
+// samples using a Sobel filter.
+//
+vec3 mx_normal_from_samples_sobel(float S[9], float _scale)
+{
+ float nx = S[0] - S[2] + (2.0*S[3]) - (2.0*S[5]) + S[6] - S[8];
+ float ny = S[0] + (2.0*S[1]) + S[2] - S[6] - (2.0*S[7]) - S[8];
+ float nz = max(_scale, M_FLOAT_EPS) * sqrt(max(1.0 - nx * nx - ny * ny, M_FLOAT_EPS));
+ vec3 norm = normalize(vec3(nx, ny, nz));
+ return (norm + 1.0) * 0.5;
+}
+
+//
+// Apply filter for float samples S, using weights W.
+// sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 }
+//
+float mx_convolution_float(float S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
+{
+ float result = 0.0;
+ for (int i = 0; i < sampleCount; i++)
+ {
+ result += S[i]*W[i+offset];
+ }
+ return result;
+}
+
+//
+// Apply filter for vec2 samples S, using weights W.
+// sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 }
+//
+vec2 mx_convolution_vec2(vec2 S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
+{
+ vec2 result = vec2(0.0);
+ for (int i=0; i>(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_fractal_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_fractal_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_fractal_noise_vec2(vec3 p, int octaves, float lacunarity, float diminish)
+{
+ return vec2(mx_fractal_noise_float(p, octaves, lacunarity, diminish),
+ mx_fractal_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish));
+}
+
+vec4 mx_fractal_noise_vec4(vec3 p, int octaves, float lacunarity, float diminish)
+{
+ vec3 c = mx_fractal_noise_vec3(p, octaves, lacunarity, diminish);
+ float f = mx_fractal_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish);
+ return vec4(c, f);
+}
+
+float mx_worley_distance(vec2 p, int x, int y, int xoff, int yoff, float jitter, int metric)
+{
+ 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;
+
+ vec2 cellpos = vec2(float(x), float(y)) + off;
+ 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 Euclidian 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 off = mx_cell_noise_vec3(vec3(x+xoff, y+yoff, z+zoff));
+
+ off -= 0.5f;
+ off *= jitter;
+ off += 0.5f;
+
+ vec3 cellpos = vec3(float(x), float(y), float(z)) + off;
+ 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 Euclidian or Distance^2
+ return dot(diff, diff);
+}
+
+float mx_worley_noise_float(vec2 p, float jitter, int metric)
+{
+ int X, Y;
+ 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)
+ 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);
+ sqdist = min(sqdist, dist);
+ }
+ }
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+}
+
+vec2 mx_worley_noise_vec2(vec2 p, float jitter, int metric)
+{
+ int X, Y;
+ vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
+ vec2 sqdist = vec2(1e6f, 1e6f);
+ 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);
+ if (dist < sqdist.x)
+ {
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.y = dist;
+ }
+ }
+ }
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+}
+
+vec3 mx_worley_noise_vec3(vec2 p, float jitter, int metric)
+{
+ int X, Y;
+ vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
+ vec3 sqdist = vec3(1e6f, 1e6f, 1e6f);
+ 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);
+ if (dist < sqdist.x)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = dist;
+ }
+ else if (dist < sqdist.z)
+ {
+ sqdist.z = dist;
+ }
+ }
+ }
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+}
+
+float mx_worley_noise_float(vec3 p, float jitter, 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;
+ 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);
+ sqdist = min(sqdist, dist);
+ }
+ }
+ }
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+}
+
+vec2 mx_worley_noise_vec2(vec3 p, float jitter, 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);
+ 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);
+ if (dist < sqdist.x)
+ {
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.y = dist;
+ }
+ }
+ }
+ }
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+}
+
+vec3 mx_worley_noise_vec3(vec3 p, float jitter, 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);
+ 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);
+ if (dist < sqdist.x)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = sqdist.x;
+ sqdist.x = dist;
+ }
+ else if (dist < sqdist.y)
+ {
+ sqdist.z = sqdist.y;
+ sqdist.y = dist;
+ }
+ else if (dist < sqdist.z)
+ {
+ sqdist.z = dist;
+ }
+ }
+ }
+ }
+ if (metric == 0)
+ sqdist = sqrt(sqdist);
+ return sqdist;
+}
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+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);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+#include "mx_smoothstep_float.metal"
+
+void mx_smoothstep_vec4(vec4 val, vec4 low, vec4 high, out vec4 result)
+{
+ float f;
+ mx_smoothstep_float(val.x, low.x, high.x, f); result.x = f;
+ mx_smoothstep_float(val.y, low.y, high.y, f); result.y = f;
+ mx_smoothstep_float(val.z, low.z, high.z, f); result.z = f;
+ mx_smoothstep_float(val.w, low.w, high.w, f); result.w = f;
+}
+#include "mx_smoothstep_float.metal"
+
+void mx_smoothstep_vec2(vec2 val, vec2 low, vec2 high, out vec2 result)
+{
+ float f;
+ mx_smoothstep_float(val.x, low.x, high.x, f); result.x = f;
+ mx_smoothstep_float(val.y, low.y, high.y, f); result.y = f;
+}
+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);
+}
+#include "mx_burn_float.metal"
+
+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;
+}
+#include "mx_smoothstep_float.metal"
+
+void mx_smoothstep_vec4FA(vec4 val, float low, float high, out vec4 result)
+{
+ float f;
+ mx_smoothstep_float(val.x, low, high, f); result.x = f;
+ mx_smoothstep_float(val.y, low, high, f); result.y = f;
+ mx_smoothstep_float(val.z, low, high, f); result.z = f;
+ mx_smoothstep_float(val.w, low, high, f); result.w = f;
+}
+#include "mx_smoothstep_float.metal"
+
+void mx_smoothstep_vec2FA(vec2 val, float low, float high, out vec2 result)
+{
+ float f;
+ mx_smoothstep_float(val.x, low, high, f); result.x = f;
+ mx_smoothstep_float(val.y, low, high, f); result.y = f;
+}
+#include "mx_dodge_float.metal"
+
+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;
+}
+void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, out vec3 result)
+{
+ // Decode the normal map.
+ value = all(value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+}
+void mx_smoothstep_float(float val, float low, float high, out float result)
+{
+ if (val <= low)
+ result = 0.0;
+ else if (val >= high)
+ result = 1.0;
+ else
+ result = smoothstep(low, high, val);
+}
+#include "mx_burn_float.metal"
+
+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;
+}
+#include "mx_smoothstep_float.metal"
+
+void mx_smoothstep_vec3FA(vec3 val, float low, float high, out vec3 result)
+{
+ float f;
+ mx_smoothstep_float(val.x, low, high, f); result.x = f;
+ mx_smoothstep_float(val.y, low, high, f); result.y = f;
+ mx_smoothstep_float(val.z, low, high, f); result.z = f;
+}
+#include "mx_dodge_float.metal"
+
+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;
+}
+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);
+}
+#include "mx_smoothstep_float.metal"
+
+void mx_smoothstep_vec3(vec3 val, vec3 low, vec3 high, thread vec3& result)
+ {
+ float f;
+ mx_smoothstep_float(val.x, low.x, high.x, f); result.x = f;
+ mx_smoothstep_float(val.y, low.y, high.y, f); result.y = f;
+ mx_smoothstep_float(val.z, low.z, high.z, f); result.z = f;
+ }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;
+}
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#ifdef __DECL_GL_MATH_FUNCTIONS__
+
+float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+float3x3 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 = 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 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 = 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;
+}
+
+template
+T1 mod(T1 x, T2 y)
+{
+ return x - y * floor(x/y);
+}
+
+template
+T atan(T y_over_x) { return ::atan(y_over_x); }
+
+template
+T atan(T y, T x) { return ::atan2(y, x); }
+
+#define lessThan(a, b) ((a) < (b))
+#define lessThanEqual(a, b) ((a) <= (b))
+#define greaterThan(a, b) ((a) > (b))
+#define greaterThanEqual(a, b) ((a) >= (b))
+#define equal(a, b) ((a) == (b))
+#define notEqual(a, b) ((a) != (b))
+
+#endif
+// Restrict to 7x7 kernel size for performance reasons
+#define MX_MAX_SAMPLE_COUNT 49
+// Size of all weights for all levels (including level 1)
+#define MX_WEIGHT_ARRAY_SIZE 84
+
+//
+// Function to compute the sample size relative to a texture coordinate
+//
+vec2 mx_compute_sample_size_uv(vec2 uv, float filterSize, float filterOffset)
+{
+ vec2 derivUVx = dFdx(uv) * 0.5f;
+ vec2 derivUVy = dFdy(uv) * 0.5f;
+ float derivX = abs(derivUVx.x) + abs(derivUVy.x);
+ float derivY = abs(derivUVx.y) + abs(derivUVy.y);
+ float sampleSizeU = 2.0f * filterSize * derivX + filterOffset;
+ if (sampleSizeU < 1.0E-05f)
+ sampleSizeU = 1.0E-05f;
+ float sampleSizeV = 2.0f * filterSize * derivY + filterOffset;
+ if (sampleSizeV < 1.0E-05f)
+ sampleSizeV = 1.0E-05f;
+ return vec2(sampleSizeU, sampleSizeV);
+}
+
+//
+// Compute a normal mapped to 0..1 space based on a set of input
+// samples using a Sobel filter.
+//
+vec3 mx_normal_from_samples_sobel(constant float S[9], float _scale)
+{
+ float nx = S[0] - S[2] + (2.0*S[3]) - (2.0*S[5]) + S[6] - S[8];
+ float ny = S[0] + (2.0*S[1]) + S[2] - S[6] - (2.0*S[7]) - S[8];
+ float nz = max(_scale, M_FLOAT_EPS) * sqrt(max(1.0 - nx * nx - ny * ny, M_FLOAT_EPS));
+ vec3 norm = normalize(vec3(nx, ny, nz));
+ return (norm + 1.0) * 0.5;
+}
+
+//
+// Apply filter for float samples S, using weights W.
+// sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 }
+//
+float mx_convolution_float(float S[MX_MAX_SAMPLE_COUNT], constant float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
+{
+ float result = 0.0;
+ for (int i = 0; i < sampleCount; i++)
+ {
+ result += S[i]*W[i+offset];
+ }
+ return result;
+}
+
+//
+// Apply filter for vec2 samples S, using weights W.
+// sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 }
+//
+vec2 mx_convolution_vec2(vec2 S[MX_MAX_SAMPLE_COUNT], constant float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount)
+{
+ vec2 result = vec2(0.0);
+ for (int i=0; i tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+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;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+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;
+}
+void mx_directional_light(LightData light, vec3 position, out lightshader result)
+{
+ result.direction = -light.direction;
+ result.intensity = light.color * light.intensity;
+}
+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;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+void mx_directional_light(LightData light, float3 position, thread lightshader& result)
+{
+ result.direction = -light.direction;
+ result.intensity = light.color * light.intensity;
+}
+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;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void mx_generalized_schlick_bsdf(float weight, color color0, color color90, float exponent, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "R")
+ {
+ bsdf = weight * generalized_schlick_bsdf(N, U, color(1.0), color(0.0), roughness.x, roughness.y, color0, color90, exponent, distribution);
+ }
+ else if (scatter_mode == "T")
+ {
+ bsdf = weight * generalized_schlick_bsdf(N, U, color(0.0), color(1.0), roughness.x, roughness.y, color0, color90, exponent, distribution);
+ }
+ else
+ {
+ bsdf = weight * generalized_schlick_bsdf(N, U, color(1.0), color(1.0), roughness.x, roughness.y, color0, color90, exponent, distribution);
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+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);
+ }
+}
+#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;
+}
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "R")
+ {
+ bsdf = weight * dielectric_bsdf(N, U, tint, color(0.0), roughness.x, roughness.y, ior, distribution);
+ }
+ else if (scatter_mode == "T")
+ {
+ bsdf = weight * dielectric_bsdf(N, U, color(0.0), tint, roughness.x, roughness.y, ior, distribution);
+ }
+ else
+ {
+ bsdf = weight * dielectric_bsdf(N, U, tint, tint, roughness.x, roughness.y, ior, distribution);
+ }
+}
+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);
+}
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf = _color * weight * diffuse(N);
+}
+void mx_displacement_float(float displacement, float scale, output displacementshader result)
+{
+ result = vector(displacement * scale);
+}
+void mx_anisotropic_vdf(vector absorption, vector scattering, float anisotropy, output VDF vdf)
+{
+ // TODO: Need to remap parameters to match the new closure,
+ // or change the MaterialX spec to OSL parameterization.
+ vdf = 0;
+}
+void mx_displacement_vector3(vector displacement, float scale, output displacementshader result)
+{
+ result = displacement * scale;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+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;
+ }
+}
+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));
+}
+#include "../lib/mx_microfacet_specular.osl"
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+#include "../lib/mx_microfacet_specular.osl"
+
+void mx_generalized_schlick_bsdf(float weight, color color0, color color90, float exponent, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ float avgF0 = dot(color0, color(1.0 / 3.0));
+ float ior = mx_f0_to_ior(avgF0);
+
+ if (scatter_mode == "T")
+ {
+ bsdf.response = weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = weight;
+ return;
+ }
+
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ color dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, color0, color90) * comp;
+ float avgDirAlbedo = dot(dirAlbedo, color(1.0 / 3.0));
+ bsdf.throughput = 1.0 - avgDirAlbedo * weight;
+
+ // Calculate the reflection response, setting IOR to zero to disable internal Fresnel.
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, 0);
+
+ if (scatter_mode == "RT")
+ {
+ bsdf.response += bsdf.throughput * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 1);
+ }
+}
+void mx_burley_diffuse_bsdf(float weight, color reflectance, float roughness, normal N, output BSDF bsdf)
+{
+ // TODO: Implement properly.
+ bsdf.response = reflectance * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+#include "../lib/mx_microfacet_specular.osl"
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+#include "../lib/mx_microfacet_sheen.osl"
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+void mx_anisotropic_vdf(vector absorption, vector scattering, float anisotropy, output VDF vdf)
+{
+ // Not implemented in vanilla OSL
+ vdf = 0; // volume_henyey_greenstein(color(absorption), color(scattering), color(0.0), anisotropy);
+}
+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));
+}
+#include "mx_microfacet.osl"
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+#include "mx_microfacet.osl"
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void mx_displacement_float(float disp, float scale, out displacementshader result)
+{
+ result.offset = vec3(disp);
+ result.scale = scale;
+}
+void mx_displacement_vector3(vec3 disp, float scale, out displacementshader result)
+{
+ result.offset = disp;
+ result.scale = scale;
+}
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+#include "lib/mx_microfacet_specular.glsl"
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+#include "lib/mx_microfacet_diffuse.glsl"
+
+void mx_burley_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ bsdf.response *= mx_burley_diffuse(L, V, normal, NdotL, roughness);
+}
+
+void mx_burley_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotV = clamp(dot(normal, V), M_FLOAT_EPS, 1.0);
+
+ vec3 Li = mx_environment_irradiance(normal) *
+ mx_burley_diffuse_dir_albedo(NdotV, roughness);
+ bsdf.response = Li * color * weight;
+}
+/// 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));
+}
+#include "lib/mx_microfacet_diffuse.glsl"
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+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);
+}
+#include "lib/mx_microfacet_sheen.glsl"
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+void mx_add_edf(vec3 N, vec3 L, EDF in1, EDF in2, out EDF result)
+{
+ result = in1 + in2;
+}
+#include "lib/mx_microfacet_specular.glsl"
+
+void mx_generalized_schlick_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color0, vec3 color90, float exponent, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_schlick_airy(color0, color90, exponent, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_schlick(color0, color90, exponent);
+ }
+ 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, color0, color90) * 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_generalized_schlick_bsdf_transmission(vec3 V, float weight, vec3 color0, vec3 color90, float exponent, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_schlick_airy(color0, color90, exponent, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_schlick(color0, color90, exponent);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+ vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, color0, color90) * comp;
+ float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0));
+ bsdf.throughput = vec3(1.0 - avgDirAlbedo * weight);
+
+ if (scatter_mode != 0)
+ {
+ float avgF0 = dot(color0, vec3(1.0 / 3.0));
+ fd.ior = vec3(mx_f0_to_ior(avgF0));
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, color0) * weight;
+ }
+}
+
+void mx_generalized_schlick_bsdf_indirect(vec3 V, float weight, vec3 color0, vec3 color90, float exponent, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_schlick_airy(color0, color90, exponent, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_schlick(color0, color90, exponent);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+ vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, color0, color90) * 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;
+}
+#include "lib/mx_microfacet_diffuse.glsl"
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+#include "lib/mx_microfacet_specular.glsl"
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+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;
+ }
+}
+void mx_anisotropic_vdf(vec3 absorption, vec3 scattering, float anisotropy, inout BSDF bsdf)
+{
+ // TODO: Add some approximation for volumetric light absorption.
+}
+#include "lib/mx_microfacet.glsl"
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+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);
+}
+#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;
+}
+#include "mx_microfacet.glsl"
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+#include "mx_microfacet_specular.glsl"
+
+// 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);
+}
+
+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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance);
+}
+#include "mx_microfacet_specular.glsl"
+
+float mx_latlong_compute_lod(float alpha)
+{
+ // Select a mip level based on input alpha.
+ float lodBias = alpha < 0.25 ? sqrt(alpha) : 0.5*alpha + 0.375;
+ return lodBias * float($envRadianceMips);
+}
+
+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_compute_lod(avgAlpha), $envRadiance);
+ return Li * FG;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance);
+}
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+#include "mx_microfacet.glsl"
+
+// http://www.aconty.com/pdf/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);
+}
+#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);
+}
+// 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));
+}
+#include "mx_microfacet.glsl"
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+#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);
+}
+#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/JsMaterialXGenShader.js b/JsMaterialXGenShader.js
new file mode 100644
index 0000000000..0c4f95937e
--- /dev/null
+++ b/JsMaterialXGenShader.js
@@ -0,0 +1,21 @@
+
+var MaterialX = (function() {
+ var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
+ if (typeof __filename !== 'undefined') _scriptDir = _scriptDir || __filename;
+ return (
+function(MaterialX) {
+ MaterialX = MaterialX || {};
+
+var Module=typeof MaterialX!=="undefined"?MaterialX:{};var readyPromiseResolve,readyPromiseReject;Module["ready"]=new Promise(function(resolve,reject){readyPromiseResolve=resolve;readyPromiseReject=reject});if(!Module.expectedDataFileDownloads){Module.expectedDataFileDownloads=0}Module.expectedDataFileDownloads++;(function(){var loadPackage=function(metadata){var PACKAGE_PATH;if(typeof window==="object"){PACKAGE_PATH=window["encodeURIComponent"](window.location.pathname.toString().substring(0,window.location.pathname.toString().lastIndexOf("/"))+"/")}else if(typeof location!=="undefined"){PACKAGE_PATH=encodeURIComponent(location.pathname.toString().substring(0,location.pathname.toString().lastIndexOf("/"))+"/")}else{throw"using preloaded data can only be done on a web page or in a web worker"}var PACKAGE_NAME="../../bin/JsMaterialXGenShader.data";var REMOTE_PACKAGE_BASE="JsMaterialXGenShader.data";if(typeof Module["locateFilePackage"]==="function"&&!Module["locateFile"]){Module["locateFile"]=Module["locateFilePackage"];err("warning: you defined Module.locateFilePackage, that has been renamed to Module.locateFile (using your locateFilePackage for now)")}var REMOTE_PACKAGE_NAME=Module["locateFile"]?Module["locateFile"](REMOTE_PACKAGE_BASE,""):REMOTE_PACKAGE_BASE;var REMOTE_PACKAGE_SIZE=metadata["remote_package_size"];var PACKAGE_UUID=metadata["package_uuid"];function fetchRemotePackage(packageName,packageSize,callback,errback){var xhr=new XMLHttpRequest;xhr.open("GET",packageName,true);xhr.responseType="arraybuffer";xhr.onprogress=function(event){var url=packageName;var size=packageSize;if(event.total)size=event.total;if(event.loaded){if(!xhr.addedTotal){xhr.addedTotal=true;if(!Module.dataFileDownloads)Module.dataFileDownloads={};Module.dataFileDownloads[url]={loaded:event.loaded,total:size}}else{Module.dataFileDownloads[url].loaded=event.loaded}var total=0;var loaded=0;var num=0;for(var download in Module.dataFileDownloads){var data=Module.dataFileDownloads[download];total+=data.total;loaded+=data.loaded;num++}total=Math.ceil(total*Module.expectedDataFileDownloads/num);if(Module["setStatus"])Module["setStatus"]("Downloading data... ("+loaded+"/"+total+")")}else if(!Module.dataFileDownloads){if(Module["setStatus"])Module["setStatus"]("Downloading data...")}};xhr.onerror=function(event){throw new Error("NetworkError for: "+packageName)};xhr.onload=function(event){if(xhr.status==200||xhr.status==304||xhr.status==206||xhr.status==0&&xhr.response){var packageData=xhr.response;callback(packageData)}else{throw new Error(xhr.statusText+" : "+xhr.responseURL)}};xhr.send(null)}function handleError(error){console.error("package error:",error)}var fetchedCallback=null;var fetched=Module["getPreloadedPackage"]?Module["getPreloadedPackage"](REMOTE_PACKAGE_NAME,REMOTE_PACKAGE_SIZE):null;if(!fetched)fetchRemotePackage(REMOTE_PACKAGE_NAME,REMOTE_PACKAGE_SIZE,function(data){if(fetchedCallback){fetchedCallback(data);fetchedCallback=null}else{fetched=data}},handleError);function runWithFS(){function assert(check,msg){if(!check)throw msg+(new Error).stack}Module["FS_createPath"]("/","libraries",true,true);Module["FS_createPath"]("/libraries","targets",true,true);Module["FS_createPath"]("/libraries","cmlib",true,true);Module["FS_createPath"]("/libraries","stdlib",true,true);Module["FS_createPath"]("/libraries/stdlib","genosl",true,true);Module["FS_createPath"]("/libraries/stdlib/genosl","lib",true,true);Module["FS_createPath"]("/libraries/stdlib/genosl","include",true,true);Module["FS_createPath"]("/libraries/stdlib","genglsl",true,true);Module["FS_createPath"]("/libraries/stdlib/genglsl","lib",true,true);Module["FS_createPath"]("/libraries/stdlib","genmdl",true,true);Module["FS_createPath"]("/libraries/stdlib","genmsl",true,true);Module["FS_createPath"]("/libraries/stdlib/genmsl","lib",true,true);Module["FS_createPath"]("/libraries","bxdf",true,true);Module["FS_createPath"]("/libraries/bxdf","lama",true,true);Module["FS_createPath"]("/libraries/bxdf","translation",true,true);Module["FS_createPath"]("/libraries","lights",true,true);Module["FS_createPath"]("/libraries/lights","genglsl",true,true);Module["FS_createPath"]("/libraries/lights","genmsl",true,true);Module["FS_createPath"]("/libraries","pbrlib",true,true);Module["FS_createPath"]("/libraries/pbrlib","genosl",true,true);Module["FS_createPath"]("/libraries/pbrlib/genosl","legacy",true,true);Module["FS_createPath"]("/libraries/pbrlib/genosl","lib",true,true);Module["FS_createPath"]("/libraries/pbrlib","genglsl",true,true);Module["FS_createPath"]("/libraries/pbrlib/genglsl","lib",true,true);Module["FS_createPath"]("/libraries/pbrlib","genmdl",true,true);Module["FS_createPath"]("/libraries/pbrlib","genmsl",true,true);function DataRequest(start,end,audio){this.start=start;this.end=end;this.audio=audio}DataRequest.prototype={requests:{},open:function(mode,name){this.name=name;this.requests[name]=this;Module["addRunDependency"]("fp "+this.name)},send:function(){},onload:function(){var byteArray=this.byteArray.subarray(this.start,this.end);this.finish(byteArray)},finish:function(byteArray){var that=this;Module["FS_createDataFile"](this.name,null,byteArray,true,true,true);Module["removeRunDependency"]("fp "+that.name);this.requests[this.name]=null}};var files=metadata["files"];for(var i=0;i1){thisProgram=process["argv"][1].replace(/\\/g,"/")}arguments_=process["argv"].slice(2);process["on"]("uncaughtException",function(ex){if(!(ex instanceof ExitStatus)){throw ex}});process["on"]("unhandledRejection",abort);quit_=function(status){process["exit"](status)};Module["inspect"]=function(){return"[Emscripten Module object]"}}else if(ENVIRONMENT_IS_SHELL){if(typeof read!="undefined"){read_=function shell_read(f){return read(f)}}readBinary=function readBinary(f){var data;if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){arguments_=scriptArgs}else if(typeof arguments!="undefined"){arguments_=arguments}if(typeof quit==="function"){quit_=function(status){quit(status)}}if(typeof print!=="undefined"){if(typeof console==="undefined")console={};console.log=print;console.warn=console.error=typeof printErr!=="undefined"?printErr:print}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){if(ENVIRONMENT_IS_WORKER){scriptDirectory=self.location.href}else if(typeof document!=="undefined"&&document.currentScript){scriptDirectory=document.currentScript.src}if(_scriptDir){scriptDirectory=_scriptDir}if(scriptDirectory.indexOf("blob:")!==0){scriptDirectory=scriptDirectory.substr(0,scriptDirectory.lastIndexOf("/")+1)}else{scriptDirectory=""}{read_=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(ENVIRONMENT_IS_WORKER){readBinary=function(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.responseType="arraybuffer";xhr.send(null);return new Uint8Array(xhr.response)}}readAsync=function(url,onload,onerror){var xhr=new XMLHttpRequest;xhr.open("GET",url,true);xhr.responseType="arraybuffer";xhr.onload=function(){if(xhr.status==200||xhr.status==0&&xhr.response){onload(xhr.response);return}onerror()};xhr.onerror=onerror;xhr.send(null)}}setWindowTitle=function(title){document.title=title}}else{}var out=Module["print"]||console.log.bind(console);var err=Module["printErr"]||console.warn.bind(console);for(key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}moduleOverrides=null;if(Module["arguments"])arguments_=Module["arguments"];if(Module["thisProgram"])thisProgram=Module["thisProgram"];if(Module["quit"])quit_=Module["quit"];var STACK_ALIGN=16;function alignMemory(size,factor){if(!factor)factor=STACK_ALIGN;return Math.ceil(size/factor)*factor}var tempRet0=0;var setTempRet0=function(value){tempRet0=value};var getTempRet0=function(){return tempRet0};var wasmBinary;if(Module["wasmBinary"])wasmBinary=Module["wasmBinary"];var noExitRuntime=Module["noExitRuntime"]||true;if(typeof WebAssembly!=="object"){abort("no native wasm support detected")}var wasmMemory;var ABORT=false;var EXITSTATUS;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var UTF8Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf8"):undefined;function UTF8ArrayToString(heap,idx,maxBytesToRead){var endIdx=idx+maxBytesToRead;var endPtr=idx;while(heap[endPtr]&&!(endPtr>=endIdx))++endPtr;if(endPtr-idx>16&&heap.subarray&&UTF8Decoder){return UTF8Decoder.decode(heap.subarray(idx,endPtr))}else{var str="";while(idx>10,56320|ch&1023)}}}return str}function UTF8ToString(ptr,maxBytesToRead){return ptr?UTF8ArrayToString(HEAPU8,ptr,maxBytesToRead):""}function stringToUTF8Array(str,heap,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i=55296&&u<=57343){var u1=str.charCodeAt(++i);u=65536+((u&1023)<<10)|u1&1023}if(u<=127){if(outIdx>=endIdx)break;heap[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;heap[outIdx++]=192|u>>6;heap[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;heap[outIdx++]=224|u>>12;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}else{if(outIdx+3>=endIdx)break;heap[outIdx++]=240|u>>18;heap[outIdx++]=128|u>>12&63;heap[outIdx++]=128|u>>6&63;heap[outIdx++]=128|u&63}}heap[outIdx]=0;return outIdx-startIdx}function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}function lengthBytesUTF8(str){var len=0;for(var i=0;i=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127)++len;else if(u<=2047)len+=2;else if(u<=65535)len+=3;else len+=4}return len}var UTF16Decoder=typeof TextDecoder!=="undefined"?new TextDecoder("utf-16le"):undefined;function UTF16ToString(ptr,maxBytesToRead){var endPtr=ptr;var idx=endPtr>>1;var maxIdx=idx+maxBytesToRead/2;while(!(idx>=maxIdx)&&HEAPU16[idx])++idx;endPtr=idx<<1;if(endPtr-ptr>32&&UTF16Decoder){return UTF16Decoder.decode(HEAPU8.subarray(ptr,endPtr))}else{var str="";for(var i=0;!(i>=maxBytesToRead/2);++i){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)break;str+=String.fromCharCode(codeUnit)}return str}}function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}function lengthBytesUTF16(str){return str.length*2}function UTF32ToString(ptr,maxBytesToRead){var i=0;var str="";while(!(i>=maxBytesToRead/4)){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)break;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}return str}function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}function lengthBytesUTF32(str){var len=0;for(var i=0;i=55296&&codeUnit<=57343)++i;len+=4}return len}function writeArrayToMemory(array,buffer){HEAP8.set(array,buffer)}function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}function alignUp(x,multiple){if(x%multiple>0){x+=multiple-x%multiple}return x}var buffer,HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;function updateGlobalBufferAndViews(buf){buffer=buf;Module["HEAP8"]=HEAP8=new Int8Array(buf);Module["HEAP16"]=HEAP16=new Int16Array(buf);Module["HEAP32"]=HEAP32=new Int32Array(buf);Module["HEAPU8"]=HEAPU8=new Uint8Array(buf);Module["HEAPU16"]=HEAPU16=new Uint16Array(buf);Module["HEAPU32"]=HEAPU32=new Uint32Array(buf);Module["HEAPF32"]=HEAPF32=new Float32Array(buf);Module["HEAPF64"]=HEAPF64=new Float64Array(buf)}var INITIAL_MEMORY=Module["INITIAL_MEMORY"]||16777216;var wasmTable;var __ATPRERUN__=[];var __ATINIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function initRuntime(){runtimeInitialized=true;if(!Module["noFSInit"]&&!FS.init.initialized)FS.init();TTY.init();callRuntimeCallbacks(__ATINIT__)}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}function addOnInit(cb){__ATINIT__.unshift(cb)}function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["preloadedImages"]={};Module["preloadedAudios"]={};function abort(what){if(Module["onAbort"]){Module["onAbort"](what)}what+="";err(what);ABORT=true;EXITSTATUS=1;what="abort("+what+"). Build with -s ASSERTIONS=1 for more info.";var e=new WebAssembly.RuntimeError(what);readyPromiseReject(e);throw e}var dataURIPrefix="data:application/octet-stream;base64,";function isDataURI(filename){return filename.startsWith(dataURIPrefix)}function isFileURI(filename){return filename.startsWith("file://")}var wasmBinaryFile="JsMaterialXGenShader.wasm";if(!isDataURI(wasmBinaryFile)){wasmBinaryFile=locateFile(wasmBinaryFile)}function getBinary(file){try{if(file==wasmBinaryFile&&wasmBinary){return new Uint8Array(wasmBinary)}if(readBinary){return readBinary(file)}else{throw"both async and sync fetching of the wasm failed"}}catch(err){abort(err)}}function getBinaryPromise(){if(!wasmBinary&&(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER)){if(typeof fetch==="function"&&!isFileURI(wasmBinaryFile)){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){if(!response["ok"]){throw"failed to load wasm binary file at '"+wasmBinaryFile+"'"}return response["arrayBuffer"]()}).catch(function(){return getBinary(wasmBinaryFile)})}else{if(readAsync){return new Promise(function(resolve,reject){readAsync(wasmBinaryFile,function(response){resolve(new Uint8Array(response))},reject)})}}}return Promise.resolve().then(function(){return getBinary(wasmBinaryFile)})}function createWasm(){var info={"a":asmLibraryArg};function receiveInstance(instance,module){var exports=instance.exports;Module["asm"]=exports;wasmMemory=Module["asm"]["Wa"];updateGlobalBufferAndViews(wasmMemory.buffer);wasmTable=Module["asm"]["Za"];addOnInit(Module["asm"]["Xa"]);removeRunDependency("wasm-instantiate")}addRunDependency("wasm-instantiate");function receiveInstantiationResult(result){receiveInstance(result["instance"])}function instantiateArrayBuffer(receiver){return getBinaryPromise().then(function(binary){var result=WebAssembly.instantiate(binary,info);return result}).then(receiver,function(reason){err("failed to asynchronously prepare wasm: "+reason);abort(reason)})}function instantiateAsync(){if(!wasmBinary&&typeof WebAssembly.instantiateStreaming==="function"&&!isDataURI(wasmBinaryFile)&&!isFileURI(wasmBinaryFile)&&typeof fetch==="function"){return fetch(wasmBinaryFile,{credentials:"same-origin"}).then(function(response){var result=WebAssembly.instantiateStreaming(response,info);return result.then(receiveInstantiationResult,function(reason){err("wasm streaming compile failed: "+reason);err("falling back to ArrayBuffer instantiation");return instantiateArrayBuffer(receiveInstantiationResult)})})}else{return instantiateArrayBuffer(receiveInstantiationResult)}}if(Module["instantiateWasm"]){try{var exports=Module["instantiateWasm"](info,receiveInstance);return exports}catch(e){err("Module.instantiateWasm callback failed with error: "+e);return false}}instantiateAsync().catch(readyPromiseReject);return{}}var tempDouble;var tempI64;var ASM_CONSTS={173212:function(){Module["TreeIterator"]["prototype"][Symbol.iterator]=function(){return this}},173296:function(){Module["GraphIterator"]["prototype"][Symbol.iterator]=function(){return this}},173381:function(){Module["InheritanceIterator"]["prototype"][Symbol.iterator]=function(){return this}}};function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback(Module);continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){wasmTable.get(func)()}else{wasmTable.get(func)(callback.arg)}}else{func(callback.arg===undefined?null:callback.arg)}}}function ___assert_fail(condition,filename,line,func){abort("Assertion failed: "+UTF8ToString(condition)+", at: "+[filename?UTF8ToString(filename):"unknown filename",line,func?UTF8ToString(func):"unknown function"])}var ExceptionInfoAttrs={DESTRUCTOR_OFFSET:0,REFCOUNT_OFFSET:4,TYPE_OFFSET:8,CAUGHT_OFFSET:12,RETHROWN_OFFSET:13,SIZE:16};function ___cxa_allocate_exception(size){return _malloc(size+ExceptionInfoAttrs.SIZE)+ExceptionInfoAttrs.SIZE}function ExceptionInfo(excPtr){this.excPtr=excPtr;this.ptr=excPtr-ExceptionInfoAttrs.SIZE;this.set_type=function(type){HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]=type};this.get_type=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.TYPE_OFFSET>>2]};this.set_destructor=function(destructor){HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]=destructor};this.get_destructor=function(){return HEAP32[this.ptr+ExceptionInfoAttrs.DESTRUCTOR_OFFSET>>2]};this.set_refcount=function(refcount){HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=refcount};this.set_caught=function(caught){caught=caught?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]=caught};this.get_caught=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.CAUGHT_OFFSET>>0]!=0};this.set_rethrown=function(rethrown){rethrown=rethrown?1:0;HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]=rethrown};this.get_rethrown=function(){return HEAP8[this.ptr+ExceptionInfoAttrs.RETHROWN_OFFSET>>0]!=0};this.init=function(type,destructor){this.set_type(type);this.set_destructor(destructor);this.set_refcount(0);this.set_caught(false);this.set_rethrown(false)};this.add_ref=function(){var value=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=value+1};this.release_ref=function(){var prev=HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2];HEAP32[this.ptr+ExceptionInfoAttrs.REFCOUNT_OFFSET>>2]=prev-1;return prev===1}}function CatchInfo(ptr){this.free=function(){_free(this.ptr);this.ptr=0};this.set_base_ptr=function(basePtr){HEAP32[this.ptr>>2]=basePtr};this.get_base_ptr=function(){return HEAP32[this.ptr>>2]};this.set_adjusted_ptr=function(adjustedPtr){var ptrSize=4;HEAP32[this.ptr+ptrSize>>2]=adjustedPtr};this.get_adjusted_ptr=function(){var ptrSize=4;return HEAP32[this.ptr+ptrSize>>2]};this.get_exception_ptr=function(){var isPointer=___cxa_is_pointer_type(this.get_exception_info().get_type());if(isPointer){return HEAP32[this.get_base_ptr()>>2]}var adjusted=this.get_adjusted_ptr();if(adjusted!==0)return adjusted;return this.get_base_ptr()};this.get_exception_info=function(){return new ExceptionInfo(this.get_base_ptr())};if(ptr===undefined){this.ptr=_malloc(8);this.set_adjusted_ptr(0)}else{this.ptr=ptr}}var exceptionCaught=[];function exception_addRef(info){info.add_ref()}var uncaughtExceptionCount=0;function ___cxa_begin_catch(ptr){var catchInfo=new CatchInfo(ptr);var info=catchInfo.get_exception_info();if(!info.get_caught()){info.set_caught(true);uncaughtExceptionCount--}info.set_rethrown(false);exceptionCaught.push(catchInfo);exception_addRef(info);return catchInfo.get_exception_ptr()}var exceptionLast=0;function ___cxa_free_exception(ptr){return _free(new ExceptionInfo(ptr).ptr)}function exception_decRef(info){if(info.release_ref()&&!info.get_rethrown()){var destructor=info.get_destructor();if(destructor){wasmTable.get(destructor)(info.excPtr)}___cxa_free_exception(info.excPtr)}}function ___cxa_end_catch(){_setThrew(0);var catchInfo=exceptionCaught.pop();exception_decRef(catchInfo.get_exception_info());catchInfo.free();exceptionLast=0}function ___resumeException(catchInfoPtr){var catchInfo=new CatchInfo(catchInfoPtr);var ptr=catchInfo.get_base_ptr();if(!exceptionLast){exceptionLast=ptr}catchInfo.free();throw ptr}function ___cxa_find_matching_catch_2(){var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0|0}var info=new ExceptionInfo(thrown);var thrownType=info.get_type();var catchInfo=new CatchInfo;catchInfo.set_base_ptr(thrown);if(!thrownType){setTempRet0(0);return catchInfo.ptr|0}var typeArray=Array.prototype.slice.call(arguments);var stackTop=stackSave();var exceptionThrowBuf=stackAlloc(4);HEAP32[exceptionThrowBuf>>2]=thrown;for(var i=0;i>2];if(thrown!==adjusted){catchInfo.set_adjusted_ptr(adjusted)}setTempRet0(caughtType);return catchInfo.ptr|0}}stackRestore(stackTop);setTempRet0(thrownType);return catchInfo.ptr|0}function ___cxa_find_matching_catch_3(){var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0|0}var info=new ExceptionInfo(thrown);var thrownType=info.get_type();var catchInfo=new CatchInfo;catchInfo.set_base_ptr(thrown);if(!thrownType){setTempRet0(0);return catchInfo.ptr|0}var typeArray=Array.prototype.slice.call(arguments);var stackTop=stackSave();var exceptionThrowBuf=stackAlloc(4);HEAP32[exceptionThrowBuf>>2]=thrown;for(var i=0;i>2];if(thrown!==adjusted){catchInfo.set_adjusted_ptr(adjusted)}setTempRet0(caughtType);return catchInfo.ptr|0}}stackRestore(stackTop);setTempRet0(thrownType);return catchInfo.ptr|0}function ___cxa_find_matching_catch_4(){var thrown=exceptionLast;if(!thrown){setTempRet0(0);return 0|0}var info=new ExceptionInfo(thrown);var thrownType=info.get_type();var catchInfo=new CatchInfo;catchInfo.set_base_ptr(thrown);if(!thrownType){setTempRet0(0);return catchInfo.ptr|0}var typeArray=Array.prototype.slice.call(arguments);var stackTop=stackSave();var exceptionThrowBuf=stackAlloc(4);HEAP32[exceptionThrowBuf>>2]=thrown;for(var i=0;i>2];if(thrown!==adjusted){catchInfo.set_adjusted_ptr(adjusted)}setTempRet0(caughtType);return catchInfo.ptr|0}}stackRestore(stackTop);setTempRet0(thrownType);return catchInfo.ptr|0}function ___cxa_rethrow(){var catchInfo=exceptionCaught.pop();if(!catchInfo){abort("no exception to throw")}var info=catchInfo.get_exception_info();var ptr=catchInfo.get_base_ptr();if(!info.get_rethrown()){exceptionCaught.push(catchInfo);info.set_rethrown(true);info.set_caught(false);uncaughtExceptionCount++}else{catchInfo.free()}exceptionLast=ptr;throw ptr}function ___cxa_throw(ptr,type,destructor){var info=new ExceptionInfo(ptr);info.init(type,destructor);exceptionLast=ptr;uncaughtExceptionCount++;throw ptr}function ___cxa_uncaught_exceptions(){return uncaughtExceptionCount}function setErrNo(value){HEAP32[___errno_location()>>2]=value;return value}var PATH={splitPath:function(filename){var splitPathRe=/^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;return splitPathRe.exec(filename).slice(1)},normalizeArray:function(parts,allowAboveRoot){var up=0;for(var i=parts.length-1;i>=0;i--){var last=parts[i];if(last==="."){parts.splice(i,1)}else if(last===".."){parts.splice(i,1);up++}else if(up){parts.splice(i,1);up--}}if(allowAboveRoot){for(;up;up--){parts.unshift("..")}}return parts},normalize:function(path){var isAbsolute=path.charAt(0)==="/",trailingSlash=path.substr(-1)==="/";path=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),!isAbsolute).join("/");if(!path&&!isAbsolute){path="."}if(path&&trailingSlash){path+="/"}return(isAbsolute?"/":"")+path},dirname:function(path){var result=PATH.splitPath(path),root=result[0],dir=result[1];if(!root&&!dir){return"."}if(dir){dir=dir.substr(0,dir.length-1)}return root+dir},basename:function(path){if(path==="/")return"/";path=PATH.normalize(path);path=path.replace(/\/$/,"");var lastSlash=path.lastIndexOf("/");if(lastSlash===-1)return path;return path.substr(lastSlash+1)},extname:function(path){return PATH.splitPath(path)[3]},join:function(){var paths=Array.prototype.slice.call(arguments,0);return PATH.normalize(paths.join("/"))},join2:function(l,r){return PATH.normalize(l+"/"+r)}};function getRandomDevice(){if(typeof crypto==="object"&&typeof crypto["getRandomValues"]==="function"){var randomBuffer=new Uint8Array(1);return function(){crypto.getRandomValues(randomBuffer);return randomBuffer[0]}}else if(ENVIRONMENT_IS_NODE){try{var crypto_module=require("crypto");return function(){return crypto_module["randomBytes"](1)[0]}}catch(e){}}return function(){abort("randomDevice")}}var PATH_FS={resolve:function(){var resolvedPath="",resolvedAbsolute=false;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path=i>=0?arguments[i]:FS.cwd();if(typeof path!=="string"){throw new TypeError("Arguments to path.resolve must be strings")}else if(!path){return""}resolvedPath=path+"/"+resolvedPath;resolvedAbsolute=path.charAt(0)==="/"}resolvedPath=PATH.normalizeArray(resolvedPath.split("/").filter(function(p){return!!p}),!resolvedAbsolute).join("/");return(resolvedAbsolute?"/":"")+resolvedPath||"."},relative:function(from,to){from=PATH_FS.resolve(from).substr(1);to=PATH_FS.resolve(to).substr(1);function trim(arr){var start=0;for(;start=0;end--){if(arr[end]!=="")break}if(start>end)return[];return arr.slice(start,end-start+1)}var fromParts=trim(from.split("/"));var toParts=trim(to.split("/"));var length=Math.min(fromParts.length,toParts.length);var samePartsLength=length;for(var i=0;i0){result=buf.slice(0,bytesRead).toString("utf-8")}else{result=null}}else if(typeof window!="undefined"&&typeof window.prompt=="function"){result=window.prompt("Input: ");if(result!==null){result+="\n"}}else if(typeof readline=="function"){result=readline();if(result!==null){result+="\n"}}if(!result){return null}tty.input=intArrayFromString(result,true)}return tty.input.shift()},put_char:function(tty,val){if(val===null||val===10){out(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){out(UTF8ArrayToString(tty.output,0));tty.output=[]}}},default_tty1_ops:{put_char:function(tty,val){if(val===null||val===10){err(UTF8ArrayToString(tty.output,0));tty.output=[]}else{if(val!=0)tty.output.push(val)}},flush:function(tty){if(tty.output&&tty.output.length>0){err(UTF8ArrayToString(tty.output,0));tty.output=[]}}}};function mmapAlloc(size){var alignedSize=alignMemory(size,65536);var ptr=_malloc(alignedSize);while(size=newCapacity)return;var CAPACITY_DOUBLING_MAX=1024*1024;newCapacity=Math.max(newCapacity,prevCapacity*(prevCapacity>>0);if(prevCapacity!=0)newCapacity=Math.max(newCapacity,256);var oldContents=node.contents;node.contents=new Uint8Array(newCapacity);if(node.usedBytes>0)node.contents.set(oldContents.subarray(0,node.usedBytes),0)},resizeFileStorage:function(node,newSize){if(node.usedBytes==newSize)return;if(newSize==0){node.contents=null;node.usedBytes=0}else{var oldContents=node.contents;node.contents=new Uint8Array(newSize);if(oldContents){node.contents.set(oldContents.subarray(0,Math.min(newSize,node.usedBytes)))}node.usedBytes=newSize}},node_ops:{getattr:function(node){var attr={};attr.dev=FS.isChrdev(node.mode)?node.id:1;attr.ino=node.id;attr.mode=node.mode;attr.nlink=1;attr.uid=0;attr.gid=0;attr.rdev=node.rdev;if(FS.isDir(node.mode)){attr.size=4096}else if(FS.isFile(node.mode)){attr.size=node.usedBytes}else if(FS.isLink(node.mode)){attr.size=node.link.length}else{attr.size=0}attr.atime=new Date(node.timestamp);attr.mtime=new Date(node.timestamp);attr.ctime=new Date(node.timestamp);attr.blksize=4096;attr.blocks=Math.ceil(attr.size/attr.blksize);return attr},setattr:function(node,attr){if(attr.mode!==undefined){node.mode=attr.mode}if(attr.timestamp!==undefined){node.timestamp=attr.timestamp}if(attr.size!==undefined){MEMFS.resizeFileStorage(node,attr.size)}},lookup:function(parent,name){throw FS.genericErrors[44]},mknod:function(parent,name,mode,dev){return MEMFS.createNode(parent,name,mode,dev)},rename:function(old_node,new_dir,new_name){if(FS.isDir(old_node.mode)){var new_node;try{new_node=FS.lookupNode(new_dir,new_name)}catch(e){}if(new_node){for(var i in new_node.contents){throw new FS.ErrnoError(55)}}}delete old_node.parent.contents[old_node.name];old_node.parent.timestamp=Date.now();old_node.name=new_name;new_dir.contents[new_name]=old_node;new_dir.timestamp=old_node.parent.timestamp;old_node.parent=new_dir},unlink:function(parent,name){delete parent.contents[name];parent.timestamp=Date.now()},rmdir:function(parent,name){var node=FS.lookupNode(parent,name);for(var i in node.contents){throw new FS.ErrnoError(55)}delete parent.contents[name];parent.timestamp=Date.now()},readdir:function(node){var entries=[".",".."];for(var key in node.contents){if(!node.contents.hasOwnProperty(key)){continue}entries.push(key)}return entries},symlink:function(parent,newname,oldpath){var node=MEMFS.createNode(parent,newname,511|40960,0);node.link=oldpath;return node},readlink:function(node){if(!FS.isLink(node.mode)){throw new FS.ErrnoError(28)}return node.link}},stream_ops:{read:function(stream,buffer,offset,length,position){var contents=stream.node.contents;if(position>=stream.node.usedBytes)return 0;var size=Math.min(stream.node.usedBytes-position,length);if(size>8&&contents.subarray){buffer.set(contents.subarray(position,position+size),offset)}else{for(var i=0;i0||position+length8){throw new FS.ErrnoError(32)}var parts=PATH.normalizeArray(path.split("/").filter(function(p){return!!p}),false);var current=FS.root;var current_path="/";for(var i=0;i40){throw new FS.ErrnoError(32)}}}}return{path:current_path,node:current}},getPath:function(node){var path;while(true){if(FS.isRoot(node)){var mount=node.mount.mountpoint;if(!path)return mount;return mount[mount.length-1]!=="/"?mount+"/"+path:mount+path}path=path?node.name+"/"+path:node.name;node=node.parent}},hashName:function(parentid,name){var hash=0;for(var i=0;i>>0)%FS.nameTable.length},hashAddNode:function(node){var hash=FS.hashName(node.parent.id,node.name);node.name_next=FS.nameTable[hash];FS.nameTable[hash]=node},hashRemoveNode:function(node){var hash=FS.hashName(node.parent.id,node.name);if(FS.nameTable[hash]===node){FS.nameTable[hash]=node.name_next}else{var current=FS.nameTable[hash];while(current){if(current.name_next===node){current.name_next=node.name_next;break}current=current.name_next}}},lookupNode:function(parent,name){var errCode=FS.mayLookup(parent);if(errCode){throw new FS.ErrnoError(errCode,parent)}var hash=FS.hashName(parent.id,name);for(var node=FS.nameTable[hash];node;node=node.name_next){var nodeName=node.name;if(node.parent.id===parent.id&&nodeName===name){return node}}return FS.lookup(parent,name)},createNode:function(parent,name,mode,rdev){var node=new FS.FSNode(parent,name,mode,rdev);FS.hashAddNode(node);return node},destroyNode:function(node){FS.hashRemoveNode(node)},isRoot:function(node){return node===node.parent},isMountpoint:function(node){return!!node.mounted},isFile:function(mode){return(mode&61440)===32768},isDir:function(mode){return(mode&61440)===16384},isLink:function(mode){return(mode&61440)===40960},isChrdev:function(mode){return(mode&61440)===8192},isBlkdev:function(mode){return(mode&61440)===24576},isFIFO:function(mode){return(mode&61440)===4096},isSocket:function(mode){return(mode&49152)===49152},flagModes:{"r":0,"r+":2,"w":577,"w+":578,"a":1089,"a+":1090},modeStringToFlags:function(str){var flags=FS.flagModes[str];if(typeof flags==="undefined"){throw new Error("Unknown file open mode: "+str)}return flags},flagsToPermissionString:function(flag){var perms=["r","w","rw"][flag&3];if(flag&512){perms+="w"}return perms},nodePermissions:function(node,perms){if(FS.ignorePermissions){return 0}if(perms.includes("r")&&!(node.mode&292)){return 2}else if(perms.includes("w")&&!(node.mode&146)){return 2}else if(perms.includes("x")&&!(node.mode&73)){return 2}return 0},mayLookup:function(dir){var errCode=FS.nodePermissions(dir,"x");if(errCode)return errCode;if(!dir.node_ops.lookup)return 2;return 0},mayCreate:function(dir,name){try{var node=FS.lookupNode(dir,name);return 20}catch(e){}return FS.nodePermissions(dir,"wx")},mayDelete:function(dir,name,isdir){var node;try{node=FS.lookupNode(dir,name)}catch(e){return e.errno}var errCode=FS.nodePermissions(dir,"wx");if(errCode){return errCode}if(isdir){if(!FS.isDir(node.mode)){return 54}if(FS.isRoot(node)||FS.getPath(node)===FS.cwd()){return 10}}else{if(FS.isDir(node.mode)){return 31}}return 0},mayOpen:function(node,flags){if(!node){return 44}if(FS.isLink(node.mode)){return 32}else if(FS.isDir(node.mode)){if(FS.flagsToPermissionString(flags)!=="r"||flags&512){return 31}}return FS.nodePermissions(node,FS.flagsToPermissionString(flags))},MAX_OPEN_FDS:4096,nextfd:function(fd_start,fd_end){fd_start=fd_start||0;fd_end=fd_end||FS.MAX_OPEN_FDS;for(var fd=fd_start;fd<=fd_end;fd++){if(!FS.streams[fd]){return fd}}throw new FS.ErrnoError(33)},getStream:function(fd){return FS.streams[fd]},createStream:function(stream,fd_start,fd_end){if(!FS.FSStream){FS.FSStream=function(){};FS.FSStream.prototype={object:{get:function(){return this.node},set:function(val){this.node=val}},isRead:{get:function(){return(this.flags&2097155)!==1}},isWrite:{get:function(){return(this.flags&2097155)!==0}},isAppend:{get:function(){return this.flags&1024}}}}var newStream=new FS.FSStream;for(var p in stream){newStream[p]=stream[p]}stream=newStream;var fd=FS.nextfd(fd_start,fd_end);stream.fd=fd;FS.streams[fd]=stream;return stream},closeStream:function(fd){FS.streams[fd]=null},chrdev_stream_ops:{open:function(stream){var device=FS.getDevice(stream.node.rdev);stream.stream_ops=device.stream_ops;if(stream.stream_ops.open){stream.stream_ops.open(stream)}},llseek:function(){throw new FS.ErrnoError(70)}},major:function(dev){return dev>>8},minor:function(dev){return dev&255},makedev:function(ma,mi){return ma<<8|mi},registerDevice:function(dev,ops){FS.devices[dev]={stream_ops:ops}},getDevice:function(dev){return FS.devices[dev]},getMounts:function(mount){var mounts=[];var check=[mount];while(check.length){var m=check.pop();mounts.push(m);check.push.apply(check,m.mounts)}return mounts},syncfs:function(populate,callback){if(typeof populate==="function"){callback=populate;populate=false}FS.syncFSRequests++;if(FS.syncFSRequests>1){err("warning: "+FS.syncFSRequests+" FS.syncfs operations in flight at once, probably just doing extra work")}var mounts=FS.getMounts(FS.root.mount);var completed=0;function doCallback(errCode){FS.syncFSRequests--;return callback(errCode)}function done(errCode){if(errCode){if(!done.errored){done.errored=true;return doCallback(errCode)}return}if(++completed>=mounts.length){doCallback(null)}}mounts.forEach(function(mount){if(!mount.type.syncfs){return done(null)}mount.type.syncfs(mount,populate,done)})},mount:function(type,opts,mountpoint){var root=mountpoint==="/";var pseudo=!mountpoint;var node;if(root&&FS.root){throw new FS.ErrnoError(10)}else if(!root&&!pseudo){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});mountpoint=lookup.path;node=lookup.node;if(FS.isMountpoint(node)){throw new FS.ErrnoError(10)}if(!FS.isDir(node.mode)){throw new FS.ErrnoError(54)}}var mount={type:type,opts:opts,mountpoint:mountpoint,mounts:[]};var mountRoot=type.mount(mount);mountRoot.mount=mount;mount.root=mountRoot;if(root){FS.root=mountRoot}else if(node){node.mounted=mount;if(node.mount){node.mount.mounts.push(mount)}}return mountRoot},unmount:function(mountpoint){var lookup=FS.lookupPath(mountpoint,{follow_mount:false});if(!FS.isMountpoint(lookup.node)){throw new FS.ErrnoError(28)}var node=lookup.node;var mount=node.mounted;var mounts=FS.getMounts(mount);Object.keys(FS.nameTable).forEach(function(hash){var current=FS.nameTable[hash];while(current){var next=current.name_next;if(mounts.includes(current.mount)){FS.destroyNode(current)}current=next}});node.mounted=null;var idx=node.mount.mounts.indexOf(mount);node.mount.mounts.splice(idx,1)},lookup:function(parent,name){return parent.node_ops.lookup(parent,name)},mknod:function(path,mode,dev){var lookup=FS.lookupPath(path,{parent:true});var parent=lookup.node;var name=PATH.basename(path);if(!name||name==="."||name===".."){throw new FS.ErrnoError(28)}var errCode=FS.mayCreate(parent,name);if(errCode){throw new FS.ErrnoError(errCode)}if(!parent.node_ops.mknod){throw new FS.ErrnoError(63)}return parent.node_ops.mknod(parent,name,mode,dev)},create:function(path,mode){mode=mode!==undefined?mode:438;mode&=4095;mode|=32768;return FS.mknod(path,mode,0)},mkdir:function(path,mode){mode=mode!==undefined?mode:511;mode&=511|512;mode|=16384;return FS.mknod(path,mode,0)},mkdirTree:function(path,mode){var dirs=path.split("/");var d="";for(var i=0;i"})},staticInit:function(){FS.ensureErrnoError();FS.nameTable=new Array(4096);FS.mount(MEMFS,{},"/");FS.createDefaultDirectories();FS.createDefaultDevices();FS.createSpecialDirectories();FS.filesystems={"MEMFS":MEMFS}},init:function(input,output,error){FS.init.initialized=true;FS.ensureErrnoError();Module["stdin"]=input||Module["stdin"];Module["stdout"]=output||Module["stdout"];Module["stderr"]=error||Module["stderr"];FS.createStandardStreams()},quit:function(){FS.init.initialized=false;var fflush=Module["_fflush"];if(fflush)fflush(0);for(var i=0;ithis.length-1||idx<0){return undefined}var chunkOffset=idx%this.chunkSize;var chunkNum=idx/this.chunkSize|0;return this.getter(chunkNum)[chunkOffset]};LazyUint8Array.prototype.setDataGetter=function LazyUint8Array_setDataGetter(getter){this.getter=getter};LazyUint8Array.prototype.cacheLength=function LazyUint8Array_cacheLength(){var xhr=new XMLHttpRequest;xhr.open("HEAD",url,false);xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);var datalength=Number(xhr.getResponseHeader("Content-length"));var header;var hasByteServing=(header=xhr.getResponseHeader("Accept-Ranges"))&&header==="bytes";var usesGzip=(header=xhr.getResponseHeader("Content-Encoding"))&&header==="gzip";var chunkSize=1024*1024;if(!hasByteServing)chunkSize=datalength;var doXHR=function(from,to){if(from>to)throw new Error("invalid range ("+from+", "+to+") or no bytes requested!");if(to>datalength-1)throw new Error("only "+datalength+" bytes available! programmer error!");var xhr=new XMLHttpRequest;xhr.open("GET",url,false);if(datalength!==chunkSize)xhr.setRequestHeader("Range","bytes="+from+"-"+to);if(typeof Uint8Array!="undefined")xhr.responseType="arraybuffer";if(xhr.overrideMimeType){xhr.overrideMimeType("text/plain; charset=x-user-defined")}xhr.send(null);if(!(xhr.status>=200&&xhr.status<300||xhr.status===304))throw new Error("Couldn't load "+url+". Status: "+xhr.status);if(xhr.response!==undefined){return new Uint8Array(xhr.response||[])}else{return intArrayFromString(xhr.responseText||"",true)}};var lazyArray=this;lazyArray.setDataGetter(function(chunkNum){var start=chunkNum*chunkSize;var end=(chunkNum+1)*chunkSize-1;end=Math.min(end,datalength-1);if(typeof lazyArray.chunks[chunkNum]==="undefined"){lazyArray.chunks[chunkNum]=doXHR(start,end)}if(typeof lazyArray.chunks[chunkNum]==="undefined")throw new Error("doXHR failed!");return lazyArray.chunks[chunkNum]});if(usesGzip||!datalength){chunkSize=datalength=1;datalength=this.getter(0).length;chunkSize=datalength;out("LazyFiles on gzip forces download of the whole file when length is accessed")}this._length=datalength;this._chunkSize=chunkSize;this.lengthKnown=true};if(typeof XMLHttpRequest!=="undefined"){if(!ENVIRONMENT_IS_WORKER)throw"Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc";var lazyArray=new LazyUint8Array;Object.defineProperties(lazyArray,{length:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._length}},chunkSize:{get:function(){if(!this.lengthKnown){this.cacheLength()}return this._chunkSize}}});var properties={isDevice:false,contents:lazyArray}}else{var properties={isDevice:false,url:url}}var node=FS.createFile(parent,name,properties,canRead,canWrite);if(properties.contents){node.contents=properties.contents}else if(properties.url){node.contents=null;node.url=properties.url}Object.defineProperties(node,{usedBytes:{get:function(){return this.contents.length}}});var stream_ops={};var keys=Object.keys(node.stream_ops);keys.forEach(function(key){var fn=node.stream_ops[key];stream_ops[key]=function forceLoadLazyFile(){FS.forceLoadFile(node);return fn.apply(null,arguments)}});stream_ops.read=function stream_ops_read(stream,buffer,offset,length,position){FS.forceLoadFile(node);var contents=stream.node.contents;if(position>=contents.length)return 0;var size=Math.min(contents.length-position,length);if(contents.slice){for(var i=0;i>2]=stat.dev;HEAP32[buf+4>>2]=0;HEAP32[buf+8>>2]=stat.ino;HEAP32[buf+12>>2]=stat.mode;HEAP32[buf+16>>2]=stat.nlink;HEAP32[buf+20>>2]=stat.uid;HEAP32[buf+24>>2]=stat.gid;HEAP32[buf+28>>2]=stat.rdev;HEAP32[buf+32>>2]=0;tempI64=[stat.size>>>0,(tempDouble=stat.size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+40>>2]=tempI64[0],HEAP32[buf+44>>2]=tempI64[1];HEAP32[buf+48>>2]=4096;HEAP32[buf+52>>2]=stat.blocks;HEAP32[buf+56>>2]=stat.atime.getTime()/1e3|0;HEAP32[buf+60>>2]=0;HEAP32[buf+64>>2]=stat.mtime.getTime()/1e3|0;HEAP32[buf+68>>2]=0;HEAP32[buf+72>>2]=stat.ctime.getTime()/1e3|0;HEAP32[buf+76>>2]=0;tempI64=[stat.ino>>>0,(tempDouble=stat.ino,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[buf+80>>2]=tempI64[0],HEAP32[buf+84>>2]=tempI64[1];return 0},doMsync:function(addr,stream,len,flags,offset){var buffer=HEAPU8.slice(addr,addr+len);FS.msync(stream,buffer,offset,len,flags)},doMkdir:function(path,mode){path=PATH.normalize(path);if(path[path.length-1]==="/")path=path.substr(0,path.length-1);FS.mkdir(path,mode,0);return 0},doMknod:function(path,mode,dev){switch(mode&61440){case 32768:case 8192:case 24576:case 4096:case 49152:break;default:return-28}FS.mknod(path,mode,dev);return 0},doReadlink:function(path,buf,bufsize){if(bufsize<=0)return-28;var ret=FS.readlink(path);var len=Math.min(bufsize,lengthBytesUTF8(ret));var endChar=HEAP8[buf+len];stringToUTF8(ret,buf,bufsize+1);HEAP8[buf+len]=endChar;return len},doAccess:function(path,amode){if(amode&~7){return-28}var node;var lookup=FS.lookupPath(path,{follow:true});node=lookup.node;if(!node){return-44}var perms="";if(amode&4)perms+="r";if(amode&2)perms+="w";if(amode&1)perms+="x";if(perms&&FS.nodePermissions(node,perms)){return-2}return 0},doDup:function(path,flags,suggestFD){var suggest=FS.getStream(suggestFD);if(suggest)FS.close(suggest);return FS.open(path,flags,0,suggestFD,suggestFD).fd},doReadv:function(stream,iov,iovcnt,offset){var ret=0;for(var i=0;i>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.read(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr;if(curr>2];var len=HEAP32[iov+(i*8+4)>>2];var curr=FS.write(stream,HEAP8,ptr,len,offset);if(curr<0)return-1;ret+=curr}return ret},varargs:undefined,get:function(){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret},getStr:function(ptr){var ret=UTF8ToString(ptr);return ret},getStreamFromFD:function(fd){var stream=FS.getStream(fd);if(!stream)throw new FS.ErrnoError(8);return stream},get64:function(low,high){return low}};function ___sys_fcntl64(fd,cmd,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(cmd){case 0:{var arg=SYSCALLS.get();if(arg<0){return-28}var newStream;newStream=FS.open(stream.path,stream.flags,0,arg);return newStream.fd}case 1:case 2:return 0;case 3:return stream.flags;case 4:{var arg=SYSCALLS.get();stream.flags|=arg;return 0}case 12:{var arg=SYSCALLS.get();var offset=0;HEAP16[arg+offset>>1]=2;return 0}case 13:case 14:return 0;case 16:case 8:return-28;case 9:setErrNo(28);return-1;default:{return-28}}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_getdents64(fd,dirp,count){try{var stream=SYSCALLS.getStreamFromFD(fd);if(!stream.getdents){stream.getdents=FS.readdir(stream.path)}var struct_size=280;var pos=0;var off=FS.llseek(stream,0,1);var idx=Math.floor(off/struct_size);while(idx>>0,(tempDouble=id,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[dirp+pos>>2]=tempI64[0],HEAP32[dirp+pos+4>>2]=tempI64[1];tempI64=[(idx+1)*struct_size>>>0,(tempDouble=(idx+1)*struct_size,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[dirp+pos+8>>2]=tempI64[0],HEAP32[dirp+pos+12>>2]=tempI64[1];HEAP16[dirp+pos+16>>1]=280;HEAP8[dirp+pos+18>>0]=type;stringToUTF8(name,dirp+pos+19,256);pos+=struct_size;idx+=1}FS.llseek(stream,idx*struct_size,0);return pos}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_ioctl(fd,op,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(fd);switch(op){case 21509:case 21505:{if(!stream.tty)return-59;return 0}case 21510:case 21511:case 21512:case 21506:case 21507:case 21508:{if(!stream.tty)return-59;return 0}case 21519:{if(!stream.tty)return-59;var argp=SYSCALLS.get();HEAP32[argp>>2]=0;return 0}case 21520:{if(!stream.tty)return-59;return-28}case 21531:{var argp=SYSCALLS.get();return FS.ioctl(stream,op,argp)}case 21523:{if(!stream.tty)return-59;return 0}case 21524:{if(!stream.tty)return-59;return 0}default:abort("bad ioctl syscall "+op)}}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_open(path,flags,varargs){SYSCALLS.varargs=varargs;try{var pathname=SYSCALLS.getStr(path);var mode=varargs?SYSCALLS.get():0;var stream=FS.open(pathname,flags,mode);return stream.fd}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___sys_stat64(path,buf){try{path=SYSCALLS.getStr(path);return SYSCALLS.doStat(FS.stat,path,buf)}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}var tupleRegistrations={};function runDestructors(destructors){while(destructors.length){var ptr=destructors.pop();var del=destructors.pop();del(ptr)}}function simpleReadValueFromPointer(pointer){return this["fromWireType"](HEAPU32[pointer>>2])}var awaitingDependencies={};var registeredTypes={};var typeDependencies={};var char_0=48;var char_9=57;function makeLegalFunctionName(name){if(undefined===name){return"_unknown"}name=name.replace(/[^a-zA-Z0-9_]/g,"$");var f=name.charCodeAt(0);if(f>=char_0&&f<=char_9){return"_"+name}else{return name}}function createNamedFunction(name,body){name=makeLegalFunctionName(name);return new Function("body","return function "+name+"() {\n"+' "use strict";'+" return body.apply(this, arguments);\n"+"};\n")(body)}function extendError(baseErrorType,errorName){var errorClass=createNamedFunction(errorName,function(message){this.name=errorName;this.message=message;var stack=new Error(message).stack;if(stack!==undefined){this.stack=this.toString()+"\n"+stack.replace(/^Error(:[^\n]*)?\n/,"")}});errorClass.prototype=Object.create(baseErrorType.prototype);errorClass.prototype.constructor=errorClass;errorClass.prototype.toString=function(){if(this.message===undefined){return this.name}else{return this.name+": "+this.message}};return errorClass}var InternalError=undefined;function throwInternalError(message){throw new InternalError(message)}function whenDependentTypesAreResolved(myTypes,dependentTypes,getTypeConverters){myTypes.forEach(function(type){typeDependencies[type]=dependentTypes});function onComplete(typeConverters){var myTypeConverters=getTypeConverters(typeConverters);if(myTypeConverters.length!==myTypes.length){throwInternalError("Mismatched type converter count")}for(var i=0;i>shift])},destructorFunction:null})}function ClassHandle_isAliasOf(other){if(!(this instanceof ClassHandle)){return false}if(!(other instanceof ClassHandle)){return false}var leftClass=this.$$.ptrType.registeredClass;var left=this.$$.ptr;var rightClass=other.$$.ptrType.registeredClass;var right=other.$$.ptr;while(leftClass.baseClass){left=leftClass.upcast(left);leftClass=leftClass.baseClass}while(rightClass.baseClass){right=rightClass.upcast(right);rightClass=rightClass.baseClass}return leftClass===rightClass&&left===right}function shallowCopyInternalPointer(o){return{count:o.count,deleteScheduled:o.deleteScheduled,preservePointerOnDelete:o.preservePointerOnDelete,ptr:o.ptr,ptrType:o.ptrType,smartPtr:o.smartPtr,smartPtrType:o.smartPtrType}}function throwInstanceAlreadyDeleted(obj){function getInstanceTypeName(handle){return handle.$$.ptrType.registeredClass.name}throwBindingError(getInstanceTypeName(obj)+" instance already deleted")}var finalizationGroup=false;function detachFinalizer(handle){}function runDestructor($$){if($$.smartPtr){$$.smartPtrType.rawDestructor($$.smartPtr)}else{$$.ptrType.registeredClass.rawDestructor($$.ptr)}}function releaseClassHandle($$){$$.count.value-=1;var toDelete=0===$$.count.value;if(toDelete){runDestructor($$)}}function attachFinalizer(handle){if("undefined"===typeof FinalizationGroup){attachFinalizer=function(handle){return handle};return handle}finalizationGroup=new FinalizationGroup(function(iter){for(var result=iter.next();!result.done;result=iter.next()){var $$=result.value;if(!$$.ptr){console.warn("object already deleted: "+$$.ptr)}else{releaseClassHandle($$)}}});attachFinalizer=function(handle){finalizationGroup.register(handle,handle.$$,handle.$$);return handle};detachFinalizer=function(handle){finalizationGroup.unregister(handle.$$)};return attachFinalizer(handle)}function ClassHandle_clone(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.preservePointerOnDelete){this.$$.count.value+=1;return this}else{var clone=attachFinalizer(Object.create(Object.getPrototypeOf(this),{$$:{value:shallowCopyInternalPointer(this.$$)}}));clone.$$.count.value+=1;clone.$$.deleteScheduled=false;return clone}}function ClassHandle_delete(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}detachFinalizer(this);releaseClassHandle(this.$$);if(!this.$$.preservePointerOnDelete){this.$$.smartPtr=undefined;this.$$.ptr=undefined}}function ClassHandle_isDeleted(){return!this.$$.ptr}var delayFunction=undefined;var deletionQueue=[];function flushPendingDeletes(){while(deletionQueue.length){var obj=deletionQueue.pop();obj.$$.deleteScheduled=false;obj["delete"]()}}function ClassHandle_deleteLater(){if(!this.$$.ptr){throwInstanceAlreadyDeleted(this)}if(this.$$.deleteScheduled&&!this.$$.preservePointerOnDelete){throwBindingError("Object already scheduled for deletion")}deletionQueue.push(this);if(deletionQueue.length===1&&delayFunction){delayFunction(flushPendingDeletes)}this.$$.deleteScheduled=true;return this}function init_ClassHandle(){ClassHandle.prototype["isAliasOf"]=ClassHandle_isAliasOf;ClassHandle.prototype["clone"]=ClassHandle_clone;ClassHandle.prototype["delete"]=ClassHandle_delete;ClassHandle.prototype["isDeleted"]=ClassHandle_isDeleted;ClassHandle.prototype["deleteLater"]=ClassHandle_deleteLater}function ClassHandle(){}var registeredPointers={};function ensureOverloadTable(proto,methodName,humanName){if(undefined===proto[methodName].overloadTable){var prevFunc=proto[methodName];proto[methodName]=function(){if(!proto[methodName].overloadTable.hasOwnProperty(arguments.length)){throwBindingError("Function '"+humanName+"' called with an invalid number of arguments ("+arguments.length+") - expects one of ("+proto[methodName].overloadTable+")!")}return proto[methodName].overloadTable[arguments.length].apply(this,arguments)};proto[methodName].overloadTable=[];proto[methodName].overloadTable[prevFunc.argCount]=prevFunc}}function exposePublicSymbol(name,value,numArguments){if(Module.hasOwnProperty(name)){if(undefined===numArguments||undefined!==Module[name].overloadTable&&undefined!==Module[name].overloadTable[numArguments]){throwBindingError("Cannot register public name '"+name+"' twice")}ensureOverloadTable(Module,name,name);if(Module.hasOwnProperty(numArguments)){throwBindingError("Cannot register multiple overloads of a function with the same number of arguments ("+numArguments+")!")}Module[name].overloadTable[numArguments]=value}else{Module[name]=value;if(undefined!==numArguments){Module[name].numArguments=numArguments}}}function RegisteredClass(name,constructor,instancePrototype,rawDestructor,baseClass,getActualType,upcast,downcast){this.name=name;this.constructor=constructor;this.instancePrototype=instancePrototype;this.rawDestructor=rawDestructor;this.baseClass=baseClass;this.getActualType=getActualType;this.upcast=upcast;this.downcast=downcast;this.pureVirtualFunctions=[]}function upcastPointer(ptr,ptrClass,desiredClass){while(ptrClass!==desiredClass){if(!ptrClass.upcast){throwBindingError("Expected null or instance of "+desiredClass.name+", got an instance of "+ptrClass.name)}ptr=ptrClass.upcast(ptr);ptrClass=ptrClass.baseClass}return ptr}function constNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function genericPointerToWireType(destructors,handle){var ptr;if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}if(this.isSmartPointer){ptr=this.rawConstructor();if(destructors!==null){destructors.push(this.rawDestructor,ptr)}return ptr}else{return 0}}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(!this.isConst&&handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);if(this.isSmartPointer){if(undefined===handle.$$.smartPtr){throwBindingError("Passing raw pointer to smart pointer is illegal")}switch(this.sharingPolicy){case 0:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{throwBindingError("Cannot convert argument of type "+(handle.$$.smartPtrType?handle.$$.smartPtrType.name:handle.$$.ptrType.name)+" to parameter type "+this.name)}break;case 1:ptr=handle.$$.smartPtr;break;case 2:if(handle.$$.smartPtrType===this){ptr=handle.$$.smartPtr}else{var clonedHandle=handle["clone"]();ptr=this.rawShare(ptr,__emval_register(function(){clonedHandle["delete"]()}));if(destructors!==null){destructors.push(this.rawDestructor,ptr)}}break;default:throwBindingError("Unsupporting sharing policy")}}return ptr}function nonConstNoSmartPtrRawPointerToWireType(destructors,handle){if(handle===null){if(this.isReference){throwBindingError("null is not a valid "+this.name)}return 0}if(!handle.$$){throwBindingError('Cannot pass "'+_embind_repr(handle)+'" as a '+this.name)}if(!handle.$$.ptr){throwBindingError("Cannot pass deleted object as a pointer of type "+this.name)}if(handle.$$.ptrType.isConst){throwBindingError("Cannot convert argument of type "+handle.$$.ptrType.name+" to parameter type "+this.name)}var handleClass=handle.$$.ptrType.registeredClass;var ptr=upcastPointer(handle.$$.ptr,handleClass,this.registeredClass);return ptr}function RegisteredPointer_getPointee(ptr){if(this.rawGetPointee){ptr=this.rawGetPointee(ptr)}return ptr}function RegisteredPointer_destructor(ptr){if(this.rawDestructor){this.rawDestructor(ptr)}}function RegisteredPointer_deleteObject(handle){if(handle!==null){handle["delete"]()}}function downcastPointer(ptr,ptrClass,desiredClass){if(ptrClass===desiredClass){return ptr}if(undefined===desiredClass.baseClass){return null}var rv=downcastPointer(ptr,ptrClass,desiredClass.baseClass);if(rv===null){return null}return desiredClass.downcast(rv)}function getInheritedInstanceCount(){return Object.keys(registeredInstances).length}function getLiveInheritedInstances(){var rv=[];for(var k in registeredInstances){if(registeredInstances.hasOwnProperty(k)){rv.push(registeredInstances[k])}}return rv}function setDelayFunction(fn){delayFunction=fn;if(deletionQueue.length&&delayFunction){delayFunction(flushPendingDeletes)}}function init_embind(){Module["getInheritedInstanceCount"]=getInheritedInstanceCount;Module["getLiveInheritedInstances"]=getLiveInheritedInstances;Module["flushPendingDeletes"]=flushPendingDeletes;Module["setDelayFunction"]=setDelayFunction}var registeredInstances={};function getBasestPointer(class_,ptr){if(ptr===undefined){throwBindingError("ptr should not be undefined")}while(class_.baseClass){ptr=class_.upcast(ptr);class_=class_.baseClass}return ptr}function getInheritedInstance(class_,ptr){ptr=getBasestPointer(class_,ptr);return registeredInstances[ptr]}function makeClassHandle(prototype,record){if(!record.ptrType||!record.ptr){throwInternalError("makeClassHandle requires ptr and ptrType")}var hasSmartPtrType=!!record.smartPtrType;var hasSmartPtr=!!record.smartPtr;if(hasSmartPtrType!==hasSmartPtr){throwInternalError("Both smartPtrType and smartPtr must be specified")}record.count={value:1};return attachFinalizer(Object.create(prototype,{$$:{value:record}}))}function RegisteredPointer_fromWireType(ptr){var rawPointer=this.getPointee(ptr);if(!rawPointer){this.destructor(ptr);return null}var registeredInstance=getInheritedInstance(this.registeredClass,rawPointer);if(undefined!==registeredInstance){if(0===registeredInstance.$$.count.value){registeredInstance.$$.ptr=rawPointer;registeredInstance.$$.smartPtr=ptr;return registeredInstance["clone"]()}else{var rv=registeredInstance["clone"]();this.destructor(ptr);return rv}}function makeDefaultHandle(){if(this.isSmartPointer){return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this.pointeeType,ptr:rawPointer,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(this.registeredClass.instancePrototype,{ptrType:this,ptr:ptr})}}var actualType=this.registeredClass.getActualType(rawPointer);var registeredPointerRecord=registeredPointers[actualType];if(!registeredPointerRecord){return makeDefaultHandle.call(this)}var toType;if(this.isConst){toType=registeredPointerRecord.constPointerType}else{toType=registeredPointerRecord.pointerType}var dp=downcastPointer(rawPointer,this.registeredClass,toType.registeredClass);if(dp===null){return makeDefaultHandle.call(this)}if(this.isSmartPointer){return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp,smartPtrType:this,smartPtr:ptr})}else{return makeClassHandle(toType.registeredClass.instancePrototype,{ptrType:toType,ptr:dp})}}function init_RegisteredPointer(){RegisteredPointer.prototype.getPointee=RegisteredPointer_getPointee;RegisteredPointer.prototype.destructor=RegisteredPointer_destructor;RegisteredPointer.prototype["argPackAdvance"]=8;RegisteredPointer.prototype["readValueFromPointer"]=simpleReadValueFromPointer;RegisteredPointer.prototype["deleteObject"]=RegisteredPointer_deleteObject;RegisteredPointer.prototype["fromWireType"]=RegisteredPointer_fromWireType}function RegisteredPointer(name,registeredClass,isReference,isConst,isSmartPointer,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor){this.name=name;this.registeredClass=registeredClass;this.isReference=isReference;this.isConst=isConst;this.isSmartPointer=isSmartPointer;this.pointeeType=pointeeType;this.sharingPolicy=sharingPolicy;this.rawGetPointee=rawGetPointee;this.rawConstructor=rawConstructor;this.rawShare=rawShare;this.rawDestructor=rawDestructor;if(!isSmartPointer&®isteredClass.baseClass===undefined){if(isConst){this["toWireType"]=constNoSmartPtrRawPointerToWireType;this.destructorFunction=null}else{this["toWireType"]=nonConstNoSmartPtrRawPointerToWireType;this.destructorFunction=null}}else{this["toWireType"]=genericPointerToWireType}}function replacePublicSymbol(name,value,numArguments){if(!Module.hasOwnProperty(name)){throwInternalError("Replacing nonexistant public symbol")}if(undefined!==Module[name].overloadTable&&undefined!==numArguments){Module[name].overloadTable[numArguments]=value}else{Module[name]=value;Module[name].argCount=numArguments}}function dynCallLegacy(sig,ptr,args){var f=Module["dynCall_"+sig];return args&&args.length?f.apply(null,[ptr].concat(args)):f.call(null,ptr)}function dynCall(sig,ptr,args){if(sig.includes("j")){return dynCallLegacy(sig,ptr,args)}return wasmTable.get(ptr).apply(null,args)}function getDynCaller(sig,ptr){var argCache=[];return function(){argCache.length=arguments.length;for(var i=0;i0?", ":"")+argsListWired}invokerFnBody+=(returns?"var rv = ":"")+"invoker(fn"+(argsListWired.length>0?", ":"")+argsListWired+");\n";if(needsDestructorStack){invokerFnBody+="runDestructors(destructors);\n"}else{for(var i=isClassMethodFunc?1:2;i>2)+i])}return array}function __embind_register_class_class_function(rawClassType,methodName,argCount,rawArgTypesAddr,invokerSignature,rawInvoker,fn){var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);methodName=readLatin1String(methodName);rawInvoker=embind__requireFunction(invokerSignature,rawInvoker);whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName=classType.name+"."+methodName;function unboundTypesHandler(){throwUnboundTypeError("Cannot call "+humanName+" due to unbound types",rawArgTypes)}var proto=classType.registeredClass.constructor;if(undefined===proto[methodName]){unboundTypesHandler.argCount=argCount-1;proto[methodName]=unboundTypesHandler}else{ensureOverloadTable(proto,methodName,humanName);proto[methodName].overloadTable[argCount-1]=unboundTypesHandler}whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));var func=craftInvokerFunction(humanName,invokerArgsArray,null,rawInvoker,fn);if(undefined===proto[methodName].overloadTable){func.argCount=argCount-1;proto[methodName]=func}else{proto[methodName].overloadTable[argCount-1]=func}return[]});return[]})}function validateThis(this_,classType,humanName){if(!(this_ instanceof Object)){throwBindingError(humanName+' with invalid "this": '+this_)}if(!(this_ instanceof classType.registeredClass.constructor)){throwBindingError(humanName+' incompatible with "this" of type '+this_.constructor.name)}if(!this_.$$.ptr){throwBindingError("cannot call emscripten binding method "+humanName+" on deleted object")}return upcastPointer(this_.$$.ptr,this_.$$.ptrType.registeredClass,classType.registeredClass)}function __embind_register_class_class_property(rawClassType,fieldName,rawFieldType,rawFieldPtr,getterSignature,getter,setterSignature,setter){fieldName=readLatin1String(fieldName);getter=embind__requireFunction(getterSignature,getter);whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName=classType.name+"."+fieldName;var desc={get:function(){throwUnboundTypeError("Cannot access "+humanName+" due to unbound types",[rawFieldType])},enumerable:true,configurable:true};if(setter){desc.set=function(){throwUnboundTypeError("Cannot access "+humanName+" due to unbound types",[rawFieldType])}}else{desc.set=function(v){throwBindingError(humanName+" is a read-only property")}}Object.defineProperty(classType.registeredClass.constructor,fieldName,desc);whenDependentTypesAreResolved([],[rawFieldType],function(fieldType){fieldType=fieldType[0];var desc={get:function(){return fieldType["fromWireType"](getter(rawFieldPtr))},enumerable:true};if(setter){setter=embind__requireFunction(setterSignature,setter);desc.set=function(v){var destructors=[];setter(rawFieldPtr,fieldType["toWireType"](destructors,v));runDestructors(destructors)}}Object.defineProperty(classType.registeredClass.constructor,fieldName,desc);return[]});return[]})}function __embind_register_class_constructor(rawClassType,argCount,rawArgTypesAddr,invokerSignature,invoker,rawConstructor){assert(argCount>0);var rawArgTypes=heap32VectorToArray(argCount,rawArgTypesAddr);invoker=embind__requireFunction(invokerSignature,invoker);var args=[rawConstructor];var destructors=[];whenDependentTypesAreResolved([],[rawClassType],function(classType){classType=classType[0];var humanName="constructor "+classType.name;if(undefined===classType.registeredClass.constructor_body){classType.registeredClass.constructor_body=[]}if(undefined!==classType.registeredClass.constructor_body[argCount-1]){throw new BindingError("Cannot register multiple constructors with identical number of parameters ("+(argCount-1)+") for class '"+classType.name+"'! Overload resolution is currently only performed using the parameter count, not actual type info!")}classType.registeredClass.constructor_body[argCount-1]=function unboundTypeHandler(){throwUnboundTypeError("Cannot construct "+classType.name+" due to unbound types",rawArgTypes)};whenDependentTypesAreResolved([],rawArgTypes,function(argTypes){classType.registeredClass.constructor_body[argCount-1]=function constructor_body(){if(arguments.length!==argCount-1){throwBindingError(humanName+" called with "+arguments.length+" arguments, expected "+(argCount-1))}destructors.length=0;args.length=argCount;for(var i=1;i4&&0===--emval_handle_array[handle].refcount){emval_handle_array[handle]=undefined;emval_free_list.push(handle)}}function count_emval_handles(){var count=0;for(var i=5;i>1])};case 2:return function(pointer){var heap=signed?HEAP32:HEAPU32;return this["fromWireType"](heap[pointer>>2])};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_enum(rawType,name,size,isSigned){var shift=getShiftFromSize(size);name=readLatin1String(name);function ctor(){}ctor.values={};registerType(rawType,{name:name,constructor:ctor,"fromWireType":function(c){return this.constructor.values[c]},"toWireType":function(destructors,c){return c.value},"argPackAdvance":8,"readValueFromPointer":enumReadValueFromPointer(name,shift,isSigned),destructorFunction:null});exposePublicSymbol(name,ctor)}function requireRegisteredType(rawType,humanName){var impl=registeredTypes[rawType];if(undefined===impl){throwBindingError(humanName+" has unknown type "+getTypeName(rawType))}return impl}function __embind_register_enum_value(rawEnumType,name,enumValue){var enumType=requireRegisteredType(rawEnumType,"enum");name=readLatin1String(name);var Enum=enumType.constructor;var Value=Object.create(enumType.constructor.prototype,{value:{value:enumValue},constructor:{value:createNamedFunction(enumType.name+"_"+name,function(){})}});Enum.values[enumValue]=Value;Enum[name]=Value}function _embind_repr(v){if(v===null){return"null"}var t=typeof v;if(t==="object"||t==="array"||t==="function"){return v.toString()}else{return""+v}}function floatReadValueFromPointer(name,shift){switch(shift){case 2:return function(pointer){return this["fromWireType"](HEAPF32[pointer>>2])};case 3:return function(pointer){return this["fromWireType"](HEAPF64[pointer>>3])};default:throw new TypeError("Unknown float type: "+name)}}function __embind_register_float(rawType,name,size){var shift=getShiftFromSize(size);name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":function(value){return value},"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}return value},"argPackAdvance":8,"readValueFromPointer":floatReadValueFromPointer(name,shift),destructorFunction:null})}function __embind_register_function(name,argCount,rawArgTypesAddr,signature,rawInvoker,fn){var argTypes=heap32VectorToArray(argCount,rawArgTypesAddr);name=readLatin1String(name);rawInvoker=embind__requireFunction(signature,rawInvoker);exposePublicSymbol(name,function(){throwUnboundTypeError("Cannot call "+name+" due to unbound types",argTypes)},argCount-1);whenDependentTypesAreResolved([],argTypes,function(argTypes){var invokerArgsArray=[argTypes[0],null].concat(argTypes.slice(1));replacePublicSymbol(name,craftInvokerFunction(name,invokerArgsArray,null,rawInvoker,fn),argCount-1);return[]})}function integerReadValueFromPointer(name,shift,signed){switch(shift){case 0:return signed?function readS8FromPointer(pointer){return HEAP8[pointer]}:function readU8FromPointer(pointer){return HEAPU8[pointer]};case 1:return signed?function readS16FromPointer(pointer){return HEAP16[pointer>>1]}:function readU16FromPointer(pointer){return HEAPU16[pointer>>1]};case 2:return signed?function readS32FromPointer(pointer){return HEAP32[pointer>>2]}:function readU32FromPointer(pointer){return HEAPU32[pointer>>2]};default:throw new TypeError("Unknown integer type: "+name)}}function __embind_register_integer(primitiveType,name,size,minRange,maxRange){name=readLatin1String(name);if(maxRange===-1){maxRange=4294967295}var shift=getShiftFromSize(size);var fromWireType=function(value){return value};if(minRange===0){var bitshift=32-8*size;fromWireType=function(value){return value<>>bitshift}}var isUnsignedType=name.includes("unsigned");registerType(primitiveType,{name:name,"fromWireType":fromWireType,"toWireType":function(destructors,value){if(typeof value!=="number"&&typeof value!=="boolean"){throw new TypeError('Cannot convert "'+_embind_repr(value)+'" to '+this.name)}if(valuemaxRange){throw new TypeError('Passing a number "'+_embind_repr(value)+'" from JS side to C/C++ side to an argument of type "'+name+'", which is outside the valid range ['+minRange+", "+maxRange+"]!")}return isUnsignedType?value>>>0:value|0},"argPackAdvance":8,"readValueFromPointer":integerReadValueFromPointer(name,shift,minRange!==0),destructorFunction:null})}function __embind_register_memory_view(rawType,dataTypeIndex,name){var typeMapping=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array];var TA=typeMapping[dataTypeIndex];function decodeMemoryView(handle){handle=handle>>2;var heap=HEAPU32;var size=heap[handle];var data=heap[handle+1];return new TA(buffer,data,size)}name=readLatin1String(name);registerType(rawType,{name:name,"fromWireType":decodeMemoryView,"argPackAdvance":8,"readValueFromPointer":decodeMemoryView},{ignoreDuplicateRegistrations:true})}function __embind_register_smart_ptr(rawType,rawPointeeType,name,sharingPolicy,getPointeeSignature,rawGetPointee,constructorSignature,rawConstructor,shareSignature,rawShare,destructorSignature,rawDestructor){name=readLatin1String(name);rawGetPointee=embind__requireFunction(getPointeeSignature,rawGetPointee);rawConstructor=embind__requireFunction(constructorSignature,rawConstructor);rawShare=embind__requireFunction(shareSignature,rawShare);rawDestructor=embind__requireFunction(destructorSignature,rawDestructor);whenDependentTypesAreResolved([rawType],[rawPointeeType],function(pointeeType){pointeeType=pointeeType[0];var registeredPointer=new RegisteredPointer(name,pointeeType.registeredClass,false,false,true,pointeeType,sharingPolicy,rawGetPointee,rawConstructor,rawShare,rawDestructor);return[registeredPointer]})}function __embind_register_std_string(rawType,name){name=readLatin1String(name);var stdStringIsUTF8=name==="std::string";registerType(rawType,{name:name,"fromWireType":function(value){var length=HEAPU32[value>>2];var str;if(stdStringIsUTF8){var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i;if(i==length||HEAPU8[currentBytePtr]==0){var maxRead=currentBytePtr-decodeStartPtr;var stringSegment=UTF8ToString(decodeStartPtr,maxRead);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+1}}}else{var a=new Array(length);for(var i=0;i>2]=length;if(stdStringIsUTF8&&valueIsOfTypeString){stringToUTF8(value,ptr+4,length+1)}else{if(valueIsOfTypeString){for(var i=0;i255){_free(ptr);throwBindingError("String has UTF-16 code units that do not fit in 8 bits")}HEAPU8[ptr+4+i]=charCode}}else{for(var i=0;i>2];var HEAP=getHeap();var str;var decodeStartPtr=value+4;for(var i=0;i<=length;++i){var currentBytePtr=value+4+i*charSize;if(i==length||HEAP[currentBytePtr>>shift]==0){var maxReadBytes=currentBytePtr-decodeStartPtr;var stringSegment=decodeString(decodeStartPtr,maxReadBytes);if(str===undefined){str=stringSegment}else{str+=String.fromCharCode(0);str+=stringSegment}decodeStartPtr=currentBytePtr+charSize}}_free(value);return str},"toWireType":function(destructors,value){if(!(typeof value==="string")){throwBindingError("Cannot pass non-string to C++ string type "+name)}var length=lengthBytesUTF(value);var ptr=_malloc(4+length+charSize);HEAPU32[ptr>>2]=length>>shift;encodeString(value,ptr+4,length+charSize);if(destructors!==null){destructors.push(_free,ptr)}return ptr},"argPackAdvance":8,"readValueFromPointer":simpleReadValueFromPointer,destructorFunction:function(ptr){_free(ptr)}})}function __embind_register_value_array(rawType,name,constructorSignature,rawConstructor,destructorSignature,rawDestructor){tupleRegistrations[rawType]={name:readLatin1String(name),rawConstructor:embind__requireFunction(constructorSignature,rawConstructor),rawDestructor:embind__requireFunction(destructorSignature,rawDestructor),elements:[]}}function __embind_register_value_array_element(rawTupleType,getterReturnType,getterSignature,getter,getterContext,setterArgumentType,setterSignature,setter,setterContext){tupleRegistrations[rawTupleType].elements.push({getterReturnType:getterReturnType,getter:embind__requireFunction(getterSignature,getter),getterContext:getterContext,setterArgumentType:setterArgumentType,setter:embind__requireFunction(setterSignature,setter),setterContext:setterContext})}function __embind_register_void(rawType,name){name=readLatin1String(name);registerType(rawType,{isVoid:true,name:name,"argPackAdvance":0,"fromWireType":function(){return undefined},"toWireType":function(destructors,o){return undefined}})}function requireHandle(handle){if(!handle){throwBindingError("Cannot use deleted val. handle = "+handle)}return emval_handle_array[handle].value}function __emval_as(handle,returnType,destructorsRef){handle=requireHandle(handle);returnType=requireRegisteredType(returnType,"emval::as");var destructors=[];var rd=__emval_register(destructors);HEAP32[destructorsRef>>2]=rd;return returnType["toWireType"](destructors,handle)}function __emval_lookupTypes(argCount,argTypes){var a=new Array(argCount);for(var i=0;i>2)+i],"parameter "+i)}return a}function __emval_call(handle,argCount,argTypes,argv){handle=requireHandle(handle);var types=__emval_lookupTypes(argCount,argTypes);var args=new Array(argCount);for(var i=0;i4){emval_handle_array[handle].refcount+=1}}function __emval_new_array(){return __emval_register([])}function __emval_new_cstring(v){return __emval_register(getStringOrSymbol(v))}function __emval_new_object(){return __emval_register({})}function __emval_run_destructors(handle){var destructors=emval_handle_array[handle].value;runDestructors(destructors);__emval_decref(handle)}function __emval_set_property(handle,key,value){handle=requireHandle(handle);key=requireHandle(key);value=requireHandle(value);handle[key]=value}function __emval_take_value(type,argv){type=requireRegisteredType(type,"_emval_take_value");var v=type["readValueFromPointer"](argv);return __emval_register(v)}function __emval_typeof(handle){handle=requireHandle(handle);return __emval_register(typeof handle)}function _abort(){abort()}var readAsmConstArgsArray=[];function readAsmConstArgs(sigPtr,buf){readAsmConstArgsArray.length=0;var ch;buf>>=2;while(ch=HEAPU8[sigPtr++]){var double=ch<105;if(double&&buf&1)buf++;readAsmConstArgsArray.push(double?HEAPF64[buf++>>1]:HEAP32[buf]);++buf}return readAsmConstArgsArray}function _emscripten_asm_const_int(code,sigPtr,argbuf){var args=readAsmConstArgs(sigPtr,argbuf);return ASM_CONSTS[code].apply(null,args)}function _emscripten_memcpy_big(dest,src,num){HEAPU8.copyWithin(dest,src,src+num)}function emscripten_realloc_buffer(size){try{wasmMemory.grow(size-buffer.byteLength+65535>>>16);updateGlobalBufferAndViews(wasmMemory.buffer);return 1}catch(e){}}function _emscripten_resize_heap(requestedSize){var oldSize=HEAPU8.length;requestedSize=requestedSize>>>0;var maxHeapSize=2147483648;if(requestedSize>maxHeapSize){return false}for(var cutDown=1;cutDown<=4;cutDown*=2){var overGrownHeapSize=oldSize+33554432/cutDown;var newSize=Math.min(maxHeapSize,alignUp(Math.max(requestedSize,overGrownHeapSize),65536));var replacement=emscripten_realloc_buffer(newSize);if(replacement){return true}}return false}var ENV={};function getExecutableName(){return thisProgram||"./this.program"}function getEnvStrings(){if(!getEnvStrings.strings){var lang=(typeof navigator==="object"&&navigator.languages&&navigator.languages[0]||"C").replace("-","_")+".UTF-8";var env={"USER":"web_user","LOGNAME":"web_user","PATH":"/","PWD":"/","HOME":"/home/web_user","LANG":lang,"_":getExecutableName()};for(var x in ENV){env[x]=ENV[x]}var strings=[];for(var x in env){strings.push(x+"="+env[x])}getEnvStrings.strings=strings}return getEnvStrings.strings}function _environ_get(__environ,environ_buf){try{var bufSize=0;getEnvStrings().forEach(function(string,i){var ptr=environ_buf+bufSize;HEAP32[__environ+i*4>>2]=ptr;writeAsciiToMemory(string,ptr);bufSize+=string.length+1});return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _environ_sizes_get(penviron_count,penviron_buf_size){try{var strings=getEnvStrings();HEAP32[penviron_count>>2]=strings.length;var bufSize=0;strings.forEach(function(string){bufSize+=string.length+1});HEAP32[penviron_buf_size>>2]=bufSize;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_close(fd){try{var stream=SYSCALLS.getStreamFromFD(fd);FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_read(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doReadv(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_seek(fd,offset_low,offset_high,whence,newOffset){try{var stream=SYSCALLS.getStreamFromFD(fd);var HIGH_OFFSET=4294967296;var offset=offset_high*HIGH_OFFSET+(offset_low>>>0);var DOUBLE_LIMIT=9007199254740992;if(offset<=-DOUBLE_LIMIT||offset>=DOUBLE_LIMIT){return-61}FS.llseek(stream,offset,whence);tempI64=[stream.position>>>0,(tempDouble=stream.position,+Math.abs(tempDouble)>=1?tempDouble>0?(Math.min(+Math.floor(tempDouble/4294967296),4294967295)|0)>>>0:~~+Math.ceil((tempDouble-+(~~tempDouble>>>0))/4294967296)>>>0:0)],HEAP32[newOffset>>2]=tempI64[0],HEAP32[newOffset+4>>2]=tempI64[1];if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _fd_write(fd,iov,iovcnt,pnum){try{var stream=SYSCALLS.getStreamFromFD(fd);var num=SYSCALLS.doWritev(stream,iov,iovcnt);HEAP32[pnum>>2]=num;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return e.errno}}function _getTempRet0(){return getTempRet0()}function _llvm_eh_typeid_for(type){return type}function _setTempRet0(val){setTempRet0(val)}function __isLeapYear(year){return year%4===0&&(year%100!==0||year%400===0)}function __arraySum(array,index){var sum=0;for(var i=0;i<=index;sum+=array[i++]){}return sum}var __MONTH_DAYS_LEAP=[31,29,31,30,31,30,31,31,30,31,30,31];var __MONTH_DAYS_REGULAR=[31,28,31,30,31,30,31,31,30,31,30,31];function __addDays(date,days){var newDate=new Date(date.getTime());while(days>0){var leap=__isLeapYear(newDate.getFullYear());var currentMonth=newDate.getMonth();var daysInCurrentMonth=(leap?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR)[currentMonth];if(days>daysInCurrentMonth-newDate.getDate()){days-=daysInCurrentMonth-newDate.getDate()+1;newDate.setDate(1);if(currentMonth<11){newDate.setMonth(currentMonth+1)}else{newDate.setMonth(0);newDate.setFullYear(newDate.getFullYear()+1)}}else{newDate.setDate(newDate.getDate()+days);return newDate}}return newDate}function _strftime(s,maxsize,format,tm){var tm_zone=HEAP32[tm+40>>2];var date={tm_sec:HEAP32[tm>>2],tm_min:HEAP32[tm+4>>2],tm_hour:HEAP32[tm+8>>2],tm_mday:HEAP32[tm+12>>2],tm_mon:HEAP32[tm+16>>2],tm_year:HEAP32[tm+20>>2],tm_wday:HEAP32[tm+24>>2],tm_yday:HEAP32[tm+28>>2],tm_isdst:HEAP32[tm+32>>2],tm_gmtoff:HEAP32[tm+36>>2],tm_zone:tm_zone?UTF8ToString(tm_zone):""};var pattern=UTF8ToString(format);var EXPANSION_RULES_1={"%c":"%a %b %d %H:%M:%S %Y","%D":"%m/%d/%y","%F":"%Y-%m-%d","%h":"%b","%r":"%I:%M:%S %p","%R":"%H:%M","%T":"%H:%M:%S","%x":"%m/%d/%y","%X":"%H:%M:%S","%Ec":"%c","%EC":"%C","%Ex":"%m/%d/%y","%EX":"%H:%M:%S","%Ey":"%y","%EY":"%Y","%Od":"%d","%Oe":"%e","%OH":"%H","%OI":"%I","%Om":"%m","%OM":"%M","%OS":"%S","%Ou":"%u","%OU":"%U","%OV":"%V","%Ow":"%w","%OW":"%W","%Oy":"%y"};for(var rule in EXPANSION_RULES_1){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_1[rule])}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];var MONTHS=["January","February","March","April","May","June","July","August","September","October","November","December"];function leadingSomething(value,digits,character){var str=typeof value==="number"?value.toString():value||"";while(str.length0?1:0}var compare;if((compare=sgn(date1.getFullYear()-date2.getFullYear()))===0){if((compare=sgn(date1.getMonth()-date2.getMonth()))===0){compare=sgn(date1.getDate()-date2.getDate())}}return compare}function getFirstWeekStartDate(janFourth){switch(janFourth.getDay()){case 0:return new Date(janFourth.getFullYear()-1,11,29);case 1:return janFourth;case 2:return new Date(janFourth.getFullYear(),0,3);case 3:return new Date(janFourth.getFullYear(),0,2);case 4:return new Date(janFourth.getFullYear(),0,1);case 5:return new Date(janFourth.getFullYear()-1,11,31);case 6:return new Date(janFourth.getFullYear()-1,11,30)}}function getWeekBasedYear(date){var thisDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);var janFourthThisYear=new Date(thisDate.getFullYear(),0,4);var janFourthNextYear=new Date(thisDate.getFullYear()+1,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);if(compareByDay(firstWeekStartThisYear,thisDate)<=0){if(compareByDay(firstWeekStartNextYear,thisDate)<=0){return thisDate.getFullYear()+1}else{return thisDate.getFullYear()}}else{return thisDate.getFullYear()-1}}var EXPANSION_RULES_2={"%a":function(date){return WEEKDAYS[date.tm_wday].substring(0,3)},"%A":function(date){return WEEKDAYS[date.tm_wday]},"%b":function(date){return MONTHS[date.tm_mon].substring(0,3)},"%B":function(date){return MONTHS[date.tm_mon]},"%C":function(date){var year=date.tm_year+1900;return leadingNulls(year/100|0,2)},"%d":function(date){return leadingNulls(date.tm_mday,2)},"%e":function(date){return leadingSomething(date.tm_mday,2," ")},"%g":function(date){return getWeekBasedYear(date).toString().substring(2)},"%G":function(date){return getWeekBasedYear(date)},"%H":function(date){return leadingNulls(date.tm_hour,2)},"%I":function(date){var twelveHour=date.tm_hour;if(twelveHour==0)twelveHour=12;else if(twelveHour>12)twelveHour-=12;return leadingNulls(twelveHour,2)},"%j":function(date){return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900)?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,date.tm_mon-1),3)},"%m":function(date){return leadingNulls(date.tm_mon+1,2)},"%M":function(date){return leadingNulls(date.tm_min,2)},"%n":function(){return"\n"},"%p":function(date){if(date.tm_hour>=0&&date.tm_hour<12){return"AM"}else{return"PM"}},"%S":function(date){return leadingNulls(date.tm_sec,2)},"%t":function(){return"\t"},"%u":function(date){return date.tm_wday||7},"%U":function(date){var janFirst=new Date(date.tm_year+1900,0,1);var firstSunday=janFirst.getDay()===0?janFirst:__addDays(janFirst,7-janFirst.getDay());var endDate=new Date(date.tm_year+1900,date.tm_mon,date.tm_mday);if(compareByDay(firstSunday,endDate)<0){var februaryFirstUntilEndMonth=__arraySum(__isLeapYear(endDate.getFullYear())?__MONTH_DAYS_LEAP:__MONTH_DAYS_REGULAR,endDate.getMonth()-1)-31;var firstSundayUntilEndJanuary=31-firstSunday.getDate();var days=firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate();return leadingNulls(Math.ceil(days/7),2)}return compareByDay(firstSunday,janFirst)===0?"01":"00"},"%V":function(date){var janFourthThisYear=new Date(date.tm_year+1900,0,4);var janFourthNextYear=new Date(date.tm_year+1901,0,4);var firstWeekStartThisYear=getFirstWeekStartDate(janFourthThisYear);var firstWeekStartNextYear=getFirstWeekStartDate(janFourthNextYear);var endDate=__addDays(new Date(date.tm_year+1900,0,1),date.tm_yday);if(compareByDay(endDate,firstWeekStartThisYear)<0){return"53"}if(compareByDay(firstWeekStartNextYear,endDate)<=0){return"01"}var daysDifference;if(firstWeekStartThisYear.getFullYear()=0;off=Math.abs(off)/60;off=off/60*100+off%60;return(ahead?"+":"-")+String("0000"+off).slice(-4)},"%Z":function(date){return date.tm_zone},"%%":function(){return"%"}};for(var rule in EXPANSION_RULES_2){if(pattern.includes(rule)){pattern=pattern.replace(new RegExp(rule,"g"),EXPANSION_RULES_2[rule](date))}}var bytes=intArrayFromString(pattern,false);if(bytes.length>maxsize){return 0}writeArrayToMemory(bytes,s);return bytes.length-1}function _strftime_l(s,maxsize,format,tm){return _strftime(s,maxsize,format,tm)}var FSNode=function(parent,name,mode,rdev){if(!parent){parent=this}this.parent=parent;this.mount=parent.mount;this.mounted=null;this.id=FS.nextInode++;this.name=name;this.mode=mode;this.node_ops={};this.stream_ops={};this.rdev=rdev};var readMode=292|73;var writeMode=146;Object.defineProperties(FSNode.prototype,{read:{get:function(){return(this.mode&readMode)===readMode},set:function(val){val?this.mode|=readMode:this.mode&=~readMode}},write:{get:function(){return(this.mode&writeMode)===writeMode},set:function(val){val?this.mode|=writeMode:this.mode&=~writeMode}},isFolder:{get:function(){return FS.isDir(this.mode)}},isDevice:{get:function(){return FS.isChrdev(this.mode)}}});FS.FSNode=FSNode;FS.staticInit();Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;InternalError=Module["InternalError"]=extendError(Error,"InternalError");embind_init_charCodes();BindingError=Module["BindingError"]=extendError(Error,"BindingError");init_ClassHandle();init_RegisteredPointer();init_embind();UnboundTypeError=Module["UnboundTypeError"]=extendError(Error,"UnboundTypeError");init_emval();function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}var asmLibraryArg={"z":___assert_fail,"n":___cxa_allocate_exception,"o":___cxa_begin_catch,"x":___cxa_end_catch,"b":___cxa_find_matching_catch_2,"h":___cxa_find_matching_catch_3,"Ka":___cxa_find_matching_catch_4,"p":___cxa_free_exception,"ba":___cxa_rethrow,"J":___cxa_throw,"va":___cxa_uncaught_exceptions,"c":___resumeException,"fa":___sys_fcntl64,"Aa":___sys_getdents64,"Da":___sys_ioctl,"ga":___sys_open,"za":___sys_stat64,"Qa":__embind_finalize_value_array,"Va":__embind_register_bigint,"Fa":__embind_register_bool,"u":__embind_register_class,"y":__embind_register_class_class_function,"s":__embind_register_class_class_property,"w":__embind_register_class_constructor,"i":__embind_register_class_function,"H":__embind_register_class_property,"R":__embind_register_constant,"Ea":__embind_register_emval,"Q":__embind_register_enum,"P":__embind_register_enum_value,"ha":__embind_register_float,"E":__embind_register_function,"M":__embind_register_integer,"K":__embind_register_memory_view,"t":__embind_register_smart_ptr,"ia":__embind_register_std_string,"_":__embind_register_std_wstring,"oa":__embind_register_value_array,"Y":__embind_register_value_array_element,"Ga":__embind_register_void,"N":__emval_as,"Ra":__emval_call,"X":__emval_call_void_method,"ea":__emval_decref,"W":__emval_get_method_caller,"Pa":__emval_get_property,"ja":__emval_incref,"Ta":__emval_new_array,"Oa":__emval_new_cstring,"La":__emval_new_object,"Na":__emval_run_destructors,"Sa":__emval_set_property,"B":__emval_take_value,"Ma":__emval_typeof,"wa":_abort,"aa":_emscripten_asm_const_int,"ta":_emscripten_memcpy_big,"Z":_emscripten_resize_heap,"xa":_environ_get,"ya":_environ_sizes_get,"U":_fd_close,"Ca":_fd_read,"Ua":_fd_seek,"Ba":_fd_write,"a":_getTempRet0,"Ha":invoke_di,"$":invoke_fi,"Ja":invoke_fifii,"ra":invoke_fii,"ma":invoke_fiii,"v":invoke_i,"g":invoke_ii,"Ia":invoke_iid,"V":invoke_iif,"la":invoke_iiff,"na":invoke_iifff,"e":invoke_iii,"j":invoke_iiii,"r":invoke_iiiii,"ca":invoke_iiiiid,"G":invoke_iiiiii,"C":invoke_iiiiiii,"L":invoke_iiiiiiii,"T":invoke_iiiiiiiiiiii,"l":invoke_v,"m":invoke_vi,"sa":invoke_vid,"I":invoke_vif,"f":invoke_vii,"qa":invoke_viif,"d":invoke_viii,"k":invoke_viiii,"ka":invoke_viiiiffiiii,"q":invoke_viiiii,"A":invoke_viiiiii,"F":invoke_viiiiiii,"da":invoke_viiiiiiiii,"O":invoke_viiiiiiiiii,"S":invoke_viiiiiiiiiiiiiii,"D":_llvm_eh_typeid_for,"pa":_setTempRet0,"ua":_strftime_l};var asm=createWasm();var ___wasm_call_ctors=Module["___wasm_call_ctors"]=function(){return(___wasm_call_ctors=Module["___wasm_call_ctors"]=Module["asm"]["Xa"]).apply(null,arguments)};var _malloc=Module["_malloc"]=function(){return(_malloc=Module["_malloc"]=Module["asm"]["Ya"]).apply(null,arguments)};var ___errno_location=Module["___errno_location"]=function(){return(___errno_location=Module["___errno_location"]=Module["asm"]["_a"]).apply(null,arguments)};var _free=Module["_free"]=function(){return(_free=Module["_free"]=Module["asm"]["$a"]).apply(null,arguments)};var ___getTypeName=Module["___getTypeName"]=function(){return(___getTypeName=Module["___getTypeName"]=Module["asm"]["ab"]).apply(null,arguments)};var ___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=function(){return(___embind_register_native_and_builtin_types=Module["___embind_register_native_and_builtin_types"]=Module["asm"]["bb"]).apply(null,arguments)};var stackSave=Module["stackSave"]=function(){return(stackSave=Module["stackSave"]=Module["asm"]["cb"]).apply(null,arguments)};var stackRestore=Module["stackRestore"]=function(){return(stackRestore=Module["stackRestore"]=Module["asm"]["db"]).apply(null,arguments)};var stackAlloc=Module["stackAlloc"]=function(){return(stackAlloc=Module["stackAlloc"]=Module["asm"]["eb"]).apply(null,arguments)};var _setThrew=Module["_setThrew"]=function(){return(_setThrew=Module["_setThrew"]=Module["asm"]["fb"]).apply(null,arguments)};var ___cxa_can_catch=Module["___cxa_can_catch"]=function(){return(___cxa_can_catch=Module["___cxa_can_catch"]=Module["asm"]["gb"]).apply(null,arguments)};var ___cxa_is_pointer_type=Module["___cxa_is_pointer_type"]=function(){return(___cxa_is_pointer_type=Module["___cxa_is_pointer_type"]=Module["asm"]["hb"]).apply(null,arguments)};var dynCall_viijii=Module["dynCall_viijii"]=function(){return(dynCall_viijii=Module["dynCall_viijii"]=Module["asm"]["ib"]).apply(null,arguments)};var dynCall_jiji=Module["dynCall_jiji"]=function(){return(dynCall_jiji=Module["dynCall_jiji"]=Module["asm"]["jb"]).apply(null,arguments)};var dynCall_iiiiij=Module["dynCall_iiiiij"]=function(){return(dynCall_iiiiij=Module["dynCall_iiiiij"]=Module["asm"]["kb"]).apply(null,arguments)};var dynCall_iiiiijj=Module["dynCall_iiiiijj"]=function(){return(dynCall_iiiiijj=Module["dynCall_iiiiijj"]=Module["asm"]["lb"]).apply(null,arguments)};var dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=function(){return(dynCall_iiiiiijj=Module["dynCall_iiiiiijj"]=Module["asm"]["mb"]).apply(null,arguments)};function invoke_ii(index,a1){var sp=stackSave();try{return wasmTable.get(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viii(index,a1,a2,a3){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_vii(index,a1,a2){var sp=stackSave();try{wasmTable.get(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiii(index,a1,a2,a3,a4){var sp=stackSave();try{return wasmTable.get(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iii(index,a1,a2){var sp=stackSave();try{return wasmTable.get(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiii(index,a1,a2,a3,a4){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiii(index,a1,a2,a3){var sp=stackSave();try{return wasmTable.get(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiii(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return wasmTable.get(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_vi(index,a1){var sp=stackSave();try{wasmTable.get(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_i(index){var sp=stackSave();try{return wasmTable.get(index)()}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_v(index){var sp=stackSave();try{wasmTable.get(index)()}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_vif(index,a1,a2){var sp=stackSave();try{wasmTable.get(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_fifii(index,a1,a2,a3,a4){var sp=stackSave();try{return wasmTable.get(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iif(index,a1,a2){var sp=stackSave();try{return wasmTable.get(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iid(index,a1,a2){var sp=stackSave();try{return wasmTable.get(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_fi(index,a1){var sp=stackSave();try{return wasmTable.get(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_di(index,a1){var sp=stackSave();try{return wasmTable.get(index)(a1)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iifff(index,a1,a2,a3,a4){var sp=stackSave();try{return wasmTable.get(index)(a1,a2,a3,a4)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiiii(index,a1,a2,a3,a4,a5,a6){var sp=stackSave();try{return wasmTable.get(index)(a1,a2,a3,a4,a5,a6)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiiiii(index,a1,a2,a3,a4,a5,a6,a7){var sp=stackSave();try{return wasmTable.get(index)(a1,a2,a3,a4,a5,a6,a7)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_fiii(index,a1,a2,a3){var sp=stackSave();try{return wasmTable.get(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiff(index,a1,a2,a3){var sp=stackSave();try{return wasmTable.get(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiiiffiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_vid(index,a1,a2){var sp=stackSave();try{wasmTable.get(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_fii(index,a1,a2){var sp=stackSave();try{return wasmTable.get(index)(a1,a2)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viif(index,a1,a2,a3){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiid(index,a1,a2,a3,a4,a5){var sp=stackSave();try{return wasmTable.get(index)(a1,a2,a3,a4,a5)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_iiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11){var sp=stackSave();try{return wasmTable.get(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}function invoke_viiiiiiiiiiiiiii(index,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15){var sp=stackSave();try{wasmTable.get(index)(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15)}catch(e){stackRestore(sp);if(e!==e+0&&e!=="longjmp")throw e;_setThrew(1,0)}}Module["addRunDependency"]=addRunDependency;Module["removeRunDependency"]=removeRunDependency;Module["FS_createPath"]=FS.createPath;Module["FS_createDataFile"]=FS.createDataFile;Module["FS_createPreloadedFile"]=FS.createPreloadedFile;Module["FS_createLazyFile"]=FS.createLazyFile;Module["FS_createDevice"]=FS.createDevice;Module["FS_unlink"]=FS.unlink;var calledRun;function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}dependenciesFulfilled=function runCaller(){if(!calledRun)run();if(!calledRun)dependenciesFulfilled=runCaller};function run(args){args=args||arguments_;if(runDependencies>0){return}preRun();if(runDependencies>0){return}function doRun(){if(calledRun)return;calledRun=true;Module["calledRun"]=true;if(ABORT)return;initRuntime();readyPromiseResolve(Module);if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout(function(){setTimeout(function(){Module["setStatus"]("")},1);doRun()},1)}else{doRun()}}Module["run"]=run;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}run();var postRegistrations=[];function onModuleReady(callback){postRegistrations.push(callback)}Module.onRuntimeInitialized=function(){for(var callback of postRegistrations){callback()}};(function(){var nodeFs;var nodePath;var nodeProcess;var pathSep;var wasmPathSep="/";var ENVIRONMENT_IS_WEB;var ENVIRONMENT_IS_NODE;var PATH_LIST_SEPARATOR=";";var callId=0;var MAX_CALL_ID=99999;function removeDuplicates(array){var seen={};return array.filter(function(item){return seen.hasOwnProperty(item)?false:seen[item]=true})}function createFilePath(fileName,filePath,sep=pathSep){var pathSlash=filePath.endsWith(sep);var fileSlash=fileName.startsWith(sep);var path;if(pathSlash||fileSlash){if(pathSlash&&fileSlash){path=filePath.substring(0,filePath.length-1)+fileName}else{path=filePath+fileName}}else{path=filePath+sep+fileName}return path}function fetchXml(fileName,searchPaths){var i=0;function fetchHandler(){var filePath=createFilePath(fileName,searchPaths[i++]);return fetch(filePath).then(function(response){if(response.status===200){return response.text().then(function(data){var url=new URL(response.url);var filePath=url.pathname.substring(1);filePath=filePath.replace(new RegExp(pathSep,"g"),wasmPathSep);return{data:data,filePath:filePath,fullPath:url.origin+url.pathname}})}else if(i/g;var matches=file.matchAll(includeRegex);var includes=[];for(var match of matches){includes.push(match[1])}return includes}function loadFile(fileToLoad,searchPaths){var promise;if(ENVIRONMENT_IS_WEB){promise=fetchXml(fileToLoad,searchPaths)}else if(ENVIRONMENT_IS_NODE){promise=loadXml(fileToLoad,searchPaths)}else{throw new Error("Unknown environment!")}return promise}function trackPath(path,filesUploaded,isFile=false){if(isFile){if(!filesUploaded.files){filesUploaded.files=[]}filesUploaded.files.push(path)}else{if(!filesUploaded.folders){filesUploaded.folders=[]}filesUploaded.folders.splice(0,0,path)}}function createInWasm(file,data,filesUploaded,wasmRootFolder,isFile=true){var folders;if(isFile){folders=file.substring(1,file.lastIndexOf(wasmPathSep)).split(wasmPathSep)}else{folders=file.substring(wasmRootFolder.length).split(wasmPathSep)}var folder=wasmRootFolder;for(var i=1;i-1?pos+1:0);var element=document.createElement("a");element.setAttribute("href","data:text/plain;charset=utf-8,"+encodeURIComponent(content));element.setAttribute("download",fileName);element.style.display="none";document.body.appendChild(element);element.click();document.body.removeChild(element)}}onModuleReady(function(){ENVIRONMENT_IS_WEB=typeof window==="object";ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof process.versions==="object"&&typeof process.versions.node==="string";if(ENVIRONMENT_IS_WEB){pathSep="/"}if(ENVIRONMENT_IS_NODE){nodeFs=require("fs");nodePath=require("path");nodeProcess=require("process");pathSep=nodePath.sep}function _readFromXmlString(doc,str,searchPath,readOptions,filesLoaded=[],initialFilePath=""){var wasmRootFolder="/readFromXml"+callId++%MAX_CALL_ID;var searchPaths=prepareSearchPaths(searchPath);try{FS.mkdir(wasmRootFolder)}catch(e){throw new Error("Failed to create folder in WASM FS.")}var includes=[];if(!readOptions||readOptions.readXIncludes){includes=getIncludes(str)}var filesUploaded={files:[],folders:[]};var wasmCwd=getWasmCwd(wasmRootFolder);var initialFileName=wasmCwd+"/ChosenToHopefullyNotClashWithAnyOtherFile123";if(initialFilePath){var sep=pathSep==="\\"?"\\\\":pathSep;initialFileName=initialFilePath.replace(new RegExp(sep,"g"),wasmPathSep);initialFileName=createFilePath(initialFileName,wasmRootFolder,wasmPathSep);createInWasm(wasmCwd,null,filesUploaded,wasmRootFolder,false)}createInWasm(initialFileName,str,filesUploaded,wasmRootFolder);function loadFiles(filesLoadedList,fileList,pathsList){var promises=[Promise.resolve()];for(var fileToLoad of fileList){var filesLoadedCopy=filesLoadedList.slice();var searchPathsCopy=pathsList.slice();var promise=loadFile(fileToLoad,searchPathsCopy).then(function(result){if(filesLoadedCopy.includes(result.fullPath)){throw new Error("Cycle detected!\n"+filesLoadedCopy.join("\n-> ")+"\n-> "+result.fullPath)}filesLoadedCopy.push(result.fullPath);var pos=result.fullPath.lastIndexOf(pathSep);var path=result.fullPath.substring(0,pos>-1?pos:0);if(!searchPathsCopy.includes(path)){searchPathsCopy.splice(0,0,path)}var includes=getIncludes(result.data);var wasmPath=createFilePath(result.filePath,wasmRootFolder,wasmPathSep);if(!filesUploaded.files.includes(wasmPath)){createInWasm(wasmPath,result.data,filesUploaded,wasmRootFolder)}return loadFiles(filesLoadedCopy,includes,searchPathsCopy)});promises.push(promise)}return Promise.all(promises)}return loadFiles(filesLoaded,includes,searchPaths).then(function(){var wasmSearchPath=getWasmSearchPath(searchPath,wasmRootFolder);FS.chdir(wasmCwd);try{var searchPathEnv=Module.getEnviron(Module.MATERIALX_SEARCH_PATH_ENV_VAR);if(searchPathEnv){var wasmSearchPathEnv=makeWasmAbsolute(searchPathEnv,wasmRootFolder);Module.setEnviron(Module.MATERIALX_SEARCH_PATH_ENV_VAR,wasmSearchPathEnv)}Module._readFromXmlFile(doc,initialFileName,wasmSearchPath,readOptions);if(searchPathEnv){Module.setEnviron(Module.MATERIALX_SEARCH_PATH_ENV_VAR,searchPathEnv)}}catch(errPtr){throw new Error("Failed to read MaterialX files from WASM FS: "+Module.getExceptionMessage(errPtr))}try{for(var file of filesUploaded.files){FS.unlink(file)}FS.chdir("/");for(var folder of filesUploaded.folders){FS.rmdir(folder)}FS.rmdir(wasmRootFolder)}catch(e){throw new Error("Failed to delete temporary files from WASM FS.")}})}Module.readFromXmlString=function(doc,str,searchPath="",readOptions=null){if(arguments.length<2||arguments.length>4){throw new Error("Function readFromXmlString called with an invalid number of arguments ("+arguments.length+") - expects 2 to 4!")}return _readFromXmlString(doc,str,searchPath,readOptions)};Module.readFromXmlFile=function(doc,fileName,searchPath="",readOptions=null){if(arguments.length<2||arguments.length>4){throw new Error("Function readFromXmlFile called with an invalid number of arguments ("+arguments.length+") - expects 2 to 4!")}var searchPaths=prepareSearchPaths(searchPath);return loadFile(fileName,searchPaths).then(function(result){var filesLoaded=[result.fullPath];var pos=result.fullPath.lastIndexOf(pathSep);var path=result.fullPath.substring(0,pos>-1?pos:0);searchPath=searchPath.concat(PATH_LIST_SEPARATOR,path);return _readFromXmlString(doc,result.data,searchPath,readOptions,filesLoaded,result.filePath)})};Module.writeToXmlFile=function(doc,fileName,writeOptions=null){if(arguments.length<2||arguments.length>3){throw new Error("Function writeToXmlFile called with an invalid number of arguments ("+arguments.length+") - expects 2 to 3!")}var file=Module.writeToXmlString(doc,writeOptions);storeFileToDisk(fileName,file)};Module.exportToXmlFile=function(doc,fileName,exportOptions=null){if(arguments.length<2||arguments.length>3){throw new Error("Function exportToXmlFile called with an invalid number of arguments ("+arguments.length+") - expects 2 to 3!")}var file=Module.exportToXmlString(doc,exportOptions);storeFileToDisk(fileName,file)}})})();
+
+
+ return MaterialX.ready
+}
+);
+})();
+if (typeof exports === 'object' && typeof module === 'object')
+ module.exports = MaterialX;
+else if (typeof define === 'function' && define['amd'])
+ define([], function() { return MaterialX; });
+else if (typeof exports === 'object')
+ exports["MaterialX"] = MaterialX;
diff --git a/JsMaterialXGenShader.wasm b/JsMaterialXGenShader.wasm
new file mode 100644
index 0000000000..8b23199220
Binary files /dev/null and b/JsMaterialXGenShader.wasm differ
diff --git a/Lights/envmap_shader.mtlx b/Lights/envmap_shader.mtlx
new file mode 100644
index 0000000000..8c9c6ec0f5
--- /dev/null
+++ b/Lights/envmap_shader.mtlx
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Lights/goegap.hdr b/Lights/goegap.hdr
new file mode 100644
index 0000000000..9708bd5583
Binary files /dev/null and b/Lights/goegap.hdr differ
diff --git a/Lights/goegap_split.hdr b/Lights/goegap_split.hdr
new file mode 100644
index 0000000000..6560d218ef
Binary files /dev/null and b/Lights/goegap_split.hdr differ
diff --git a/Lights/goegap_split.mtlx b/Lights/goegap_split.mtlx
new file mode 100644
index 0000000000..e046df5bf4
--- /dev/null
+++ b/Lights/goegap_split.mtlx
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/Lights/irradiance/goegap.hdr b/Lights/irradiance/goegap.hdr
new file mode 100644
index 0000000000..6423883051
Binary files /dev/null and b/Lights/irradiance/goegap.hdr differ
diff --git a/Lights/irradiance/goegap_split.hdr b/Lights/irradiance/goegap_split.hdr
new file mode 100644
index 0000000000..86b785b085
Binary files /dev/null and b/Lights/irradiance/goegap_split.hdr differ
diff --git a/Lights/irradiance/san_giuseppe_bridge.hdr b/Lights/irradiance/san_giuseppe_bridge.hdr
new file mode 100644
index 0000000000..614813690f
Binary files /dev/null and b/Lights/irradiance/san_giuseppe_bridge.hdr differ
diff --git a/Lights/irradiance/san_giuseppe_bridge_split.hdr b/Lights/irradiance/san_giuseppe_bridge_split.hdr
new file mode 100644
index 0000000000..c5ddbd509e
Binary files /dev/null and b/Lights/irradiance/san_giuseppe_bridge_split.hdr differ
diff --git a/Lights/irradiance/table_mountain.hdr b/Lights/irradiance/table_mountain.hdr
new file mode 100644
index 0000000000..11ec06cdb1
Binary files /dev/null and b/Lights/irradiance/table_mountain.hdr differ
diff --git a/Lights/irradiance/table_mountain_split.hdr b/Lights/irradiance/table_mountain_split.hdr
new file mode 100644
index 0000000000..fa7d7d3a91
Binary files /dev/null and b/Lights/irradiance/table_mountain_split.hdr differ
diff --git a/Lights/san_giuseppe_bridge.hdr b/Lights/san_giuseppe_bridge.hdr
new file mode 100644
index 0000000000..b8ab76731b
Binary files /dev/null and b/Lights/san_giuseppe_bridge.hdr differ
diff --git a/Lights/san_giuseppe_bridge_split.hdr b/Lights/san_giuseppe_bridge_split.hdr
new file mode 100644
index 0000000000..2f1b71dced
Binary files /dev/null and b/Lights/san_giuseppe_bridge_split.hdr differ
diff --git a/Lights/san_giuseppe_bridge_split.mtlx b/Lights/san_giuseppe_bridge_split.mtlx
new file mode 100644
index 0000000000..570c7b4f70
--- /dev/null
+++ b/Lights/san_giuseppe_bridge_split.mtlx
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/Lights/table_mountain.hdr b/Lights/table_mountain.hdr
new file mode 100644
index 0000000000..9e34e9aa4a
Binary files /dev/null and b/Lights/table_mountain.hdr differ
diff --git a/Lights/table_mountain_split.hdr b/Lights/table_mountain_split.hdr
new file mode 100644
index 0000000000..5230ecfafc
Binary files /dev/null and b/Lights/table_mountain_split.hdr differ
diff --git a/Lights/table_mountain_split.mtlx b/Lights/table_mountain_split.mtlx
new file mode 100644
index 0000000000..b16050465f
--- /dev/null
+++ b/Lights/table_mountain_split.mtlx
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/Materials/Examples/GltfPbr/boombox/BoomBox_baseColor.png b/Materials/Examples/GltfPbr/boombox/BoomBox_baseColor.png
new file mode 100644
index 0000000000..992429df5b
Binary files /dev/null and b/Materials/Examples/GltfPbr/boombox/BoomBox_baseColor.png differ
diff --git a/Materials/Examples/GltfPbr/boombox/BoomBox_emissive.png b/Materials/Examples/GltfPbr/boombox/BoomBox_emissive.png
new file mode 100644
index 0000000000..315a499403
Binary files /dev/null and b/Materials/Examples/GltfPbr/boombox/BoomBox_emissive.png differ
diff --git a/Materials/Examples/GltfPbr/boombox/BoomBox_normal.png b/Materials/Examples/GltfPbr/boombox/BoomBox_normal.png
new file mode 100644
index 0000000000..19d7afbe7f
Binary files /dev/null and b/Materials/Examples/GltfPbr/boombox/BoomBox_normal.png differ
diff --git a/Materials/Examples/GltfPbr/boombox/BoomBox_occlusionRoughnessMetallic.png b/Materials/Examples/GltfPbr/boombox/BoomBox_occlusionRoughnessMetallic.png
new file mode 100644
index 0000000000..4370d6092f
Binary files /dev/null and b/Materials/Examples/GltfPbr/boombox/BoomBox_occlusionRoughnessMetallic.png differ
diff --git a/Materials/Examples/GltfPbr/gltf_pbr_boombox.mtlx b/Materials/Examples/GltfPbr/gltf_pbr_boombox.mtlx
new file mode 100644
index 0000000000..72d23a8c78
--- /dev/null
+++ b/Materials/Examples/GltfPbr/gltf_pbr_boombox.mtlx
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Materials/Examples/GltfPbr/gltf_pbr_carpaint.mtlx b/Materials/Examples/GltfPbr/gltf_pbr_carpaint.mtlx
new file mode 100644
index 0000000000..9abae490bf
--- /dev/null
+++ b/Materials/Examples/GltfPbr/gltf_pbr_carpaint.mtlx
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Materials/Examples/GltfPbr/gltf_pbr_default.mtlx b/Materials/Examples/GltfPbr/gltf_pbr_default.mtlx
new file mode 100644
index 0000000000..98c90ce0c2
--- /dev/null
+++ b/Materials/Examples/GltfPbr/gltf_pbr_default.mtlx
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Materials/Examples/GltfPbr/gltf_pbr_glass.mtlx b/Materials/Examples/GltfPbr/gltf_pbr_glass.mtlx
new file mode 100644
index 0000000000..24ff427fcf
--- /dev/null
+++ b/Materials/Examples/GltfPbr/gltf_pbr_glass.mtlx
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Materials/Examples/GltfPbr/gltf_pbr_gold.mtlx b/Materials/Examples/GltfPbr/gltf_pbr_gold.mtlx
new file mode 100644
index 0000000000..6c0d79d74b
--- /dev/null
+++ b/Materials/Examples/GltfPbr/gltf_pbr_gold.mtlx
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Materials/Examples/GltfPbr/gltf_pbr_plastic.mtlx b/Materials/Examples/GltfPbr/gltf_pbr_plastic.mtlx
new file mode 100644
index 0000000000..860bc5ce6c
--- /dev/null
+++ b/Materials/Examples/GltfPbr/gltf_pbr_plastic.mtlx
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Materials/Examples/StandardSurface/Car_Paint.glsl.frag b/Materials/Examples/StandardSurface/Car_Paint.glsl.frag
new file mode 100644
index 0000000000..56acbf2b97
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Car_Paint.glsl.frag
@@ -0,0 +1,1706 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform float SR_carpaint_base = 0.500000;
+uniform vec3 SR_carpaint_base_color = vec3(0.103779, 0.592120, 0.850649);
+uniform float SR_carpaint_diffuse_roughness = 0.000000;
+uniform float SR_carpaint_metalness = 0.000000;
+uniform float SR_carpaint_specular = 1.000000;
+uniform vec3 SR_carpaint_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_carpaint_specular_roughness = 0.400000;
+uniform float SR_carpaint_specular_IOR = 1.500000;
+uniform float SR_carpaint_specular_anisotropy = 0.500000;
+uniform float SR_carpaint_specular_rotation = 0.000000;
+uniform float SR_carpaint_transmission = 0.000000;
+uniform vec3 SR_carpaint_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_carpaint_transmission_depth = 0.000000;
+uniform vec3 SR_carpaint_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float SR_carpaint_transmission_scatter_anisotropy = 0.000000;
+uniform float SR_carpaint_transmission_dispersion = 0.000000;
+uniform float SR_carpaint_transmission_extra_roughness = 0.000000;
+uniform float SR_carpaint_subsurface = 0.000000;
+uniform vec3 SR_carpaint_subsurface_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_carpaint_subsurface_radius = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_carpaint_subsurface_scale = 1.000000;
+uniform float SR_carpaint_subsurface_anisotropy = 0.000000;
+uniform float SR_carpaint_sheen = 0.000000;
+uniform vec3 SR_carpaint_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_carpaint_sheen_roughness = 0.300000;
+uniform float SR_carpaint_coat = 1.000000;
+uniform vec3 SR_carpaint_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_carpaint_coat_roughness = 0.000000;
+uniform float SR_carpaint_coat_anisotropy = 0.000000;
+uniform float SR_carpaint_coat_rotation = 0.000000;
+uniform float SR_carpaint_coat_IOR = 1.500000;
+uniform float SR_carpaint_coat_affect_color = 0.000000;
+uniform float SR_carpaint_coat_affect_roughness = 0.000000;
+uniform float SR_carpaint_thin_film_thickness = 0.000000;
+uniform float SR_carpaint_thin_film_IOR = 1.500000;
+uniform float SR_carpaint_emission = 0.000000;
+uniform vec3 SR_carpaint_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_carpaint_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool SR_carpaint_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_carpaint_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(SR_carpaint_base, SR_carpaint_base_color, SR_carpaint_diffuse_roughness, SR_carpaint_metalness, SR_carpaint_specular, SR_carpaint_specular_color, SR_carpaint_specular_roughness, SR_carpaint_specular_IOR, SR_carpaint_specular_anisotropy, SR_carpaint_specular_rotation, SR_carpaint_transmission, SR_carpaint_transmission_color, SR_carpaint_transmission_depth, SR_carpaint_transmission_scatter, SR_carpaint_transmission_scatter_anisotropy, SR_carpaint_transmission_dispersion, SR_carpaint_transmission_extra_roughness, SR_carpaint_subsurface, SR_carpaint_subsurface_color, SR_carpaint_subsurface_radius, SR_carpaint_subsurface_scale, SR_carpaint_subsurface_anisotropy, SR_carpaint_sheen, SR_carpaint_sheen_color, SR_carpaint_sheen_roughness, SR_carpaint_coat, SR_carpaint_coat_color, SR_carpaint_coat_roughness, SR_carpaint_coat_anisotropy, SR_carpaint_coat_rotation, SR_carpaint_coat_IOR, geomprop_Nworld_out1, SR_carpaint_coat_affect_color, SR_carpaint_coat_affect_roughness, SR_carpaint_thin_film_thickness, SR_carpaint_thin_film_IOR, SR_carpaint_emission, SR_carpaint_emission_color, SR_carpaint_opacity, SR_carpaint_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_carpaint_out);
+ material Car_Paint_out = SR_carpaint_out;
+ out1 = vec4(Car_Paint_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/Car_Paint.glsl.vert b/Materials/Examples/StandardSurface/Car_Paint.glsl.vert
new file mode 100644
index 0000000000..6133cb8f5e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Car_Paint.glsl.vert
@@ -0,0 +1,28 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/Car_Paint.mdl b/Materials/Examples/StandardSurface/Car_Paint.mdl
new file mode 100644
index 0000000000..976ffa48b5
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Car_Paint.mdl
@@ -0,0 +1,175 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material Car_Paint
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ float SR_carpaint_base = 0.5,
+ color SR_carpaint_base_color = color(0.103779, 0.59212, 0.850649),
+ float SR_carpaint_diffuse_roughness = 0,
+ float SR_carpaint_metalness = 0,
+ float SR_carpaint_specular = 1,
+ color SR_carpaint_specular_color = color(1, 1, 1),
+ float SR_carpaint_specular_roughness = 0.4,
+ uniform float SR_carpaint_specular_IOR = 1.5,
+ float SR_carpaint_specular_anisotropy = 0.5,
+ float SR_carpaint_specular_rotation = 0,
+ float SR_carpaint_transmission = 0,
+ color SR_carpaint_transmission_color = color(1, 1, 1),
+ float SR_carpaint_transmission_depth = 0,
+ color SR_carpaint_transmission_scatter = color(0, 0, 0),
+ float SR_carpaint_transmission_scatter_anisotropy = 0,
+ float SR_carpaint_transmission_dispersion = 0,
+ float SR_carpaint_transmission_extra_roughness = 0,
+ float SR_carpaint_subsurface = 0,
+ color SR_carpaint_subsurface_color = color(1, 1, 1),
+ color SR_carpaint_subsurface_radius = color(1, 1, 1),
+ float SR_carpaint_subsurface_scale = 1,
+ float SR_carpaint_subsurface_anisotropy = 0,
+ float SR_carpaint_sheen = 0,
+ color SR_carpaint_sheen_color = color(1, 1, 1),
+ float SR_carpaint_sheen_roughness = 0.3,
+ float SR_carpaint_coat = 1,
+ color SR_carpaint_coat_color = color(1, 1, 1),
+ float SR_carpaint_coat_roughness = 0,
+ float SR_carpaint_coat_anisotropy = 0,
+ float SR_carpaint_coat_rotation = 0,
+ uniform float SR_carpaint_coat_IOR = 1.5,
+ float SR_carpaint_coat_affect_color = 0,
+ float SR_carpaint_coat_affect_roughness = 0,
+ float SR_carpaint_thin_film_thickness = 0,
+ float SR_carpaint_thin_film_IOR = 1.5,
+ float SR_carpaint_emission = 0,
+ color SR_carpaint_emission_color = color(1, 1, 1),
+ color SR_carpaint_opacity = color(1, 1, 1),
+ bool SR_carpaint_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ material SR_carpaint_out = NG_standard_surface_surfaceshader_100(SR_carpaint_base, SR_carpaint_base_color, SR_carpaint_diffuse_roughness, SR_carpaint_metalness, SR_carpaint_specular, SR_carpaint_specular_color, SR_carpaint_specular_roughness, SR_carpaint_specular_IOR, SR_carpaint_specular_anisotropy, SR_carpaint_specular_rotation, SR_carpaint_transmission, SR_carpaint_transmission_color, SR_carpaint_transmission_depth, SR_carpaint_transmission_scatter, SR_carpaint_transmission_scatter_anisotropy, SR_carpaint_transmission_dispersion, SR_carpaint_transmission_extra_roughness, SR_carpaint_subsurface, SR_carpaint_subsurface_color, SR_carpaint_subsurface_radius, SR_carpaint_subsurface_scale, SR_carpaint_subsurface_anisotropy, SR_carpaint_sheen, SR_carpaint_sheen_color, SR_carpaint_sheen_roughness, SR_carpaint_coat, SR_carpaint_coat_color, SR_carpaint_coat_roughness, SR_carpaint_coat_anisotropy, SR_carpaint_coat_rotation, SR_carpaint_coat_IOR, geomprop_Nworld_out1, SR_carpaint_coat_affect_color, SR_carpaint_coat_affect_roughness, SR_carpaint_thin_film_thickness, SR_carpaint_thin_film_IOR, SR_carpaint_emission, SR_carpaint_emission_color, SR_carpaint_opacity, SR_carpaint_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1);
+ material Car_Paint_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: SR_carpaint_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = Car_Paint_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/Car_Paint.msl.frag b/Materials/Examples/StandardSurface/Car_Paint.msl.frag
new file mode 100644
index 0000000000..7be7c9ea6c
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Car_Paint.msl.frag
@@ -0,0 +1,2324 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ float SR_carpaint_base;
+ vec3 SR_carpaint_base_color;
+ float SR_carpaint_diffuse_roughness;
+ float SR_carpaint_metalness;
+ float SR_carpaint_specular;
+ vec3 SR_carpaint_specular_color;
+ float SR_carpaint_specular_roughness;
+ float SR_carpaint_specular_IOR;
+ float SR_carpaint_specular_anisotropy;
+ float SR_carpaint_specular_rotation;
+ float SR_carpaint_transmission;
+ vec3 SR_carpaint_transmission_color;
+ float SR_carpaint_transmission_depth;
+ vec3 SR_carpaint_transmission_scatter;
+ float SR_carpaint_transmission_scatter_anisotropy;
+ float SR_carpaint_transmission_dispersion;
+ float SR_carpaint_transmission_extra_roughness;
+ float SR_carpaint_subsurface;
+ vec3 SR_carpaint_subsurface_color;
+ vec3 SR_carpaint_subsurface_radius;
+ float SR_carpaint_subsurface_scale;
+ float SR_carpaint_subsurface_anisotropy;
+ float SR_carpaint_sheen;
+ vec3 SR_carpaint_sheen_color;
+ float SR_carpaint_sheen_roughness;
+ float SR_carpaint_coat;
+ vec3 SR_carpaint_coat_color;
+ float SR_carpaint_coat_roughness;
+ float SR_carpaint_coat_anisotropy;
+ float SR_carpaint_coat_rotation;
+ float SR_carpaint_coat_IOR;
+ float SR_carpaint_coat_affect_color;
+ float SR_carpaint_coat_affect_roughness;
+ float SR_carpaint_thin_film_thickness;
+ float SR_carpaint_thin_film_IOR;
+ float SR_carpaint_emission;
+ vec3 SR_carpaint_emission_color;
+ vec3 SR_carpaint_opacity;
+ bool SR_carpaint_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+ , float SR_carpaint_base
+
+ , vec3 SR_carpaint_base_color
+
+ , float SR_carpaint_diffuse_roughness
+
+ , float SR_carpaint_metalness
+
+ , float SR_carpaint_specular
+
+ , vec3 SR_carpaint_specular_color
+
+ , float SR_carpaint_specular_roughness
+
+ , float SR_carpaint_specular_IOR
+
+ , float SR_carpaint_specular_anisotropy
+
+ , float SR_carpaint_specular_rotation
+
+ , float SR_carpaint_transmission
+
+ , vec3 SR_carpaint_transmission_color
+
+ , float SR_carpaint_transmission_depth
+
+ , vec3 SR_carpaint_transmission_scatter
+
+ , float SR_carpaint_transmission_scatter_anisotropy
+
+ , float SR_carpaint_transmission_dispersion
+
+ , float SR_carpaint_transmission_extra_roughness
+
+ , float SR_carpaint_subsurface
+
+ , vec3 SR_carpaint_subsurface_color
+
+ , vec3 SR_carpaint_subsurface_radius
+
+ , float SR_carpaint_subsurface_scale
+
+ , float SR_carpaint_subsurface_anisotropy
+
+ , float SR_carpaint_sheen
+
+ , vec3 SR_carpaint_sheen_color
+
+ , float SR_carpaint_sheen_roughness
+
+ , float SR_carpaint_coat
+
+ , vec3 SR_carpaint_coat_color
+
+ , float SR_carpaint_coat_roughness
+
+ , float SR_carpaint_coat_anisotropy
+
+ , float SR_carpaint_coat_rotation
+
+ , float SR_carpaint_coat_IOR
+
+ , float SR_carpaint_coat_affect_color
+
+ , float SR_carpaint_coat_affect_roughness
+
+ , float SR_carpaint_thin_film_thickness
+
+ , float SR_carpaint_thin_film_IOR
+
+ , float SR_carpaint_emission
+
+ , vec3 SR_carpaint_emission_color
+
+ , vec3 SR_carpaint_opacity
+
+ , bool SR_carpaint_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+ , SR_carpaint_base(SR_carpaint_base)
+
+ , SR_carpaint_base_color(SR_carpaint_base_color)
+
+ , SR_carpaint_diffuse_roughness(SR_carpaint_diffuse_roughness)
+
+ , SR_carpaint_metalness(SR_carpaint_metalness)
+
+ , SR_carpaint_specular(SR_carpaint_specular)
+
+ , SR_carpaint_specular_color(SR_carpaint_specular_color)
+
+ , SR_carpaint_specular_roughness(SR_carpaint_specular_roughness)
+
+ , SR_carpaint_specular_IOR(SR_carpaint_specular_IOR)
+
+ , SR_carpaint_specular_anisotropy(SR_carpaint_specular_anisotropy)
+
+ , SR_carpaint_specular_rotation(SR_carpaint_specular_rotation)
+
+ , SR_carpaint_transmission(SR_carpaint_transmission)
+
+ , SR_carpaint_transmission_color(SR_carpaint_transmission_color)
+
+ , SR_carpaint_transmission_depth(SR_carpaint_transmission_depth)
+
+ , SR_carpaint_transmission_scatter(SR_carpaint_transmission_scatter)
+
+ , SR_carpaint_transmission_scatter_anisotropy(SR_carpaint_transmission_scatter_anisotropy)
+
+ , SR_carpaint_transmission_dispersion(SR_carpaint_transmission_dispersion)
+
+ , SR_carpaint_transmission_extra_roughness(SR_carpaint_transmission_extra_roughness)
+
+ , SR_carpaint_subsurface(SR_carpaint_subsurface)
+
+ , SR_carpaint_subsurface_color(SR_carpaint_subsurface_color)
+
+ , SR_carpaint_subsurface_radius(SR_carpaint_subsurface_radius)
+
+ , SR_carpaint_subsurface_scale(SR_carpaint_subsurface_scale)
+
+ , SR_carpaint_subsurface_anisotropy(SR_carpaint_subsurface_anisotropy)
+
+ , SR_carpaint_sheen(SR_carpaint_sheen)
+
+ , SR_carpaint_sheen_color(SR_carpaint_sheen_color)
+
+ , SR_carpaint_sheen_roughness(SR_carpaint_sheen_roughness)
+
+ , SR_carpaint_coat(SR_carpaint_coat)
+
+ , SR_carpaint_coat_color(SR_carpaint_coat_color)
+
+ , SR_carpaint_coat_roughness(SR_carpaint_coat_roughness)
+
+ , SR_carpaint_coat_anisotropy(SR_carpaint_coat_anisotropy)
+
+ , SR_carpaint_coat_rotation(SR_carpaint_coat_rotation)
+
+ , SR_carpaint_coat_IOR(SR_carpaint_coat_IOR)
+
+ , SR_carpaint_coat_affect_color(SR_carpaint_coat_affect_color)
+
+ , SR_carpaint_coat_affect_roughness(SR_carpaint_coat_affect_roughness)
+
+ , SR_carpaint_thin_film_thickness(SR_carpaint_thin_film_thickness)
+
+ , SR_carpaint_thin_film_IOR(SR_carpaint_thin_film_IOR)
+
+ , SR_carpaint_emission(SR_carpaint_emission)
+
+ , SR_carpaint_emission_color(SR_carpaint_emission_color)
+
+ , SR_carpaint_opacity(SR_carpaint_opacity)
+
+ , SR_carpaint_thin_walled(SR_carpaint_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+ float SR_carpaint_base;
+
+
+ vec3 SR_carpaint_base_color;
+
+
+ float SR_carpaint_diffuse_roughness;
+
+
+ float SR_carpaint_metalness;
+
+
+ float SR_carpaint_specular;
+
+
+ vec3 SR_carpaint_specular_color;
+
+
+ float SR_carpaint_specular_roughness;
+
+
+ float SR_carpaint_specular_IOR;
+
+
+ float SR_carpaint_specular_anisotropy;
+
+
+ float SR_carpaint_specular_rotation;
+
+
+ float SR_carpaint_transmission;
+
+
+ vec3 SR_carpaint_transmission_color;
+
+
+ float SR_carpaint_transmission_depth;
+
+
+ vec3 SR_carpaint_transmission_scatter;
+
+
+ float SR_carpaint_transmission_scatter_anisotropy;
+
+
+ float SR_carpaint_transmission_dispersion;
+
+
+ float SR_carpaint_transmission_extra_roughness;
+
+
+ float SR_carpaint_subsurface;
+
+
+ vec3 SR_carpaint_subsurface_color;
+
+
+ vec3 SR_carpaint_subsurface_radius;
+
+
+ float SR_carpaint_subsurface_scale;
+
+
+ float SR_carpaint_subsurface_anisotropy;
+
+
+ float SR_carpaint_sheen;
+
+
+ vec3 SR_carpaint_sheen_color;
+
+
+ float SR_carpaint_sheen_roughness;
+
+
+ float SR_carpaint_coat;
+
+
+ vec3 SR_carpaint_coat_color;
+
+
+ float SR_carpaint_coat_roughness;
+
+
+ float SR_carpaint_coat_anisotropy;
+
+
+ float SR_carpaint_coat_rotation;
+
+
+ float SR_carpaint_coat_IOR;
+
+
+ float SR_carpaint_coat_affect_color;
+
+
+ float SR_carpaint_coat_affect_roughness;
+
+
+ float SR_carpaint_thin_film_thickness;
+
+
+ float SR_carpaint_thin_film_IOR;
+
+
+ float SR_carpaint_emission;
+
+
+ vec3 SR_carpaint_emission_color;
+
+
+ vec3 SR_carpaint_opacity;
+
+
+ bool SR_carpaint_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_carpaint_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(SR_carpaint_base, SR_carpaint_base_color, SR_carpaint_diffuse_roughness, SR_carpaint_metalness, SR_carpaint_specular, SR_carpaint_specular_color, SR_carpaint_specular_roughness, SR_carpaint_specular_IOR, SR_carpaint_specular_anisotropy, SR_carpaint_specular_rotation, SR_carpaint_transmission, SR_carpaint_transmission_color, SR_carpaint_transmission_depth, SR_carpaint_transmission_scatter, SR_carpaint_transmission_scatter_anisotropy, SR_carpaint_transmission_dispersion, SR_carpaint_transmission_extra_roughness, SR_carpaint_subsurface, SR_carpaint_subsurface_color, SR_carpaint_subsurface_radius, SR_carpaint_subsurface_scale, SR_carpaint_subsurface_anisotropy, SR_carpaint_sheen, SR_carpaint_sheen_color, SR_carpaint_sheen_roughness, SR_carpaint_coat, SR_carpaint_coat_color, SR_carpaint_coat_roughness, SR_carpaint_coat_anisotropy, SR_carpaint_coat_rotation, SR_carpaint_coat_IOR, geomprop_Nworld_out1, SR_carpaint_coat_affect_color, SR_carpaint_coat_affect_roughness, SR_carpaint_thin_film_thickness, SR_carpaint_thin_film_IOR, SR_carpaint_emission, SR_carpaint_emission_color, SR_carpaint_opacity, SR_carpaint_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_carpaint_out);
+ material Car_Paint_out = SR_carpaint_out;
+ out1 = float4(Car_Paint_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(0)]], sampler u_envRadiance_sampler [[sampler(0)]]
+, texture2d u_envIrradiance_tex [[texture(1)]], sampler u_envIrradiance_sampler [[sampler(1)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+ , u_pub.SR_carpaint_base
+ , u_pub.SR_carpaint_base_color
+ , u_pub.SR_carpaint_diffuse_roughness
+ , u_pub.SR_carpaint_metalness
+ , u_pub.SR_carpaint_specular
+ , u_pub.SR_carpaint_specular_color
+ , u_pub.SR_carpaint_specular_roughness
+ , u_pub.SR_carpaint_specular_IOR
+ , u_pub.SR_carpaint_specular_anisotropy
+ , u_pub.SR_carpaint_specular_rotation
+ , u_pub.SR_carpaint_transmission
+ , u_pub.SR_carpaint_transmission_color
+ , u_pub.SR_carpaint_transmission_depth
+ , u_pub.SR_carpaint_transmission_scatter
+ , u_pub.SR_carpaint_transmission_scatter_anisotropy
+ , u_pub.SR_carpaint_transmission_dispersion
+ , u_pub.SR_carpaint_transmission_extra_roughness
+ , u_pub.SR_carpaint_subsurface
+ , u_pub.SR_carpaint_subsurface_color
+ , u_pub.SR_carpaint_subsurface_radius
+ , u_pub.SR_carpaint_subsurface_scale
+ , u_pub.SR_carpaint_subsurface_anisotropy
+ , u_pub.SR_carpaint_sheen
+ , u_pub.SR_carpaint_sheen_color
+ , u_pub.SR_carpaint_sheen_roughness
+ , u_pub.SR_carpaint_coat
+ , u_pub.SR_carpaint_coat_color
+ , u_pub.SR_carpaint_coat_roughness
+ , u_pub.SR_carpaint_coat_anisotropy
+ , u_pub.SR_carpaint_coat_rotation
+ , u_pub.SR_carpaint_coat_IOR
+ , u_pub.SR_carpaint_coat_affect_color
+ , u_pub.SR_carpaint_coat_affect_roughness
+ , u_pub.SR_carpaint_thin_film_thickness
+ , u_pub.SR_carpaint_thin_film_IOR
+ , u_pub.SR_carpaint_emission
+ , u_pub.SR_carpaint_emission_color
+ , u_pub.SR_carpaint_opacity
+ , u_pub.SR_carpaint_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/Car_Paint.msl.vert b/Materials/Examples/StandardSurface/Car_Paint.msl.vert
new file mode 100644
index 0000000000..94cfee4565
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Car_Paint.msl.vert
@@ -0,0 +1,110 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'SR_carpaint'. Function already called in this scope.
+ // Omitted node 'Car_Paint'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(3) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Car_Paint.osl b/Materials/Examples/StandardSurface/Car_Paint.osl
new file mode 100644
index 0000000000..f036f0c2d3
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Car_Paint.osl
@@ -0,0 +1,637 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader Car_Paint
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "Car_Paint"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_base = 0.5
+ [[
+ string widget = "number"
+ ]],
+ color SR_carpaint_base_color = color(0.103779, 0.59212, 0.850649),
+ float SR_carpaint_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_metalness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_carpaint_specular_color = color(1, 1, 1),
+ float SR_carpaint_specular_roughness = 0.4
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_specular_anisotropy = 0.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_carpaint_transmission_color = color(1, 1, 1),
+ float SR_carpaint_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_carpaint_transmission_scatter = color(0, 0, 0),
+ float SR_carpaint_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_carpaint_subsurface_color = color(1, 1, 1),
+ color SR_carpaint_subsurface_radius = color(1, 1, 1),
+ float SR_carpaint_subsurface_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_carpaint_sheen_color = color(1, 1, 1),
+ float SR_carpaint_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_coat = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_carpaint_coat_color = color(1, 1, 1),
+ float SR_carpaint_coat_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_carpaint_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_carpaint_emission_color = color(1, 1, 1),
+ color SR_carpaint_opacity = color(1, 1, 1),
+ int SR_carpaint_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ surfaceshader SR_carpaint_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(SR_carpaint_base, SR_carpaint_base_color, SR_carpaint_diffuse_roughness, SR_carpaint_metalness, SR_carpaint_specular, SR_carpaint_specular_color, SR_carpaint_specular_roughness, SR_carpaint_specular_IOR, SR_carpaint_specular_anisotropy, SR_carpaint_specular_rotation, SR_carpaint_transmission, SR_carpaint_transmission_color, SR_carpaint_transmission_depth, SR_carpaint_transmission_scatter, SR_carpaint_transmission_scatter_anisotropy, SR_carpaint_transmission_dispersion, SR_carpaint_transmission_extra_roughness, SR_carpaint_subsurface, SR_carpaint_subsurface_color, SR_carpaint_subsurface_radius, SR_carpaint_subsurface_scale, SR_carpaint_subsurface_anisotropy, SR_carpaint_sheen, SR_carpaint_sheen_color, SR_carpaint_sheen_roughness, SR_carpaint_coat, SR_carpaint_coat_color, SR_carpaint_coat_roughness, SR_carpaint_coat_anisotropy, SR_carpaint_coat_rotation, SR_carpaint_coat_IOR, geomprop_Nworld_out1, SR_carpaint_coat_affect_color, SR_carpaint_coat_affect_roughness, SR_carpaint_thin_film_thickness, SR_carpaint_thin_film_IOR, SR_carpaint_emission, SR_carpaint_emission_color, SR_carpaint_opacity, SR_carpaint_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_carpaint_out);
+ MATERIAL Car_Paint_out = mx_surfacematerial(SR_carpaint_out, displacementshader1);
+ out = Car_Paint_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Chrome.glsl.frag b/Materials/Examples/StandardSurface/Chrome.glsl.frag
new file mode 100644
index 0000000000..0d044534f9
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Chrome.glsl.frag
@@ -0,0 +1,1706 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform float SR_chrome_base = 1.000000;
+uniform vec3 SR_chrome_base_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_chrome_diffuse_roughness = 0.000000;
+uniform float SR_chrome_metalness = 1.000000;
+uniform float SR_chrome_specular = 1.000000;
+uniform vec3 SR_chrome_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_chrome_specular_roughness = 0.000000;
+uniform float SR_chrome_specular_IOR = 1.500000;
+uniform float SR_chrome_specular_anisotropy = 0.000000;
+uniform float SR_chrome_specular_rotation = 0.000000;
+uniform float SR_chrome_transmission = 0.000000;
+uniform vec3 SR_chrome_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_chrome_transmission_depth = 0.000000;
+uniform vec3 SR_chrome_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float SR_chrome_transmission_scatter_anisotropy = 0.000000;
+uniform float SR_chrome_transmission_dispersion = 0.000000;
+uniform float SR_chrome_transmission_extra_roughness = 0.000000;
+uniform float SR_chrome_subsurface = 0.000000;
+uniform vec3 SR_chrome_subsurface_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_chrome_subsurface_radius = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_chrome_subsurface_scale = 1.000000;
+uniform float SR_chrome_subsurface_anisotropy = 0.000000;
+uniform float SR_chrome_sheen = 0.000000;
+uniform vec3 SR_chrome_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_chrome_sheen_roughness = 0.300000;
+uniform float SR_chrome_coat = 0.000000;
+uniform vec3 SR_chrome_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_chrome_coat_roughness = 0.100000;
+uniform float SR_chrome_coat_anisotropy = 0.000000;
+uniform float SR_chrome_coat_rotation = 0.000000;
+uniform float SR_chrome_coat_IOR = 1.500000;
+uniform float SR_chrome_coat_affect_color = 0.000000;
+uniform float SR_chrome_coat_affect_roughness = 0.000000;
+uniform float SR_chrome_thin_film_thickness = 0.000000;
+uniform float SR_chrome_thin_film_IOR = 1.500000;
+uniform float SR_chrome_emission = 0.000000;
+uniform vec3 SR_chrome_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_chrome_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool SR_chrome_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_chrome_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(SR_chrome_base, SR_chrome_base_color, SR_chrome_diffuse_roughness, SR_chrome_metalness, SR_chrome_specular, SR_chrome_specular_color, SR_chrome_specular_roughness, SR_chrome_specular_IOR, SR_chrome_specular_anisotropy, SR_chrome_specular_rotation, SR_chrome_transmission, SR_chrome_transmission_color, SR_chrome_transmission_depth, SR_chrome_transmission_scatter, SR_chrome_transmission_scatter_anisotropy, SR_chrome_transmission_dispersion, SR_chrome_transmission_extra_roughness, SR_chrome_subsurface, SR_chrome_subsurface_color, SR_chrome_subsurface_radius, SR_chrome_subsurface_scale, SR_chrome_subsurface_anisotropy, SR_chrome_sheen, SR_chrome_sheen_color, SR_chrome_sheen_roughness, SR_chrome_coat, SR_chrome_coat_color, SR_chrome_coat_roughness, SR_chrome_coat_anisotropy, SR_chrome_coat_rotation, SR_chrome_coat_IOR, geomprop_Nworld_out1, SR_chrome_coat_affect_color, SR_chrome_coat_affect_roughness, SR_chrome_thin_film_thickness, SR_chrome_thin_film_IOR, SR_chrome_emission, SR_chrome_emission_color, SR_chrome_opacity, SR_chrome_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_chrome_out);
+ material Chrome_out = SR_chrome_out;
+ out1 = vec4(Chrome_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/Chrome.glsl.vert b/Materials/Examples/StandardSurface/Chrome.glsl.vert
new file mode 100644
index 0000000000..6133cb8f5e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Chrome.glsl.vert
@@ -0,0 +1,28 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/Chrome.mdl b/Materials/Examples/StandardSurface/Chrome.mdl
new file mode 100644
index 0000000000..cf1158541f
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Chrome.mdl
@@ -0,0 +1,175 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material Chrome
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ float SR_chrome_base = 1,
+ color SR_chrome_base_color = color(1, 1, 1),
+ float SR_chrome_diffuse_roughness = 0,
+ float SR_chrome_metalness = 1,
+ float SR_chrome_specular = 1,
+ color SR_chrome_specular_color = color(1, 1, 1),
+ float SR_chrome_specular_roughness = 0,
+ uniform float SR_chrome_specular_IOR = 1.5,
+ float SR_chrome_specular_anisotropy = 0,
+ float SR_chrome_specular_rotation = 0,
+ float SR_chrome_transmission = 0,
+ color SR_chrome_transmission_color = color(1, 1, 1),
+ float SR_chrome_transmission_depth = 0,
+ color SR_chrome_transmission_scatter = color(0, 0, 0),
+ float SR_chrome_transmission_scatter_anisotropy = 0,
+ float SR_chrome_transmission_dispersion = 0,
+ float SR_chrome_transmission_extra_roughness = 0,
+ float SR_chrome_subsurface = 0,
+ color SR_chrome_subsurface_color = color(1, 1, 1),
+ color SR_chrome_subsurface_radius = color(1, 1, 1),
+ float SR_chrome_subsurface_scale = 1,
+ float SR_chrome_subsurface_anisotropy = 0,
+ float SR_chrome_sheen = 0,
+ color SR_chrome_sheen_color = color(1, 1, 1),
+ float SR_chrome_sheen_roughness = 0.3,
+ float SR_chrome_coat = 0,
+ color SR_chrome_coat_color = color(1, 1, 1),
+ float SR_chrome_coat_roughness = 0.1,
+ float SR_chrome_coat_anisotropy = 0,
+ float SR_chrome_coat_rotation = 0,
+ uniform float SR_chrome_coat_IOR = 1.5,
+ float SR_chrome_coat_affect_color = 0,
+ float SR_chrome_coat_affect_roughness = 0,
+ float SR_chrome_thin_film_thickness = 0,
+ float SR_chrome_thin_film_IOR = 1.5,
+ float SR_chrome_emission = 0,
+ color SR_chrome_emission_color = color(1, 1, 1),
+ color SR_chrome_opacity = color(1, 1, 1),
+ bool SR_chrome_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ material SR_chrome_out = NG_standard_surface_surfaceshader_100(SR_chrome_base, SR_chrome_base_color, SR_chrome_diffuse_roughness, SR_chrome_metalness, SR_chrome_specular, SR_chrome_specular_color, SR_chrome_specular_roughness, SR_chrome_specular_IOR, SR_chrome_specular_anisotropy, SR_chrome_specular_rotation, SR_chrome_transmission, SR_chrome_transmission_color, SR_chrome_transmission_depth, SR_chrome_transmission_scatter, SR_chrome_transmission_scatter_anisotropy, SR_chrome_transmission_dispersion, SR_chrome_transmission_extra_roughness, SR_chrome_subsurface, SR_chrome_subsurface_color, SR_chrome_subsurface_radius, SR_chrome_subsurface_scale, SR_chrome_subsurface_anisotropy, SR_chrome_sheen, SR_chrome_sheen_color, SR_chrome_sheen_roughness, SR_chrome_coat, SR_chrome_coat_color, SR_chrome_coat_roughness, SR_chrome_coat_anisotropy, SR_chrome_coat_rotation, SR_chrome_coat_IOR, geomprop_Nworld_out1, SR_chrome_coat_affect_color, SR_chrome_coat_affect_roughness, SR_chrome_thin_film_thickness, SR_chrome_thin_film_IOR, SR_chrome_emission, SR_chrome_emission_color, SR_chrome_opacity, SR_chrome_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1);
+ material Chrome_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: SR_chrome_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = Chrome_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/Chrome.msl.frag b/Materials/Examples/StandardSurface/Chrome.msl.frag
new file mode 100644
index 0000000000..6da08e6188
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Chrome.msl.frag
@@ -0,0 +1,2324 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ float SR_chrome_base;
+ vec3 SR_chrome_base_color;
+ float SR_chrome_diffuse_roughness;
+ float SR_chrome_metalness;
+ float SR_chrome_specular;
+ vec3 SR_chrome_specular_color;
+ float SR_chrome_specular_roughness;
+ float SR_chrome_specular_IOR;
+ float SR_chrome_specular_anisotropy;
+ float SR_chrome_specular_rotation;
+ float SR_chrome_transmission;
+ vec3 SR_chrome_transmission_color;
+ float SR_chrome_transmission_depth;
+ vec3 SR_chrome_transmission_scatter;
+ float SR_chrome_transmission_scatter_anisotropy;
+ float SR_chrome_transmission_dispersion;
+ float SR_chrome_transmission_extra_roughness;
+ float SR_chrome_subsurface;
+ vec3 SR_chrome_subsurface_color;
+ vec3 SR_chrome_subsurface_radius;
+ float SR_chrome_subsurface_scale;
+ float SR_chrome_subsurface_anisotropy;
+ float SR_chrome_sheen;
+ vec3 SR_chrome_sheen_color;
+ float SR_chrome_sheen_roughness;
+ float SR_chrome_coat;
+ vec3 SR_chrome_coat_color;
+ float SR_chrome_coat_roughness;
+ float SR_chrome_coat_anisotropy;
+ float SR_chrome_coat_rotation;
+ float SR_chrome_coat_IOR;
+ float SR_chrome_coat_affect_color;
+ float SR_chrome_coat_affect_roughness;
+ float SR_chrome_thin_film_thickness;
+ float SR_chrome_thin_film_IOR;
+ float SR_chrome_emission;
+ vec3 SR_chrome_emission_color;
+ vec3 SR_chrome_opacity;
+ bool SR_chrome_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+ , float SR_chrome_base
+
+ , vec3 SR_chrome_base_color
+
+ , float SR_chrome_diffuse_roughness
+
+ , float SR_chrome_metalness
+
+ , float SR_chrome_specular
+
+ , vec3 SR_chrome_specular_color
+
+ , float SR_chrome_specular_roughness
+
+ , float SR_chrome_specular_IOR
+
+ , float SR_chrome_specular_anisotropy
+
+ , float SR_chrome_specular_rotation
+
+ , float SR_chrome_transmission
+
+ , vec3 SR_chrome_transmission_color
+
+ , float SR_chrome_transmission_depth
+
+ , vec3 SR_chrome_transmission_scatter
+
+ , float SR_chrome_transmission_scatter_anisotropy
+
+ , float SR_chrome_transmission_dispersion
+
+ , float SR_chrome_transmission_extra_roughness
+
+ , float SR_chrome_subsurface
+
+ , vec3 SR_chrome_subsurface_color
+
+ , vec3 SR_chrome_subsurface_radius
+
+ , float SR_chrome_subsurface_scale
+
+ , float SR_chrome_subsurface_anisotropy
+
+ , float SR_chrome_sheen
+
+ , vec3 SR_chrome_sheen_color
+
+ , float SR_chrome_sheen_roughness
+
+ , float SR_chrome_coat
+
+ , vec3 SR_chrome_coat_color
+
+ , float SR_chrome_coat_roughness
+
+ , float SR_chrome_coat_anisotropy
+
+ , float SR_chrome_coat_rotation
+
+ , float SR_chrome_coat_IOR
+
+ , float SR_chrome_coat_affect_color
+
+ , float SR_chrome_coat_affect_roughness
+
+ , float SR_chrome_thin_film_thickness
+
+ , float SR_chrome_thin_film_IOR
+
+ , float SR_chrome_emission
+
+ , vec3 SR_chrome_emission_color
+
+ , vec3 SR_chrome_opacity
+
+ , bool SR_chrome_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+ , SR_chrome_base(SR_chrome_base)
+
+ , SR_chrome_base_color(SR_chrome_base_color)
+
+ , SR_chrome_diffuse_roughness(SR_chrome_diffuse_roughness)
+
+ , SR_chrome_metalness(SR_chrome_metalness)
+
+ , SR_chrome_specular(SR_chrome_specular)
+
+ , SR_chrome_specular_color(SR_chrome_specular_color)
+
+ , SR_chrome_specular_roughness(SR_chrome_specular_roughness)
+
+ , SR_chrome_specular_IOR(SR_chrome_specular_IOR)
+
+ , SR_chrome_specular_anisotropy(SR_chrome_specular_anisotropy)
+
+ , SR_chrome_specular_rotation(SR_chrome_specular_rotation)
+
+ , SR_chrome_transmission(SR_chrome_transmission)
+
+ , SR_chrome_transmission_color(SR_chrome_transmission_color)
+
+ , SR_chrome_transmission_depth(SR_chrome_transmission_depth)
+
+ , SR_chrome_transmission_scatter(SR_chrome_transmission_scatter)
+
+ , SR_chrome_transmission_scatter_anisotropy(SR_chrome_transmission_scatter_anisotropy)
+
+ , SR_chrome_transmission_dispersion(SR_chrome_transmission_dispersion)
+
+ , SR_chrome_transmission_extra_roughness(SR_chrome_transmission_extra_roughness)
+
+ , SR_chrome_subsurface(SR_chrome_subsurface)
+
+ , SR_chrome_subsurface_color(SR_chrome_subsurface_color)
+
+ , SR_chrome_subsurface_radius(SR_chrome_subsurface_radius)
+
+ , SR_chrome_subsurface_scale(SR_chrome_subsurface_scale)
+
+ , SR_chrome_subsurface_anisotropy(SR_chrome_subsurface_anisotropy)
+
+ , SR_chrome_sheen(SR_chrome_sheen)
+
+ , SR_chrome_sheen_color(SR_chrome_sheen_color)
+
+ , SR_chrome_sheen_roughness(SR_chrome_sheen_roughness)
+
+ , SR_chrome_coat(SR_chrome_coat)
+
+ , SR_chrome_coat_color(SR_chrome_coat_color)
+
+ , SR_chrome_coat_roughness(SR_chrome_coat_roughness)
+
+ , SR_chrome_coat_anisotropy(SR_chrome_coat_anisotropy)
+
+ , SR_chrome_coat_rotation(SR_chrome_coat_rotation)
+
+ , SR_chrome_coat_IOR(SR_chrome_coat_IOR)
+
+ , SR_chrome_coat_affect_color(SR_chrome_coat_affect_color)
+
+ , SR_chrome_coat_affect_roughness(SR_chrome_coat_affect_roughness)
+
+ , SR_chrome_thin_film_thickness(SR_chrome_thin_film_thickness)
+
+ , SR_chrome_thin_film_IOR(SR_chrome_thin_film_IOR)
+
+ , SR_chrome_emission(SR_chrome_emission)
+
+ , SR_chrome_emission_color(SR_chrome_emission_color)
+
+ , SR_chrome_opacity(SR_chrome_opacity)
+
+ , SR_chrome_thin_walled(SR_chrome_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+ float SR_chrome_base;
+
+
+ vec3 SR_chrome_base_color;
+
+
+ float SR_chrome_diffuse_roughness;
+
+
+ float SR_chrome_metalness;
+
+
+ float SR_chrome_specular;
+
+
+ vec3 SR_chrome_specular_color;
+
+
+ float SR_chrome_specular_roughness;
+
+
+ float SR_chrome_specular_IOR;
+
+
+ float SR_chrome_specular_anisotropy;
+
+
+ float SR_chrome_specular_rotation;
+
+
+ float SR_chrome_transmission;
+
+
+ vec3 SR_chrome_transmission_color;
+
+
+ float SR_chrome_transmission_depth;
+
+
+ vec3 SR_chrome_transmission_scatter;
+
+
+ float SR_chrome_transmission_scatter_anisotropy;
+
+
+ float SR_chrome_transmission_dispersion;
+
+
+ float SR_chrome_transmission_extra_roughness;
+
+
+ float SR_chrome_subsurface;
+
+
+ vec3 SR_chrome_subsurface_color;
+
+
+ vec3 SR_chrome_subsurface_radius;
+
+
+ float SR_chrome_subsurface_scale;
+
+
+ float SR_chrome_subsurface_anisotropy;
+
+
+ float SR_chrome_sheen;
+
+
+ vec3 SR_chrome_sheen_color;
+
+
+ float SR_chrome_sheen_roughness;
+
+
+ float SR_chrome_coat;
+
+
+ vec3 SR_chrome_coat_color;
+
+
+ float SR_chrome_coat_roughness;
+
+
+ float SR_chrome_coat_anisotropy;
+
+
+ float SR_chrome_coat_rotation;
+
+
+ float SR_chrome_coat_IOR;
+
+
+ float SR_chrome_coat_affect_color;
+
+
+ float SR_chrome_coat_affect_roughness;
+
+
+ float SR_chrome_thin_film_thickness;
+
+
+ float SR_chrome_thin_film_IOR;
+
+
+ float SR_chrome_emission;
+
+
+ vec3 SR_chrome_emission_color;
+
+
+ vec3 SR_chrome_opacity;
+
+
+ bool SR_chrome_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_chrome_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(SR_chrome_base, SR_chrome_base_color, SR_chrome_diffuse_roughness, SR_chrome_metalness, SR_chrome_specular, SR_chrome_specular_color, SR_chrome_specular_roughness, SR_chrome_specular_IOR, SR_chrome_specular_anisotropy, SR_chrome_specular_rotation, SR_chrome_transmission, SR_chrome_transmission_color, SR_chrome_transmission_depth, SR_chrome_transmission_scatter, SR_chrome_transmission_scatter_anisotropy, SR_chrome_transmission_dispersion, SR_chrome_transmission_extra_roughness, SR_chrome_subsurface, SR_chrome_subsurface_color, SR_chrome_subsurface_radius, SR_chrome_subsurface_scale, SR_chrome_subsurface_anisotropy, SR_chrome_sheen, SR_chrome_sheen_color, SR_chrome_sheen_roughness, SR_chrome_coat, SR_chrome_coat_color, SR_chrome_coat_roughness, SR_chrome_coat_anisotropy, SR_chrome_coat_rotation, SR_chrome_coat_IOR, geomprop_Nworld_out1, SR_chrome_coat_affect_color, SR_chrome_coat_affect_roughness, SR_chrome_thin_film_thickness, SR_chrome_thin_film_IOR, SR_chrome_emission, SR_chrome_emission_color, SR_chrome_opacity, SR_chrome_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_chrome_out);
+ material Chrome_out = SR_chrome_out;
+ out1 = float4(Chrome_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(0)]], sampler u_envRadiance_sampler [[sampler(0)]]
+, texture2d u_envIrradiance_tex [[texture(1)]], sampler u_envIrradiance_sampler [[sampler(1)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+ , u_pub.SR_chrome_base
+ , u_pub.SR_chrome_base_color
+ , u_pub.SR_chrome_diffuse_roughness
+ , u_pub.SR_chrome_metalness
+ , u_pub.SR_chrome_specular
+ , u_pub.SR_chrome_specular_color
+ , u_pub.SR_chrome_specular_roughness
+ , u_pub.SR_chrome_specular_IOR
+ , u_pub.SR_chrome_specular_anisotropy
+ , u_pub.SR_chrome_specular_rotation
+ , u_pub.SR_chrome_transmission
+ , u_pub.SR_chrome_transmission_color
+ , u_pub.SR_chrome_transmission_depth
+ , u_pub.SR_chrome_transmission_scatter
+ , u_pub.SR_chrome_transmission_scatter_anisotropy
+ , u_pub.SR_chrome_transmission_dispersion
+ , u_pub.SR_chrome_transmission_extra_roughness
+ , u_pub.SR_chrome_subsurface
+ , u_pub.SR_chrome_subsurface_color
+ , u_pub.SR_chrome_subsurface_radius
+ , u_pub.SR_chrome_subsurface_scale
+ , u_pub.SR_chrome_subsurface_anisotropy
+ , u_pub.SR_chrome_sheen
+ , u_pub.SR_chrome_sheen_color
+ , u_pub.SR_chrome_sheen_roughness
+ , u_pub.SR_chrome_coat
+ , u_pub.SR_chrome_coat_color
+ , u_pub.SR_chrome_coat_roughness
+ , u_pub.SR_chrome_coat_anisotropy
+ , u_pub.SR_chrome_coat_rotation
+ , u_pub.SR_chrome_coat_IOR
+ , u_pub.SR_chrome_coat_affect_color
+ , u_pub.SR_chrome_coat_affect_roughness
+ , u_pub.SR_chrome_thin_film_thickness
+ , u_pub.SR_chrome_thin_film_IOR
+ , u_pub.SR_chrome_emission
+ , u_pub.SR_chrome_emission_color
+ , u_pub.SR_chrome_opacity
+ , u_pub.SR_chrome_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/Chrome.msl.vert b/Materials/Examples/StandardSurface/Chrome.msl.vert
new file mode 100644
index 0000000000..89ee7b118d
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Chrome.msl.vert
@@ -0,0 +1,110 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'SR_chrome'. Function already called in this scope.
+ // Omitted node 'Chrome'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(3) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Chrome.osl b/Materials/Examples/StandardSurface/Chrome.osl
new file mode 100644
index 0000000000..154009adb4
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Chrome.osl
@@ -0,0 +1,637 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader Chrome
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "Chrome"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_base = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_chrome_base_color = color(1, 1, 1),
+ float SR_chrome_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_metalness = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_chrome_specular_color = color(1, 1, 1),
+ float SR_chrome_specular_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_chrome_transmission_color = color(1, 1, 1),
+ float SR_chrome_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_chrome_transmission_scatter = color(0, 0, 0),
+ float SR_chrome_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_chrome_subsurface_color = color(1, 1, 1),
+ color SR_chrome_subsurface_radius = color(1, 1, 1),
+ float SR_chrome_subsurface_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_chrome_sheen_color = color(1, 1, 1),
+ float SR_chrome_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_chrome_coat_color = color(1, 1, 1),
+ float SR_chrome_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_chrome_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_chrome_emission_color = color(1, 1, 1),
+ color SR_chrome_opacity = color(1, 1, 1),
+ int SR_chrome_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ surfaceshader SR_chrome_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(SR_chrome_base, SR_chrome_base_color, SR_chrome_diffuse_roughness, SR_chrome_metalness, SR_chrome_specular, SR_chrome_specular_color, SR_chrome_specular_roughness, SR_chrome_specular_IOR, SR_chrome_specular_anisotropy, SR_chrome_specular_rotation, SR_chrome_transmission, SR_chrome_transmission_color, SR_chrome_transmission_depth, SR_chrome_transmission_scatter, SR_chrome_transmission_scatter_anisotropy, SR_chrome_transmission_dispersion, SR_chrome_transmission_extra_roughness, SR_chrome_subsurface, SR_chrome_subsurface_color, SR_chrome_subsurface_radius, SR_chrome_subsurface_scale, SR_chrome_subsurface_anisotropy, SR_chrome_sheen, SR_chrome_sheen_color, SR_chrome_sheen_roughness, SR_chrome_coat, SR_chrome_coat_color, SR_chrome_coat_roughness, SR_chrome_coat_anisotropy, SR_chrome_coat_rotation, SR_chrome_coat_IOR, geomprop_Nworld_out1, SR_chrome_coat_affect_color, SR_chrome_coat_affect_roughness, SR_chrome_thin_film_thickness, SR_chrome_thin_film_IOR, SR_chrome_emission, SR_chrome_emission_color, SR_chrome_opacity, SR_chrome_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_chrome_out);
+ MATERIAL Chrome_out = mx_surfacematerial(SR_chrome_out, displacementshader1);
+ out = Chrome_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Copper.glsl.frag b/Materials/Examples/StandardSurface/Copper.glsl.frag
new file mode 100644
index 0000000000..0b211d2697
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Copper.glsl.frag
@@ -0,0 +1,1706 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform float SR_copper_base = 1.000000;
+uniform vec3 SR_copper_base_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_copper_diffuse_roughness = 0.000000;
+uniform float SR_copper_metalness = 1.000000;
+uniform float SR_copper_specular = 0.000000;
+uniform vec3 SR_copper_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_copper_specular_roughness = 0.250000;
+uniform float SR_copper_specular_IOR = 1.500000;
+uniform float SR_copper_specular_anisotropy = 0.000000;
+uniform float SR_copper_specular_rotation = 0.000000;
+uniform float SR_copper_transmission = 0.000000;
+uniform vec3 SR_copper_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_copper_transmission_depth = 0.000000;
+uniform vec3 SR_copper_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float SR_copper_transmission_scatter_anisotropy = 0.000000;
+uniform float SR_copper_transmission_dispersion = 0.000000;
+uniform float SR_copper_transmission_extra_roughness = 0.000000;
+uniform float SR_copper_subsurface = 0.000000;
+uniform vec3 SR_copper_subsurface_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_copper_subsurface_radius = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_copper_subsurface_scale = 1.000000;
+uniform float SR_copper_subsurface_anisotropy = 0.000000;
+uniform float SR_copper_sheen = 0.000000;
+uniform vec3 SR_copper_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_copper_sheen_roughness = 0.300000;
+uniform float SR_copper_coat = 1.000000;
+uniform vec3 SR_copper_coat_color = vec3(0.964680, 0.376263, 0.258183);
+uniform float SR_copper_coat_roughness = 0.200000;
+uniform float SR_copper_coat_anisotropy = 0.000000;
+uniform float SR_copper_coat_rotation = 0.000000;
+uniform float SR_copper_coat_IOR = 1.500000;
+uniform float SR_copper_coat_affect_color = 0.000000;
+uniform float SR_copper_coat_affect_roughness = 0.000000;
+uniform float SR_copper_thin_film_thickness = 0.000000;
+uniform float SR_copper_thin_film_IOR = 1.500000;
+uniform float SR_copper_emission = 0.000000;
+uniform vec3 SR_copper_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_copper_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool SR_copper_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_copper_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(SR_copper_base, SR_copper_base_color, SR_copper_diffuse_roughness, SR_copper_metalness, SR_copper_specular, SR_copper_specular_color, SR_copper_specular_roughness, SR_copper_specular_IOR, SR_copper_specular_anisotropy, SR_copper_specular_rotation, SR_copper_transmission, SR_copper_transmission_color, SR_copper_transmission_depth, SR_copper_transmission_scatter, SR_copper_transmission_scatter_anisotropy, SR_copper_transmission_dispersion, SR_copper_transmission_extra_roughness, SR_copper_subsurface, SR_copper_subsurface_color, SR_copper_subsurface_radius, SR_copper_subsurface_scale, SR_copper_subsurface_anisotropy, SR_copper_sheen, SR_copper_sheen_color, SR_copper_sheen_roughness, SR_copper_coat, SR_copper_coat_color, SR_copper_coat_roughness, SR_copper_coat_anisotropy, SR_copper_coat_rotation, SR_copper_coat_IOR, geomprop_Nworld_out1, SR_copper_coat_affect_color, SR_copper_coat_affect_roughness, SR_copper_thin_film_thickness, SR_copper_thin_film_IOR, SR_copper_emission, SR_copper_emission_color, SR_copper_opacity, SR_copper_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_copper_out);
+ material Copper_out = SR_copper_out;
+ out1 = vec4(Copper_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/Copper.glsl.vert b/Materials/Examples/StandardSurface/Copper.glsl.vert
new file mode 100644
index 0000000000..6133cb8f5e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Copper.glsl.vert
@@ -0,0 +1,28 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/Copper.mdl b/Materials/Examples/StandardSurface/Copper.mdl
new file mode 100644
index 0000000000..b243297d28
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Copper.mdl
@@ -0,0 +1,175 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material Copper
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ float SR_copper_base = 1,
+ color SR_copper_base_color = color(1, 1, 1),
+ float SR_copper_diffuse_roughness = 0,
+ float SR_copper_metalness = 1,
+ float SR_copper_specular = 0,
+ color SR_copper_specular_color = color(1, 1, 1),
+ float SR_copper_specular_roughness = 0.25,
+ uniform float SR_copper_specular_IOR = 1.5,
+ float SR_copper_specular_anisotropy = 0,
+ float SR_copper_specular_rotation = 0,
+ float SR_copper_transmission = 0,
+ color SR_copper_transmission_color = color(1, 1, 1),
+ float SR_copper_transmission_depth = 0,
+ color SR_copper_transmission_scatter = color(0, 0, 0),
+ float SR_copper_transmission_scatter_anisotropy = 0,
+ float SR_copper_transmission_dispersion = 0,
+ float SR_copper_transmission_extra_roughness = 0,
+ float SR_copper_subsurface = 0,
+ color SR_copper_subsurface_color = color(1, 1, 1),
+ color SR_copper_subsurface_radius = color(1, 1, 1),
+ float SR_copper_subsurface_scale = 1,
+ float SR_copper_subsurface_anisotropy = 0,
+ float SR_copper_sheen = 0,
+ color SR_copper_sheen_color = color(1, 1, 1),
+ float SR_copper_sheen_roughness = 0.3,
+ float SR_copper_coat = 1,
+ color SR_copper_coat_color = color(0.96468, 0.376263, 0.258183),
+ float SR_copper_coat_roughness = 0.2,
+ float SR_copper_coat_anisotropy = 0,
+ float SR_copper_coat_rotation = 0,
+ uniform float SR_copper_coat_IOR = 1.5,
+ float SR_copper_coat_affect_color = 0,
+ float SR_copper_coat_affect_roughness = 0,
+ float SR_copper_thin_film_thickness = 0,
+ float SR_copper_thin_film_IOR = 1.5,
+ float SR_copper_emission = 0,
+ color SR_copper_emission_color = color(1, 1, 1),
+ color SR_copper_opacity = color(1, 1, 1),
+ bool SR_copper_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ material SR_copper_out = NG_standard_surface_surfaceshader_100(SR_copper_base, SR_copper_base_color, SR_copper_diffuse_roughness, SR_copper_metalness, SR_copper_specular, SR_copper_specular_color, SR_copper_specular_roughness, SR_copper_specular_IOR, SR_copper_specular_anisotropy, SR_copper_specular_rotation, SR_copper_transmission, SR_copper_transmission_color, SR_copper_transmission_depth, SR_copper_transmission_scatter, SR_copper_transmission_scatter_anisotropy, SR_copper_transmission_dispersion, SR_copper_transmission_extra_roughness, SR_copper_subsurface, SR_copper_subsurface_color, SR_copper_subsurface_radius, SR_copper_subsurface_scale, SR_copper_subsurface_anisotropy, SR_copper_sheen, SR_copper_sheen_color, SR_copper_sheen_roughness, SR_copper_coat, SR_copper_coat_color, SR_copper_coat_roughness, SR_copper_coat_anisotropy, SR_copper_coat_rotation, SR_copper_coat_IOR, geomprop_Nworld_out1, SR_copper_coat_affect_color, SR_copper_coat_affect_roughness, SR_copper_thin_film_thickness, SR_copper_thin_film_IOR, SR_copper_emission, SR_copper_emission_color, SR_copper_opacity, SR_copper_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1);
+ material Copper_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: SR_copper_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = Copper_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/Copper.msl.frag b/Materials/Examples/StandardSurface/Copper.msl.frag
new file mode 100644
index 0000000000..33719024f2
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Copper.msl.frag
@@ -0,0 +1,2324 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ float SR_copper_base;
+ vec3 SR_copper_base_color;
+ float SR_copper_diffuse_roughness;
+ float SR_copper_metalness;
+ float SR_copper_specular;
+ vec3 SR_copper_specular_color;
+ float SR_copper_specular_roughness;
+ float SR_copper_specular_IOR;
+ float SR_copper_specular_anisotropy;
+ float SR_copper_specular_rotation;
+ float SR_copper_transmission;
+ vec3 SR_copper_transmission_color;
+ float SR_copper_transmission_depth;
+ vec3 SR_copper_transmission_scatter;
+ float SR_copper_transmission_scatter_anisotropy;
+ float SR_copper_transmission_dispersion;
+ float SR_copper_transmission_extra_roughness;
+ float SR_copper_subsurface;
+ vec3 SR_copper_subsurface_color;
+ vec3 SR_copper_subsurface_radius;
+ float SR_copper_subsurface_scale;
+ float SR_copper_subsurface_anisotropy;
+ float SR_copper_sheen;
+ vec3 SR_copper_sheen_color;
+ float SR_copper_sheen_roughness;
+ float SR_copper_coat;
+ vec3 SR_copper_coat_color;
+ float SR_copper_coat_roughness;
+ float SR_copper_coat_anisotropy;
+ float SR_copper_coat_rotation;
+ float SR_copper_coat_IOR;
+ float SR_copper_coat_affect_color;
+ float SR_copper_coat_affect_roughness;
+ float SR_copper_thin_film_thickness;
+ float SR_copper_thin_film_IOR;
+ float SR_copper_emission;
+ vec3 SR_copper_emission_color;
+ vec3 SR_copper_opacity;
+ bool SR_copper_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+ , float SR_copper_base
+
+ , vec3 SR_copper_base_color
+
+ , float SR_copper_diffuse_roughness
+
+ , float SR_copper_metalness
+
+ , float SR_copper_specular
+
+ , vec3 SR_copper_specular_color
+
+ , float SR_copper_specular_roughness
+
+ , float SR_copper_specular_IOR
+
+ , float SR_copper_specular_anisotropy
+
+ , float SR_copper_specular_rotation
+
+ , float SR_copper_transmission
+
+ , vec3 SR_copper_transmission_color
+
+ , float SR_copper_transmission_depth
+
+ , vec3 SR_copper_transmission_scatter
+
+ , float SR_copper_transmission_scatter_anisotropy
+
+ , float SR_copper_transmission_dispersion
+
+ , float SR_copper_transmission_extra_roughness
+
+ , float SR_copper_subsurface
+
+ , vec3 SR_copper_subsurface_color
+
+ , vec3 SR_copper_subsurface_radius
+
+ , float SR_copper_subsurface_scale
+
+ , float SR_copper_subsurface_anisotropy
+
+ , float SR_copper_sheen
+
+ , vec3 SR_copper_sheen_color
+
+ , float SR_copper_sheen_roughness
+
+ , float SR_copper_coat
+
+ , vec3 SR_copper_coat_color
+
+ , float SR_copper_coat_roughness
+
+ , float SR_copper_coat_anisotropy
+
+ , float SR_copper_coat_rotation
+
+ , float SR_copper_coat_IOR
+
+ , float SR_copper_coat_affect_color
+
+ , float SR_copper_coat_affect_roughness
+
+ , float SR_copper_thin_film_thickness
+
+ , float SR_copper_thin_film_IOR
+
+ , float SR_copper_emission
+
+ , vec3 SR_copper_emission_color
+
+ , vec3 SR_copper_opacity
+
+ , bool SR_copper_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+ , SR_copper_base(SR_copper_base)
+
+ , SR_copper_base_color(SR_copper_base_color)
+
+ , SR_copper_diffuse_roughness(SR_copper_diffuse_roughness)
+
+ , SR_copper_metalness(SR_copper_metalness)
+
+ , SR_copper_specular(SR_copper_specular)
+
+ , SR_copper_specular_color(SR_copper_specular_color)
+
+ , SR_copper_specular_roughness(SR_copper_specular_roughness)
+
+ , SR_copper_specular_IOR(SR_copper_specular_IOR)
+
+ , SR_copper_specular_anisotropy(SR_copper_specular_anisotropy)
+
+ , SR_copper_specular_rotation(SR_copper_specular_rotation)
+
+ , SR_copper_transmission(SR_copper_transmission)
+
+ , SR_copper_transmission_color(SR_copper_transmission_color)
+
+ , SR_copper_transmission_depth(SR_copper_transmission_depth)
+
+ , SR_copper_transmission_scatter(SR_copper_transmission_scatter)
+
+ , SR_copper_transmission_scatter_anisotropy(SR_copper_transmission_scatter_anisotropy)
+
+ , SR_copper_transmission_dispersion(SR_copper_transmission_dispersion)
+
+ , SR_copper_transmission_extra_roughness(SR_copper_transmission_extra_roughness)
+
+ , SR_copper_subsurface(SR_copper_subsurface)
+
+ , SR_copper_subsurface_color(SR_copper_subsurface_color)
+
+ , SR_copper_subsurface_radius(SR_copper_subsurface_radius)
+
+ , SR_copper_subsurface_scale(SR_copper_subsurface_scale)
+
+ , SR_copper_subsurface_anisotropy(SR_copper_subsurface_anisotropy)
+
+ , SR_copper_sheen(SR_copper_sheen)
+
+ , SR_copper_sheen_color(SR_copper_sheen_color)
+
+ , SR_copper_sheen_roughness(SR_copper_sheen_roughness)
+
+ , SR_copper_coat(SR_copper_coat)
+
+ , SR_copper_coat_color(SR_copper_coat_color)
+
+ , SR_copper_coat_roughness(SR_copper_coat_roughness)
+
+ , SR_copper_coat_anisotropy(SR_copper_coat_anisotropy)
+
+ , SR_copper_coat_rotation(SR_copper_coat_rotation)
+
+ , SR_copper_coat_IOR(SR_copper_coat_IOR)
+
+ , SR_copper_coat_affect_color(SR_copper_coat_affect_color)
+
+ , SR_copper_coat_affect_roughness(SR_copper_coat_affect_roughness)
+
+ , SR_copper_thin_film_thickness(SR_copper_thin_film_thickness)
+
+ , SR_copper_thin_film_IOR(SR_copper_thin_film_IOR)
+
+ , SR_copper_emission(SR_copper_emission)
+
+ , SR_copper_emission_color(SR_copper_emission_color)
+
+ , SR_copper_opacity(SR_copper_opacity)
+
+ , SR_copper_thin_walled(SR_copper_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+ float SR_copper_base;
+
+
+ vec3 SR_copper_base_color;
+
+
+ float SR_copper_diffuse_roughness;
+
+
+ float SR_copper_metalness;
+
+
+ float SR_copper_specular;
+
+
+ vec3 SR_copper_specular_color;
+
+
+ float SR_copper_specular_roughness;
+
+
+ float SR_copper_specular_IOR;
+
+
+ float SR_copper_specular_anisotropy;
+
+
+ float SR_copper_specular_rotation;
+
+
+ float SR_copper_transmission;
+
+
+ vec3 SR_copper_transmission_color;
+
+
+ float SR_copper_transmission_depth;
+
+
+ vec3 SR_copper_transmission_scatter;
+
+
+ float SR_copper_transmission_scatter_anisotropy;
+
+
+ float SR_copper_transmission_dispersion;
+
+
+ float SR_copper_transmission_extra_roughness;
+
+
+ float SR_copper_subsurface;
+
+
+ vec3 SR_copper_subsurface_color;
+
+
+ vec3 SR_copper_subsurface_radius;
+
+
+ float SR_copper_subsurface_scale;
+
+
+ float SR_copper_subsurface_anisotropy;
+
+
+ float SR_copper_sheen;
+
+
+ vec3 SR_copper_sheen_color;
+
+
+ float SR_copper_sheen_roughness;
+
+
+ float SR_copper_coat;
+
+
+ vec3 SR_copper_coat_color;
+
+
+ float SR_copper_coat_roughness;
+
+
+ float SR_copper_coat_anisotropy;
+
+
+ float SR_copper_coat_rotation;
+
+
+ float SR_copper_coat_IOR;
+
+
+ float SR_copper_coat_affect_color;
+
+
+ float SR_copper_coat_affect_roughness;
+
+
+ float SR_copper_thin_film_thickness;
+
+
+ float SR_copper_thin_film_IOR;
+
+
+ float SR_copper_emission;
+
+
+ vec3 SR_copper_emission_color;
+
+
+ vec3 SR_copper_opacity;
+
+
+ bool SR_copper_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_copper_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(SR_copper_base, SR_copper_base_color, SR_copper_diffuse_roughness, SR_copper_metalness, SR_copper_specular, SR_copper_specular_color, SR_copper_specular_roughness, SR_copper_specular_IOR, SR_copper_specular_anisotropy, SR_copper_specular_rotation, SR_copper_transmission, SR_copper_transmission_color, SR_copper_transmission_depth, SR_copper_transmission_scatter, SR_copper_transmission_scatter_anisotropy, SR_copper_transmission_dispersion, SR_copper_transmission_extra_roughness, SR_copper_subsurface, SR_copper_subsurface_color, SR_copper_subsurface_radius, SR_copper_subsurface_scale, SR_copper_subsurface_anisotropy, SR_copper_sheen, SR_copper_sheen_color, SR_copper_sheen_roughness, SR_copper_coat, SR_copper_coat_color, SR_copper_coat_roughness, SR_copper_coat_anisotropy, SR_copper_coat_rotation, SR_copper_coat_IOR, geomprop_Nworld_out1, SR_copper_coat_affect_color, SR_copper_coat_affect_roughness, SR_copper_thin_film_thickness, SR_copper_thin_film_IOR, SR_copper_emission, SR_copper_emission_color, SR_copper_opacity, SR_copper_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_copper_out);
+ material Copper_out = SR_copper_out;
+ out1 = float4(Copper_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(0)]], sampler u_envRadiance_sampler [[sampler(0)]]
+, texture2d u_envIrradiance_tex [[texture(1)]], sampler u_envIrradiance_sampler [[sampler(1)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+ , u_pub.SR_copper_base
+ , u_pub.SR_copper_base_color
+ , u_pub.SR_copper_diffuse_roughness
+ , u_pub.SR_copper_metalness
+ , u_pub.SR_copper_specular
+ , u_pub.SR_copper_specular_color
+ , u_pub.SR_copper_specular_roughness
+ , u_pub.SR_copper_specular_IOR
+ , u_pub.SR_copper_specular_anisotropy
+ , u_pub.SR_copper_specular_rotation
+ , u_pub.SR_copper_transmission
+ , u_pub.SR_copper_transmission_color
+ , u_pub.SR_copper_transmission_depth
+ , u_pub.SR_copper_transmission_scatter
+ , u_pub.SR_copper_transmission_scatter_anisotropy
+ , u_pub.SR_copper_transmission_dispersion
+ , u_pub.SR_copper_transmission_extra_roughness
+ , u_pub.SR_copper_subsurface
+ , u_pub.SR_copper_subsurface_color
+ , u_pub.SR_copper_subsurface_radius
+ , u_pub.SR_copper_subsurface_scale
+ , u_pub.SR_copper_subsurface_anisotropy
+ , u_pub.SR_copper_sheen
+ , u_pub.SR_copper_sheen_color
+ , u_pub.SR_copper_sheen_roughness
+ , u_pub.SR_copper_coat
+ , u_pub.SR_copper_coat_color
+ , u_pub.SR_copper_coat_roughness
+ , u_pub.SR_copper_coat_anisotropy
+ , u_pub.SR_copper_coat_rotation
+ , u_pub.SR_copper_coat_IOR
+ , u_pub.SR_copper_coat_affect_color
+ , u_pub.SR_copper_coat_affect_roughness
+ , u_pub.SR_copper_thin_film_thickness
+ , u_pub.SR_copper_thin_film_IOR
+ , u_pub.SR_copper_emission
+ , u_pub.SR_copper_emission_color
+ , u_pub.SR_copper_opacity
+ , u_pub.SR_copper_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/Copper.msl.vert b/Materials/Examples/StandardSurface/Copper.msl.vert
new file mode 100644
index 0000000000..0dedb1965c
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Copper.msl.vert
@@ -0,0 +1,110 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'SR_copper'. Function already called in this scope.
+ // Omitted node 'Copper'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(3) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Copper.osl b/Materials/Examples/StandardSurface/Copper.osl
new file mode 100644
index 0000000000..f6d66ad34c
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Copper.osl
@@ -0,0 +1,637 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader Copper
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "Copper"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_base = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_copper_base_color = color(1, 1, 1),
+ float SR_copper_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_metalness = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_specular = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_copper_specular_color = color(1, 1, 1),
+ float SR_copper_specular_roughness = 0.25
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_copper_transmission_color = color(1, 1, 1),
+ float SR_copper_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_copper_transmission_scatter = color(0, 0, 0),
+ float SR_copper_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_copper_subsurface_color = color(1, 1, 1),
+ color SR_copper_subsurface_radius = color(1, 1, 1),
+ float SR_copper_subsurface_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_copper_sheen_color = color(1, 1, 1),
+ float SR_copper_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_coat = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_copper_coat_color = color(0.96468, 0.376263, 0.258183),
+ float SR_copper_coat_roughness = 0.2
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_copper_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_copper_emission_color = color(1, 1, 1),
+ color SR_copper_opacity = color(1, 1, 1),
+ int SR_copper_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ surfaceshader SR_copper_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(SR_copper_base, SR_copper_base_color, SR_copper_diffuse_roughness, SR_copper_metalness, SR_copper_specular, SR_copper_specular_color, SR_copper_specular_roughness, SR_copper_specular_IOR, SR_copper_specular_anisotropy, SR_copper_specular_rotation, SR_copper_transmission, SR_copper_transmission_color, SR_copper_transmission_depth, SR_copper_transmission_scatter, SR_copper_transmission_scatter_anisotropy, SR_copper_transmission_dispersion, SR_copper_transmission_extra_roughness, SR_copper_subsurface, SR_copper_subsurface_color, SR_copper_subsurface_radius, SR_copper_subsurface_scale, SR_copper_subsurface_anisotropy, SR_copper_sheen, SR_copper_sheen_color, SR_copper_sheen_roughness, SR_copper_coat, SR_copper_coat_color, SR_copper_coat_roughness, SR_copper_coat_anisotropy, SR_copper_coat_rotation, SR_copper_coat_IOR, geomprop_Nworld_out1, SR_copper_coat_affect_color, SR_copper_coat_affect_roughness, SR_copper_thin_film_thickness, SR_copper_thin_film_IOR, SR_copper_emission, SR_copper_emission_color, SR_copper_opacity, SR_copper_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_copper_out);
+ MATERIAL Copper_out = mx_surfacematerial(SR_copper_out, displacementshader1);
+ out = Copper_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Default.glsl.frag b/Materials/Examples/StandardSurface/Default.glsl.frag
new file mode 100644
index 0000000000..e347b04c52
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Default.glsl.frag
@@ -0,0 +1,1706 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform float SR_default_base = 1.000000;
+uniform vec3 SR_default_base_color = vec3(0.800000, 0.800000, 0.800000);
+uniform float SR_default_diffuse_roughness = 0.000000;
+uniform float SR_default_metalness = 0.000000;
+uniform float SR_default_specular = 1.000000;
+uniform vec3 SR_default_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_default_specular_roughness = 0.200000;
+uniform float SR_default_specular_IOR = 1.500000;
+uniform float SR_default_specular_anisotropy = 0.000000;
+uniform float SR_default_specular_rotation = 0.000000;
+uniform float SR_default_transmission = 0.000000;
+uniform vec3 SR_default_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_default_transmission_depth = 0.000000;
+uniform vec3 SR_default_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float SR_default_transmission_scatter_anisotropy = 0.000000;
+uniform float SR_default_transmission_dispersion = 0.000000;
+uniform float SR_default_transmission_extra_roughness = 0.000000;
+uniform float SR_default_subsurface = 0.000000;
+uniform vec3 SR_default_subsurface_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_default_subsurface_radius = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_default_subsurface_scale = 1.000000;
+uniform float SR_default_subsurface_anisotropy = 0.000000;
+uniform float SR_default_sheen = 0.000000;
+uniform vec3 SR_default_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_default_sheen_roughness = 0.300000;
+uniform float SR_default_coat = 0.000000;
+uniform vec3 SR_default_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_default_coat_roughness = 0.100000;
+uniform float SR_default_coat_anisotropy = 0.000000;
+uniform float SR_default_coat_rotation = 0.000000;
+uniform float SR_default_coat_IOR = 1.500000;
+uniform float SR_default_coat_affect_color = 0.000000;
+uniform float SR_default_coat_affect_roughness = 0.000000;
+uniform float SR_default_thin_film_thickness = 0.000000;
+uniform float SR_default_thin_film_IOR = 1.500000;
+uniform float SR_default_emission = 0.000000;
+uniform vec3 SR_default_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_default_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool SR_default_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_default_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(SR_default_base, SR_default_base_color, SR_default_diffuse_roughness, SR_default_metalness, SR_default_specular, SR_default_specular_color, SR_default_specular_roughness, SR_default_specular_IOR, SR_default_specular_anisotropy, SR_default_specular_rotation, SR_default_transmission, SR_default_transmission_color, SR_default_transmission_depth, SR_default_transmission_scatter, SR_default_transmission_scatter_anisotropy, SR_default_transmission_dispersion, SR_default_transmission_extra_roughness, SR_default_subsurface, SR_default_subsurface_color, SR_default_subsurface_radius, SR_default_subsurface_scale, SR_default_subsurface_anisotropy, SR_default_sheen, SR_default_sheen_color, SR_default_sheen_roughness, SR_default_coat, SR_default_coat_color, SR_default_coat_roughness, SR_default_coat_anisotropy, SR_default_coat_rotation, SR_default_coat_IOR, geomprop_Nworld_out1, SR_default_coat_affect_color, SR_default_coat_affect_roughness, SR_default_thin_film_thickness, SR_default_thin_film_IOR, SR_default_emission, SR_default_emission_color, SR_default_opacity, SR_default_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_default_out);
+ material Default_out = SR_default_out;
+ out1 = vec4(Default_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/Default.glsl.vert b/Materials/Examples/StandardSurface/Default.glsl.vert
new file mode 100644
index 0000000000..6133cb8f5e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Default.glsl.vert
@@ -0,0 +1,28 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/Default.mdl b/Materials/Examples/StandardSurface/Default.mdl
new file mode 100644
index 0000000000..14c3f03a70
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Default.mdl
@@ -0,0 +1,175 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material Default
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ float SR_default_base = 1,
+ color SR_default_base_color = color(0.8, 0.8, 0.8),
+ float SR_default_diffuse_roughness = 0,
+ float SR_default_metalness = 0,
+ float SR_default_specular = 1,
+ color SR_default_specular_color = color(1, 1, 1),
+ float SR_default_specular_roughness = 0.2,
+ uniform float SR_default_specular_IOR = 1.5,
+ float SR_default_specular_anisotropy = 0,
+ float SR_default_specular_rotation = 0,
+ float SR_default_transmission = 0,
+ color SR_default_transmission_color = color(1, 1, 1),
+ float SR_default_transmission_depth = 0,
+ color SR_default_transmission_scatter = color(0, 0, 0),
+ float SR_default_transmission_scatter_anisotropy = 0,
+ float SR_default_transmission_dispersion = 0,
+ float SR_default_transmission_extra_roughness = 0,
+ float SR_default_subsurface = 0,
+ color SR_default_subsurface_color = color(1, 1, 1),
+ color SR_default_subsurface_radius = color(1, 1, 1),
+ float SR_default_subsurface_scale = 1,
+ float SR_default_subsurface_anisotropy = 0,
+ float SR_default_sheen = 0,
+ color SR_default_sheen_color = color(1, 1, 1),
+ float SR_default_sheen_roughness = 0.3,
+ float SR_default_coat = 0,
+ color SR_default_coat_color = color(1, 1, 1),
+ float SR_default_coat_roughness = 0.1,
+ float SR_default_coat_anisotropy = 0,
+ float SR_default_coat_rotation = 0,
+ uniform float SR_default_coat_IOR = 1.5,
+ float SR_default_coat_affect_color = 0,
+ float SR_default_coat_affect_roughness = 0,
+ float SR_default_thin_film_thickness = 0,
+ float SR_default_thin_film_IOR = 1.5,
+ float SR_default_emission = 0,
+ color SR_default_emission_color = color(1, 1, 1),
+ color SR_default_opacity = color(1, 1, 1),
+ bool SR_default_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ material SR_default_out = NG_standard_surface_surfaceshader_100(SR_default_base, SR_default_base_color, SR_default_diffuse_roughness, SR_default_metalness, SR_default_specular, SR_default_specular_color, SR_default_specular_roughness, SR_default_specular_IOR, SR_default_specular_anisotropy, SR_default_specular_rotation, SR_default_transmission, SR_default_transmission_color, SR_default_transmission_depth, SR_default_transmission_scatter, SR_default_transmission_scatter_anisotropy, SR_default_transmission_dispersion, SR_default_transmission_extra_roughness, SR_default_subsurface, SR_default_subsurface_color, SR_default_subsurface_radius, SR_default_subsurface_scale, SR_default_subsurface_anisotropy, SR_default_sheen, SR_default_sheen_color, SR_default_sheen_roughness, SR_default_coat, SR_default_coat_color, SR_default_coat_roughness, SR_default_coat_anisotropy, SR_default_coat_rotation, SR_default_coat_IOR, geomprop_Nworld_out1, SR_default_coat_affect_color, SR_default_coat_affect_roughness, SR_default_thin_film_thickness, SR_default_thin_film_IOR, SR_default_emission, SR_default_emission_color, SR_default_opacity, SR_default_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1);
+ material Default_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: SR_default_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = Default_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/Default.msl.frag b/Materials/Examples/StandardSurface/Default.msl.frag
new file mode 100644
index 0000000000..9e8c5cbcbf
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Default.msl.frag
@@ -0,0 +1,2324 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ float SR_default_base;
+ vec3 SR_default_base_color;
+ float SR_default_diffuse_roughness;
+ float SR_default_metalness;
+ float SR_default_specular;
+ vec3 SR_default_specular_color;
+ float SR_default_specular_roughness;
+ float SR_default_specular_IOR;
+ float SR_default_specular_anisotropy;
+ float SR_default_specular_rotation;
+ float SR_default_transmission;
+ vec3 SR_default_transmission_color;
+ float SR_default_transmission_depth;
+ vec3 SR_default_transmission_scatter;
+ float SR_default_transmission_scatter_anisotropy;
+ float SR_default_transmission_dispersion;
+ float SR_default_transmission_extra_roughness;
+ float SR_default_subsurface;
+ vec3 SR_default_subsurface_color;
+ vec3 SR_default_subsurface_radius;
+ float SR_default_subsurface_scale;
+ float SR_default_subsurface_anisotropy;
+ float SR_default_sheen;
+ vec3 SR_default_sheen_color;
+ float SR_default_sheen_roughness;
+ float SR_default_coat;
+ vec3 SR_default_coat_color;
+ float SR_default_coat_roughness;
+ float SR_default_coat_anisotropy;
+ float SR_default_coat_rotation;
+ float SR_default_coat_IOR;
+ float SR_default_coat_affect_color;
+ float SR_default_coat_affect_roughness;
+ float SR_default_thin_film_thickness;
+ float SR_default_thin_film_IOR;
+ float SR_default_emission;
+ vec3 SR_default_emission_color;
+ vec3 SR_default_opacity;
+ bool SR_default_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+ , float SR_default_base
+
+ , vec3 SR_default_base_color
+
+ , float SR_default_diffuse_roughness
+
+ , float SR_default_metalness
+
+ , float SR_default_specular
+
+ , vec3 SR_default_specular_color
+
+ , float SR_default_specular_roughness
+
+ , float SR_default_specular_IOR
+
+ , float SR_default_specular_anisotropy
+
+ , float SR_default_specular_rotation
+
+ , float SR_default_transmission
+
+ , vec3 SR_default_transmission_color
+
+ , float SR_default_transmission_depth
+
+ , vec3 SR_default_transmission_scatter
+
+ , float SR_default_transmission_scatter_anisotropy
+
+ , float SR_default_transmission_dispersion
+
+ , float SR_default_transmission_extra_roughness
+
+ , float SR_default_subsurface
+
+ , vec3 SR_default_subsurface_color
+
+ , vec3 SR_default_subsurface_radius
+
+ , float SR_default_subsurface_scale
+
+ , float SR_default_subsurface_anisotropy
+
+ , float SR_default_sheen
+
+ , vec3 SR_default_sheen_color
+
+ , float SR_default_sheen_roughness
+
+ , float SR_default_coat
+
+ , vec3 SR_default_coat_color
+
+ , float SR_default_coat_roughness
+
+ , float SR_default_coat_anisotropy
+
+ , float SR_default_coat_rotation
+
+ , float SR_default_coat_IOR
+
+ , float SR_default_coat_affect_color
+
+ , float SR_default_coat_affect_roughness
+
+ , float SR_default_thin_film_thickness
+
+ , float SR_default_thin_film_IOR
+
+ , float SR_default_emission
+
+ , vec3 SR_default_emission_color
+
+ , vec3 SR_default_opacity
+
+ , bool SR_default_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+ , SR_default_base(SR_default_base)
+
+ , SR_default_base_color(SR_default_base_color)
+
+ , SR_default_diffuse_roughness(SR_default_diffuse_roughness)
+
+ , SR_default_metalness(SR_default_metalness)
+
+ , SR_default_specular(SR_default_specular)
+
+ , SR_default_specular_color(SR_default_specular_color)
+
+ , SR_default_specular_roughness(SR_default_specular_roughness)
+
+ , SR_default_specular_IOR(SR_default_specular_IOR)
+
+ , SR_default_specular_anisotropy(SR_default_specular_anisotropy)
+
+ , SR_default_specular_rotation(SR_default_specular_rotation)
+
+ , SR_default_transmission(SR_default_transmission)
+
+ , SR_default_transmission_color(SR_default_transmission_color)
+
+ , SR_default_transmission_depth(SR_default_transmission_depth)
+
+ , SR_default_transmission_scatter(SR_default_transmission_scatter)
+
+ , SR_default_transmission_scatter_anisotropy(SR_default_transmission_scatter_anisotropy)
+
+ , SR_default_transmission_dispersion(SR_default_transmission_dispersion)
+
+ , SR_default_transmission_extra_roughness(SR_default_transmission_extra_roughness)
+
+ , SR_default_subsurface(SR_default_subsurface)
+
+ , SR_default_subsurface_color(SR_default_subsurface_color)
+
+ , SR_default_subsurface_radius(SR_default_subsurface_radius)
+
+ , SR_default_subsurface_scale(SR_default_subsurface_scale)
+
+ , SR_default_subsurface_anisotropy(SR_default_subsurface_anisotropy)
+
+ , SR_default_sheen(SR_default_sheen)
+
+ , SR_default_sheen_color(SR_default_sheen_color)
+
+ , SR_default_sheen_roughness(SR_default_sheen_roughness)
+
+ , SR_default_coat(SR_default_coat)
+
+ , SR_default_coat_color(SR_default_coat_color)
+
+ , SR_default_coat_roughness(SR_default_coat_roughness)
+
+ , SR_default_coat_anisotropy(SR_default_coat_anisotropy)
+
+ , SR_default_coat_rotation(SR_default_coat_rotation)
+
+ , SR_default_coat_IOR(SR_default_coat_IOR)
+
+ , SR_default_coat_affect_color(SR_default_coat_affect_color)
+
+ , SR_default_coat_affect_roughness(SR_default_coat_affect_roughness)
+
+ , SR_default_thin_film_thickness(SR_default_thin_film_thickness)
+
+ , SR_default_thin_film_IOR(SR_default_thin_film_IOR)
+
+ , SR_default_emission(SR_default_emission)
+
+ , SR_default_emission_color(SR_default_emission_color)
+
+ , SR_default_opacity(SR_default_opacity)
+
+ , SR_default_thin_walled(SR_default_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+ float SR_default_base;
+
+
+ vec3 SR_default_base_color;
+
+
+ float SR_default_diffuse_roughness;
+
+
+ float SR_default_metalness;
+
+
+ float SR_default_specular;
+
+
+ vec3 SR_default_specular_color;
+
+
+ float SR_default_specular_roughness;
+
+
+ float SR_default_specular_IOR;
+
+
+ float SR_default_specular_anisotropy;
+
+
+ float SR_default_specular_rotation;
+
+
+ float SR_default_transmission;
+
+
+ vec3 SR_default_transmission_color;
+
+
+ float SR_default_transmission_depth;
+
+
+ vec3 SR_default_transmission_scatter;
+
+
+ float SR_default_transmission_scatter_anisotropy;
+
+
+ float SR_default_transmission_dispersion;
+
+
+ float SR_default_transmission_extra_roughness;
+
+
+ float SR_default_subsurface;
+
+
+ vec3 SR_default_subsurface_color;
+
+
+ vec3 SR_default_subsurface_radius;
+
+
+ float SR_default_subsurface_scale;
+
+
+ float SR_default_subsurface_anisotropy;
+
+
+ float SR_default_sheen;
+
+
+ vec3 SR_default_sheen_color;
+
+
+ float SR_default_sheen_roughness;
+
+
+ float SR_default_coat;
+
+
+ vec3 SR_default_coat_color;
+
+
+ float SR_default_coat_roughness;
+
+
+ float SR_default_coat_anisotropy;
+
+
+ float SR_default_coat_rotation;
+
+
+ float SR_default_coat_IOR;
+
+
+ float SR_default_coat_affect_color;
+
+
+ float SR_default_coat_affect_roughness;
+
+
+ float SR_default_thin_film_thickness;
+
+
+ float SR_default_thin_film_IOR;
+
+
+ float SR_default_emission;
+
+
+ vec3 SR_default_emission_color;
+
+
+ vec3 SR_default_opacity;
+
+
+ bool SR_default_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_default_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(SR_default_base, SR_default_base_color, SR_default_diffuse_roughness, SR_default_metalness, SR_default_specular, SR_default_specular_color, SR_default_specular_roughness, SR_default_specular_IOR, SR_default_specular_anisotropy, SR_default_specular_rotation, SR_default_transmission, SR_default_transmission_color, SR_default_transmission_depth, SR_default_transmission_scatter, SR_default_transmission_scatter_anisotropy, SR_default_transmission_dispersion, SR_default_transmission_extra_roughness, SR_default_subsurface, SR_default_subsurface_color, SR_default_subsurface_radius, SR_default_subsurface_scale, SR_default_subsurface_anisotropy, SR_default_sheen, SR_default_sheen_color, SR_default_sheen_roughness, SR_default_coat, SR_default_coat_color, SR_default_coat_roughness, SR_default_coat_anisotropy, SR_default_coat_rotation, SR_default_coat_IOR, geomprop_Nworld_out1, SR_default_coat_affect_color, SR_default_coat_affect_roughness, SR_default_thin_film_thickness, SR_default_thin_film_IOR, SR_default_emission, SR_default_emission_color, SR_default_opacity, SR_default_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_default_out);
+ material Default_out = SR_default_out;
+ out1 = float4(Default_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(0)]], sampler u_envRadiance_sampler [[sampler(0)]]
+, texture2d u_envIrradiance_tex [[texture(1)]], sampler u_envIrradiance_sampler [[sampler(1)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+ , u_pub.SR_default_base
+ , u_pub.SR_default_base_color
+ , u_pub.SR_default_diffuse_roughness
+ , u_pub.SR_default_metalness
+ , u_pub.SR_default_specular
+ , u_pub.SR_default_specular_color
+ , u_pub.SR_default_specular_roughness
+ , u_pub.SR_default_specular_IOR
+ , u_pub.SR_default_specular_anisotropy
+ , u_pub.SR_default_specular_rotation
+ , u_pub.SR_default_transmission
+ , u_pub.SR_default_transmission_color
+ , u_pub.SR_default_transmission_depth
+ , u_pub.SR_default_transmission_scatter
+ , u_pub.SR_default_transmission_scatter_anisotropy
+ , u_pub.SR_default_transmission_dispersion
+ , u_pub.SR_default_transmission_extra_roughness
+ , u_pub.SR_default_subsurface
+ , u_pub.SR_default_subsurface_color
+ , u_pub.SR_default_subsurface_radius
+ , u_pub.SR_default_subsurface_scale
+ , u_pub.SR_default_subsurface_anisotropy
+ , u_pub.SR_default_sheen
+ , u_pub.SR_default_sheen_color
+ , u_pub.SR_default_sheen_roughness
+ , u_pub.SR_default_coat
+ , u_pub.SR_default_coat_color
+ , u_pub.SR_default_coat_roughness
+ , u_pub.SR_default_coat_anisotropy
+ , u_pub.SR_default_coat_rotation
+ , u_pub.SR_default_coat_IOR
+ , u_pub.SR_default_coat_affect_color
+ , u_pub.SR_default_coat_affect_roughness
+ , u_pub.SR_default_thin_film_thickness
+ , u_pub.SR_default_thin_film_IOR
+ , u_pub.SR_default_emission
+ , u_pub.SR_default_emission_color
+ , u_pub.SR_default_opacity
+ , u_pub.SR_default_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/Default.msl.vert b/Materials/Examples/StandardSurface/Default.msl.vert
new file mode 100644
index 0000000000..122fb73b05
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Default.msl.vert
@@ -0,0 +1,110 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'SR_default'. Function already called in this scope.
+ // Omitted node 'Default'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(3) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Default.osl b/Materials/Examples/StandardSurface/Default.osl
new file mode 100644
index 0000000000..493f58dd86
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Default.osl
@@ -0,0 +1,637 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader Default
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "Default"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_base = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_default_base_color = color(0.8, 0.8, 0.8),
+ float SR_default_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_metalness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_default_specular_color = color(1, 1, 1),
+ float SR_default_specular_roughness = 0.2
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_default_transmission_color = color(1, 1, 1),
+ float SR_default_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_default_transmission_scatter = color(0, 0, 0),
+ float SR_default_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_default_subsurface_color = color(1, 1, 1),
+ color SR_default_subsurface_radius = color(1, 1, 1),
+ float SR_default_subsurface_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_default_sheen_color = color(1, 1, 1),
+ float SR_default_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_default_coat_color = color(1, 1, 1),
+ float SR_default_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_default_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_default_emission_color = color(1, 1, 1),
+ color SR_default_opacity = color(1, 1, 1),
+ int SR_default_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ surfaceshader SR_default_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(SR_default_base, SR_default_base_color, SR_default_diffuse_roughness, SR_default_metalness, SR_default_specular, SR_default_specular_color, SR_default_specular_roughness, SR_default_specular_IOR, SR_default_specular_anisotropy, SR_default_specular_rotation, SR_default_transmission, SR_default_transmission_color, SR_default_transmission_depth, SR_default_transmission_scatter, SR_default_transmission_scatter_anisotropy, SR_default_transmission_dispersion, SR_default_transmission_extra_roughness, SR_default_subsurface, SR_default_subsurface_color, SR_default_subsurface_radius, SR_default_subsurface_scale, SR_default_subsurface_anisotropy, SR_default_sheen, SR_default_sheen_color, SR_default_sheen_roughness, SR_default_coat, SR_default_coat_color, SR_default_coat_roughness, SR_default_coat_anisotropy, SR_default_coat_rotation, SR_default_coat_IOR, geomprop_Nworld_out1, SR_default_coat_affect_color, SR_default_coat_affect_roughness, SR_default_thin_film_thickness, SR_default_thin_film_IOR, SR_default_emission, SR_default_emission_color, SR_default_opacity, SR_default_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_default_out);
+ MATERIAL Default_out = mx_surfacematerial(SR_default_out, displacementshader1);
+ out = Default_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Glass.glsl.frag b/Materials/Examples/StandardSurface/Glass.glsl.frag
new file mode 100644
index 0000000000..c054b3cd10
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Glass.glsl.frag
@@ -0,0 +1,1706 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform float SR_glass_base = 0.000000;
+uniform vec3 SR_glass_base_color = vec3(0.800000, 0.800000, 0.800000);
+uniform float SR_glass_diffuse_roughness = 0.000000;
+uniform float SR_glass_metalness = 0.000000;
+uniform float SR_glass_specular = 1.000000;
+uniform vec3 SR_glass_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_glass_specular_roughness = 0.010000;
+uniform float SR_glass_specular_IOR = 1.520000;
+uniform float SR_glass_specular_anisotropy = 0.000000;
+uniform float SR_glass_specular_rotation = 0.000000;
+uniform float SR_glass_transmission = 1.000000;
+uniform vec3 SR_glass_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_glass_transmission_depth = 0.000000;
+uniform vec3 SR_glass_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float SR_glass_transmission_scatter_anisotropy = 0.000000;
+uniform float SR_glass_transmission_dispersion = 0.000000;
+uniform float SR_glass_transmission_extra_roughness = 0.000000;
+uniform float SR_glass_subsurface = 0.000000;
+uniform vec3 SR_glass_subsurface_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_glass_subsurface_radius = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_glass_subsurface_scale = 1.000000;
+uniform float SR_glass_subsurface_anisotropy = 0.000000;
+uniform float SR_glass_sheen = 0.000000;
+uniform vec3 SR_glass_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_glass_sheen_roughness = 0.300000;
+uniform float SR_glass_coat = 0.000000;
+uniform vec3 SR_glass_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_glass_coat_roughness = 0.100000;
+uniform float SR_glass_coat_anisotropy = 0.000000;
+uniform float SR_glass_coat_rotation = 0.000000;
+uniform float SR_glass_coat_IOR = 1.500000;
+uniform float SR_glass_coat_affect_color = 0.000000;
+uniform float SR_glass_coat_affect_roughness = 0.000000;
+uniform float SR_glass_thin_film_thickness = 0.000000;
+uniform float SR_glass_thin_film_IOR = 1.500000;
+uniform float SR_glass_emission = 0.000000;
+uniform vec3 SR_glass_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_glass_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool SR_glass_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_glass_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(SR_glass_base, SR_glass_base_color, SR_glass_diffuse_roughness, SR_glass_metalness, SR_glass_specular, SR_glass_specular_color, SR_glass_specular_roughness, SR_glass_specular_IOR, SR_glass_specular_anisotropy, SR_glass_specular_rotation, SR_glass_transmission, SR_glass_transmission_color, SR_glass_transmission_depth, SR_glass_transmission_scatter, SR_glass_transmission_scatter_anisotropy, SR_glass_transmission_dispersion, SR_glass_transmission_extra_roughness, SR_glass_subsurface, SR_glass_subsurface_color, SR_glass_subsurface_radius, SR_glass_subsurface_scale, SR_glass_subsurface_anisotropy, SR_glass_sheen, SR_glass_sheen_color, SR_glass_sheen_roughness, SR_glass_coat, SR_glass_coat_color, SR_glass_coat_roughness, SR_glass_coat_anisotropy, SR_glass_coat_rotation, SR_glass_coat_IOR, geomprop_Nworld_out1, SR_glass_coat_affect_color, SR_glass_coat_affect_roughness, SR_glass_thin_film_thickness, SR_glass_thin_film_IOR, SR_glass_emission, SR_glass_emission_color, SR_glass_opacity, SR_glass_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_glass_out);
+ material Glass_out = SR_glass_out;
+ out1 = vec4(Glass_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/Glass.glsl.vert b/Materials/Examples/StandardSurface/Glass.glsl.vert
new file mode 100644
index 0000000000..6133cb8f5e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Glass.glsl.vert
@@ -0,0 +1,28 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/Glass.mdl b/Materials/Examples/StandardSurface/Glass.mdl
new file mode 100644
index 0000000000..92982212a6
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Glass.mdl
@@ -0,0 +1,175 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material Glass
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ float SR_glass_base = 0,
+ color SR_glass_base_color = color(0.8, 0.8, 0.8),
+ float SR_glass_diffuse_roughness = 0,
+ float SR_glass_metalness = 0,
+ float SR_glass_specular = 1,
+ color SR_glass_specular_color = color(1, 1, 1),
+ float SR_glass_specular_roughness = 0.01,
+ uniform float SR_glass_specular_IOR = 1.52,
+ float SR_glass_specular_anisotropy = 0,
+ float SR_glass_specular_rotation = 0,
+ float SR_glass_transmission = 1,
+ color SR_glass_transmission_color = color(1, 1, 1),
+ float SR_glass_transmission_depth = 0,
+ color SR_glass_transmission_scatter = color(0, 0, 0),
+ float SR_glass_transmission_scatter_anisotropy = 0,
+ float SR_glass_transmission_dispersion = 0,
+ float SR_glass_transmission_extra_roughness = 0,
+ float SR_glass_subsurface = 0,
+ color SR_glass_subsurface_color = color(1, 1, 1),
+ color SR_glass_subsurface_radius = color(1, 1, 1),
+ float SR_glass_subsurface_scale = 1,
+ float SR_glass_subsurface_anisotropy = 0,
+ float SR_glass_sheen = 0,
+ color SR_glass_sheen_color = color(1, 1, 1),
+ float SR_glass_sheen_roughness = 0.3,
+ float SR_glass_coat = 0,
+ color SR_glass_coat_color = color(1, 1, 1),
+ float SR_glass_coat_roughness = 0.1,
+ float SR_glass_coat_anisotropy = 0,
+ float SR_glass_coat_rotation = 0,
+ uniform float SR_glass_coat_IOR = 1.5,
+ float SR_glass_coat_affect_color = 0,
+ float SR_glass_coat_affect_roughness = 0,
+ float SR_glass_thin_film_thickness = 0,
+ float SR_glass_thin_film_IOR = 1.5,
+ float SR_glass_emission = 0,
+ color SR_glass_emission_color = color(1, 1, 1),
+ color SR_glass_opacity = color(1, 1, 1),
+ bool SR_glass_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ material SR_glass_out = NG_standard_surface_surfaceshader_100(SR_glass_base, SR_glass_base_color, SR_glass_diffuse_roughness, SR_glass_metalness, SR_glass_specular, SR_glass_specular_color, SR_glass_specular_roughness, SR_glass_specular_IOR, SR_glass_specular_anisotropy, SR_glass_specular_rotation, SR_glass_transmission, SR_glass_transmission_color, SR_glass_transmission_depth, SR_glass_transmission_scatter, SR_glass_transmission_scatter_anisotropy, SR_glass_transmission_dispersion, SR_glass_transmission_extra_roughness, SR_glass_subsurface, SR_glass_subsurface_color, SR_glass_subsurface_radius, SR_glass_subsurface_scale, SR_glass_subsurface_anisotropy, SR_glass_sheen, SR_glass_sheen_color, SR_glass_sheen_roughness, SR_glass_coat, SR_glass_coat_color, SR_glass_coat_roughness, SR_glass_coat_anisotropy, SR_glass_coat_rotation, SR_glass_coat_IOR, geomprop_Nworld_out1, SR_glass_coat_affect_color, SR_glass_coat_affect_roughness, SR_glass_thin_film_thickness, SR_glass_thin_film_IOR, SR_glass_emission, SR_glass_emission_color, SR_glass_opacity, SR_glass_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1);
+ material Glass_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: SR_glass_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = Glass_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/Glass.msl.frag b/Materials/Examples/StandardSurface/Glass.msl.frag
new file mode 100644
index 0000000000..41a2abf61a
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Glass.msl.frag
@@ -0,0 +1,2324 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ float SR_glass_base;
+ vec3 SR_glass_base_color;
+ float SR_glass_diffuse_roughness;
+ float SR_glass_metalness;
+ float SR_glass_specular;
+ vec3 SR_glass_specular_color;
+ float SR_glass_specular_roughness;
+ float SR_glass_specular_IOR;
+ float SR_glass_specular_anisotropy;
+ float SR_glass_specular_rotation;
+ float SR_glass_transmission;
+ vec3 SR_glass_transmission_color;
+ float SR_glass_transmission_depth;
+ vec3 SR_glass_transmission_scatter;
+ float SR_glass_transmission_scatter_anisotropy;
+ float SR_glass_transmission_dispersion;
+ float SR_glass_transmission_extra_roughness;
+ float SR_glass_subsurface;
+ vec3 SR_glass_subsurface_color;
+ vec3 SR_glass_subsurface_radius;
+ float SR_glass_subsurface_scale;
+ float SR_glass_subsurface_anisotropy;
+ float SR_glass_sheen;
+ vec3 SR_glass_sheen_color;
+ float SR_glass_sheen_roughness;
+ float SR_glass_coat;
+ vec3 SR_glass_coat_color;
+ float SR_glass_coat_roughness;
+ float SR_glass_coat_anisotropy;
+ float SR_glass_coat_rotation;
+ float SR_glass_coat_IOR;
+ float SR_glass_coat_affect_color;
+ float SR_glass_coat_affect_roughness;
+ float SR_glass_thin_film_thickness;
+ float SR_glass_thin_film_IOR;
+ float SR_glass_emission;
+ vec3 SR_glass_emission_color;
+ vec3 SR_glass_opacity;
+ bool SR_glass_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+ , float SR_glass_base
+
+ , vec3 SR_glass_base_color
+
+ , float SR_glass_diffuse_roughness
+
+ , float SR_glass_metalness
+
+ , float SR_glass_specular
+
+ , vec3 SR_glass_specular_color
+
+ , float SR_glass_specular_roughness
+
+ , float SR_glass_specular_IOR
+
+ , float SR_glass_specular_anisotropy
+
+ , float SR_glass_specular_rotation
+
+ , float SR_glass_transmission
+
+ , vec3 SR_glass_transmission_color
+
+ , float SR_glass_transmission_depth
+
+ , vec3 SR_glass_transmission_scatter
+
+ , float SR_glass_transmission_scatter_anisotropy
+
+ , float SR_glass_transmission_dispersion
+
+ , float SR_glass_transmission_extra_roughness
+
+ , float SR_glass_subsurface
+
+ , vec3 SR_glass_subsurface_color
+
+ , vec3 SR_glass_subsurface_radius
+
+ , float SR_glass_subsurface_scale
+
+ , float SR_glass_subsurface_anisotropy
+
+ , float SR_glass_sheen
+
+ , vec3 SR_glass_sheen_color
+
+ , float SR_glass_sheen_roughness
+
+ , float SR_glass_coat
+
+ , vec3 SR_glass_coat_color
+
+ , float SR_glass_coat_roughness
+
+ , float SR_glass_coat_anisotropy
+
+ , float SR_glass_coat_rotation
+
+ , float SR_glass_coat_IOR
+
+ , float SR_glass_coat_affect_color
+
+ , float SR_glass_coat_affect_roughness
+
+ , float SR_glass_thin_film_thickness
+
+ , float SR_glass_thin_film_IOR
+
+ , float SR_glass_emission
+
+ , vec3 SR_glass_emission_color
+
+ , vec3 SR_glass_opacity
+
+ , bool SR_glass_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+ , SR_glass_base(SR_glass_base)
+
+ , SR_glass_base_color(SR_glass_base_color)
+
+ , SR_glass_diffuse_roughness(SR_glass_diffuse_roughness)
+
+ , SR_glass_metalness(SR_glass_metalness)
+
+ , SR_glass_specular(SR_glass_specular)
+
+ , SR_glass_specular_color(SR_glass_specular_color)
+
+ , SR_glass_specular_roughness(SR_glass_specular_roughness)
+
+ , SR_glass_specular_IOR(SR_glass_specular_IOR)
+
+ , SR_glass_specular_anisotropy(SR_glass_specular_anisotropy)
+
+ , SR_glass_specular_rotation(SR_glass_specular_rotation)
+
+ , SR_glass_transmission(SR_glass_transmission)
+
+ , SR_glass_transmission_color(SR_glass_transmission_color)
+
+ , SR_glass_transmission_depth(SR_glass_transmission_depth)
+
+ , SR_glass_transmission_scatter(SR_glass_transmission_scatter)
+
+ , SR_glass_transmission_scatter_anisotropy(SR_glass_transmission_scatter_anisotropy)
+
+ , SR_glass_transmission_dispersion(SR_glass_transmission_dispersion)
+
+ , SR_glass_transmission_extra_roughness(SR_glass_transmission_extra_roughness)
+
+ , SR_glass_subsurface(SR_glass_subsurface)
+
+ , SR_glass_subsurface_color(SR_glass_subsurface_color)
+
+ , SR_glass_subsurface_radius(SR_glass_subsurface_radius)
+
+ , SR_glass_subsurface_scale(SR_glass_subsurface_scale)
+
+ , SR_glass_subsurface_anisotropy(SR_glass_subsurface_anisotropy)
+
+ , SR_glass_sheen(SR_glass_sheen)
+
+ , SR_glass_sheen_color(SR_glass_sheen_color)
+
+ , SR_glass_sheen_roughness(SR_glass_sheen_roughness)
+
+ , SR_glass_coat(SR_glass_coat)
+
+ , SR_glass_coat_color(SR_glass_coat_color)
+
+ , SR_glass_coat_roughness(SR_glass_coat_roughness)
+
+ , SR_glass_coat_anisotropy(SR_glass_coat_anisotropy)
+
+ , SR_glass_coat_rotation(SR_glass_coat_rotation)
+
+ , SR_glass_coat_IOR(SR_glass_coat_IOR)
+
+ , SR_glass_coat_affect_color(SR_glass_coat_affect_color)
+
+ , SR_glass_coat_affect_roughness(SR_glass_coat_affect_roughness)
+
+ , SR_glass_thin_film_thickness(SR_glass_thin_film_thickness)
+
+ , SR_glass_thin_film_IOR(SR_glass_thin_film_IOR)
+
+ , SR_glass_emission(SR_glass_emission)
+
+ , SR_glass_emission_color(SR_glass_emission_color)
+
+ , SR_glass_opacity(SR_glass_opacity)
+
+ , SR_glass_thin_walled(SR_glass_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+ float SR_glass_base;
+
+
+ vec3 SR_glass_base_color;
+
+
+ float SR_glass_diffuse_roughness;
+
+
+ float SR_glass_metalness;
+
+
+ float SR_glass_specular;
+
+
+ vec3 SR_glass_specular_color;
+
+
+ float SR_glass_specular_roughness;
+
+
+ float SR_glass_specular_IOR;
+
+
+ float SR_glass_specular_anisotropy;
+
+
+ float SR_glass_specular_rotation;
+
+
+ float SR_glass_transmission;
+
+
+ vec3 SR_glass_transmission_color;
+
+
+ float SR_glass_transmission_depth;
+
+
+ vec3 SR_glass_transmission_scatter;
+
+
+ float SR_glass_transmission_scatter_anisotropy;
+
+
+ float SR_glass_transmission_dispersion;
+
+
+ float SR_glass_transmission_extra_roughness;
+
+
+ float SR_glass_subsurface;
+
+
+ vec3 SR_glass_subsurface_color;
+
+
+ vec3 SR_glass_subsurface_radius;
+
+
+ float SR_glass_subsurface_scale;
+
+
+ float SR_glass_subsurface_anisotropy;
+
+
+ float SR_glass_sheen;
+
+
+ vec3 SR_glass_sheen_color;
+
+
+ float SR_glass_sheen_roughness;
+
+
+ float SR_glass_coat;
+
+
+ vec3 SR_glass_coat_color;
+
+
+ float SR_glass_coat_roughness;
+
+
+ float SR_glass_coat_anisotropy;
+
+
+ float SR_glass_coat_rotation;
+
+
+ float SR_glass_coat_IOR;
+
+
+ float SR_glass_coat_affect_color;
+
+
+ float SR_glass_coat_affect_roughness;
+
+
+ float SR_glass_thin_film_thickness;
+
+
+ float SR_glass_thin_film_IOR;
+
+
+ float SR_glass_emission;
+
+
+ vec3 SR_glass_emission_color;
+
+
+ vec3 SR_glass_opacity;
+
+
+ bool SR_glass_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_glass_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(SR_glass_base, SR_glass_base_color, SR_glass_diffuse_roughness, SR_glass_metalness, SR_glass_specular, SR_glass_specular_color, SR_glass_specular_roughness, SR_glass_specular_IOR, SR_glass_specular_anisotropy, SR_glass_specular_rotation, SR_glass_transmission, SR_glass_transmission_color, SR_glass_transmission_depth, SR_glass_transmission_scatter, SR_glass_transmission_scatter_anisotropy, SR_glass_transmission_dispersion, SR_glass_transmission_extra_roughness, SR_glass_subsurface, SR_glass_subsurface_color, SR_glass_subsurface_radius, SR_glass_subsurface_scale, SR_glass_subsurface_anisotropy, SR_glass_sheen, SR_glass_sheen_color, SR_glass_sheen_roughness, SR_glass_coat, SR_glass_coat_color, SR_glass_coat_roughness, SR_glass_coat_anisotropy, SR_glass_coat_rotation, SR_glass_coat_IOR, geomprop_Nworld_out1, SR_glass_coat_affect_color, SR_glass_coat_affect_roughness, SR_glass_thin_film_thickness, SR_glass_thin_film_IOR, SR_glass_emission, SR_glass_emission_color, SR_glass_opacity, SR_glass_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_glass_out);
+ material Glass_out = SR_glass_out;
+ out1 = float4(Glass_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(0)]], sampler u_envRadiance_sampler [[sampler(0)]]
+, texture2d u_envIrradiance_tex [[texture(1)]], sampler u_envIrradiance_sampler [[sampler(1)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+ , u_pub.SR_glass_base
+ , u_pub.SR_glass_base_color
+ , u_pub.SR_glass_diffuse_roughness
+ , u_pub.SR_glass_metalness
+ , u_pub.SR_glass_specular
+ , u_pub.SR_glass_specular_color
+ , u_pub.SR_glass_specular_roughness
+ , u_pub.SR_glass_specular_IOR
+ , u_pub.SR_glass_specular_anisotropy
+ , u_pub.SR_glass_specular_rotation
+ , u_pub.SR_glass_transmission
+ , u_pub.SR_glass_transmission_color
+ , u_pub.SR_glass_transmission_depth
+ , u_pub.SR_glass_transmission_scatter
+ , u_pub.SR_glass_transmission_scatter_anisotropy
+ , u_pub.SR_glass_transmission_dispersion
+ , u_pub.SR_glass_transmission_extra_roughness
+ , u_pub.SR_glass_subsurface
+ , u_pub.SR_glass_subsurface_color
+ , u_pub.SR_glass_subsurface_radius
+ , u_pub.SR_glass_subsurface_scale
+ , u_pub.SR_glass_subsurface_anisotropy
+ , u_pub.SR_glass_sheen
+ , u_pub.SR_glass_sheen_color
+ , u_pub.SR_glass_sheen_roughness
+ , u_pub.SR_glass_coat
+ , u_pub.SR_glass_coat_color
+ , u_pub.SR_glass_coat_roughness
+ , u_pub.SR_glass_coat_anisotropy
+ , u_pub.SR_glass_coat_rotation
+ , u_pub.SR_glass_coat_IOR
+ , u_pub.SR_glass_coat_affect_color
+ , u_pub.SR_glass_coat_affect_roughness
+ , u_pub.SR_glass_thin_film_thickness
+ , u_pub.SR_glass_thin_film_IOR
+ , u_pub.SR_glass_emission
+ , u_pub.SR_glass_emission_color
+ , u_pub.SR_glass_opacity
+ , u_pub.SR_glass_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/Glass.msl.vert b/Materials/Examples/StandardSurface/Glass.msl.vert
new file mode 100644
index 0000000000..c7d90391d7
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Glass.msl.vert
@@ -0,0 +1,110 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'SR_glass'. Function already called in this scope.
+ // Omitted node 'Glass'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(3) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Glass.osl b/Materials/Examples/StandardSurface/Glass.osl
new file mode 100644
index 0000000000..92d32527be
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Glass.osl
@@ -0,0 +1,637 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader Glass
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "Glass"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_base = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_base_color = color(0.8, 0.8, 0.8),
+ float SR_glass_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_metalness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_specular_color = color(1, 1, 1),
+ float SR_glass_specular_roughness = 0.01
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_specular_IOR = 1.52
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_transmission = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_transmission_color = color(1, 1, 1),
+ float SR_glass_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_transmission_scatter = color(0, 0, 0),
+ float SR_glass_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_subsurface_color = color(1, 1, 1),
+ color SR_glass_subsurface_radius = color(1, 1, 1),
+ float SR_glass_subsurface_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_sheen_color = color(1, 1, 1),
+ float SR_glass_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_coat_color = color(1, 1, 1),
+ float SR_glass_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_emission_color = color(1, 1, 1),
+ color SR_glass_opacity = color(1, 1, 1),
+ int SR_glass_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ surfaceshader SR_glass_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(SR_glass_base, SR_glass_base_color, SR_glass_diffuse_roughness, SR_glass_metalness, SR_glass_specular, SR_glass_specular_color, SR_glass_specular_roughness, SR_glass_specular_IOR, SR_glass_specular_anisotropy, SR_glass_specular_rotation, SR_glass_transmission, SR_glass_transmission_color, SR_glass_transmission_depth, SR_glass_transmission_scatter, SR_glass_transmission_scatter_anisotropy, SR_glass_transmission_dispersion, SR_glass_transmission_extra_roughness, SR_glass_subsurface, SR_glass_subsurface_color, SR_glass_subsurface_radius, SR_glass_subsurface_scale, SR_glass_subsurface_anisotropy, SR_glass_sheen, SR_glass_sheen_color, SR_glass_sheen_roughness, SR_glass_coat, SR_glass_coat_color, SR_glass_coat_roughness, SR_glass_coat_anisotropy, SR_glass_coat_rotation, SR_glass_coat_IOR, geomprop_Nworld_out1, SR_glass_coat_affect_color, SR_glass_coat_affect_roughness, SR_glass_thin_film_thickness, SR_glass_thin_film_IOR, SR_glass_emission, SR_glass_emission_color, SR_glass_opacity, SR_glass_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_glass_out);
+ MATERIAL Glass_out = mx_surfacematerial(SR_glass_out, displacementshader1);
+ out = Glass_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/GlassTinted.glsl.frag b/Materials/Examples/StandardSurface/GlassTinted.glsl.frag
new file mode 100644
index 0000000000..9b54d4134d
--- /dev/null
+++ b/Materials/Examples/StandardSurface/GlassTinted.glsl.frag
@@ -0,0 +1,1706 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform float SR_glass_tinted_base = 0.000000;
+uniform vec3 SR_glass_tinted_base_color = vec3(0.800000, 0.800000, 0.800000);
+uniform float SR_glass_tinted_diffuse_roughness = 0.000000;
+uniform float SR_glass_tinted_metalness = 0.000000;
+uniform float SR_glass_tinted_specular = 1.000000;
+uniform vec3 SR_glass_tinted_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_glass_tinted_specular_roughness = 0.150000;
+uniform float SR_glass_tinted_specular_IOR = 1.540000;
+uniform float SR_glass_tinted_specular_anisotropy = 0.000000;
+uniform float SR_glass_tinted_specular_rotation = 0.000000;
+uniform float SR_glass_tinted_transmission = 1.000000;
+uniform vec3 SR_glass_tinted_transmission_color = vec3(0.200000, 0.100000, 1.000000);
+uniform float SR_glass_tinted_transmission_depth = 0.000000;
+uniform vec3 SR_glass_tinted_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float SR_glass_tinted_transmission_scatter_anisotropy = 0.000000;
+uniform float SR_glass_tinted_transmission_dispersion = 0.000000;
+uniform float SR_glass_tinted_transmission_extra_roughness = 0.000000;
+uniform float SR_glass_tinted_subsurface = 0.000000;
+uniform vec3 SR_glass_tinted_subsurface_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_glass_tinted_subsurface_radius = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_glass_tinted_subsurface_scale = 1.000000;
+uniform float SR_glass_tinted_subsurface_anisotropy = 0.000000;
+uniform float SR_glass_tinted_sheen = 0.000000;
+uniform vec3 SR_glass_tinted_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_glass_tinted_sheen_roughness = 0.300000;
+uniform float SR_glass_tinted_coat = 0.000000;
+uniform vec3 SR_glass_tinted_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_glass_tinted_coat_roughness = 0.100000;
+uniform float SR_glass_tinted_coat_anisotropy = 0.000000;
+uniform float SR_glass_tinted_coat_rotation = 0.000000;
+uniform float SR_glass_tinted_coat_IOR = 1.500000;
+uniform float SR_glass_tinted_coat_affect_color = 0.000000;
+uniform float SR_glass_tinted_coat_affect_roughness = 0.000000;
+uniform float SR_glass_tinted_thin_film_thickness = 0.000000;
+uniform float SR_glass_tinted_thin_film_IOR = 1.500000;
+uniform float SR_glass_tinted_emission = 0.000000;
+uniform vec3 SR_glass_tinted_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_glass_tinted_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool SR_glass_tinted_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_glass_tinted_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(SR_glass_tinted_base, SR_glass_tinted_base_color, SR_glass_tinted_diffuse_roughness, SR_glass_tinted_metalness, SR_glass_tinted_specular, SR_glass_tinted_specular_color, SR_glass_tinted_specular_roughness, SR_glass_tinted_specular_IOR, SR_glass_tinted_specular_anisotropy, SR_glass_tinted_specular_rotation, SR_glass_tinted_transmission, SR_glass_tinted_transmission_color, SR_glass_tinted_transmission_depth, SR_glass_tinted_transmission_scatter, SR_glass_tinted_transmission_scatter_anisotropy, SR_glass_tinted_transmission_dispersion, SR_glass_tinted_transmission_extra_roughness, SR_glass_tinted_subsurface, SR_glass_tinted_subsurface_color, SR_glass_tinted_subsurface_radius, SR_glass_tinted_subsurface_scale, SR_glass_tinted_subsurface_anisotropy, SR_glass_tinted_sheen, SR_glass_tinted_sheen_color, SR_glass_tinted_sheen_roughness, SR_glass_tinted_coat, SR_glass_tinted_coat_color, SR_glass_tinted_coat_roughness, SR_glass_tinted_coat_anisotropy, SR_glass_tinted_coat_rotation, SR_glass_tinted_coat_IOR, geomprop_Nworld_out1, SR_glass_tinted_coat_affect_color, SR_glass_tinted_coat_affect_roughness, SR_glass_tinted_thin_film_thickness, SR_glass_tinted_thin_film_IOR, SR_glass_tinted_emission, SR_glass_tinted_emission_color, SR_glass_tinted_opacity, SR_glass_tinted_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_glass_tinted_out);
+ material GlassTinted_out = SR_glass_tinted_out;
+ out1 = vec4(GlassTinted_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/GlassTinted.glsl.vert b/Materials/Examples/StandardSurface/GlassTinted.glsl.vert
new file mode 100644
index 0000000000..6133cb8f5e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/GlassTinted.glsl.vert
@@ -0,0 +1,28 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/GlassTinted.mdl b/Materials/Examples/StandardSurface/GlassTinted.mdl
new file mode 100644
index 0000000000..5e273cfdbc
--- /dev/null
+++ b/Materials/Examples/StandardSurface/GlassTinted.mdl
@@ -0,0 +1,175 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material GlassTinted
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ float SR_glass_tinted_base = 0,
+ color SR_glass_tinted_base_color = color(0.8, 0.8, 0.8),
+ float SR_glass_tinted_diffuse_roughness = 0,
+ float SR_glass_tinted_metalness = 0,
+ float SR_glass_tinted_specular = 1,
+ color SR_glass_tinted_specular_color = color(1, 1, 1),
+ float SR_glass_tinted_specular_roughness = 0.15,
+ uniform float SR_glass_tinted_specular_IOR = 1.54,
+ float SR_glass_tinted_specular_anisotropy = 0,
+ float SR_glass_tinted_specular_rotation = 0,
+ float SR_glass_tinted_transmission = 1,
+ color SR_glass_tinted_transmission_color = color(0.2, 0.1, 1),
+ float SR_glass_tinted_transmission_depth = 0,
+ color SR_glass_tinted_transmission_scatter = color(0, 0, 0),
+ float SR_glass_tinted_transmission_scatter_anisotropy = 0,
+ float SR_glass_tinted_transmission_dispersion = 0,
+ float SR_glass_tinted_transmission_extra_roughness = 0,
+ float SR_glass_tinted_subsurface = 0,
+ color SR_glass_tinted_subsurface_color = color(1, 1, 1),
+ color SR_glass_tinted_subsurface_radius = color(1, 1, 1),
+ float SR_glass_tinted_subsurface_scale = 1,
+ float SR_glass_tinted_subsurface_anisotropy = 0,
+ float SR_glass_tinted_sheen = 0,
+ color SR_glass_tinted_sheen_color = color(1, 1, 1),
+ float SR_glass_tinted_sheen_roughness = 0.3,
+ float SR_glass_tinted_coat = 0,
+ color SR_glass_tinted_coat_color = color(1, 1, 1),
+ float SR_glass_tinted_coat_roughness = 0.1,
+ float SR_glass_tinted_coat_anisotropy = 0,
+ float SR_glass_tinted_coat_rotation = 0,
+ uniform float SR_glass_tinted_coat_IOR = 1.5,
+ float SR_glass_tinted_coat_affect_color = 0,
+ float SR_glass_tinted_coat_affect_roughness = 0,
+ float SR_glass_tinted_thin_film_thickness = 0,
+ float SR_glass_tinted_thin_film_IOR = 1.5,
+ float SR_glass_tinted_emission = 0,
+ color SR_glass_tinted_emission_color = color(1, 1, 1),
+ color SR_glass_tinted_opacity = color(1, 1, 1),
+ bool SR_glass_tinted_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ material SR_glass_tinted_out = NG_standard_surface_surfaceshader_100(SR_glass_tinted_base, SR_glass_tinted_base_color, SR_glass_tinted_diffuse_roughness, SR_glass_tinted_metalness, SR_glass_tinted_specular, SR_glass_tinted_specular_color, SR_glass_tinted_specular_roughness, SR_glass_tinted_specular_IOR, SR_glass_tinted_specular_anisotropy, SR_glass_tinted_specular_rotation, SR_glass_tinted_transmission, SR_glass_tinted_transmission_color, SR_glass_tinted_transmission_depth, SR_glass_tinted_transmission_scatter, SR_glass_tinted_transmission_scatter_anisotropy, SR_glass_tinted_transmission_dispersion, SR_glass_tinted_transmission_extra_roughness, SR_glass_tinted_subsurface, SR_glass_tinted_subsurface_color, SR_glass_tinted_subsurface_radius, SR_glass_tinted_subsurface_scale, SR_glass_tinted_subsurface_anisotropy, SR_glass_tinted_sheen, SR_glass_tinted_sheen_color, SR_glass_tinted_sheen_roughness, SR_glass_tinted_coat, SR_glass_tinted_coat_color, SR_glass_tinted_coat_roughness, SR_glass_tinted_coat_anisotropy, SR_glass_tinted_coat_rotation, SR_glass_tinted_coat_IOR, geomprop_Nworld_out1, SR_glass_tinted_coat_affect_color, SR_glass_tinted_coat_affect_roughness, SR_glass_tinted_thin_film_thickness, SR_glass_tinted_thin_film_IOR, SR_glass_tinted_emission, SR_glass_tinted_emission_color, SR_glass_tinted_opacity, SR_glass_tinted_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1);
+ material GlassTinted_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: SR_glass_tinted_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = GlassTinted_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/GlassTinted.msl.frag b/Materials/Examples/StandardSurface/GlassTinted.msl.frag
new file mode 100644
index 0000000000..fab26e0206
--- /dev/null
+++ b/Materials/Examples/StandardSurface/GlassTinted.msl.frag
@@ -0,0 +1,2324 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ float SR_glass_tinted_base;
+ vec3 SR_glass_tinted_base_color;
+ float SR_glass_tinted_diffuse_roughness;
+ float SR_glass_tinted_metalness;
+ float SR_glass_tinted_specular;
+ vec3 SR_glass_tinted_specular_color;
+ float SR_glass_tinted_specular_roughness;
+ float SR_glass_tinted_specular_IOR;
+ float SR_glass_tinted_specular_anisotropy;
+ float SR_glass_tinted_specular_rotation;
+ float SR_glass_tinted_transmission;
+ vec3 SR_glass_tinted_transmission_color;
+ float SR_glass_tinted_transmission_depth;
+ vec3 SR_glass_tinted_transmission_scatter;
+ float SR_glass_tinted_transmission_scatter_anisotropy;
+ float SR_glass_tinted_transmission_dispersion;
+ float SR_glass_tinted_transmission_extra_roughness;
+ float SR_glass_tinted_subsurface;
+ vec3 SR_glass_tinted_subsurface_color;
+ vec3 SR_glass_tinted_subsurface_radius;
+ float SR_glass_tinted_subsurface_scale;
+ float SR_glass_tinted_subsurface_anisotropy;
+ float SR_glass_tinted_sheen;
+ vec3 SR_glass_tinted_sheen_color;
+ float SR_glass_tinted_sheen_roughness;
+ float SR_glass_tinted_coat;
+ vec3 SR_glass_tinted_coat_color;
+ float SR_glass_tinted_coat_roughness;
+ float SR_glass_tinted_coat_anisotropy;
+ float SR_glass_tinted_coat_rotation;
+ float SR_glass_tinted_coat_IOR;
+ float SR_glass_tinted_coat_affect_color;
+ float SR_glass_tinted_coat_affect_roughness;
+ float SR_glass_tinted_thin_film_thickness;
+ float SR_glass_tinted_thin_film_IOR;
+ float SR_glass_tinted_emission;
+ vec3 SR_glass_tinted_emission_color;
+ vec3 SR_glass_tinted_opacity;
+ bool SR_glass_tinted_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+ , float SR_glass_tinted_base
+
+ , vec3 SR_glass_tinted_base_color
+
+ , float SR_glass_tinted_diffuse_roughness
+
+ , float SR_glass_tinted_metalness
+
+ , float SR_glass_tinted_specular
+
+ , vec3 SR_glass_tinted_specular_color
+
+ , float SR_glass_tinted_specular_roughness
+
+ , float SR_glass_tinted_specular_IOR
+
+ , float SR_glass_tinted_specular_anisotropy
+
+ , float SR_glass_tinted_specular_rotation
+
+ , float SR_glass_tinted_transmission
+
+ , vec3 SR_glass_tinted_transmission_color
+
+ , float SR_glass_tinted_transmission_depth
+
+ , vec3 SR_glass_tinted_transmission_scatter
+
+ , float SR_glass_tinted_transmission_scatter_anisotropy
+
+ , float SR_glass_tinted_transmission_dispersion
+
+ , float SR_glass_tinted_transmission_extra_roughness
+
+ , float SR_glass_tinted_subsurface
+
+ , vec3 SR_glass_tinted_subsurface_color
+
+ , vec3 SR_glass_tinted_subsurface_radius
+
+ , float SR_glass_tinted_subsurface_scale
+
+ , float SR_glass_tinted_subsurface_anisotropy
+
+ , float SR_glass_tinted_sheen
+
+ , vec3 SR_glass_tinted_sheen_color
+
+ , float SR_glass_tinted_sheen_roughness
+
+ , float SR_glass_tinted_coat
+
+ , vec3 SR_glass_tinted_coat_color
+
+ , float SR_glass_tinted_coat_roughness
+
+ , float SR_glass_tinted_coat_anisotropy
+
+ , float SR_glass_tinted_coat_rotation
+
+ , float SR_glass_tinted_coat_IOR
+
+ , float SR_glass_tinted_coat_affect_color
+
+ , float SR_glass_tinted_coat_affect_roughness
+
+ , float SR_glass_tinted_thin_film_thickness
+
+ , float SR_glass_tinted_thin_film_IOR
+
+ , float SR_glass_tinted_emission
+
+ , vec3 SR_glass_tinted_emission_color
+
+ , vec3 SR_glass_tinted_opacity
+
+ , bool SR_glass_tinted_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+ , SR_glass_tinted_base(SR_glass_tinted_base)
+
+ , SR_glass_tinted_base_color(SR_glass_tinted_base_color)
+
+ , SR_glass_tinted_diffuse_roughness(SR_glass_tinted_diffuse_roughness)
+
+ , SR_glass_tinted_metalness(SR_glass_tinted_metalness)
+
+ , SR_glass_tinted_specular(SR_glass_tinted_specular)
+
+ , SR_glass_tinted_specular_color(SR_glass_tinted_specular_color)
+
+ , SR_glass_tinted_specular_roughness(SR_glass_tinted_specular_roughness)
+
+ , SR_glass_tinted_specular_IOR(SR_glass_tinted_specular_IOR)
+
+ , SR_glass_tinted_specular_anisotropy(SR_glass_tinted_specular_anisotropy)
+
+ , SR_glass_tinted_specular_rotation(SR_glass_tinted_specular_rotation)
+
+ , SR_glass_tinted_transmission(SR_glass_tinted_transmission)
+
+ , SR_glass_tinted_transmission_color(SR_glass_tinted_transmission_color)
+
+ , SR_glass_tinted_transmission_depth(SR_glass_tinted_transmission_depth)
+
+ , SR_glass_tinted_transmission_scatter(SR_glass_tinted_transmission_scatter)
+
+ , SR_glass_tinted_transmission_scatter_anisotropy(SR_glass_tinted_transmission_scatter_anisotropy)
+
+ , SR_glass_tinted_transmission_dispersion(SR_glass_tinted_transmission_dispersion)
+
+ , SR_glass_tinted_transmission_extra_roughness(SR_glass_tinted_transmission_extra_roughness)
+
+ , SR_glass_tinted_subsurface(SR_glass_tinted_subsurface)
+
+ , SR_glass_tinted_subsurface_color(SR_glass_tinted_subsurface_color)
+
+ , SR_glass_tinted_subsurface_radius(SR_glass_tinted_subsurface_radius)
+
+ , SR_glass_tinted_subsurface_scale(SR_glass_tinted_subsurface_scale)
+
+ , SR_glass_tinted_subsurface_anisotropy(SR_glass_tinted_subsurface_anisotropy)
+
+ , SR_glass_tinted_sheen(SR_glass_tinted_sheen)
+
+ , SR_glass_tinted_sheen_color(SR_glass_tinted_sheen_color)
+
+ , SR_glass_tinted_sheen_roughness(SR_glass_tinted_sheen_roughness)
+
+ , SR_glass_tinted_coat(SR_glass_tinted_coat)
+
+ , SR_glass_tinted_coat_color(SR_glass_tinted_coat_color)
+
+ , SR_glass_tinted_coat_roughness(SR_glass_tinted_coat_roughness)
+
+ , SR_glass_tinted_coat_anisotropy(SR_glass_tinted_coat_anisotropy)
+
+ , SR_glass_tinted_coat_rotation(SR_glass_tinted_coat_rotation)
+
+ , SR_glass_tinted_coat_IOR(SR_glass_tinted_coat_IOR)
+
+ , SR_glass_tinted_coat_affect_color(SR_glass_tinted_coat_affect_color)
+
+ , SR_glass_tinted_coat_affect_roughness(SR_glass_tinted_coat_affect_roughness)
+
+ , SR_glass_tinted_thin_film_thickness(SR_glass_tinted_thin_film_thickness)
+
+ , SR_glass_tinted_thin_film_IOR(SR_glass_tinted_thin_film_IOR)
+
+ , SR_glass_tinted_emission(SR_glass_tinted_emission)
+
+ , SR_glass_tinted_emission_color(SR_glass_tinted_emission_color)
+
+ , SR_glass_tinted_opacity(SR_glass_tinted_opacity)
+
+ , SR_glass_tinted_thin_walled(SR_glass_tinted_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+ float SR_glass_tinted_base;
+
+
+ vec3 SR_glass_tinted_base_color;
+
+
+ float SR_glass_tinted_diffuse_roughness;
+
+
+ float SR_glass_tinted_metalness;
+
+
+ float SR_glass_tinted_specular;
+
+
+ vec3 SR_glass_tinted_specular_color;
+
+
+ float SR_glass_tinted_specular_roughness;
+
+
+ float SR_glass_tinted_specular_IOR;
+
+
+ float SR_glass_tinted_specular_anisotropy;
+
+
+ float SR_glass_tinted_specular_rotation;
+
+
+ float SR_glass_tinted_transmission;
+
+
+ vec3 SR_glass_tinted_transmission_color;
+
+
+ float SR_glass_tinted_transmission_depth;
+
+
+ vec3 SR_glass_tinted_transmission_scatter;
+
+
+ float SR_glass_tinted_transmission_scatter_anisotropy;
+
+
+ float SR_glass_tinted_transmission_dispersion;
+
+
+ float SR_glass_tinted_transmission_extra_roughness;
+
+
+ float SR_glass_tinted_subsurface;
+
+
+ vec3 SR_glass_tinted_subsurface_color;
+
+
+ vec3 SR_glass_tinted_subsurface_radius;
+
+
+ float SR_glass_tinted_subsurface_scale;
+
+
+ float SR_glass_tinted_subsurface_anisotropy;
+
+
+ float SR_glass_tinted_sheen;
+
+
+ vec3 SR_glass_tinted_sheen_color;
+
+
+ float SR_glass_tinted_sheen_roughness;
+
+
+ float SR_glass_tinted_coat;
+
+
+ vec3 SR_glass_tinted_coat_color;
+
+
+ float SR_glass_tinted_coat_roughness;
+
+
+ float SR_glass_tinted_coat_anisotropy;
+
+
+ float SR_glass_tinted_coat_rotation;
+
+
+ float SR_glass_tinted_coat_IOR;
+
+
+ float SR_glass_tinted_coat_affect_color;
+
+
+ float SR_glass_tinted_coat_affect_roughness;
+
+
+ float SR_glass_tinted_thin_film_thickness;
+
+
+ float SR_glass_tinted_thin_film_IOR;
+
+
+ float SR_glass_tinted_emission;
+
+
+ vec3 SR_glass_tinted_emission_color;
+
+
+ vec3 SR_glass_tinted_opacity;
+
+
+ bool SR_glass_tinted_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_glass_tinted_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(SR_glass_tinted_base, SR_glass_tinted_base_color, SR_glass_tinted_diffuse_roughness, SR_glass_tinted_metalness, SR_glass_tinted_specular, SR_glass_tinted_specular_color, SR_glass_tinted_specular_roughness, SR_glass_tinted_specular_IOR, SR_glass_tinted_specular_anisotropy, SR_glass_tinted_specular_rotation, SR_glass_tinted_transmission, SR_glass_tinted_transmission_color, SR_glass_tinted_transmission_depth, SR_glass_tinted_transmission_scatter, SR_glass_tinted_transmission_scatter_anisotropy, SR_glass_tinted_transmission_dispersion, SR_glass_tinted_transmission_extra_roughness, SR_glass_tinted_subsurface, SR_glass_tinted_subsurface_color, SR_glass_tinted_subsurface_radius, SR_glass_tinted_subsurface_scale, SR_glass_tinted_subsurface_anisotropy, SR_glass_tinted_sheen, SR_glass_tinted_sheen_color, SR_glass_tinted_sheen_roughness, SR_glass_tinted_coat, SR_glass_tinted_coat_color, SR_glass_tinted_coat_roughness, SR_glass_tinted_coat_anisotropy, SR_glass_tinted_coat_rotation, SR_glass_tinted_coat_IOR, geomprop_Nworld_out1, SR_glass_tinted_coat_affect_color, SR_glass_tinted_coat_affect_roughness, SR_glass_tinted_thin_film_thickness, SR_glass_tinted_thin_film_IOR, SR_glass_tinted_emission, SR_glass_tinted_emission_color, SR_glass_tinted_opacity, SR_glass_tinted_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_glass_tinted_out);
+ material GlassTinted_out = SR_glass_tinted_out;
+ out1 = float4(GlassTinted_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(0)]], sampler u_envRadiance_sampler [[sampler(0)]]
+, texture2d u_envIrradiance_tex [[texture(1)]], sampler u_envIrradiance_sampler [[sampler(1)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+ , u_pub.SR_glass_tinted_base
+ , u_pub.SR_glass_tinted_base_color
+ , u_pub.SR_glass_tinted_diffuse_roughness
+ , u_pub.SR_glass_tinted_metalness
+ , u_pub.SR_glass_tinted_specular
+ , u_pub.SR_glass_tinted_specular_color
+ , u_pub.SR_glass_tinted_specular_roughness
+ , u_pub.SR_glass_tinted_specular_IOR
+ , u_pub.SR_glass_tinted_specular_anisotropy
+ , u_pub.SR_glass_tinted_specular_rotation
+ , u_pub.SR_glass_tinted_transmission
+ , u_pub.SR_glass_tinted_transmission_color
+ , u_pub.SR_glass_tinted_transmission_depth
+ , u_pub.SR_glass_tinted_transmission_scatter
+ , u_pub.SR_glass_tinted_transmission_scatter_anisotropy
+ , u_pub.SR_glass_tinted_transmission_dispersion
+ , u_pub.SR_glass_tinted_transmission_extra_roughness
+ , u_pub.SR_glass_tinted_subsurface
+ , u_pub.SR_glass_tinted_subsurface_color
+ , u_pub.SR_glass_tinted_subsurface_radius
+ , u_pub.SR_glass_tinted_subsurface_scale
+ , u_pub.SR_glass_tinted_subsurface_anisotropy
+ , u_pub.SR_glass_tinted_sheen
+ , u_pub.SR_glass_tinted_sheen_color
+ , u_pub.SR_glass_tinted_sheen_roughness
+ , u_pub.SR_glass_tinted_coat
+ , u_pub.SR_glass_tinted_coat_color
+ , u_pub.SR_glass_tinted_coat_roughness
+ , u_pub.SR_glass_tinted_coat_anisotropy
+ , u_pub.SR_glass_tinted_coat_rotation
+ , u_pub.SR_glass_tinted_coat_IOR
+ , u_pub.SR_glass_tinted_coat_affect_color
+ , u_pub.SR_glass_tinted_coat_affect_roughness
+ , u_pub.SR_glass_tinted_thin_film_thickness
+ , u_pub.SR_glass_tinted_thin_film_IOR
+ , u_pub.SR_glass_tinted_emission
+ , u_pub.SR_glass_tinted_emission_color
+ , u_pub.SR_glass_tinted_opacity
+ , u_pub.SR_glass_tinted_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/GlassTinted.msl.vert b/Materials/Examples/StandardSurface/GlassTinted.msl.vert
new file mode 100644
index 0000000000..c2c8403972
--- /dev/null
+++ b/Materials/Examples/StandardSurface/GlassTinted.msl.vert
@@ -0,0 +1,110 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'SR_glass_tinted'. Function already called in this scope.
+ // Omitted node 'GlassTinted'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(3) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/GlassTinted.osl b/Materials/Examples/StandardSurface/GlassTinted.osl
new file mode 100644
index 0000000000..c1b7893df7
--- /dev/null
+++ b/Materials/Examples/StandardSurface/GlassTinted.osl
@@ -0,0 +1,637 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader GlassTinted
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "GlassTinted"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_base = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_tinted_base_color = color(0.8, 0.8, 0.8),
+ float SR_glass_tinted_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_metalness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_tinted_specular_color = color(1, 1, 1),
+ float SR_glass_tinted_specular_roughness = 0.15
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_specular_IOR = 1.54
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_transmission = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_tinted_transmission_color = color(0.2, 0.1, 1),
+ float SR_glass_tinted_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_tinted_transmission_scatter = color(0, 0, 0),
+ float SR_glass_tinted_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_tinted_subsurface_color = color(1, 1, 1),
+ color SR_glass_tinted_subsurface_radius = color(1, 1, 1),
+ float SR_glass_tinted_subsurface_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_tinted_sheen_color = color(1, 1, 1),
+ float SR_glass_tinted_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_tinted_coat_color = color(1, 1, 1),
+ float SR_glass_tinted_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_glass_tinted_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_glass_tinted_emission_color = color(1, 1, 1),
+ color SR_glass_tinted_opacity = color(1, 1, 1),
+ int SR_glass_tinted_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ surfaceshader SR_glass_tinted_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(SR_glass_tinted_base, SR_glass_tinted_base_color, SR_glass_tinted_diffuse_roughness, SR_glass_tinted_metalness, SR_glass_tinted_specular, SR_glass_tinted_specular_color, SR_glass_tinted_specular_roughness, SR_glass_tinted_specular_IOR, SR_glass_tinted_specular_anisotropy, SR_glass_tinted_specular_rotation, SR_glass_tinted_transmission, SR_glass_tinted_transmission_color, SR_glass_tinted_transmission_depth, SR_glass_tinted_transmission_scatter, SR_glass_tinted_transmission_scatter_anisotropy, SR_glass_tinted_transmission_dispersion, SR_glass_tinted_transmission_extra_roughness, SR_glass_tinted_subsurface, SR_glass_tinted_subsurface_color, SR_glass_tinted_subsurface_radius, SR_glass_tinted_subsurface_scale, SR_glass_tinted_subsurface_anisotropy, SR_glass_tinted_sheen, SR_glass_tinted_sheen_color, SR_glass_tinted_sheen_roughness, SR_glass_tinted_coat, SR_glass_tinted_coat_color, SR_glass_tinted_coat_roughness, SR_glass_tinted_coat_anisotropy, SR_glass_tinted_coat_rotation, SR_glass_tinted_coat_IOR, geomprop_Nworld_out1, SR_glass_tinted_coat_affect_color, SR_glass_tinted_coat_affect_roughness, SR_glass_tinted_thin_film_thickness, SR_glass_tinted_thin_film_IOR, SR_glass_tinted_emission, SR_glass_tinted_emission_color, SR_glass_tinted_opacity, SR_glass_tinted_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_glass_tinted_out);
+ MATERIAL GlassTinted_out = mx_surfacematerial(SR_glass_tinted_out, displacementshader1);
+ out = GlassTinted_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Gold.glsl.frag b/Materials/Examples/StandardSurface/Gold.glsl.frag
new file mode 100644
index 0000000000..178a09910e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Gold.glsl.frag
@@ -0,0 +1,1706 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform float SR_gold_base = 1.000000;
+uniform vec3 SR_gold_base_color = vec3(0.944000, 0.776000, 0.373000);
+uniform float SR_gold_diffuse_roughness = 0.000000;
+uniform float SR_gold_metalness = 1.000000;
+uniform float SR_gold_specular = 1.000000;
+uniform vec3 SR_gold_specular_color = vec3(0.998000, 0.981000, 0.751000);
+uniform float SR_gold_specular_roughness = 0.020000;
+uniform float SR_gold_specular_IOR = 1.500000;
+uniform float SR_gold_specular_anisotropy = 0.000000;
+uniform float SR_gold_specular_rotation = 0.000000;
+uniform float SR_gold_transmission = 0.000000;
+uniform vec3 SR_gold_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_gold_transmission_depth = 0.000000;
+uniform vec3 SR_gold_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float SR_gold_transmission_scatter_anisotropy = 0.000000;
+uniform float SR_gold_transmission_dispersion = 0.000000;
+uniform float SR_gold_transmission_extra_roughness = 0.000000;
+uniform float SR_gold_subsurface = 0.000000;
+uniform vec3 SR_gold_subsurface_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_gold_subsurface_radius = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_gold_subsurface_scale = 1.000000;
+uniform float SR_gold_subsurface_anisotropy = 0.000000;
+uniform float SR_gold_sheen = 0.000000;
+uniform vec3 SR_gold_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_gold_sheen_roughness = 0.300000;
+uniform float SR_gold_coat = 0.000000;
+uniform vec3 SR_gold_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_gold_coat_roughness = 0.100000;
+uniform float SR_gold_coat_anisotropy = 0.000000;
+uniform float SR_gold_coat_rotation = 0.000000;
+uniform float SR_gold_coat_IOR = 1.500000;
+uniform float SR_gold_coat_affect_color = 0.000000;
+uniform float SR_gold_coat_affect_roughness = 0.000000;
+uniform float SR_gold_thin_film_thickness = 0.000000;
+uniform float SR_gold_thin_film_IOR = 1.500000;
+uniform float SR_gold_emission = 0.000000;
+uniform vec3 SR_gold_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_gold_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool SR_gold_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_gold_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(SR_gold_base, SR_gold_base_color, SR_gold_diffuse_roughness, SR_gold_metalness, SR_gold_specular, SR_gold_specular_color, SR_gold_specular_roughness, SR_gold_specular_IOR, SR_gold_specular_anisotropy, SR_gold_specular_rotation, SR_gold_transmission, SR_gold_transmission_color, SR_gold_transmission_depth, SR_gold_transmission_scatter, SR_gold_transmission_scatter_anisotropy, SR_gold_transmission_dispersion, SR_gold_transmission_extra_roughness, SR_gold_subsurface, SR_gold_subsurface_color, SR_gold_subsurface_radius, SR_gold_subsurface_scale, SR_gold_subsurface_anisotropy, SR_gold_sheen, SR_gold_sheen_color, SR_gold_sheen_roughness, SR_gold_coat, SR_gold_coat_color, SR_gold_coat_roughness, SR_gold_coat_anisotropy, SR_gold_coat_rotation, SR_gold_coat_IOR, geomprop_Nworld_out1, SR_gold_coat_affect_color, SR_gold_coat_affect_roughness, SR_gold_thin_film_thickness, SR_gold_thin_film_IOR, SR_gold_emission, SR_gold_emission_color, SR_gold_opacity, SR_gold_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_gold_out);
+ material Gold_out = SR_gold_out;
+ out1 = vec4(Gold_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/Gold.glsl.vert b/Materials/Examples/StandardSurface/Gold.glsl.vert
new file mode 100644
index 0000000000..6133cb8f5e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Gold.glsl.vert
@@ -0,0 +1,28 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/Gold.mdl b/Materials/Examples/StandardSurface/Gold.mdl
new file mode 100644
index 0000000000..c72683ab0e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Gold.mdl
@@ -0,0 +1,175 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material Gold
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ float SR_gold_base = 1,
+ color SR_gold_base_color = color(0.944, 0.776, 0.373),
+ float SR_gold_diffuse_roughness = 0,
+ float SR_gold_metalness = 1,
+ float SR_gold_specular = 1,
+ color SR_gold_specular_color = color(0.998, 0.981, 0.751),
+ float SR_gold_specular_roughness = 0.02,
+ uniform float SR_gold_specular_IOR = 1.5,
+ float SR_gold_specular_anisotropy = 0,
+ float SR_gold_specular_rotation = 0,
+ float SR_gold_transmission = 0,
+ color SR_gold_transmission_color = color(1, 1, 1),
+ float SR_gold_transmission_depth = 0,
+ color SR_gold_transmission_scatter = color(0, 0, 0),
+ float SR_gold_transmission_scatter_anisotropy = 0,
+ float SR_gold_transmission_dispersion = 0,
+ float SR_gold_transmission_extra_roughness = 0,
+ float SR_gold_subsurface = 0,
+ color SR_gold_subsurface_color = color(1, 1, 1),
+ color SR_gold_subsurface_radius = color(1, 1, 1),
+ float SR_gold_subsurface_scale = 1,
+ float SR_gold_subsurface_anisotropy = 0,
+ float SR_gold_sheen = 0,
+ color SR_gold_sheen_color = color(1, 1, 1),
+ float SR_gold_sheen_roughness = 0.3,
+ float SR_gold_coat = 0,
+ color SR_gold_coat_color = color(1, 1, 1),
+ float SR_gold_coat_roughness = 0.1,
+ float SR_gold_coat_anisotropy = 0,
+ float SR_gold_coat_rotation = 0,
+ uniform float SR_gold_coat_IOR = 1.5,
+ float SR_gold_coat_affect_color = 0,
+ float SR_gold_coat_affect_roughness = 0,
+ float SR_gold_thin_film_thickness = 0,
+ float SR_gold_thin_film_IOR = 1.5,
+ float SR_gold_emission = 0,
+ color SR_gold_emission_color = color(1, 1, 1),
+ color SR_gold_opacity = color(1, 1, 1),
+ bool SR_gold_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ material SR_gold_out = NG_standard_surface_surfaceshader_100(SR_gold_base, SR_gold_base_color, SR_gold_diffuse_roughness, SR_gold_metalness, SR_gold_specular, SR_gold_specular_color, SR_gold_specular_roughness, SR_gold_specular_IOR, SR_gold_specular_anisotropy, SR_gold_specular_rotation, SR_gold_transmission, SR_gold_transmission_color, SR_gold_transmission_depth, SR_gold_transmission_scatter, SR_gold_transmission_scatter_anisotropy, SR_gold_transmission_dispersion, SR_gold_transmission_extra_roughness, SR_gold_subsurface, SR_gold_subsurface_color, SR_gold_subsurface_radius, SR_gold_subsurface_scale, SR_gold_subsurface_anisotropy, SR_gold_sheen, SR_gold_sheen_color, SR_gold_sheen_roughness, SR_gold_coat, SR_gold_coat_color, SR_gold_coat_roughness, SR_gold_coat_anisotropy, SR_gold_coat_rotation, SR_gold_coat_IOR, geomprop_Nworld_out1, SR_gold_coat_affect_color, SR_gold_coat_affect_roughness, SR_gold_thin_film_thickness, SR_gold_thin_film_IOR, SR_gold_emission, SR_gold_emission_color, SR_gold_opacity, SR_gold_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1);
+ material Gold_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: SR_gold_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = Gold_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/Gold.msl.frag b/Materials/Examples/StandardSurface/Gold.msl.frag
new file mode 100644
index 0000000000..2d4ea5e89a
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Gold.msl.frag
@@ -0,0 +1,2324 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ float SR_gold_base;
+ vec3 SR_gold_base_color;
+ float SR_gold_diffuse_roughness;
+ float SR_gold_metalness;
+ float SR_gold_specular;
+ vec3 SR_gold_specular_color;
+ float SR_gold_specular_roughness;
+ float SR_gold_specular_IOR;
+ float SR_gold_specular_anisotropy;
+ float SR_gold_specular_rotation;
+ float SR_gold_transmission;
+ vec3 SR_gold_transmission_color;
+ float SR_gold_transmission_depth;
+ vec3 SR_gold_transmission_scatter;
+ float SR_gold_transmission_scatter_anisotropy;
+ float SR_gold_transmission_dispersion;
+ float SR_gold_transmission_extra_roughness;
+ float SR_gold_subsurface;
+ vec3 SR_gold_subsurface_color;
+ vec3 SR_gold_subsurface_radius;
+ float SR_gold_subsurface_scale;
+ float SR_gold_subsurface_anisotropy;
+ float SR_gold_sheen;
+ vec3 SR_gold_sheen_color;
+ float SR_gold_sheen_roughness;
+ float SR_gold_coat;
+ vec3 SR_gold_coat_color;
+ float SR_gold_coat_roughness;
+ float SR_gold_coat_anisotropy;
+ float SR_gold_coat_rotation;
+ float SR_gold_coat_IOR;
+ float SR_gold_coat_affect_color;
+ float SR_gold_coat_affect_roughness;
+ float SR_gold_thin_film_thickness;
+ float SR_gold_thin_film_IOR;
+ float SR_gold_emission;
+ vec3 SR_gold_emission_color;
+ vec3 SR_gold_opacity;
+ bool SR_gold_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+ , float SR_gold_base
+
+ , vec3 SR_gold_base_color
+
+ , float SR_gold_diffuse_roughness
+
+ , float SR_gold_metalness
+
+ , float SR_gold_specular
+
+ , vec3 SR_gold_specular_color
+
+ , float SR_gold_specular_roughness
+
+ , float SR_gold_specular_IOR
+
+ , float SR_gold_specular_anisotropy
+
+ , float SR_gold_specular_rotation
+
+ , float SR_gold_transmission
+
+ , vec3 SR_gold_transmission_color
+
+ , float SR_gold_transmission_depth
+
+ , vec3 SR_gold_transmission_scatter
+
+ , float SR_gold_transmission_scatter_anisotropy
+
+ , float SR_gold_transmission_dispersion
+
+ , float SR_gold_transmission_extra_roughness
+
+ , float SR_gold_subsurface
+
+ , vec3 SR_gold_subsurface_color
+
+ , vec3 SR_gold_subsurface_radius
+
+ , float SR_gold_subsurface_scale
+
+ , float SR_gold_subsurface_anisotropy
+
+ , float SR_gold_sheen
+
+ , vec3 SR_gold_sheen_color
+
+ , float SR_gold_sheen_roughness
+
+ , float SR_gold_coat
+
+ , vec3 SR_gold_coat_color
+
+ , float SR_gold_coat_roughness
+
+ , float SR_gold_coat_anisotropy
+
+ , float SR_gold_coat_rotation
+
+ , float SR_gold_coat_IOR
+
+ , float SR_gold_coat_affect_color
+
+ , float SR_gold_coat_affect_roughness
+
+ , float SR_gold_thin_film_thickness
+
+ , float SR_gold_thin_film_IOR
+
+ , float SR_gold_emission
+
+ , vec3 SR_gold_emission_color
+
+ , vec3 SR_gold_opacity
+
+ , bool SR_gold_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+ , SR_gold_base(SR_gold_base)
+
+ , SR_gold_base_color(SR_gold_base_color)
+
+ , SR_gold_diffuse_roughness(SR_gold_diffuse_roughness)
+
+ , SR_gold_metalness(SR_gold_metalness)
+
+ , SR_gold_specular(SR_gold_specular)
+
+ , SR_gold_specular_color(SR_gold_specular_color)
+
+ , SR_gold_specular_roughness(SR_gold_specular_roughness)
+
+ , SR_gold_specular_IOR(SR_gold_specular_IOR)
+
+ , SR_gold_specular_anisotropy(SR_gold_specular_anisotropy)
+
+ , SR_gold_specular_rotation(SR_gold_specular_rotation)
+
+ , SR_gold_transmission(SR_gold_transmission)
+
+ , SR_gold_transmission_color(SR_gold_transmission_color)
+
+ , SR_gold_transmission_depth(SR_gold_transmission_depth)
+
+ , SR_gold_transmission_scatter(SR_gold_transmission_scatter)
+
+ , SR_gold_transmission_scatter_anisotropy(SR_gold_transmission_scatter_anisotropy)
+
+ , SR_gold_transmission_dispersion(SR_gold_transmission_dispersion)
+
+ , SR_gold_transmission_extra_roughness(SR_gold_transmission_extra_roughness)
+
+ , SR_gold_subsurface(SR_gold_subsurface)
+
+ , SR_gold_subsurface_color(SR_gold_subsurface_color)
+
+ , SR_gold_subsurface_radius(SR_gold_subsurface_radius)
+
+ , SR_gold_subsurface_scale(SR_gold_subsurface_scale)
+
+ , SR_gold_subsurface_anisotropy(SR_gold_subsurface_anisotropy)
+
+ , SR_gold_sheen(SR_gold_sheen)
+
+ , SR_gold_sheen_color(SR_gold_sheen_color)
+
+ , SR_gold_sheen_roughness(SR_gold_sheen_roughness)
+
+ , SR_gold_coat(SR_gold_coat)
+
+ , SR_gold_coat_color(SR_gold_coat_color)
+
+ , SR_gold_coat_roughness(SR_gold_coat_roughness)
+
+ , SR_gold_coat_anisotropy(SR_gold_coat_anisotropy)
+
+ , SR_gold_coat_rotation(SR_gold_coat_rotation)
+
+ , SR_gold_coat_IOR(SR_gold_coat_IOR)
+
+ , SR_gold_coat_affect_color(SR_gold_coat_affect_color)
+
+ , SR_gold_coat_affect_roughness(SR_gold_coat_affect_roughness)
+
+ , SR_gold_thin_film_thickness(SR_gold_thin_film_thickness)
+
+ , SR_gold_thin_film_IOR(SR_gold_thin_film_IOR)
+
+ , SR_gold_emission(SR_gold_emission)
+
+ , SR_gold_emission_color(SR_gold_emission_color)
+
+ , SR_gold_opacity(SR_gold_opacity)
+
+ , SR_gold_thin_walled(SR_gold_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+ float SR_gold_base;
+
+
+ vec3 SR_gold_base_color;
+
+
+ float SR_gold_diffuse_roughness;
+
+
+ float SR_gold_metalness;
+
+
+ float SR_gold_specular;
+
+
+ vec3 SR_gold_specular_color;
+
+
+ float SR_gold_specular_roughness;
+
+
+ float SR_gold_specular_IOR;
+
+
+ float SR_gold_specular_anisotropy;
+
+
+ float SR_gold_specular_rotation;
+
+
+ float SR_gold_transmission;
+
+
+ vec3 SR_gold_transmission_color;
+
+
+ float SR_gold_transmission_depth;
+
+
+ vec3 SR_gold_transmission_scatter;
+
+
+ float SR_gold_transmission_scatter_anisotropy;
+
+
+ float SR_gold_transmission_dispersion;
+
+
+ float SR_gold_transmission_extra_roughness;
+
+
+ float SR_gold_subsurface;
+
+
+ vec3 SR_gold_subsurface_color;
+
+
+ vec3 SR_gold_subsurface_radius;
+
+
+ float SR_gold_subsurface_scale;
+
+
+ float SR_gold_subsurface_anisotropy;
+
+
+ float SR_gold_sheen;
+
+
+ vec3 SR_gold_sheen_color;
+
+
+ float SR_gold_sheen_roughness;
+
+
+ float SR_gold_coat;
+
+
+ vec3 SR_gold_coat_color;
+
+
+ float SR_gold_coat_roughness;
+
+
+ float SR_gold_coat_anisotropy;
+
+
+ float SR_gold_coat_rotation;
+
+
+ float SR_gold_coat_IOR;
+
+
+ float SR_gold_coat_affect_color;
+
+
+ float SR_gold_coat_affect_roughness;
+
+
+ float SR_gold_thin_film_thickness;
+
+
+ float SR_gold_thin_film_IOR;
+
+
+ float SR_gold_emission;
+
+
+ vec3 SR_gold_emission_color;
+
+
+ vec3 SR_gold_opacity;
+
+
+ bool SR_gold_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_gold_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(SR_gold_base, SR_gold_base_color, SR_gold_diffuse_roughness, SR_gold_metalness, SR_gold_specular, SR_gold_specular_color, SR_gold_specular_roughness, SR_gold_specular_IOR, SR_gold_specular_anisotropy, SR_gold_specular_rotation, SR_gold_transmission, SR_gold_transmission_color, SR_gold_transmission_depth, SR_gold_transmission_scatter, SR_gold_transmission_scatter_anisotropy, SR_gold_transmission_dispersion, SR_gold_transmission_extra_roughness, SR_gold_subsurface, SR_gold_subsurface_color, SR_gold_subsurface_radius, SR_gold_subsurface_scale, SR_gold_subsurface_anisotropy, SR_gold_sheen, SR_gold_sheen_color, SR_gold_sheen_roughness, SR_gold_coat, SR_gold_coat_color, SR_gold_coat_roughness, SR_gold_coat_anisotropy, SR_gold_coat_rotation, SR_gold_coat_IOR, geomprop_Nworld_out1, SR_gold_coat_affect_color, SR_gold_coat_affect_roughness, SR_gold_thin_film_thickness, SR_gold_thin_film_IOR, SR_gold_emission, SR_gold_emission_color, SR_gold_opacity, SR_gold_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_gold_out);
+ material Gold_out = SR_gold_out;
+ out1 = float4(Gold_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(0)]], sampler u_envRadiance_sampler [[sampler(0)]]
+, texture2d u_envIrradiance_tex [[texture(1)]], sampler u_envIrradiance_sampler [[sampler(1)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+ , u_pub.SR_gold_base
+ , u_pub.SR_gold_base_color
+ , u_pub.SR_gold_diffuse_roughness
+ , u_pub.SR_gold_metalness
+ , u_pub.SR_gold_specular
+ , u_pub.SR_gold_specular_color
+ , u_pub.SR_gold_specular_roughness
+ , u_pub.SR_gold_specular_IOR
+ , u_pub.SR_gold_specular_anisotropy
+ , u_pub.SR_gold_specular_rotation
+ , u_pub.SR_gold_transmission
+ , u_pub.SR_gold_transmission_color
+ , u_pub.SR_gold_transmission_depth
+ , u_pub.SR_gold_transmission_scatter
+ , u_pub.SR_gold_transmission_scatter_anisotropy
+ , u_pub.SR_gold_transmission_dispersion
+ , u_pub.SR_gold_transmission_extra_roughness
+ , u_pub.SR_gold_subsurface
+ , u_pub.SR_gold_subsurface_color
+ , u_pub.SR_gold_subsurface_radius
+ , u_pub.SR_gold_subsurface_scale
+ , u_pub.SR_gold_subsurface_anisotropy
+ , u_pub.SR_gold_sheen
+ , u_pub.SR_gold_sheen_color
+ , u_pub.SR_gold_sheen_roughness
+ , u_pub.SR_gold_coat
+ , u_pub.SR_gold_coat_color
+ , u_pub.SR_gold_coat_roughness
+ , u_pub.SR_gold_coat_anisotropy
+ , u_pub.SR_gold_coat_rotation
+ , u_pub.SR_gold_coat_IOR
+ , u_pub.SR_gold_coat_affect_color
+ , u_pub.SR_gold_coat_affect_roughness
+ , u_pub.SR_gold_thin_film_thickness
+ , u_pub.SR_gold_thin_film_IOR
+ , u_pub.SR_gold_emission
+ , u_pub.SR_gold_emission_color
+ , u_pub.SR_gold_opacity
+ , u_pub.SR_gold_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/Gold.msl.vert b/Materials/Examples/StandardSurface/Gold.msl.vert
new file mode 100644
index 0000000000..92f28c67ce
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Gold.msl.vert
@@ -0,0 +1,110 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'SR_gold'. Function already called in this scope.
+ // Omitted node 'Gold'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(3) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Gold.osl b/Materials/Examples/StandardSurface/Gold.osl
new file mode 100644
index 0000000000..6d848126c0
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Gold.osl
@@ -0,0 +1,637 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader Gold
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "Gold"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_base = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_gold_base_color = color(0.944, 0.776, 0.373),
+ float SR_gold_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_metalness = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_gold_specular_color = color(0.998, 0.981, 0.751),
+ float SR_gold_specular_roughness = 0.02
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_gold_transmission_color = color(1, 1, 1),
+ float SR_gold_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_gold_transmission_scatter = color(0, 0, 0),
+ float SR_gold_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_gold_subsurface_color = color(1, 1, 1),
+ color SR_gold_subsurface_radius = color(1, 1, 1),
+ float SR_gold_subsurface_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_gold_sheen_color = color(1, 1, 1),
+ float SR_gold_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_gold_coat_color = color(1, 1, 1),
+ float SR_gold_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_gold_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_gold_emission_color = color(1, 1, 1),
+ color SR_gold_opacity = color(1, 1, 1),
+ int SR_gold_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ surfaceshader SR_gold_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(SR_gold_base, SR_gold_base_color, SR_gold_diffuse_roughness, SR_gold_metalness, SR_gold_specular, SR_gold_specular_color, SR_gold_specular_roughness, SR_gold_specular_IOR, SR_gold_specular_anisotropy, SR_gold_specular_rotation, SR_gold_transmission, SR_gold_transmission_color, SR_gold_transmission_depth, SR_gold_transmission_scatter, SR_gold_transmission_scatter_anisotropy, SR_gold_transmission_dispersion, SR_gold_transmission_extra_roughness, SR_gold_subsurface, SR_gold_subsurface_color, SR_gold_subsurface_radius, SR_gold_subsurface_scale, SR_gold_subsurface_anisotropy, SR_gold_sheen, SR_gold_sheen_color, SR_gold_sheen_roughness, SR_gold_coat, SR_gold_coat_color, SR_gold_coat_roughness, SR_gold_coat_anisotropy, SR_gold_coat_rotation, SR_gold_coat_IOR, geomprop_Nworld_out1, SR_gold_coat_affect_color, SR_gold_coat_affect_roughness, SR_gold_thin_film_thickness, SR_gold_thin_film_IOR, SR_gold_emission, SR_gold_emission_color, SR_gold_opacity, SR_gold_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_gold_out);
+ MATERIAL Gold_out = mx_surfacematerial(SR_gold_out, displacementshader1);
+ out = Gold_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Greysphere.glsl.frag b/Materials/Examples/StandardSurface/Greysphere.glsl.frag
new file mode 100644
index 0000000000..3d19e63ded
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere.glsl.frag
@@ -0,0 +1,1706 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform float SR_greysphere_base = 1.000000;
+uniform vec3 SR_greysphere_base_color = vec3(0.180000, 0.180000, 0.180000);
+uniform float SR_greysphere_diffuse_roughness = 0.000000;
+uniform float SR_greysphere_metalness = 0.000000;
+uniform float SR_greysphere_specular = 1.000000;
+uniform vec3 SR_greysphere_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_greysphere_specular_roughness = 0.700000;
+uniform float SR_greysphere_specular_IOR = 1.500000;
+uniform float SR_greysphere_specular_anisotropy = 0.000000;
+uniform float SR_greysphere_specular_rotation = 0.000000;
+uniform float SR_greysphere_transmission = 0.000000;
+uniform vec3 SR_greysphere_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_greysphere_transmission_depth = 0.000000;
+uniform vec3 SR_greysphere_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float SR_greysphere_transmission_scatter_anisotropy = 0.000000;
+uniform float SR_greysphere_transmission_dispersion = 0.000000;
+uniform float SR_greysphere_transmission_extra_roughness = 0.000000;
+uniform float SR_greysphere_subsurface = 0.000000;
+uniform vec3 SR_greysphere_subsurface_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_greysphere_subsurface_radius = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_greysphere_subsurface_scale = 1.000000;
+uniform float SR_greysphere_subsurface_anisotropy = 0.000000;
+uniform float SR_greysphere_sheen = 0.000000;
+uniform vec3 SR_greysphere_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_greysphere_sheen_roughness = 0.300000;
+uniform float SR_greysphere_coat = 0.000000;
+uniform vec3 SR_greysphere_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_greysphere_coat_roughness = 0.100000;
+uniform float SR_greysphere_coat_anisotropy = 0.000000;
+uniform float SR_greysphere_coat_rotation = 0.000000;
+uniform float SR_greysphere_coat_IOR = 1.500000;
+uniform float SR_greysphere_coat_affect_color = 0.000000;
+uniform float SR_greysphere_coat_affect_roughness = 0.000000;
+uniform float SR_greysphere_thin_film_thickness = 0.000000;
+uniform float SR_greysphere_thin_film_IOR = 1.500000;
+uniform float SR_greysphere_emission = 0.000000;
+uniform vec3 SR_greysphere_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_greysphere_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool SR_greysphere_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_greysphere_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(SR_greysphere_base, SR_greysphere_base_color, SR_greysphere_diffuse_roughness, SR_greysphere_metalness, SR_greysphere_specular, SR_greysphere_specular_color, SR_greysphere_specular_roughness, SR_greysphere_specular_IOR, SR_greysphere_specular_anisotropy, SR_greysphere_specular_rotation, SR_greysphere_transmission, SR_greysphere_transmission_color, SR_greysphere_transmission_depth, SR_greysphere_transmission_scatter, SR_greysphere_transmission_scatter_anisotropy, SR_greysphere_transmission_dispersion, SR_greysphere_transmission_extra_roughness, SR_greysphere_subsurface, SR_greysphere_subsurface_color, SR_greysphere_subsurface_radius, SR_greysphere_subsurface_scale, SR_greysphere_subsurface_anisotropy, SR_greysphere_sheen, SR_greysphere_sheen_color, SR_greysphere_sheen_roughness, SR_greysphere_coat, SR_greysphere_coat_color, SR_greysphere_coat_roughness, SR_greysphere_coat_anisotropy, SR_greysphere_coat_rotation, SR_greysphere_coat_IOR, geomprop_Nworld_out1, SR_greysphere_coat_affect_color, SR_greysphere_coat_affect_roughness, SR_greysphere_thin_film_thickness, SR_greysphere_thin_film_IOR, SR_greysphere_emission, SR_greysphere_emission_color, SR_greysphere_opacity, SR_greysphere_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_greysphere_out);
+ material Greysphere_out = SR_greysphere_out;
+ out1 = vec4(Greysphere_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/Greysphere.glsl.vert b/Materials/Examples/StandardSurface/Greysphere.glsl.vert
new file mode 100644
index 0000000000..6133cb8f5e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere.glsl.vert
@@ -0,0 +1,28 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/Greysphere.mdl b/Materials/Examples/StandardSurface/Greysphere.mdl
new file mode 100644
index 0000000000..ed3a05434d
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere.mdl
@@ -0,0 +1,175 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material Greysphere
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ float SR_greysphere_base = 1,
+ color SR_greysphere_base_color = color(0.18, 0.18, 0.18),
+ float SR_greysphere_diffuse_roughness = 0,
+ float SR_greysphere_metalness = 0,
+ float SR_greysphere_specular = 1,
+ color SR_greysphere_specular_color = color(1, 1, 1),
+ float SR_greysphere_specular_roughness = 0.7,
+ uniform float SR_greysphere_specular_IOR = 1.5,
+ float SR_greysphere_specular_anisotropy = 0,
+ float SR_greysphere_specular_rotation = 0,
+ float SR_greysphere_transmission = 0,
+ color SR_greysphere_transmission_color = color(1, 1, 1),
+ float SR_greysphere_transmission_depth = 0,
+ color SR_greysphere_transmission_scatter = color(0, 0, 0),
+ float SR_greysphere_transmission_scatter_anisotropy = 0,
+ float SR_greysphere_transmission_dispersion = 0,
+ float SR_greysphere_transmission_extra_roughness = 0,
+ float SR_greysphere_subsurface = 0,
+ color SR_greysphere_subsurface_color = color(1, 1, 1),
+ color SR_greysphere_subsurface_radius = color(1, 1, 1),
+ float SR_greysphere_subsurface_scale = 1,
+ float SR_greysphere_subsurface_anisotropy = 0,
+ float SR_greysphere_sheen = 0,
+ color SR_greysphere_sheen_color = color(1, 1, 1),
+ float SR_greysphere_sheen_roughness = 0.3,
+ float SR_greysphere_coat = 0,
+ color SR_greysphere_coat_color = color(1, 1, 1),
+ float SR_greysphere_coat_roughness = 0.1,
+ float SR_greysphere_coat_anisotropy = 0,
+ float SR_greysphere_coat_rotation = 0,
+ uniform float SR_greysphere_coat_IOR = 1.5,
+ float SR_greysphere_coat_affect_color = 0,
+ float SR_greysphere_coat_affect_roughness = 0,
+ float SR_greysphere_thin_film_thickness = 0,
+ float SR_greysphere_thin_film_IOR = 1.5,
+ float SR_greysphere_emission = 0,
+ color SR_greysphere_emission_color = color(1, 1, 1),
+ color SR_greysphere_opacity = color(1, 1, 1),
+ bool SR_greysphere_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ material SR_greysphere_out = NG_standard_surface_surfaceshader_100(SR_greysphere_base, SR_greysphere_base_color, SR_greysphere_diffuse_roughness, SR_greysphere_metalness, SR_greysphere_specular, SR_greysphere_specular_color, SR_greysphere_specular_roughness, SR_greysphere_specular_IOR, SR_greysphere_specular_anisotropy, SR_greysphere_specular_rotation, SR_greysphere_transmission, SR_greysphere_transmission_color, SR_greysphere_transmission_depth, SR_greysphere_transmission_scatter, SR_greysphere_transmission_scatter_anisotropy, SR_greysphere_transmission_dispersion, SR_greysphere_transmission_extra_roughness, SR_greysphere_subsurface, SR_greysphere_subsurface_color, SR_greysphere_subsurface_radius, SR_greysphere_subsurface_scale, SR_greysphere_subsurface_anisotropy, SR_greysphere_sheen, SR_greysphere_sheen_color, SR_greysphere_sheen_roughness, SR_greysphere_coat, SR_greysphere_coat_color, SR_greysphere_coat_roughness, SR_greysphere_coat_anisotropy, SR_greysphere_coat_rotation, SR_greysphere_coat_IOR, geomprop_Nworld_out1, SR_greysphere_coat_affect_color, SR_greysphere_coat_affect_roughness, SR_greysphere_thin_film_thickness, SR_greysphere_thin_film_IOR, SR_greysphere_emission, SR_greysphere_emission_color, SR_greysphere_opacity, SR_greysphere_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1);
+ material Greysphere_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: SR_greysphere_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = Greysphere_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/Greysphere.msl.frag b/Materials/Examples/StandardSurface/Greysphere.msl.frag
new file mode 100644
index 0000000000..1a19633aed
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere.msl.frag
@@ -0,0 +1,2324 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ float SR_greysphere_base;
+ vec3 SR_greysphere_base_color;
+ float SR_greysphere_diffuse_roughness;
+ float SR_greysphere_metalness;
+ float SR_greysphere_specular;
+ vec3 SR_greysphere_specular_color;
+ float SR_greysphere_specular_roughness;
+ float SR_greysphere_specular_IOR;
+ float SR_greysphere_specular_anisotropy;
+ float SR_greysphere_specular_rotation;
+ float SR_greysphere_transmission;
+ vec3 SR_greysphere_transmission_color;
+ float SR_greysphere_transmission_depth;
+ vec3 SR_greysphere_transmission_scatter;
+ float SR_greysphere_transmission_scatter_anisotropy;
+ float SR_greysphere_transmission_dispersion;
+ float SR_greysphere_transmission_extra_roughness;
+ float SR_greysphere_subsurface;
+ vec3 SR_greysphere_subsurface_color;
+ vec3 SR_greysphere_subsurface_radius;
+ float SR_greysphere_subsurface_scale;
+ float SR_greysphere_subsurface_anisotropy;
+ float SR_greysphere_sheen;
+ vec3 SR_greysphere_sheen_color;
+ float SR_greysphere_sheen_roughness;
+ float SR_greysphere_coat;
+ vec3 SR_greysphere_coat_color;
+ float SR_greysphere_coat_roughness;
+ float SR_greysphere_coat_anisotropy;
+ float SR_greysphere_coat_rotation;
+ float SR_greysphere_coat_IOR;
+ float SR_greysphere_coat_affect_color;
+ float SR_greysphere_coat_affect_roughness;
+ float SR_greysphere_thin_film_thickness;
+ float SR_greysphere_thin_film_IOR;
+ float SR_greysphere_emission;
+ vec3 SR_greysphere_emission_color;
+ vec3 SR_greysphere_opacity;
+ bool SR_greysphere_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+ , float SR_greysphere_base
+
+ , vec3 SR_greysphere_base_color
+
+ , float SR_greysphere_diffuse_roughness
+
+ , float SR_greysphere_metalness
+
+ , float SR_greysphere_specular
+
+ , vec3 SR_greysphere_specular_color
+
+ , float SR_greysphere_specular_roughness
+
+ , float SR_greysphere_specular_IOR
+
+ , float SR_greysphere_specular_anisotropy
+
+ , float SR_greysphere_specular_rotation
+
+ , float SR_greysphere_transmission
+
+ , vec3 SR_greysphere_transmission_color
+
+ , float SR_greysphere_transmission_depth
+
+ , vec3 SR_greysphere_transmission_scatter
+
+ , float SR_greysphere_transmission_scatter_anisotropy
+
+ , float SR_greysphere_transmission_dispersion
+
+ , float SR_greysphere_transmission_extra_roughness
+
+ , float SR_greysphere_subsurface
+
+ , vec3 SR_greysphere_subsurface_color
+
+ , vec3 SR_greysphere_subsurface_radius
+
+ , float SR_greysphere_subsurface_scale
+
+ , float SR_greysphere_subsurface_anisotropy
+
+ , float SR_greysphere_sheen
+
+ , vec3 SR_greysphere_sheen_color
+
+ , float SR_greysphere_sheen_roughness
+
+ , float SR_greysphere_coat
+
+ , vec3 SR_greysphere_coat_color
+
+ , float SR_greysphere_coat_roughness
+
+ , float SR_greysphere_coat_anisotropy
+
+ , float SR_greysphere_coat_rotation
+
+ , float SR_greysphere_coat_IOR
+
+ , float SR_greysphere_coat_affect_color
+
+ , float SR_greysphere_coat_affect_roughness
+
+ , float SR_greysphere_thin_film_thickness
+
+ , float SR_greysphere_thin_film_IOR
+
+ , float SR_greysphere_emission
+
+ , vec3 SR_greysphere_emission_color
+
+ , vec3 SR_greysphere_opacity
+
+ , bool SR_greysphere_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+ , SR_greysphere_base(SR_greysphere_base)
+
+ , SR_greysphere_base_color(SR_greysphere_base_color)
+
+ , SR_greysphere_diffuse_roughness(SR_greysphere_diffuse_roughness)
+
+ , SR_greysphere_metalness(SR_greysphere_metalness)
+
+ , SR_greysphere_specular(SR_greysphere_specular)
+
+ , SR_greysphere_specular_color(SR_greysphere_specular_color)
+
+ , SR_greysphere_specular_roughness(SR_greysphere_specular_roughness)
+
+ , SR_greysphere_specular_IOR(SR_greysphere_specular_IOR)
+
+ , SR_greysphere_specular_anisotropy(SR_greysphere_specular_anisotropy)
+
+ , SR_greysphere_specular_rotation(SR_greysphere_specular_rotation)
+
+ , SR_greysphere_transmission(SR_greysphere_transmission)
+
+ , SR_greysphere_transmission_color(SR_greysphere_transmission_color)
+
+ , SR_greysphere_transmission_depth(SR_greysphere_transmission_depth)
+
+ , SR_greysphere_transmission_scatter(SR_greysphere_transmission_scatter)
+
+ , SR_greysphere_transmission_scatter_anisotropy(SR_greysphere_transmission_scatter_anisotropy)
+
+ , SR_greysphere_transmission_dispersion(SR_greysphere_transmission_dispersion)
+
+ , SR_greysphere_transmission_extra_roughness(SR_greysphere_transmission_extra_roughness)
+
+ , SR_greysphere_subsurface(SR_greysphere_subsurface)
+
+ , SR_greysphere_subsurface_color(SR_greysphere_subsurface_color)
+
+ , SR_greysphere_subsurface_radius(SR_greysphere_subsurface_radius)
+
+ , SR_greysphere_subsurface_scale(SR_greysphere_subsurface_scale)
+
+ , SR_greysphere_subsurface_anisotropy(SR_greysphere_subsurface_anisotropy)
+
+ , SR_greysphere_sheen(SR_greysphere_sheen)
+
+ , SR_greysphere_sheen_color(SR_greysphere_sheen_color)
+
+ , SR_greysphere_sheen_roughness(SR_greysphere_sheen_roughness)
+
+ , SR_greysphere_coat(SR_greysphere_coat)
+
+ , SR_greysphere_coat_color(SR_greysphere_coat_color)
+
+ , SR_greysphere_coat_roughness(SR_greysphere_coat_roughness)
+
+ , SR_greysphere_coat_anisotropy(SR_greysphere_coat_anisotropy)
+
+ , SR_greysphere_coat_rotation(SR_greysphere_coat_rotation)
+
+ , SR_greysphere_coat_IOR(SR_greysphere_coat_IOR)
+
+ , SR_greysphere_coat_affect_color(SR_greysphere_coat_affect_color)
+
+ , SR_greysphere_coat_affect_roughness(SR_greysphere_coat_affect_roughness)
+
+ , SR_greysphere_thin_film_thickness(SR_greysphere_thin_film_thickness)
+
+ , SR_greysphere_thin_film_IOR(SR_greysphere_thin_film_IOR)
+
+ , SR_greysphere_emission(SR_greysphere_emission)
+
+ , SR_greysphere_emission_color(SR_greysphere_emission_color)
+
+ , SR_greysphere_opacity(SR_greysphere_opacity)
+
+ , SR_greysphere_thin_walled(SR_greysphere_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+ float SR_greysphere_base;
+
+
+ vec3 SR_greysphere_base_color;
+
+
+ float SR_greysphere_diffuse_roughness;
+
+
+ float SR_greysphere_metalness;
+
+
+ float SR_greysphere_specular;
+
+
+ vec3 SR_greysphere_specular_color;
+
+
+ float SR_greysphere_specular_roughness;
+
+
+ float SR_greysphere_specular_IOR;
+
+
+ float SR_greysphere_specular_anisotropy;
+
+
+ float SR_greysphere_specular_rotation;
+
+
+ float SR_greysphere_transmission;
+
+
+ vec3 SR_greysphere_transmission_color;
+
+
+ float SR_greysphere_transmission_depth;
+
+
+ vec3 SR_greysphere_transmission_scatter;
+
+
+ float SR_greysphere_transmission_scatter_anisotropy;
+
+
+ float SR_greysphere_transmission_dispersion;
+
+
+ float SR_greysphere_transmission_extra_roughness;
+
+
+ float SR_greysphere_subsurface;
+
+
+ vec3 SR_greysphere_subsurface_color;
+
+
+ vec3 SR_greysphere_subsurface_radius;
+
+
+ float SR_greysphere_subsurface_scale;
+
+
+ float SR_greysphere_subsurface_anisotropy;
+
+
+ float SR_greysphere_sheen;
+
+
+ vec3 SR_greysphere_sheen_color;
+
+
+ float SR_greysphere_sheen_roughness;
+
+
+ float SR_greysphere_coat;
+
+
+ vec3 SR_greysphere_coat_color;
+
+
+ float SR_greysphere_coat_roughness;
+
+
+ float SR_greysphere_coat_anisotropy;
+
+
+ float SR_greysphere_coat_rotation;
+
+
+ float SR_greysphere_coat_IOR;
+
+
+ float SR_greysphere_coat_affect_color;
+
+
+ float SR_greysphere_coat_affect_roughness;
+
+
+ float SR_greysphere_thin_film_thickness;
+
+
+ float SR_greysphere_thin_film_IOR;
+
+
+ float SR_greysphere_emission;
+
+
+ vec3 SR_greysphere_emission_color;
+
+
+ vec3 SR_greysphere_opacity;
+
+
+ bool SR_greysphere_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_greysphere_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(SR_greysphere_base, SR_greysphere_base_color, SR_greysphere_diffuse_roughness, SR_greysphere_metalness, SR_greysphere_specular, SR_greysphere_specular_color, SR_greysphere_specular_roughness, SR_greysphere_specular_IOR, SR_greysphere_specular_anisotropy, SR_greysphere_specular_rotation, SR_greysphere_transmission, SR_greysphere_transmission_color, SR_greysphere_transmission_depth, SR_greysphere_transmission_scatter, SR_greysphere_transmission_scatter_anisotropy, SR_greysphere_transmission_dispersion, SR_greysphere_transmission_extra_roughness, SR_greysphere_subsurface, SR_greysphere_subsurface_color, SR_greysphere_subsurface_radius, SR_greysphere_subsurface_scale, SR_greysphere_subsurface_anisotropy, SR_greysphere_sheen, SR_greysphere_sheen_color, SR_greysphere_sheen_roughness, SR_greysphere_coat, SR_greysphere_coat_color, SR_greysphere_coat_roughness, SR_greysphere_coat_anisotropy, SR_greysphere_coat_rotation, SR_greysphere_coat_IOR, geomprop_Nworld_out1, SR_greysphere_coat_affect_color, SR_greysphere_coat_affect_roughness, SR_greysphere_thin_film_thickness, SR_greysphere_thin_film_IOR, SR_greysphere_emission, SR_greysphere_emission_color, SR_greysphere_opacity, SR_greysphere_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_greysphere_out);
+ material Greysphere_out = SR_greysphere_out;
+ out1 = float4(Greysphere_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(0)]], sampler u_envRadiance_sampler [[sampler(0)]]
+, texture2d u_envIrradiance_tex [[texture(1)]], sampler u_envIrradiance_sampler [[sampler(1)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+ , u_pub.SR_greysphere_base
+ , u_pub.SR_greysphere_base_color
+ , u_pub.SR_greysphere_diffuse_roughness
+ , u_pub.SR_greysphere_metalness
+ , u_pub.SR_greysphere_specular
+ , u_pub.SR_greysphere_specular_color
+ , u_pub.SR_greysphere_specular_roughness
+ , u_pub.SR_greysphere_specular_IOR
+ , u_pub.SR_greysphere_specular_anisotropy
+ , u_pub.SR_greysphere_specular_rotation
+ , u_pub.SR_greysphere_transmission
+ , u_pub.SR_greysphere_transmission_color
+ , u_pub.SR_greysphere_transmission_depth
+ , u_pub.SR_greysphere_transmission_scatter
+ , u_pub.SR_greysphere_transmission_scatter_anisotropy
+ , u_pub.SR_greysphere_transmission_dispersion
+ , u_pub.SR_greysphere_transmission_extra_roughness
+ , u_pub.SR_greysphere_subsurface
+ , u_pub.SR_greysphere_subsurface_color
+ , u_pub.SR_greysphere_subsurface_radius
+ , u_pub.SR_greysphere_subsurface_scale
+ , u_pub.SR_greysphere_subsurface_anisotropy
+ , u_pub.SR_greysphere_sheen
+ , u_pub.SR_greysphere_sheen_color
+ , u_pub.SR_greysphere_sheen_roughness
+ , u_pub.SR_greysphere_coat
+ , u_pub.SR_greysphere_coat_color
+ , u_pub.SR_greysphere_coat_roughness
+ , u_pub.SR_greysphere_coat_anisotropy
+ , u_pub.SR_greysphere_coat_rotation
+ , u_pub.SR_greysphere_coat_IOR
+ , u_pub.SR_greysphere_coat_affect_color
+ , u_pub.SR_greysphere_coat_affect_roughness
+ , u_pub.SR_greysphere_thin_film_thickness
+ , u_pub.SR_greysphere_thin_film_IOR
+ , u_pub.SR_greysphere_emission
+ , u_pub.SR_greysphere_emission_color
+ , u_pub.SR_greysphere_opacity
+ , u_pub.SR_greysphere_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/Greysphere.msl.vert b/Materials/Examples/StandardSurface/Greysphere.msl.vert
new file mode 100644
index 0000000000..e5a6a9bf2b
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere.msl.vert
@@ -0,0 +1,110 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'SR_greysphere'. Function already called in this scope.
+ // Omitted node 'Greysphere'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(3) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Greysphere.osl b/Materials/Examples/StandardSurface/Greysphere.osl
new file mode 100644
index 0000000000..32ad25f9a0
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere.osl
@@ -0,0 +1,637 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader Greysphere
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "Greysphere"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_base = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_greysphere_base_color = color(0.18, 0.18, 0.18),
+ float SR_greysphere_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_metalness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_greysphere_specular_color = color(1, 1, 1),
+ float SR_greysphere_specular_roughness = 0.7
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_greysphere_transmission_color = color(1, 1, 1),
+ float SR_greysphere_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_greysphere_transmission_scatter = color(0, 0, 0),
+ float SR_greysphere_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_greysphere_subsurface_color = color(1, 1, 1),
+ color SR_greysphere_subsurface_radius = color(1, 1, 1),
+ float SR_greysphere_subsurface_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_greysphere_sheen_color = color(1, 1, 1),
+ float SR_greysphere_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_greysphere_coat_color = color(1, 1, 1),
+ float SR_greysphere_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_greysphere_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_greysphere_emission_color = color(1, 1, 1),
+ color SR_greysphere_opacity = color(1, 1, 1),
+ int SR_greysphere_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ surfaceshader SR_greysphere_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(SR_greysphere_base, SR_greysphere_base_color, SR_greysphere_diffuse_roughness, SR_greysphere_metalness, SR_greysphere_specular, SR_greysphere_specular_color, SR_greysphere_specular_roughness, SR_greysphere_specular_IOR, SR_greysphere_specular_anisotropy, SR_greysphere_specular_rotation, SR_greysphere_transmission, SR_greysphere_transmission_color, SR_greysphere_transmission_depth, SR_greysphere_transmission_scatter, SR_greysphere_transmission_scatter_anisotropy, SR_greysphere_transmission_dispersion, SR_greysphere_transmission_extra_roughness, SR_greysphere_subsurface, SR_greysphere_subsurface_color, SR_greysphere_subsurface_radius, SR_greysphere_subsurface_scale, SR_greysphere_subsurface_anisotropy, SR_greysphere_sheen, SR_greysphere_sheen_color, SR_greysphere_sheen_roughness, SR_greysphere_coat, SR_greysphere_coat_color, SR_greysphere_coat_roughness, SR_greysphere_coat_anisotropy, SR_greysphere_coat_rotation, SR_greysphere_coat_IOR, geomprop_Nworld_out1, SR_greysphere_coat_affect_color, SR_greysphere_coat_affect_roughness, SR_greysphere_thin_film_thickness, SR_greysphere_thin_film_IOR, SR_greysphere_emission, SR_greysphere_emission_color, SR_greysphere_opacity, SR_greysphere_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_greysphere_out);
+ MATERIAL Greysphere_out = mx_surfacematerial(SR_greysphere_out, displacementshader1);
+ out = Greysphere_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Greysphere_Calibration.glsl.frag b/Materials/Examples/StandardSurface/Greysphere_Calibration.glsl.frag
new file mode 100644
index 0000000000..6fca1c0c46
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere_Calibration.glsl.frag
@@ -0,0 +1,1791 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform vec2 place2d_pivot = vec2(0.500000, 0.500000);
+uniform vec2 place2d_scale = vec2(0.210000, 0.210000);
+uniform float place2d_rotate = 0.000000;
+uniform vec2 place2d_offset = vec2(-1.660000, -0.490000);
+uniform int place2d_operationorder = 0;
+uniform sampler2D image1_file;
+uniform int image1_layer = 0;
+uniform vec3 image1_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int image1_uaddressmode = 1;
+uniform int image1_vaddressmode = 1;
+uniform int image1_filtertype = 1;
+uniform int image1_framerange = 0;
+uniform int image1_frameoffset = 0;
+uniform int image1_frameendaction = 0;
+uniform vec2 image1_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 image1_uv_offset = vec2(0.000000, 0.000000);
+uniform float SR_Greysphere_Calibration_base = 1.000000;
+uniform float SR_Greysphere_Calibration_diffuse_roughness = 0.000000;
+uniform float SR_Greysphere_Calibration_metalness = 0.000000;
+uniform float SR_Greysphere_Calibration_specular = 1.000000;
+uniform vec3 SR_Greysphere_Calibration_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_Greysphere_Calibration_specular_roughness = 0.700000;
+uniform float SR_Greysphere_Calibration_specular_IOR = 1.500000;
+uniform float SR_Greysphere_Calibration_specular_anisotropy = 0.000000;
+uniform float SR_Greysphere_Calibration_specular_rotation = 0.000000;
+uniform float SR_Greysphere_Calibration_transmission = 0.000000;
+uniform vec3 SR_Greysphere_Calibration_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_Greysphere_Calibration_transmission_depth = 0.000000;
+uniform vec3 SR_Greysphere_Calibration_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float SR_Greysphere_Calibration_transmission_scatter_anisotropy = 0.000000;
+uniform float SR_Greysphere_Calibration_transmission_dispersion = 0.000000;
+uniform float SR_Greysphere_Calibration_transmission_extra_roughness = 0.000000;
+uniform float SR_Greysphere_Calibration_subsurface = 0.000000;
+uniform vec3 SR_Greysphere_Calibration_subsurface_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_Greysphere_Calibration_subsurface_radius = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_Greysphere_Calibration_subsurface_scale = 1.000000;
+uniform float SR_Greysphere_Calibration_subsurface_anisotropy = 0.000000;
+uniform float SR_Greysphere_Calibration_sheen = 0.000000;
+uniform vec3 SR_Greysphere_Calibration_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_Greysphere_Calibration_sheen_roughness = 0.300000;
+uniform float SR_Greysphere_Calibration_coat = 0.000000;
+uniform vec3 SR_Greysphere_Calibration_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_Greysphere_Calibration_coat_roughness = 0.100000;
+uniform float SR_Greysphere_Calibration_coat_anisotropy = 0.000000;
+uniform float SR_Greysphere_Calibration_coat_rotation = 0.000000;
+uniform float SR_Greysphere_Calibration_coat_IOR = 1.500000;
+uniform float SR_Greysphere_Calibration_coat_affect_color = 0.000000;
+uniform float SR_Greysphere_Calibration_coat_affect_roughness = 0.000000;
+uniform float SR_Greysphere_Calibration_thin_film_thickness = 0.000000;
+uniform float SR_Greysphere_Calibration_thin_film_IOR = 1.500000;
+uniform float SR_Greysphere_Calibration_emission = 0.000000;
+uniform vec3 SR_Greysphere_Calibration_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_Greysphere_Calibration_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool SR_Greysphere_Calibration_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+void mx_rotate_vector2(vec2 _in, float amount, out vec2 result)
+{
+ float rotationRadians = radians(amount);
+ float sa = sin(rotationRadians);
+ float ca = cos(rotationRadians);
+ result = vec2(ca*_in.x + sa*_in.y, -sa*_in.x + ca*_in.y);
+}
+
+void NG_place2d_vector2(vec2 texcoord1, vec2 pivot, vec2 scale, float rotate, vec2 offset, int operationorder, out vec2 out1)
+{
+ vec2 N_subpivot_out = texcoord1 - pivot;
+ vec2 N_applyscale_out = N_subpivot_out / scale;
+ vec2 N_applyoffset2_out = N_subpivot_out - offset;
+ vec2 N_applyrot_out = vec2(0.0);
+ mx_rotate_vector2(N_applyscale_out, rotate, N_applyrot_out);
+ vec2 N_applyrot2_out = vec2(0.0);
+ mx_rotate_vector2(N_applyoffset2_out, rotate, N_applyrot2_out);
+ vec2 N_applyoffset_out = N_applyrot_out - offset;
+ vec2 N_applyscale2_out = N_applyrot2_out / scale;
+ vec2 N_addpivot_out = N_applyoffset_out + pivot;
+ vec2 N_addpivot2_out = N_applyscale2_out + pivot;
+ vec2 N_switch_operationorder_out = vec2(0.0);
+ if (float(operationorder) < float(1))
+ {
+ N_switch_operationorder_out = N_addpivot_out;
+ }
+ else if (float(operationorder) < float(2))
+ {
+ N_switch_operationorder_out = N_addpivot2_out;
+ }
+ else if (float(operationorder) < float(3))
+ {
+ N_switch_operationorder_out = vec2(0.000000, 0.000000);
+ }
+ else if (float(operationorder) < float(4))
+ {
+ N_switch_operationorder_out = vec2(0.000000, 0.000000);
+ }
+ else if (float(operationorder) < float(5))
+ {
+ N_switch_operationorder_out = vec2(0.000000, 0.000000);
+ }
+ out1 = N_switch_operationorder_out;
+}
+
+vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+{
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+}
+
+void mx_image_color3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 texcoord1_out = vd.texcoord_0;
+ vec2 place2d_out = vec2(0.0);
+ NG_place2d_vector2(texcoord1_out, place2d_pivot, place2d_scale, place2d_rotate, place2d_offset, place2d_operationorder, place2d_out);
+ vec3 image1_out = vec3(0.0);
+ mx_image_color3(image1_file, image1_layer, image1_default, place2d_out, image1_uaddressmode, image1_vaddressmode, image1_filtertype, image1_framerange, image1_frameoffset, image1_frameendaction, image1_uv_scale, image1_uv_offset, image1_out);
+ surfaceshader SR_Greysphere_Calibration_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(SR_Greysphere_Calibration_base, image1_out, SR_Greysphere_Calibration_diffuse_roughness, SR_Greysphere_Calibration_metalness, SR_Greysphere_Calibration_specular, SR_Greysphere_Calibration_specular_color, SR_Greysphere_Calibration_specular_roughness, SR_Greysphere_Calibration_specular_IOR, SR_Greysphere_Calibration_specular_anisotropy, SR_Greysphere_Calibration_specular_rotation, SR_Greysphere_Calibration_transmission, SR_Greysphere_Calibration_transmission_color, SR_Greysphere_Calibration_transmission_depth, SR_Greysphere_Calibration_transmission_scatter, SR_Greysphere_Calibration_transmission_scatter_anisotropy, SR_Greysphere_Calibration_transmission_dispersion, SR_Greysphere_Calibration_transmission_extra_roughness, SR_Greysphere_Calibration_subsurface, SR_Greysphere_Calibration_subsurface_color, SR_Greysphere_Calibration_subsurface_radius, SR_Greysphere_Calibration_subsurface_scale, SR_Greysphere_Calibration_subsurface_anisotropy, SR_Greysphere_Calibration_sheen, SR_Greysphere_Calibration_sheen_color, SR_Greysphere_Calibration_sheen_roughness, SR_Greysphere_Calibration_coat, SR_Greysphere_Calibration_coat_color, SR_Greysphere_Calibration_coat_roughness, SR_Greysphere_Calibration_coat_anisotropy, SR_Greysphere_Calibration_coat_rotation, SR_Greysphere_Calibration_coat_IOR, geomprop_Nworld_out1, SR_Greysphere_Calibration_coat_affect_color, SR_Greysphere_Calibration_coat_affect_roughness, SR_Greysphere_Calibration_thin_film_thickness, SR_Greysphere_Calibration_thin_film_IOR, SR_Greysphere_Calibration_emission, SR_Greysphere_Calibration_emission_color, SR_Greysphere_Calibration_opacity, SR_Greysphere_Calibration_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_Greysphere_Calibration_out);
+ material Greysphere_Calibration_out = SR_Greysphere_Calibration_out;
+ out1 = vec4(Greysphere_Calibration_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/Greysphere_Calibration.glsl.vert b/Materials/Examples/StandardSurface/Greysphere_Calibration.glsl.vert
new file mode 100644
index 0000000000..60b47d493e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere_Calibration.glsl.vert
@@ -0,0 +1,31 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+in vec2 i_texcoord_0;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/Greysphere_Calibration.mdl b/Materials/Examples/StandardSurface/Greysphere_Calibration.mdl
new file mode 100644
index 0000000000..41b55f4c69
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere_Calibration.mdl
@@ -0,0 +1,215 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+float2 NG_place2d_vector2
+(
+ float2 texcoord = float2(0, 0),
+ float2 pivot = float2(0, 0),
+ float2 scale = float2(1, 1),
+ float rotate = 0,
+ float2 offset = float2(0, 0),
+ int operationorder = 0
+)
+{
+ float2 N_subpivot_out = texcoord - pivot;
+ float2 N_applyscale_out = N_subpivot_out / scale;
+ float2 N_applyoffset2_out = N_subpivot_out - offset;
+ float2 N_applyrot_out = mx::stdlib::mx_rotate2d_vector2(mxp_in:N_applyscale_out, mxp_amount:rotate);
+ float2 N_applyrot2_out = mx::stdlib::mx_rotate2d_vector2(mxp_in:N_applyoffset2_out, mxp_amount:rotate);
+ float2 N_applyoffset_out = N_applyrot_out - offset;
+ float2 N_applyscale2_out = N_applyrot2_out / scale;
+ float2 N_addpivot_out = N_applyoffset_out + pivot;
+ float2 N_addpivot2_out = N_applyscale2_out + pivot;
+ float2 N_switch_operationorder_out = mx::stdlib::mx_switch_vector2I(N_addpivot_out, N_addpivot2_out, float2(0, 0), float2(0, 0), float2(0, 0), operationorder);
+ return N_switch_operationorder_out;
+}
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material Greysphere_Calibration
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ uniform int texcoord1_index = 0,
+ float2 place2d_pivot = float2(0.5, 0.5),
+ float2 place2d_scale = float2(0.21, 0.21),
+ float place2d_rotate = 0,
+ float2 place2d_offset = float2(-1.66, -0.49),
+ int place2d_operationorder = 0,
+ uniform texture_2d image1_file = texture_2d("../../../Images/greysphere_calibration.png", tex::gamma_linear),
+ uniform string image1_layer = "",
+ color image1_default = color(0, 0, 0),
+ uniform mx_addressmode_type image1_uaddressmode = mx_addressmode_type_clamp,
+ uniform mx_addressmode_type image1_vaddressmode = mx_addressmode_type_clamp,
+ uniform mx_filterlookup_type image1_filtertype = mx_filterlookup_type_linear,
+ uniform string image1_framerange = "",
+ uniform int image1_frameoffset = 0,
+ uniform mx_addressmode_type image1_frameendaction = mx_addressmode_type_constant,
+ float SR_Greysphere_Calibration_base = 1,
+ float SR_Greysphere_Calibration_diffuse_roughness = 0,
+ float SR_Greysphere_Calibration_metalness = 0,
+ float SR_Greysphere_Calibration_specular = 1,
+ color SR_Greysphere_Calibration_specular_color = color(1, 1, 1),
+ float SR_Greysphere_Calibration_specular_roughness = 0.7,
+ uniform float SR_Greysphere_Calibration_specular_IOR = 1.5,
+ float SR_Greysphere_Calibration_specular_anisotropy = 0,
+ float SR_Greysphere_Calibration_specular_rotation = 0,
+ float SR_Greysphere_Calibration_transmission = 0,
+ color SR_Greysphere_Calibration_transmission_color = color(1, 1, 1),
+ float SR_Greysphere_Calibration_transmission_depth = 0,
+ color SR_Greysphere_Calibration_transmission_scatter = color(0, 0, 0),
+ float SR_Greysphere_Calibration_transmission_scatter_anisotropy = 0,
+ float SR_Greysphere_Calibration_transmission_dispersion = 0,
+ float SR_Greysphere_Calibration_transmission_extra_roughness = 0,
+ float SR_Greysphere_Calibration_subsurface = 0,
+ color SR_Greysphere_Calibration_subsurface_color = color(1, 1, 1),
+ color SR_Greysphere_Calibration_subsurface_radius = color(1, 1, 1),
+ float SR_Greysphere_Calibration_subsurface_scale = 1,
+ float SR_Greysphere_Calibration_subsurface_anisotropy = 0,
+ float SR_Greysphere_Calibration_sheen = 0,
+ color SR_Greysphere_Calibration_sheen_color = color(1, 1, 1),
+ float SR_Greysphere_Calibration_sheen_roughness = 0.3,
+ float SR_Greysphere_Calibration_coat = 0,
+ color SR_Greysphere_Calibration_coat_color = color(1, 1, 1),
+ float SR_Greysphere_Calibration_coat_roughness = 0.1,
+ float SR_Greysphere_Calibration_coat_anisotropy = 0,
+ float SR_Greysphere_Calibration_coat_rotation = 0,
+ uniform float SR_Greysphere_Calibration_coat_IOR = 1.5,
+ float SR_Greysphere_Calibration_coat_affect_color = 0,
+ float SR_Greysphere_Calibration_coat_affect_roughness = 0,
+ float SR_Greysphere_Calibration_thin_film_thickness = 0,
+ float SR_Greysphere_Calibration_thin_film_IOR = 1.5,
+ float SR_Greysphere_Calibration_emission = 0,
+ color SR_Greysphere_Calibration_emission_color = color(1, 1, 1),
+ color SR_Greysphere_Calibration_opacity = color(1, 1, 1),
+ bool SR_Greysphere_Calibration_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ float2 texcoord1_out = mx::stdlib::mx_texcoord_vector2(mxp_index:texcoord1_index);
+ float2 place2d_out = NG_place2d_vector2(texcoord1_out, place2d_pivot, place2d_scale, place2d_rotate, place2d_offset, place2d_operationorder);
+ color image1_out = mx::stdlib::mx_image_color3(image1_file, image1_layer, image1_default, place2d_out, image1_uaddressmode, image1_vaddressmode, image1_filtertype, image1_framerange, image1_frameoffset, image1_frameendaction);
+ material SR_Greysphere_Calibration_out = NG_standard_surface_surfaceshader_100(SR_Greysphere_Calibration_base, image1_out, SR_Greysphere_Calibration_diffuse_roughness, SR_Greysphere_Calibration_metalness, SR_Greysphere_Calibration_specular, SR_Greysphere_Calibration_specular_color, SR_Greysphere_Calibration_specular_roughness, SR_Greysphere_Calibration_specular_IOR, SR_Greysphere_Calibration_specular_anisotropy, SR_Greysphere_Calibration_specular_rotation, SR_Greysphere_Calibration_transmission, SR_Greysphere_Calibration_transmission_color, SR_Greysphere_Calibration_transmission_depth, SR_Greysphere_Calibration_transmission_scatter, SR_Greysphere_Calibration_transmission_scatter_anisotropy, SR_Greysphere_Calibration_transmission_dispersion, SR_Greysphere_Calibration_transmission_extra_roughness, SR_Greysphere_Calibration_subsurface, SR_Greysphere_Calibration_subsurface_color, SR_Greysphere_Calibration_subsurface_radius, SR_Greysphere_Calibration_subsurface_scale, SR_Greysphere_Calibration_subsurface_anisotropy, SR_Greysphere_Calibration_sheen, SR_Greysphere_Calibration_sheen_color, SR_Greysphere_Calibration_sheen_roughness, SR_Greysphere_Calibration_coat, SR_Greysphere_Calibration_coat_color, SR_Greysphere_Calibration_coat_roughness, SR_Greysphere_Calibration_coat_anisotropy, SR_Greysphere_Calibration_coat_rotation, SR_Greysphere_Calibration_coat_IOR, geomprop_Nworld_out1, SR_Greysphere_Calibration_coat_affect_color, SR_Greysphere_Calibration_coat_affect_roughness, SR_Greysphere_Calibration_thin_film_thickness, SR_Greysphere_Calibration_thin_film_IOR, SR_Greysphere_Calibration_emission, SR_Greysphere_Calibration_emission_color, SR_Greysphere_Calibration_opacity, SR_Greysphere_Calibration_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1);
+ material Greysphere_Calibration_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: SR_Greysphere_Calibration_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = Greysphere_Calibration_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/Greysphere_Calibration.msl.frag b/Materials/Examples/StandardSurface/Greysphere_Calibration.msl.frag
new file mode 100644
index 0000000000..14df84be45
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere_Calibration.msl.frag
@@ -0,0 +1,2525 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ vec2 place2d_pivot;
+ vec2 place2d_scale;
+ float place2d_rotate;
+ vec2 place2d_offset;
+ int place2d_operationorder;
+ int image1_layer;
+ vec3 image1_default;
+ int image1_uaddressmode;
+ int image1_vaddressmode;
+ int image1_filtertype;
+ int image1_framerange;
+ int image1_frameoffset;
+ int image1_frameendaction;
+ vec2 image1_uv_scale;
+ vec2 image1_uv_offset;
+ float SR_Greysphere_Calibration_base;
+ float SR_Greysphere_Calibration_diffuse_roughness;
+ float SR_Greysphere_Calibration_metalness;
+ float SR_Greysphere_Calibration_specular;
+ vec3 SR_Greysphere_Calibration_specular_color;
+ float SR_Greysphere_Calibration_specular_roughness;
+ float SR_Greysphere_Calibration_specular_IOR;
+ float SR_Greysphere_Calibration_specular_anisotropy;
+ float SR_Greysphere_Calibration_specular_rotation;
+ float SR_Greysphere_Calibration_transmission;
+ vec3 SR_Greysphere_Calibration_transmission_color;
+ float SR_Greysphere_Calibration_transmission_depth;
+ vec3 SR_Greysphere_Calibration_transmission_scatter;
+ float SR_Greysphere_Calibration_transmission_scatter_anisotropy;
+ float SR_Greysphere_Calibration_transmission_dispersion;
+ float SR_Greysphere_Calibration_transmission_extra_roughness;
+ float SR_Greysphere_Calibration_subsurface;
+ vec3 SR_Greysphere_Calibration_subsurface_color;
+ vec3 SR_Greysphere_Calibration_subsurface_radius;
+ float SR_Greysphere_Calibration_subsurface_scale;
+ float SR_Greysphere_Calibration_subsurface_anisotropy;
+ float SR_Greysphere_Calibration_sheen;
+ vec3 SR_Greysphere_Calibration_sheen_color;
+ float SR_Greysphere_Calibration_sheen_roughness;
+ float SR_Greysphere_Calibration_coat;
+ vec3 SR_Greysphere_Calibration_coat_color;
+ float SR_Greysphere_Calibration_coat_roughness;
+ float SR_Greysphere_Calibration_coat_anisotropy;
+ float SR_Greysphere_Calibration_coat_rotation;
+ float SR_Greysphere_Calibration_coat_IOR;
+ float SR_Greysphere_Calibration_coat_affect_color;
+ float SR_Greysphere_Calibration_coat_affect_roughness;
+ float SR_Greysphere_Calibration_thin_film_thickness;
+ float SR_Greysphere_Calibration_thin_film_IOR;
+ float SR_Greysphere_Calibration_emission;
+ vec3 SR_Greysphere_Calibration_emission_color;
+ vec3 SR_Greysphere_Calibration_opacity;
+ bool SR_Greysphere_Calibration_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec2 texcoord_0 ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+ , vec2 place2d_pivot
+
+ , vec2 place2d_scale
+
+ , float place2d_rotate
+
+ , vec2 place2d_offset
+
+ , int place2d_operationorder
+
+, MetalTexture image1_file , int image1_layer
+
+ , vec3 image1_default
+
+ , int image1_uaddressmode
+
+ , int image1_vaddressmode
+
+ , int image1_filtertype
+
+ , int image1_framerange
+
+ , int image1_frameoffset
+
+ , int image1_frameendaction
+
+ , vec2 image1_uv_scale
+
+ , vec2 image1_uv_offset
+
+ , float SR_Greysphere_Calibration_base
+
+ , float SR_Greysphere_Calibration_diffuse_roughness
+
+ , float SR_Greysphere_Calibration_metalness
+
+ , float SR_Greysphere_Calibration_specular
+
+ , vec3 SR_Greysphere_Calibration_specular_color
+
+ , float SR_Greysphere_Calibration_specular_roughness
+
+ , float SR_Greysphere_Calibration_specular_IOR
+
+ , float SR_Greysphere_Calibration_specular_anisotropy
+
+ , float SR_Greysphere_Calibration_specular_rotation
+
+ , float SR_Greysphere_Calibration_transmission
+
+ , vec3 SR_Greysphere_Calibration_transmission_color
+
+ , float SR_Greysphere_Calibration_transmission_depth
+
+ , vec3 SR_Greysphere_Calibration_transmission_scatter
+
+ , float SR_Greysphere_Calibration_transmission_scatter_anisotropy
+
+ , float SR_Greysphere_Calibration_transmission_dispersion
+
+ , float SR_Greysphere_Calibration_transmission_extra_roughness
+
+ , float SR_Greysphere_Calibration_subsurface
+
+ , vec3 SR_Greysphere_Calibration_subsurface_color
+
+ , vec3 SR_Greysphere_Calibration_subsurface_radius
+
+ , float SR_Greysphere_Calibration_subsurface_scale
+
+ , float SR_Greysphere_Calibration_subsurface_anisotropy
+
+ , float SR_Greysphere_Calibration_sheen
+
+ , vec3 SR_Greysphere_Calibration_sheen_color
+
+ , float SR_Greysphere_Calibration_sheen_roughness
+
+ , float SR_Greysphere_Calibration_coat
+
+ , vec3 SR_Greysphere_Calibration_coat_color
+
+ , float SR_Greysphere_Calibration_coat_roughness
+
+ , float SR_Greysphere_Calibration_coat_anisotropy
+
+ , float SR_Greysphere_Calibration_coat_rotation
+
+ , float SR_Greysphere_Calibration_coat_IOR
+
+ , float SR_Greysphere_Calibration_coat_affect_color
+
+ , float SR_Greysphere_Calibration_coat_affect_roughness
+
+ , float SR_Greysphere_Calibration_thin_film_thickness
+
+ , float SR_Greysphere_Calibration_thin_film_IOR
+
+ , float SR_Greysphere_Calibration_emission
+
+ , vec3 SR_Greysphere_Calibration_emission_color
+
+ , vec3 SR_Greysphere_Calibration_opacity
+
+ , bool SR_Greysphere_Calibration_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+ , place2d_pivot(place2d_pivot)
+
+ , place2d_scale(place2d_scale)
+
+ , place2d_rotate(place2d_rotate)
+
+ , place2d_offset(place2d_offset)
+
+ , place2d_operationorder(place2d_operationorder)
+
+, image1_file(image1_file)
+ , image1_layer(image1_layer)
+
+ , image1_default(image1_default)
+
+ , image1_uaddressmode(image1_uaddressmode)
+
+ , image1_vaddressmode(image1_vaddressmode)
+
+ , image1_filtertype(image1_filtertype)
+
+ , image1_framerange(image1_framerange)
+
+ , image1_frameoffset(image1_frameoffset)
+
+ , image1_frameendaction(image1_frameendaction)
+
+ , image1_uv_scale(image1_uv_scale)
+
+ , image1_uv_offset(image1_uv_offset)
+
+ , SR_Greysphere_Calibration_base(SR_Greysphere_Calibration_base)
+
+ , SR_Greysphere_Calibration_diffuse_roughness(SR_Greysphere_Calibration_diffuse_roughness)
+
+ , SR_Greysphere_Calibration_metalness(SR_Greysphere_Calibration_metalness)
+
+ , SR_Greysphere_Calibration_specular(SR_Greysphere_Calibration_specular)
+
+ , SR_Greysphere_Calibration_specular_color(SR_Greysphere_Calibration_specular_color)
+
+ , SR_Greysphere_Calibration_specular_roughness(SR_Greysphere_Calibration_specular_roughness)
+
+ , SR_Greysphere_Calibration_specular_IOR(SR_Greysphere_Calibration_specular_IOR)
+
+ , SR_Greysphere_Calibration_specular_anisotropy(SR_Greysphere_Calibration_specular_anisotropy)
+
+ , SR_Greysphere_Calibration_specular_rotation(SR_Greysphere_Calibration_specular_rotation)
+
+ , SR_Greysphere_Calibration_transmission(SR_Greysphere_Calibration_transmission)
+
+ , SR_Greysphere_Calibration_transmission_color(SR_Greysphere_Calibration_transmission_color)
+
+ , SR_Greysphere_Calibration_transmission_depth(SR_Greysphere_Calibration_transmission_depth)
+
+ , SR_Greysphere_Calibration_transmission_scatter(SR_Greysphere_Calibration_transmission_scatter)
+
+ , SR_Greysphere_Calibration_transmission_scatter_anisotropy(SR_Greysphere_Calibration_transmission_scatter_anisotropy)
+
+ , SR_Greysphere_Calibration_transmission_dispersion(SR_Greysphere_Calibration_transmission_dispersion)
+
+ , SR_Greysphere_Calibration_transmission_extra_roughness(SR_Greysphere_Calibration_transmission_extra_roughness)
+
+ , SR_Greysphere_Calibration_subsurface(SR_Greysphere_Calibration_subsurface)
+
+ , SR_Greysphere_Calibration_subsurface_color(SR_Greysphere_Calibration_subsurface_color)
+
+ , SR_Greysphere_Calibration_subsurface_radius(SR_Greysphere_Calibration_subsurface_radius)
+
+ , SR_Greysphere_Calibration_subsurface_scale(SR_Greysphere_Calibration_subsurface_scale)
+
+ , SR_Greysphere_Calibration_subsurface_anisotropy(SR_Greysphere_Calibration_subsurface_anisotropy)
+
+ , SR_Greysphere_Calibration_sheen(SR_Greysphere_Calibration_sheen)
+
+ , SR_Greysphere_Calibration_sheen_color(SR_Greysphere_Calibration_sheen_color)
+
+ , SR_Greysphere_Calibration_sheen_roughness(SR_Greysphere_Calibration_sheen_roughness)
+
+ , SR_Greysphere_Calibration_coat(SR_Greysphere_Calibration_coat)
+
+ , SR_Greysphere_Calibration_coat_color(SR_Greysphere_Calibration_coat_color)
+
+ , SR_Greysphere_Calibration_coat_roughness(SR_Greysphere_Calibration_coat_roughness)
+
+ , SR_Greysphere_Calibration_coat_anisotropy(SR_Greysphere_Calibration_coat_anisotropy)
+
+ , SR_Greysphere_Calibration_coat_rotation(SR_Greysphere_Calibration_coat_rotation)
+
+ , SR_Greysphere_Calibration_coat_IOR(SR_Greysphere_Calibration_coat_IOR)
+
+ , SR_Greysphere_Calibration_coat_affect_color(SR_Greysphere_Calibration_coat_affect_color)
+
+ , SR_Greysphere_Calibration_coat_affect_roughness(SR_Greysphere_Calibration_coat_affect_roughness)
+
+ , SR_Greysphere_Calibration_thin_film_thickness(SR_Greysphere_Calibration_thin_film_thickness)
+
+ , SR_Greysphere_Calibration_thin_film_IOR(SR_Greysphere_Calibration_thin_film_IOR)
+
+ , SR_Greysphere_Calibration_emission(SR_Greysphere_Calibration_emission)
+
+ , SR_Greysphere_Calibration_emission_color(SR_Greysphere_Calibration_emission_color)
+
+ , SR_Greysphere_Calibration_opacity(SR_Greysphere_Calibration_opacity)
+
+ , SR_Greysphere_Calibration_thin_walled(SR_Greysphere_Calibration_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+ vec2 place2d_pivot;
+
+
+ vec2 place2d_scale;
+
+
+ float place2d_rotate;
+
+
+ vec2 place2d_offset;
+
+
+ int place2d_operationorder;
+
+
+MetalTexture image1_file;
+ int image1_layer;
+
+
+ vec3 image1_default;
+
+
+ int image1_uaddressmode;
+
+
+ int image1_vaddressmode;
+
+
+ int image1_filtertype;
+
+
+ int image1_framerange;
+
+
+ int image1_frameoffset;
+
+
+ int image1_frameendaction;
+
+
+ vec2 image1_uv_scale;
+
+
+ vec2 image1_uv_offset;
+
+
+ float SR_Greysphere_Calibration_base;
+
+
+ float SR_Greysphere_Calibration_diffuse_roughness;
+
+
+ float SR_Greysphere_Calibration_metalness;
+
+
+ float SR_Greysphere_Calibration_specular;
+
+
+ vec3 SR_Greysphere_Calibration_specular_color;
+
+
+ float SR_Greysphere_Calibration_specular_roughness;
+
+
+ float SR_Greysphere_Calibration_specular_IOR;
+
+
+ float SR_Greysphere_Calibration_specular_anisotropy;
+
+
+ float SR_Greysphere_Calibration_specular_rotation;
+
+
+ float SR_Greysphere_Calibration_transmission;
+
+
+ vec3 SR_Greysphere_Calibration_transmission_color;
+
+
+ float SR_Greysphere_Calibration_transmission_depth;
+
+
+ vec3 SR_Greysphere_Calibration_transmission_scatter;
+
+
+ float SR_Greysphere_Calibration_transmission_scatter_anisotropy;
+
+
+ float SR_Greysphere_Calibration_transmission_dispersion;
+
+
+ float SR_Greysphere_Calibration_transmission_extra_roughness;
+
+
+ float SR_Greysphere_Calibration_subsurface;
+
+
+ vec3 SR_Greysphere_Calibration_subsurface_color;
+
+
+ vec3 SR_Greysphere_Calibration_subsurface_radius;
+
+
+ float SR_Greysphere_Calibration_subsurface_scale;
+
+
+ float SR_Greysphere_Calibration_subsurface_anisotropy;
+
+
+ float SR_Greysphere_Calibration_sheen;
+
+
+ vec3 SR_Greysphere_Calibration_sheen_color;
+
+
+ float SR_Greysphere_Calibration_sheen_roughness;
+
+
+ float SR_Greysphere_Calibration_coat;
+
+
+ vec3 SR_Greysphere_Calibration_coat_color;
+
+
+ float SR_Greysphere_Calibration_coat_roughness;
+
+
+ float SR_Greysphere_Calibration_coat_anisotropy;
+
+
+ float SR_Greysphere_Calibration_coat_rotation;
+
+
+ float SR_Greysphere_Calibration_coat_IOR;
+
+
+ float SR_Greysphere_Calibration_coat_affect_color;
+
+
+ float SR_Greysphere_Calibration_coat_affect_roughness;
+
+
+ float SR_Greysphere_Calibration_thin_film_thickness;
+
+
+ float SR_Greysphere_Calibration_thin_film_IOR;
+
+
+ float SR_Greysphere_Calibration_emission;
+
+
+ vec3 SR_Greysphere_Calibration_emission_color;
+
+
+ vec3 SR_Greysphere_Calibration_opacity;
+
+
+ bool SR_Greysphere_Calibration_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ void mx_rotate_vector2(vec2 _in, float amount, thread vec2& result)
+ {
+ float rotationRadians = radians(amount);
+ float sa = sin(rotationRadians);
+ float ca = cos(rotationRadians);
+ result = vec2(ca*_in.x + sa*_in.y, -sa*_in.x + ca*_in.y);
+ }
+
+ void NG_place2d_vector2(vec2 texcoord1, vec2 pivot, vec2 scale, float rotate, vec2 offset, int operationorder, thread vec2& out1)
+ {
+ vec2 N_subpivot_out = texcoord1 - pivot;
+ vec2 N_applyscale_out = N_subpivot_out / scale;
+ vec2 N_applyoffset2_out = N_subpivot_out - offset;
+ vec2 N_applyrot_out = vec2(0.0);
+ mx_rotate_vector2(N_applyscale_out, rotate, N_applyrot_out);
+ vec2 N_applyrot2_out = vec2(0.0);
+ mx_rotate_vector2(N_applyoffset2_out, rotate, N_applyrot2_out);
+ vec2 N_applyoffset_out = N_applyrot_out - offset;
+ vec2 N_applyscale2_out = N_applyrot2_out / scale;
+ vec2 N_addpivot_out = N_applyoffset_out + pivot;
+ vec2 N_addpivot2_out = N_applyscale2_out + pivot;
+ vec2 N_switch_operationorder_out = vec2(0.0);
+ if (float(operationorder) < float(1))
+ {
+ N_switch_operationorder_out = N_addpivot_out;
+ }
+ else if (float(operationorder) < float(2))
+ {
+ N_switch_operationorder_out = N_addpivot2_out;
+ }
+ else if (float(operationorder) < float(3))
+ {
+ N_switch_operationorder_out = vec2(0.000000, 0.000000);
+ }
+ else if (float(operationorder) < float(4))
+ {
+ N_switch_operationorder_out = vec2(0.000000, 0.000000);
+ }
+ else if (float(operationorder) < float(5))
+ {
+ N_switch_operationorder_out = vec2(0.000000, 0.000000);
+ }
+ out1 = N_switch_operationorder_out;
+ }
+
+ vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+ {
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+ }
+
+ void mx_image_color3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 texcoord1_out = vd.texcoord_0;
+ vec2 place2d_out = vec2(0.0);
+ NG_place2d_vector2(texcoord1_out, place2d_pivot, place2d_scale, place2d_rotate, place2d_offset, place2d_operationorder, place2d_out);
+ vec3 image1_out = vec3(0.0);
+ mx_image_color3(image1_file, image1_layer, image1_default, place2d_out, image1_uaddressmode, image1_vaddressmode, image1_filtertype, image1_framerange, image1_frameoffset, image1_frameendaction, image1_uv_scale, image1_uv_offset, image1_out);
+ surfaceshader SR_Greysphere_Calibration_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(SR_Greysphere_Calibration_base, image1_out, SR_Greysphere_Calibration_diffuse_roughness, SR_Greysphere_Calibration_metalness, SR_Greysphere_Calibration_specular, SR_Greysphere_Calibration_specular_color, SR_Greysphere_Calibration_specular_roughness, SR_Greysphere_Calibration_specular_IOR, SR_Greysphere_Calibration_specular_anisotropy, SR_Greysphere_Calibration_specular_rotation, SR_Greysphere_Calibration_transmission, SR_Greysphere_Calibration_transmission_color, SR_Greysphere_Calibration_transmission_depth, SR_Greysphere_Calibration_transmission_scatter, SR_Greysphere_Calibration_transmission_scatter_anisotropy, SR_Greysphere_Calibration_transmission_dispersion, SR_Greysphere_Calibration_transmission_extra_roughness, SR_Greysphere_Calibration_subsurface, SR_Greysphere_Calibration_subsurface_color, SR_Greysphere_Calibration_subsurface_radius, SR_Greysphere_Calibration_subsurface_scale, SR_Greysphere_Calibration_subsurface_anisotropy, SR_Greysphere_Calibration_sheen, SR_Greysphere_Calibration_sheen_color, SR_Greysphere_Calibration_sheen_roughness, SR_Greysphere_Calibration_coat, SR_Greysphere_Calibration_coat_color, SR_Greysphere_Calibration_coat_roughness, SR_Greysphere_Calibration_coat_anisotropy, SR_Greysphere_Calibration_coat_rotation, SR_Greysphere_Calibration_coat_IOR, geomprop_Nworld_out1, SR_Greysphere_Calibration_coat_affect_color, SR_Greysphere_Calibration_coat_affect_roughness, SR_Greysphere_Calibration_thin_film_thickness, SR_Greysphere_Calibration_thin_film_IOR, SR_Greysphere_Calibration_emission, SR_Greysphere_Calibration_emission_color, SR_Greysphere_Calibration_opacity, SR_Greysphere_Calibration_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_Greysphere_Calibration_out);
+ material Greysphere_Calibration_out = SR_Greysphere_Calibration_out;
+ out1 = float4(Greysphere_Calibration_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], texture2d image1_file_tex [[texture(0)]], sampler image1_file_sampler [[sampler(0)]]
+, constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(1)]], sampler u_envRadiance_sampler [[sampler(1)]]
+, texture2d u_envIrradiance_tex [[texture(2)]], sampler u_envIrradiance_sampler [[sampler(2)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+ , u_pub.place2d_pivot
+ , u_pub.place2d_scale
+ , u_pub.place2d_rotate
+ , u_pub.place2d_offset
+ , u_pub.place2d_operationorder
+, MetalTexture {
+image1_file_tex, image1_file_sampler }
+ , u_pub.image1_layer
+ , u_pub.image1_default
+ , u_pub.image1_uaddressmode
+ , u_pub.image1_vaddressmode
+ , u_pub.image1_filtertype
+ , u_pub.image1_framerange
+ , u_pub.image1_frameoffset
+ , u_pub.image1_frameendaction
+ , u_pub.image1_uv_scale
+ , u_pub.image1_uv_offset
+ , u_pub.SR_Greysphere_Calibration_base
+ , u_pub.SR_Greysphere_Calibration_diffuse_roughness
+ , u_pub.SR_Greysphere_Calibration_metalness
+ , u_pub.SR_Greysphere_Calibration_specular
+ , u_pub.SR_Greysphere_Calibration_specular_color
+ , u_pub.SR_Greysphere_Calibration_specular_roughness
+ , u_pub.SR_Greysphere_Calibration_specular_IOR
+ , u_pub.SR_Greysphere_Calibration_specular_anisotropy
+ , u_pub.SR_Greysphere_Calibration_specular_rotation
+ , u_pub.SR_Greysphere_Calibration_transmission
+ , u_pub.SR_Greysphere_Calibration_transmission_color
+ , u_pub.SR_Greysphere_Calibration_transmission_depth
+ , u_pub.SR_Greysphere_Calibration_transmission_scatter
+ , u_pub.SR_Greysphere_Calibration_transmission_scatter_anisotropy
+ , u_pub.SR_Greysphere_Calibration_transmission_dispersion
+ , u_pub.SR_Greysphere_Calibration_transmission_extra_roughness
+ , u_pub.SR_Greysphere_Calibration_subsurface
+ , u_pub.SR_Greysphere_Calibration_subsurface_color
+ , u_pub.SR_Greysphere_Calibration_subsurface_radius
+ , u_pub.SR_Greysphere_Calibration_subsurface_scale
+ , u_pub.SR_Greysphere_Calibration_subsurface_anisotropy
+ , u_pub.SR_Greysphere_Calibration_sheen
+ , u_pub.SR_Greysphere_Calibration_sheen_color
+ , u_pub.SR_Greysphere_Calibration_sheen_roughness
+ , u_pub.SR_Greysphere_Calibration_coat
+ , u_pub.SR_Greysphere_Calibration_coat_color
+ , u_pub.SR_Greysphere_Calibration_coat_roughness
+ , u_pub.SR_Greysphere_Calibration_coat_anisotropy
+ , u_pub.SR_Greysphere_Calibration_coat_rotation
+ , u_pub.SR_Greysphere_Calibration_coat_IOR
+ , u_pub.SR_Greysphere_Calibration_coat_affect_color
+ , u_pub.SR_Greysphere_Calibration_coat_affect_roughness
+ , u_pub.SR_Greysphere_Calibration_thin_film_thickness
+ , u_pub.SR_Greysphere_Calibration_thin_film_IOR
+ , u_pub.SR_Greysphere_Calibration_emission
+ , u_pub.SR_Greysphere_Calibration_emission_color
+ , u_pub.SR_Greysphere_Calibration_opacity
+ , u_pub.SR_Greysphere_Calibration_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/Greysphere_Calibration.msl.vert b/Materials/Examples/StandardSurface/Greysphere_Calibration.msl.vert
new file mode 100644
index 0000000000..33b568f0fd
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere_Calibration.msl.vert
@@ -0,0 +1,120 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+ vec2 i_texcoord_0 [[attribute(3)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+, vec2 i_texcoord_0
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+, i_texcoord_0(i_texcoord_0)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ vec2 i_texcoord_0;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'texcoord1'. Function already called in this scope.
+ // Omitted node 'place2d'. Function already called in this scope.
+ // Omitted node 'image1'. Function already called in this scope.
+ // Omitted node 'SR_Greysphere_Calibration'. Function already called in this scope.
+ // Omitted node 'Greysphere_Calibration'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(4) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent, i_vs.i_texcoord_0 , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Greysphere_Calibration.osl b/Materials/Examples/StandardSurface/Greysphere_Calibration.osl
new file mode 100644
index 0000000000..4ea626c263
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Greysphere_Calibration.osl
@@ -0,0 +1,737 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+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);
+}
+
+void NG_place2d_vector2(vector2 texcoord, vector2 pivot, vector2 scale, float rotate1, vector2 offset, int operationorder, output vector2 out)
+{
+ vector2 N_subpivot_out = texcoord - pivot;
+ vector2 N_applyscale_out = N_subpivot_out / scale;
+ vector2 N_applyoffset2_out = N_subpivot_out - offset;
+ vector2 N_applyrot_out = vector2(0.0, 0.0);
+ mx_rotate_vector2(N_applyscale_out, rotate1, N_applyrot_out);
+ vector2 N_applyrot2_out = vector2(0.0, 0.0);
+ mx_rotate_vector2(N_applyoffset2_out, rotate1, N_applyrot2_out);
+ vector2 N_applyoffset_out = N_applyrot_out - offset;
+ vector2 N_applyscale2_out = N_applyrot2_out / scale;
+ vector2 N_addpivot_out = N_applyoffset_out + pivot;
+ vector2 N_addpivot2_out = N_applyscale2_out + pivot;
+ vector2 N_switch_operationorder_out = vector2(0.0, 0.0);
+ if (float(operationorder) < float(1))
+ {
+ N_switch_operationorder_out = N_addpivot_out;
+ }
+ else if (float(operationorder) < float(2))
+ {
+ N_switch_operationorder_out = N_addpivot2_out;
+ }
+ else if (float(operationorder) < float(3))
+ {
+ N_switch_operationorder_out = vector2(0, 0);
+ }
+ else if (float(operationorder) < float(4))
+ {
+ N_switch_operationorder_out = vector2(0, 0);
+ }
+ else if (float(operationorder) < float(5))
+ {
+ N_switch_operationorder_out = vector2(0, 0);
+ }
+ out = N_switch_operationorder_out;
+}
+
+vector2 mx_transform_uv(vector2 texcoord)
+{
+ return texcoord;
+}
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode );
+}
+
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader Greysphere_Calibration
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "Greysphere_Calibration"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ int texcoord1_index = 0
+ [[
+ string widget = "number"
+ ]],
+ vector2 place2d_pivot = {0.5, 0.5},
+ vector2 place2d_scale = {0.21, 0.21},
+ float place2d_rotate = 0
+ [[
+ string widget = "number"
+ ]],
+ vector2 place2d_offset = {-1.66, -0.49},
+ int place2d_operationorder = 0
+ [[
+ string widget = "number"
+ ]],
+ textureresource image1_file = {"../../../Images/greysphere_calibration.png", ""}
+ [[
+ string widget = "filename"
+ ]],
+ string image1_layer = "",
+ color image1_default = color(0, 0, 0),
+ string image1_uaddressmode = "clamp",
+ string image1_vaddressmode = "clamp",
+ string image1_filtertype = "linear",
+ string image1_framerange = "",
+ int image1_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string image1_frameendaction = "constant",
+ float SR_Greysphere_Calibration_base = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_metalness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_Greysphere_Calibration_specular_color = color(1, 1, 1),
+ float SR_Greysphere_Calibration_specular_roughness = 0.7
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_Greysphere_Calibration_transmission_color = color(1, 1, 1),
+ float SR_Greysphere_Calibration_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_Greysphere_Calibration_transmission_scatter = color(0, 0, 0),
+ float SR_Greysphere_Calibration_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_Greysphere_Calibration_subsurface_color = color(1, 1, 1),
+ color SR_Greysphere_Calibration_subsurface_radius = color(1, 1, 1),
+ float SR_Greysphere_Calibration_subsurface_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_Greysphere_Calibration_sheen_color = color(1, 1, 1),
+ float SR_Greysphere_Calibration_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_Greysphere_Calibration_coat_color = color(1, 1, 1),
+ float SR_Greysphere_Calibration_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_Greysphere_Calibration_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_Greysphere_Calibration_emission_color = color(1, 1, 1),
+ color SR_Greysphere_Calibration_opacity = color(1, 1, 1),
+ int SR_Greysphere_Calibration_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ vector2 texcoord1_out = vector2(u,v);
+ vector2 place2d_out = vector2(0.0, 0.0);
+ NG_place2d_vector2(texcoord1_out, place2d_pivot, place2d_scale, place2d_rotate, place2d_offset, place2d_operationorder, place2d_out);
+ color image1_out = color(0.0);
+ mx_image_color3(image1_file, image1_layer, image1_default, place2d_out, image1_uaddressmode, image1_vaddressmode, image1_filtertype, image1_framerange, image1_frameoffset, image1_frameendaction, image1_out);
+ surfaceshader SR_Greysphere_Calibration_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(SR_Greysphere_Calibration_base, image1_out, SR_Greysphere_Calibration_diffuse_roughness, SR_Greysphere_Calibration_metalness, SR_Greysphere_Calibration_specular, SR_Greysphere_Calibration_specular_color, SR_Greysphere_Calibration_specular_roughness, SR_Greysphere_Calibration_specular_IOR, SR_Greysphere_Calibration_specular_anisotropy, SR_Greysphere_Calibration_specular_rotation, SR_Greysphere_Calibration_transmission, SR_Greysphere_Calibration_transmission_color, SR_Greysphere_Calibration_transmission_depth, SR_Greysphere_Calibration_transmission_scatter, SR_Greysphere_Calibration_transmission_scatter_anisotropy, SR_Greysphere_Calibration_transmission_dispersion, SR_Greysphere_Calibration_transmission_extra_roughness, SR_Greysphere_Calibration_subsurface, SR_Greysphere_Calibration_subsurface_color, SR_Greysphere_Calibration_subsurface_radius, SR_Greysphere_Calibration_subsurface_scale, SR_Greysphere_Calibration_subsurface_anisotropy, SR_Greysphere_Calibration_sheen, SR_Greysphere_Calibration_sheen_color, SR_Greysphere_Calibration_sheen_roughness, SR_Greysphere_Calibration_coat, SR_Greysphere_Calibration_coat_color, SR_Greysphere_Calibration_coat_roughness, SR_Greysphere_Calibration_coat_anisotropy, SR_Greysphere_Calibration_coat_rotation, SR_Greysphere_Calibration_coat_IOR, geomprop_Nworld_out1, SR_Greysphere_Calibration_coat_affect_color, SR_Greysphere_Calibration_coat_affect_roughness, SR_Greysphere_Calibration_thin_film_thickness, SR_Greysphere_Calibration_thin_film_IOR, SR_Greysphere_Calibration_emission, SR_Greysphere_Calibration_emission_color, SR_Greysphere_Calibration_opacity, SR_Greysphere_Calibration_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_Greysphere_Calibration_out);
+ MATERIAL Greysphere_Calibration_out = mx_surfacematerial(SR_Greysphere_Calibration_out, displacementshader1);
+ out = Greysphere_Calibration_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Jade.glsl.frag b/Materials/Examples/StandardSurface/Jade.glsl.frag
new file mode 100644
index 0000000000..e9d6aa0f2c
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Jade.glsl.frag
@@ -0,0 +1,1706 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform float SR_jade_base = 0.500000;
+uniform vec3 SR_jade_base_color = vec3(0.060300, 0.439800, 0.191600);
+uniform float SR_jade_diffuse_roughness = 0.000000;
+uniform float SR_jade_metalness = 0.000000;
+uniform float SR_jade_specular = 1.000000;
+uniform vec3 SR_jade_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_jade_specular_roughness = 0.250000;
+uniform float SR_jade_specular_IOR = 2.418000;
+uniform float SR_jade_specular_anisotropy = 0.500000;
+uniform float SR_jade_specular_rotation = 0.000000;
+uniform float SR_jade_transmission = 0.000000;
+uniform vec3 SR_jade_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_jade_transmission_depth = 0.000000;
+uniform vec3 SR_jade_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float SR_jade_transmission_scatter_anisotropy = 0.000000;
+uniform float SR_jade_transmission_dispersion = 0.000000;
+uniform float SR_jade_transmission_extra_roughness = 0.000000;
+uniform float SR_jade_subsurface = 0.400000;
+uniform vec3 SR_jade_subsurface_color = vec3(0.060300, 0.439800, 0.191600);
+uniform vec3 SR_jade_subsurface_radius = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_jade_subsurface_scale = 1.000000;
+uniform float SR_jade_subsurface_anisotropy = 0.000000;
+uniform float SR_jade_sheen = 0.000000;
+uniform vec3 SR_jade_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_jade_sheen_roughness = 0.300000;
+uniform float SR_jade_coat = 0.000000;
+uniform vec3 SR_jade_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float SR_jade_coat_roughness = 0.100000;
+uniform float SR_jade_coat_anisotropy = 0.000000;
+uniform float SR_jade_coat_rotation = 0.000000;
+uniform float SR_jade_coat_IOR = 1.500000;
+uniform float SR_jade_coat_affect_color = 0.000000;
+uniform float SR_jade_coat_affect_roughness = 0.000000;
+uniform float SR_jade_thin_film_thickness = 0.000000;
+uniform float SR_jade_thin_film_IOR = 1.500000;
+uniform float SR_jade_emission = 0.000000;
+uniform vec3 SR_jade_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 SR_jade_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool SR_jade_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_jade_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(SR_jade_base, SR_jade_base_color, SR_jade_diffuse_roughness, SR_jade_metalness, SR_jade_specular, SR_jade_specular_color, SR_jade_specular_roughness, SR_jade_specular_IOR, SR_jade_specular_anisotropy, SR_jade_specular_rotation, SR_jade_transmission, SR_jade_transmission_color, SR_jade_transmission_depth, SR_jade_transmission_scatter, SR_jade_transmission_scatter_anisotropy, SR_jade_transmission_dispersion, SR_jade_transmission_extra_roughness, SR_jade_subsurface, SR_jade_subsurface_color, SR_jade_subsurface_radius, SR_jade_subsurface_scale, SR_jade_subsurface_anisotropy, SR_jade_sheen, SR_jade_sheen_color, SR_jade_sheen_roughness, SR_jade_coat, SR_jade_coat_color, SR_jade_coat_roughness, SR_jade_coat_anisotropy, SR_jade_coat_rotation, SR_jade_coat_IOR, geomprop_Nworld_out1, SR_jade_coat_affect_color, SR_jade_coat_affect_roughness, SR_jade_thin_film_thickness, SR_jade_thin_film_IOR, SR_jade_emission, SR_jade_emission_color, SR_jade_opacity, SR_jade_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_jade_out);
+ material Jade_out = SR_jade_out;
+ out1 = vec4(Jade_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/Jade.glsl.vert b/Materials/Examples/StandardSurface/Jade.glsl.vert
new file mode 100644
index 0000000000..6133cb8f5e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Jade.glsl.vert
@@ -0,0 +1,28 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/Jade.mdl b/Materials/Examples/StandardSurface/Jade.mdl
new file mode 100644
index 0000000000..40c4219f06
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Jade.mdl
@@ -0,0 +1,175 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material Jade
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ float SR_jade_base = 0.5,
+ color SR_jade_base_color = color(0.0603, 0.4398, 0.1916),
+ float SR_jade_diffuse_roughness = 0,
+ float SR_jade_metalness = 0,
+ float SR_jade_specular = 1,
+ color SR_jade_specular_color = color(1, 1, 1),
+ float SR_jade_specular_roughness = 0.25,
+ uniform float SR_jade_specular_IOR = 2.418,
+ float SR_jade_specular_anisotropy = 0.5,
+ float SR_jade_specular_rotation = 0,
+ float SR_jade_transmission = 0,
+ color SR_jade_transmission_color = color(1, 1, 1),
+ float SR_jade_transmission_depth = 0,
+ color SR_jade_transmission_scatter = color(0, 0, 0),
+ float SR_jade_transmission_scatter_anisotropy = 0,
+ float SR_jade_transmission_dispersion = 0,
+ float SR_jade_transmission_extra_roughness = 0,
+ float SR_jade_subsurface = 0.4,
+ color SR_jade_subsurface_color = color(0.0603, 0.4398, 0.1916),
+ color SR_jade_subsurface_radius = color(1, 1, 1),
+ float SR_jade_subsurface_scale = 1,
+ float SR_jade_subsurface_anisotropy = 0,
+ float SR_jade_sheen = 0,
+ color SR_jade_sheen_color = color(1, 1, 1),
+ float SR_jade_sheen_roughness = 0.3,
+ float SR_jade_coat = 0,
+ color SR_jade_coat_color = color(1, 1, 1),
+ float SR_jade_coat_roughness = 0.1,
+ float SR_jade_coat_anisotropy = 0,
+ float SR_jade_coat_rotation = 0,
+ uniform float SR_jade_coat_IOR = 1.5,
+ float SR_jade_coat_affect_color = 0,
+ float SR_jade_coat_affect_roughness = 0,
+ float SR_jade_thin_film_thickness = 0,
+ float SR_jade_thin_film_IOR = 1.5,
+ float SR_jade_emission = 0,
+ color SR_jade_emission_color = color(1, 1, 1),
+ color SR_jade_opacity = color(1, 1, 1),
+ bool SR_jade_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ material SR_jade_out = NG_standard_surface_surfaceshader_100(SR_jade_base, SR_jade_base_color, SR_jade_diffuse_roughness, SR_jade_metalness, SR_jade_specular, SR_jade_specular_color, SR_jade_specular_roughness, SR_jade_specular_IOR, SR_jade_specular_anisotropy, SR_jade_specular_rotation, SR_jade_transmission, SR_jade_transmission_color, SR_jade_transmission_depth, SR_jade_transmission_scatter, SR_jade_transmission_scatter_anisotropy, SR_jade_transmission_dispersion, SR_jade_transmission_extra_roughness, SR_jade_subsurface, SR_jade_subsurface_color, SR_jade_subsurface_radius, SR_jade_subsurface_scale, SR_jade_subsurface_anisotropy, SR_jade_sheen, SR_jade_sheen_color, SR_jade_sheen_roughness, SR_jade_coat, SR_jade_coat_color, SR_jade_coat_roughness, SR_jade_coat_anisotropy, SR_jade_coat_rotation, SR_jade_coat_IOR, geomprop_Nworld_out1, SR_jade_coat_affect_color, SR_jade_coat_affect_roughness, SR_jade_thin_film_thickness, SR_jade_thin_film_IOR, SR_jade_emission, SR_jade_emission_color, SR_jade_opacity, SR_jade_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1);
+ material Jade_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: SR_jade_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = Jade_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/Jade.msl.frag b/Materials/Examples/StandardSurface/Jade.msl.frag
new file mode 100644
index 0000000000..d3a8a09a7a
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Jade.msl.frag
@@ -0,0 +1,2324 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ float SR_jade_base;
+ vec3 SR_jade_base_color;
+ float SR_jade_diffuse_roughness;
+ float SR_jade_metalness;
+ float SR_jade_specular;
+ vec3 SR_jade_specular_color;
+ float SR_jade_specular_roughness;
+ float SR_jade_specular_IOR;
+ float SR_jade_specular_anisotropy;
+ float SR_jade_specular_rotation;
+ float SR_jade_transmission;
+ vec3 SR_jade_transmission_color;
+ float SR_jade_transmission_depth;
+ vec3 SR_jade_transmission_scatter;
+ float SR_jade_transmission_scatter_anisotropy;
+ float SR_jade_transmission_dispersion;
+ float SR_jade_transmission_extra_roughness;
+ float SR_jade_subsurface;
+ vec3 SR_jade_subsurface_color;
+ vec3 SR_jade_subsurface_radius;
+ float SR_jade_subsurface_scale;
+ float SR_jade_subsurface_anisotropy;
+ float SR_jade_sheen;
+ vec3 SR_jade_sheen_color;
+ float SR_jade_sheen_roughness;
+ float SR_jade_coat;
+ vec3 SR_jade_coat_color;
+ float SR_jade_coat_roughness;
+ float SR_jade_coat_anisotropy;
+ float SR_jade_coat_rotation;
+ float SR_jade_coat_IOR;
+ float SR_jade_coat_affect_color;
+ float SR_jade_coat_affect_roughness;
+ float SR_jade_thin_film_thickness;
+ float SR_jade_thin_film_IOR;
+ float SR_jade_emission;
+ vec3 SR_jade_emission_color;
+ vec3 SR_jade_opacity;
+ bool SR_jade_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+ , float SR_jade_base
+
+ , vec3 SR_jade_base_color
+
+ , float SR_jade_diffuse_roughness
+
+ , float SR_jade_metalness
+
+ , float SR_jade_specular
+
+ , vec3 SR_jade_specular_color
+
+ , float SR_jade_specular_roughness
+
+ , float SR_jade_specular_IOR
+
+ , float SR_jade_specular_anisotropy
+
+ , float SR_jade_specular_rotation
+
+ , float SR_jade_transmission
+
+ , vec3 SR_jade_transmission_color
+
+ , float SR_jade_transmission_depth
+
+ , vec3 SR_jade_transmission_scatter
+
+ , float SR_jade_transmission_scatter_anisotropy
+
+ , float SR_jade_transmission_dispersion
+
+ , float SR_jade_transmission_extra_roughness
+
+ , float SR_jade_subsurface
+
+ , vec3 SR_jade_subsurface_color
+
+ , vec3 SR_jade_subsurface_radius
+
+ , float SR_jade_subsurface_scale
+
+ , float SR_jade_subsurface_anisotropy
+
+ , float SR_jade_sheen
+
+ , vec3 SR_jade_sheen_color
+
+ , float SR_jade_sheen_roughness
+
+ , float SR_jade_coat
+
+ , vec3 SR_jade_coat_color
+
+ , float SR_jade_coat_roughness
+
+ , float SR_jade_coat_anisotropy
+
+ , float SR_jade_coat_rotation
+
+ , float SR_jade_coat_IOR
+
+ , float SR_jade_coat_affect_color
+
+ , float SR_jade_coat_affect_roughness
+
+ , float SR_jade_thin_film_thickness
+
+ , float SR_jade_thin_film_IOR
+
+ , float SR_jade_emission
+
+ , vec3 SR_jade_emission_color
+
+ , vec3 SR_jade_opacity
+
+ , bool SR_jade_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+ , SR_jade_base(SR_jade_base)
+
+ , SR_jade_base_color(SR_jade_base_color)
+
+ , SR_jade_diffuse_roughness(SR_jade_diffuse_roughness)
+
+ , SR_jade_metalness(SR_jade_metalness)
+
+ , SR_jade_specular(SR_jade_specular)
+
+ , SR_jade_specular_color(SR_jade_specular_color)
+
+ , SR_jade_specular_roughness(SR_jade_specular_roughness)
+
+ , SR_jade_specular_IOR(SR_jade_specular_IOR)
+
+ , SR_jade_specular_anisotropy(SR_jade_specular_anisotropy)
+
+ , SR_jade_specular_rotation(SR_jade_specular_rotation)
+
+ , SR_jade_transmission(SR_jade_transmission)
+
+ , SR_jade_transmission_color(SR_jade_transmission_color)
+
+ , SR_jade_transmission_depth(SR_jade_transmission_depth)
+
+ , SR_jade_transmission_scatter(SR_jade_transmission_scatter)
+
+ , SR_jade_transmission_scatter_anisotropy(SR_jade_transmission_scatter_anisotropy)
+
+ , SR_jade_transmission_dispersion(SR_jade_transmission_dispersion)
+
+ , SR_jade_transmission_extra_roughness(SR_jade_transmission_extra_roughness)
+
+ , SR_jade_subsurface(SR_jade_subsurface)
+
+ , SR_jade_subsurface_color(SR_jade_subsurface_color)
+
+ , SR_jade_subsurface_radius(SR_jade_subsurface_radius)
+
+ , SR_jade_subsurface_scale(SR_jade_subsurface_scale)
+
+ , SR_jade_subsurface_anisotropy(SR_jade_subsurface_anisotropy)
+
+ , SR_jade_sheen(SR_jade_sheen)
+
+ , SR_jade_sheen_color(SR_jade_sheen_color)
+
+ , SR_jade_sheen_roughness(SR_jade_sheen_roughness)
+
+ , SR_jade_coat(SR_jade_coat)
+
+ , SR_jade_coat_color(SR_jade_coat_color)
+
+ , SR_jade_coat_roughness(SR_jade_coat_roughness)
+
+ , SR_jade_coat_anisotropy(SR_jade_coat_anisotropy)
+
+ , SR_jade_coat_rotation(SR_jade_coat_rotation)
+
+ , SR_jade_coat_IOR(SR_jade_coat_IOR)
+
+ , SR_jade_coat_affect_color(SR_jade_coat_affect_color)
+
+ , SR_jade_coat_affect_roughness(SR_jade_coat_affect_roughness)
+
+ , SR_jade_thin_film_thickness(SR_jade_thin_film_thickness)
+
+ , SR_jade_thin_film_IOR(SR_jade_thin_film_IOR)
+
+ , SR_jade_emission(SR_jade_emission)
+
+ , SR_jade_emission_color(SR_jade_emission_color)
+
+ , SR_jade_opacity(SR_jade_opacity)
+
+ , SR_jade_thin_walled(SR_jade_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+ float SR_jade_base;
+
+
+ vec3 SR_jade_base_color;
+
+
+ float SR_jade_diffuse_roughness;
+
+
+ float SR_jade_metalness;
+
+
+ float SR_jade_specular;
+
+
+ vec3 SR_jade_specular_color;
+
+
+ float SR_jade_specular_roughness;
+
+
+ float SR_jade_specular_IOR;
+
+
+ float SR_jade_specular_anisotropy;
+
+
+ float SR_jade_specular_rotation;
+
+
+ float SR_jade_transmission;
+
+
+ vec3 SR_jade_transmission_color;
+
+
+ float SR_jade_transmission_depth;
+
+
+ vec3 SR_jade_transmission_scatter;
+
+
+ float SR_jade_transmission_scatter_anisotropy;
+
+
+ float SR_jade_transmission_dispersion;
+
+
+ float SR_jade_transmission_extra_roughness;
+
+
+ float SR_jade_subsurface;
+
+
+ vec3 SR_jade_subsurface_color;
+
+
+ vec3 SR_jade_subsurface_radius;
+
+
+ float SR_jade_subsurface_scale;
+
+
+ float SR_jade_subsurface_anisotropy;
+
+
+ float SR_jade_sheen;
+
+
+ vec3 SR_jade_sheen_color;
+
+
+ float SR_jade_sheen_roughness;
+
+
+ float SR_jade_coat;
+
+
+ vec3 SR_jade_coat_color;
+
+
+ float SR_jade_coat_roughness;
+
+
+ float SR_jade_coat_anisotropy;
+
+
+ float SR_jade_coat_rotation;
+
+
+ float SR_jade_coat_IOR;
+
+
+ float SR_jade_coat_affect_color;
+
+
+ float SR_jade_coat_affect_roughness;
+
+
+ float SR_jade_thin_film_thickness;
+
+
+ float SR_jade_thin_film_IOR;
+
+
+ float SR_jade_emission;
+
+
+ vec3 SR_jade_emission_color;
+
+
+ vec3 SR_jade_opacity;
+
+
+ bool SR_jade_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ surfaceshader SR_jade_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(SR_jade_base, SR_jade_base_color, SR_jade_diffuse_roughness, SR_jade_metalness, SR_jade_specular, SR_jade_specular_color, SR_jade_specular_roughness, SR_jade_specular_IOR, SR_jade_specular_anisotropy, SR_jade_specular_rotation, SR_jade_transmission, SR_jade_transmission_color, SR_jade_transmission_depth, SR_jade_transmission_scatter, SR_jade_transmission_scatter_anisotropy, SR_jade_transmission_dispersion, SR_jade_transmission_extra_roughness, SR_jade_subsurface, SR_jade_subsurface_color, SR_jade_subsurface_radius, SR_jade_subsurface_scale, SR_jade_subsurface_anisotropy, SR_jade_sheen, SR_jade_sheen_color, SR_jade_sheen_roughness, SR_jade_coat, SR_jade_coat_color, SR_jade_coat_roughness, SR_jade_coat_anisotropy, SR_jade_coat_rotation, SR_jade_coat_IOR, geomprop_Nworld_out1, SR_jade_coat_affect_color, SR_jade_coat_affect_roughness, SR_jade_thin_film_thickness, SR_jade_thin_film_IOR, SR_jade_emission, SR_jade_emission_color, SR_jade_opacity, SR_jade_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_jade_out);
+ material Jade_out = SR_jade_out;
+ out1 = float4(Jade_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(0)]], sampler u_envRadiance_sampler [[sampler(0)]]
+, texture2d u_envIrradiance_tex [[texture(1)]], sampler u_envIrradiance_sampler [[sampler(1)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+ , u_pub.SR_jade_base
+ , u_pub.SR_jade_base_color
+ , u_pub.SR_jade_diffuse_roughness
+ , u_pub.SR_jade_metalness
+ , u_pub.SR_jade_specular
+ , u_pub.SR_jade_specular_color
+ , u_pub.SR_jade_specular_roughness
+ , u_pub.SR_jade_specular_IOR
+ , u_pub.SR_jade_specular_anisotropy
+ , u_pub.SR_jade_specular_rotation
+ , u_pub.SR_jade_transmission
+ , u_pub.SR_jade_transmission_color
+ , u_pub.SR_jade_transmission_depth
+ , u_pub.SR_jade_transmission_scatter
+ , u_pub.SR_jade_transmission_scatter_anisotropy
+ , u_pub.SR_jade_transmission_dispersion
+ , u_pub.SR_jade_transmission_extra_roughness
+ , u_pub.SR_jade_subsurface
+ , u_pub.SR_jade_subsurface_color
+ , u_pub.SR_jade_subsurface_radius
+ , u_pub.SR_jade_subsurface_scale
+ , u_pub.SR_jade_subsurface_anisotropy
+ , u_pub.SR_jade_sheen
+ , u_pub.SR_jade_sheen_color
+ , u_pub.SR_jade_sheen_roughness
+ , u_pub.SR_jade_coat
+ , u_pub.SR_jade_coat_color
+ , u_pub.SR_jade_coat_roughness
+ , u_pub.SR_jade_coat_anisotropy
+ , u_pub.SR_jade_coat_rotation
+ , u_pub.SR_jade_coat_IOR
+ , u_pub.SR_jade_coat_affect_color
+ , u_pub.SR_jade_coat_affect_roughness
+ , u_pub.SR_jade_thin_film_thickness
+ , u_pub.SR_jade_thin_film_IOR
+ , u_pub.SR_jade_emission
+ , u_pub.SR_jade_emission_color
+ , u_pub.SR_jade_opacity
+ , u_pub.SR_jade_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/Jade.msl.vert b/Materials/Examples/StandardSurface/Jade.msl.vert
new file mode 100644
index 0000000000..f4c77afd6b
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Jade.msl.vert
@@ -0,0 +1,110 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'SR_jade'. Function already called in this scope.
+ // Omitted node 'Jade'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(3) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/Jade.osl b/Materials/Examples/StandardSurface/Jade.osl
new file mode 100644
index 0000000000..3f1490fd93
--- /dev/null
+++ b/Materials/Examples/StandardSurface/Jade.osl
@@ -0,0 +1,637 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader Jade
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "Jade"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_base = 0.5
+ [[
+ string widget = "number"
+ ]],
+ color SR_jade_base_color = color(0.0603, 0.4398, 0.1916),
+ float SR_jade_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_metalness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color SR_jade_specular_color = color(1, 1, 1),
+ float SR_jade_specular_roughness = 0.25
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_specular_IOR = 2.418
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_specular_anisotropy = 0.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_jade_transmission_color = color(1, 1, 1),
+ float SR_jade_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_jade_transmission_scatter = color(0, 0, 0),
+ float SR_jade_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_subsurface = 0.4
+ [[
+ string widget = "number"
+ ]],
+ color SR_jade_subsurface_color = color(0.0603, 0.4398, 0.1916),
+ color SR_jade_subsurface_radius = color(1, 1, 1),
+ float SR_jade_subsurface_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_jade_sheen_color = color(1, 1, 1),
+ float SR_jade_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_jade_coat_color = color(1, 1, 1),
+ float SR_jade_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float SR_jade_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color SR_jade_emission_color = color(1, 1, 1),
+ color SR_jade_opacity = color(1, 1, 1),
+ int SR_jade_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ surfaceshader SR_jade_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(SR_jade_base, SR_jade_base_color, SR_jade_diffuse_roughness, SR_jade_metalness, SR_jade_specular, SR_jade_specular_color, SR_jade_specular_roughness, SR_jade_specular_IOR, SR_jade_specular_anisotropy, SR_jade_specular_rotation, SR_jade_transmission, SR_jade_transmission_color, SR_jade_transmission_depth, SR_jade_transmission_scatter, SR_jade_transmission_scatter_anisotropy, SR_jade_transmission_dispersion, SR_jade_transmission_extra_roughness, SR_jade_subsurface, SR_jade_subsurface_color, SR_jade_subsurface_radius, SR_jade_subsurface_scale, SR_jade_subsurface_anisotropy, SR_jade_sheen, SR_jade_sheen_color, SR_jade_sheen_roughness, SR_jade_coat, SR_jade_coat_color, SR_jade_coat_roughness, SR_jade_coat_anisotropy, SR_jade_coat_rotation, SR_jade_coat_IOR, geomprop_Nworld_out1, SR_jade_coat_affect_color, SR_jade_coat_affect_roughness, SR_jade_thin_film_thickness, SR_jade_thin_film_IOR, SR_jade_emission, SR_jade_emission_color, SR_jade_opacity, SR_jade_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_jade_out);
+ MATERIAL Jade_out = mx_surfacematerial(SR_jade_out, displacementshader1);
+ out = Jade_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Bishop_B.glsl.frag b/Materials/Examples/StandardSurface/M_Bishop_B.glsl.frag
new file mode 100644
index 0000000000..c6e9519c5a
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_B.glsl.frag
@@ -0,0 +1,1854 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform sampler2D diffuse2_file;
+uniform int diffuse2_layer = 0;
+uniform vec3 diffuse2_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int diffuse2_uaddressmode = 2;
+uniform int diffuse2_vaddressmode = 2;
+uniform int diffuse2_filtertype = 1;
+uniform int diffuse2_framerange = 0;
+uniform int diffuse2_frameoffset = 0;
+uniform int diffuse2_frameendaction = 0;
+uniform vec2 diffuse2_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 diffuse2_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D metallic2_file;
+uniform int metallic2_layer = 0;
+uniform float metallic2_default = 0.000000;
+uniform int metallic2_uaddressmode = 2;
+uniform int metallic2_vaddressmode = 2;
+uniform int metallic2_filtertype = 1;
+uniform int metallic2_framerange = 0;
+uniform int metallic2_frameoffset = 0;
+uniform int metallic2_frameendaction = 0;
+uniform vec2 metallic2_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 metallic2_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D roughness2_file;
+uniform int roughness2_layer = 0;
+uniform float roughness2_default = 0.000000;
+uniform int roughness2_uaddressmode = 2;
+uniform int roughness2_vaddressmode = 2;
+uniform int roughness2_filtertype = 1;
+uniform int roughness2_framerange = 0;
+uniform int roughness2_frameoffset = 0;
+uniform int roughness2_frameendaction = 0;
+uniform vec2 roughness2_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 roughness2_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D normal2_file;
+uniform int normal2_layer = 0;
+uniform vec3 normal2_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int normal2_uaddressmode = 2;
+uniform int normal2_vaddressmode = 2;
+uniform int normal2_filtertype = 1;
+uniform int normal2_framerange = 0;
+uniform int normal2_frameoffset = 0;
+uniform int normal2_frameendaction = 0;
+uniform vec2 normal2_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 normal2_uv_offset = vec2(0.000000, 0.000000);
+uniform int mtlxnormalmap4_space = 0;
+uniform float mtlxnormalmap4_scale = 1.000000;
+uniform float Bishop_B_base = 1.000000;
+uniform float Bishop_B_diffuse_roughness = 0.000000;
+uniform float Bishop_B_specular = 1.000000;
+uniform vec3 Bishop_B_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Bishop_B_specular_IOR = 1.500000;
+uniform float Bishop_B_specular_anisotropy = 0.000000;
+uniform float Bishop_B_specular_rotation = 0.000000;
+uniform float Bishop_B_transmission = 0.000000;
+uniform vec3 Bishop_B_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Bishop_B_transmission_depth = 0.000000;
+uniform vec3 Bishop_B_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float Bishop_B_transmission_scatter_anisotropy = 0.000000;
+uniform float Bishop_B_transmission_dispersion = 0.000000;
+uniform float Bishop_B_transmission_extra_roughness = 0.000000;
+uniform float Bishop_B_subsurface = 0.000000;
+uniform float Bishop_B_subsurface_scale = 0.003000;
+uniform float Bishop_B_subsurface_anisotropy = 0.000000;
+uniform float Bishop_B_sheen = 0.000000;
+uniform vec3 Bishop_B_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Bishop_B_sheen_roughness = 0.300000;
+uniform float Bishop_B_coat = 0.000000;
+uniform vec3 Bishop_B_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Bishop_B_coat_roughness = 0.100000;
+uniform float Bishop_B_coat_anisotropy = 0.000000;
+uniform float Bishop_B_coat_rotation = 0.000000;
+uniform float Bishop_B_coat_IOR = 1.500000;
+uniform float Bishop_B_coat_affect_color = 0.000000;
+uniform float Bishop_B_coat_affect_roughness = 0.000000;
+uniform float Bishop_B_thin_film_thickness = 0.000000;
+uniform float Bishop_B_thin_film_IOR = 1.500000;
+uniform float Bishop_B_emission = 0.000000;
+uniform vec3 Bishop_B_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 Bishop_B_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool Bishop_B_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+{
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+}
+
+void mx_image_color3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_float(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_vector3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, out vec3 out1)
+{
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+}
+
+void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, out vec3 result)
+{
+ // Decode the normal map.
+ value = (value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 diffuse2_out = vec3(0.0);
+ mx_image_color3(diffuse2_file, diffuse2_layer, diffuse2_default, geomprop_UV0_out1, diffuse2_uaddressmode, diffuse2_vaddressmode, diffuse2_filtertype, diffuse2_framerange, diffuse2_frameoffset, diffuse2_frameendaction, diffuse2_uv_scale, diffuse2_uv_offset, diffuse2_out);
+ float metallic2_out = 0.0;
+ mx_image_float(metallic2_file, metallic2_layer, metallic2_default, geomprop_UV0_out1, metallic2_uaddressmode, metallic2_vaddressmode, metallic2_filtertype, metallic2_framerange, metallic2_frameoffset, metallic2_frameendaction, metallic2_uv_scale, metallic2_uv_offset, metallic2_out);
+ float roughness2_out = 0.0;
+ mx_image_float(roughness2_file, roughness2_layer, roughness2_default, geomprop_UV0_out1, roughness2_uaddressmode, roughness2_vaddressmode, roughness2_filtertype, roughness2_framerange, roughness2_frameoffset, roughness2_frameendaction, roughness2_uv_scale, roughness2_uv_offset, roughness2_out);
+ vec3 normal2_out = vec3(0.0);
+ mx_image_vector3(normal2_file, normal2_layer, normal2_default, geomprop_UV0_out1, normal2_uaddressmode, normal2_vaddressmode, normal2_filtertype, normal2_framerange, normal2_frameoffset, normal2_frameendaction, normal2_uv_scale, normal2_uv_offset, normal2_out);
+ vec3 diffuse2_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse2_out, diffuse2_out_cm_out);
+ vec3 mtlxnormalmap4_out = vec3(0.0);
+ mx_normalmap(normal2_out, mtlxnormalmap4_space, mtlxnormalmap4_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap4_out);
+ surfaceshader Bishop_B_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(Bishop_B_base, diffuse2_out_cm_out, Bishop_B_diffuse_roughness, metallic2_out, Bishop_B_specular, Bishop_B_specular_color, roughness2_out, Bishop_B_specular_IOR, Bishop_B_specular_anisotropy, Bishop_B_specular_rotation, Bishop_B_transmission, Bishop_B_transmission_color, Bishop_B_transmission_depth, Bishop_B_transmission_scatter, Bishop_B_transmission_scatter_anisotropy, Bishop_B_transmission_dispersion, Bishop_B_transmission_extra_roughness, Bishop_B_subsurface, diffuse2_out_cm_out, diffuse2_out_cm_out, Bishop_B_subsurface_scale, Bishop_B_subsurface_anisotropy, Bishop_B_sheen, Bishop_B_sheen_color, Bishop_B_sheen_roughness, Bishop_B_coat, Bishop_B_coat_color, Bishop_B_coat_roughness, Bishop_B_coat_anisotropy, Bishop_B_coat_rotation, Bishop_B_coat_IOR, geomprop_Nworld_out1, Bishop_B_coat_affect_color, Bishop_B_coat_affect_roughness, Bishop_B_thin_film_thickness, Bishop_B_thin_film_IOR, Bishop_B_emission, Bishop_B_emission_color, Bishop_B_opacity, Bishop_B_thin_walled, mtlxnormalmap4_out, geomprop_Tworld_out1, Bishop_B_out);
+ material M_Bishop_B_out = Bishop_B_out;
+ out1 = vec4(M_Bishop_B_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Bishop_B.glsl.vert b/Materials/Examples/StandardSurface/M_Bishop_B.glsl.vert
new file mode 100644
index 0000000000..60b47d493e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_B.glsl.vert
@@ -0,0 +1,31 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+in vec2 i_texcoord_0;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Bishop_B.mdl b/Materials/Examples/StandardSurface/M_Bishop_B.mdl
new file mode 100644
index 0000000000..f56f99ea10
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_B.mdl
@@ -0,0 +1,234 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+color NG_srgb_texture_to_lin_rec709_color3
+(
+ color in1 = color(0, 0, 0)
+)
+{
+ color bias_out = in1 + 0.055;
+ color linSeg_out = in1 / 12.92;
+ float isAboveR_out = mx::stdlib::mx_ifgreater_float(float3(in1).x, 0.04045, 1, 0);
+ float isAboveG_out = mx::stdlib::mx_ifgreater_float(float3(in1).y, 0.04045, 1, 0);
+ float isAboveB_out = mx::stdlib::mx_ifgreater_float(float3(in1).z, 0.04045, 1, 0);
+ color max_out = math::max(bias_out, 0);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ color scale_out = max_out / 1.055;
+ color powSeg_out = math::pow(scale_out, 2.4);
+ color mix_out = math::lerp(linSeg_out, powSeg_out, isAbove_out);
+ return mix_out;
+}
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material M_Bishop_B
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ uniform int geomprop_UV0_index = 0,
+ uniform texture_2d diffuse2_file = texture_2d("/chess_set/bishop_black_base_color.jpg", tex::gamma_linear),
+ uniform string diffuse2_layer = "",
+ color diffuse2_default = color(0, 0, 0),
+ uniform mx_addressmode_type diffuse2_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type diffuse2_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type diffuse2_filtertype = mx_filterlookup_type_linear,
+ uniform string diffuse2_framerange = "",
+ uniform int diffuse2_frameoffset = 0,
+ uniform mx_addressmode_type diffuse2_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d metallic2_file = texture_2d("/chess_set/bishop_shared_metallic.jpg", tex::gamma_linear),
+ uniform string metallic2_layer = "",
+ float metallic2_default = 0,
+ uniform mx_addressmode_type metallic2_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type metallic2_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type metallic2_filtertype = mx_filterlookup_type_linear,
+ uniform string metallic2_framerange = "",
+ uniform int metallic2_frameoffset = 0,
+ uniform mx_addressmode_type metallic2_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d roughness2_file = texture_2d("/chess_set/bishop_black_roughness.jpg", tex::gamma_linear),
+ uniform string roughness2_layer = "",
+ float roughness2_default = 0,
+ uniform mx_addressmode_type roughness2_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type roughness2_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type roughness2_filtertype = mx_filterlookup_type_linear,
+ uniform string roughness2_framerange = "",
+ uniform int roughness2_frameoffset = 0,
+ uniform mx_addressmode_type roughness2_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d normal2_file = texture_2d("/chess_set/bishop_black_normal.jpg", tex::gamma_linear),
+ uniform string normal2_layer = "",
+ float3 normal2_default = float3(0, 0, 0),
+ uniform mx_addressmode_type normal2_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type normal2_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type normal2_filtertype = mx_filterlookup_type_linear,
+ uniform string normal2_framerange = "",
+ uniform int normal2_frameoffset = 0,
+ uniform mx_addressmode_type normal2_frameendaction = mx_addressmode_type_constant,
+ uniform string mtlxnormalmap4_space = "tangent",
+ float mtlxnormalmap4_scale = 1,
+ float Bishop_B_base = 1,
+ float Bishop_B_diffuse_roughness = 0,
+ float Bishop_B_specular = 1,
+ color Bishop_B_specular_color = color(1, 1, 1),
+ uniform float Bishop_B_specular_IOR = 1.5,
+ float Bishop_B_specular_anisotropy = 0,
+ float Bishop_B_specular_rotation = 0,
+ float Bishop_B_transmission = 0,
+ color Bishop_B_transmission_color = color(1, 1, 1),
+ float Bishop_B_transmission_depth = 0,
+ color Bishop_B_transmission_scatter = color(0, 0, 0),
+ float Bishop_B_transmission_scatter_anisotropy = 0,
+ float Bishop_B_transmission_dispersion = 0,
+ float Bishop_B_transmission_extra_roughness = 0,
+ float Bishop_B_subsurface = 0,
+ float Bishop_B_subsurface_scale = 0.003,
+ float Bishop_B_subsurface_anisotropy = 0,
+ float Bishop_B_sheen = 0,
+ color Bishop_B_sheen_color = color(1, 1, 1),
+ float Bishop_B_sheen_roughness = 0.3,
+ float Bishop_B_coat = 0,
+ color Bishop_B_coat_color = color(1, 1, 1),
+ float Bishop_B_coat_roughness = 0.1,
+ float Bishop_B_coat_anisotropy = 0,
+ float Bishop_B_coat_rotation = 0,
+ uniform float Bishop_B_coat_IOR = 1.5,
+ float Bishop_B_coat_affect_color = 0,
+ float Bishop_B_coat_affect_roughness = 0,
+ float Bishop_B_thin_film_thickness = 0,
+ float Bishop_B_thin_film_IOR = 1.5,
+ float Bishop_B_emission = 0,
+ color Bishop_B_emission_color = color(1, 1, 1),
+ color Bishop_B_opacity = color(1, 1, 1),
+ bool Bishop_B_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ float2 geomprop_UV0_out1 = mx::stdlib::mx_texcoord_vector2(mxp_index:geomprop_UV0_index);
+ color diffuse2_out = mx::stdlib::mx_image_color3(diffuse2_file, diffuse2_layer, diffuse2_default, geomprop_UV0_out1, diffuse2_uaddressmode, diffuse2_vaddressmode, diffuse2_filtertype, diffuse2_framerange, diffuse2_frameoffset, diffuse2_frameendaction);
+ float metallic2_out = mx::stdlib::mx_image_float(metallic2_file, metallic2_layer, metallic2_default, geomprop_UV0_out1, metallic2_uaddressmode, metallic2_vaddressmode, metallic2_filtertype, metallic2_framerange, metallic2_frameoffset, metallic2_frameendaction);
+ float roughness2_out = mx::stdlib::mx_image_float(roughness2_file, roughness2_layer, roughness2_default, geomprop_UV0_out1, roughness2_uaddressmode, roughness2_vaddressmode, roughness2_filtertype, roughness2_framerange, roughness2_frameoffset, roughness2_frameendaction);
+ float3 normal2_out = mx::stdlib::mx_image_vector3(normal2_file, normal2_layer, normal2_default, geomprop_UV0_out1, normal2_uaddressmode, normal2_vaddressmode, normal2_filtertype, normal2_framerange, normal2_frameoffset, normal2_frameendaction);
+ color diffuse2_out_cm_out = NG_srgb_texture_to_lin_rec709_color3(diffuse2_out);
+ float3 mtlxnormalmap4_out = mx::stdlib::mx_normalmap(mxp_in:normal2_out, mxp_space:mtlxnormalmap4_space, mxp_scale:mtlxnormalmap4_scale, mxp_normal:geomprop_Nworld_out1, mxp_tangent:geomprop_Tworld_out1);
+ material Bishop_B_out = NG_standard_surface_surfaceshader_100(Bishop_B_base, diffuse2_out_cm_out, Bishop_B_diffuse_roughness, metallic2_out, Bishop_B_specular, Bishop_B_specular_color, roughness2_out, Bishop_B_specular_IOR, Bishop_B_specular_anisotropy, Bishop_B_specular_rotation, Bishop_B_transmission, Bishop_B_transmission_color, Bishop_B_transmission_depth, Bishop_B_transmission_scatter, Bishop_B_transmission_scatter_anisotropy, Bishop_B_transmission_dispersion, Bishop_B_transmission_extra_roughness, Bishop_B_subsurface, diffuse2_out_cm_out, diffuse2_out_cm_out, Bishop_B_subsurface_scale, Bishop_B_subsurface_anisotropy, Bishop_B_sheen, Bishop_B_sheen_color, Bishop_B_sheen_roughness, Bishop_B_coat, Bishop_B_coat_color, Bishop_B_coat_roughness, Bishop_B_coat_anisotropy, Bishop_B_coat_rotation, Bishop_B_coat_IOR, geomprop_Nworld_out1, Bishop_B_coat_affect_color, Bishop_B_coat_affect_roughness, Bishop_B_thin_film_thickness, Bishop_B_thin_film_IOR, Bishop_B_emission, Bishop_B_emission_color, Bishop_B_opacity, Bishop_B_thin_walled, mtlxnormalmap4_out, geomprop_Tworld_out1);
+ material M_Bishop_B_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: Bishop_B_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = M_Bishop_B_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/M_Bishop_B.msl.frag b/Materials/Examples/StandardSurface/M_Bishop_B.msl.frag
new file mode 100644
index 0000000000..23728e93a3
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_B.msl.frag
@@ -0,0 +1,2784 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ int diffuse2_layer;
+ vec3 diffuse2_default;
+ int diffuse2_uaddressmode;
+ int diffuse2_vaddressmode;
+ int diffuse2_filtertype;
+ int diffuse2_framerange;
+ int diffuse2_frameoffset;
+ int diffuse2_frameendaction;
+ vec2 diffuse2_uv_scale;
+ vec2 diffuse2_uv_offset;
+ int metallic2_layer;
+ float metallic2_default;
+ int metallic2_uaddressmode;
+ int metallic2_vaddressmode;
+ int metallic2_filtertype;
+ int metallic2_framerange;
+ int metallic2_frameoffset;
+ int metallic2_frameendaction;
+ vec2 metallic2_uv_scale;
+ vec2 metallic2_uv_offset;
+ int roughness2_layer;
+ float roughness2_default;
+ int roughness2_uaddressmode;
+ int roughness2_vaddressmode;
+ int roughness2_filtertype;
+ int roughness2_framerange;
+ int roughness2_frameoffset;
+ int roughness2_frameendaction;
+ vec2 roughness2_uv_scale;
+ vec2 roughness2_uv_offset;
+ int normal2_layer;
+ vec3 normal2_default;
+ int normal2_uaddressmode;
+ int normal2_vaddressmode;
+ int normal2_filtertype;
+ int normal2_framerange;
+ int normal2_frameoffset;
+ int normal2_frameendaction;
+ vec2 normal2_uv_scale;
+ vec2 normal2_uv_offset;
+ int mtlxnormalmap4_space;
+ float mtlxnormalmap4_scale;
+ float Bishop_B_base;
+ float Bishop_B_diffuse_roughness;
+ float Bishop_B_specular;
+ vec3 Bishop_B_specular_color;
+ float Bishop_B_specular_IOR;
+ float Bishop_B_specular_anisotropy;
+ float Bishop_B_specular_rotation;
+ float Bishop_B_transmission;
+ vec3 Bishop_B_transmission_color;
+ float Bishop_B_transmission_depth;
+ vec3 Bishop_B_transmission_scatter;
+ float Bishop_B_transmission_scatter_anisotropy;
+ float Bishop_B_transmission_dispersion;
+ float Bishop_B_transmission_extra_roughness;
+ float Bishop_B_subsurface;
+ float Bishop_B_subsurface_scale;
+ float Bishop_B_subsurface_anisotropy;
+ float Bishop_B_sheen;
+ vec3 Bishop_B_sheen_color;
+ float Bishop_B_sheen_roughness;
+ float Bishop_B_coat;
+ vec3 Bishop_B_coat_color;
+ float Bishop_B_coat_roughness;
+ float Bishop_B_coat_anisotropy;
+ float Bishop_B_coat_rotation;
+ float Bishop_B_coat_IOR;
+ float Bishop_B_coat_affect_color;
+ float Bishop_B_coat_affect_roughness;
+ float Bishop_B_thin_film_thickness;
+ float Bishop_B_thin_film_IOR;
+ float Bishop_B_emission;
+ vec3 Bishop_B_emission_color;
+ vec3 Bishop_B_opacity;
+ bool Bishop_B_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec2 texcoord_0 ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+, MetalTexture diffuse2_file , int diffuse2_layer
+
+ , vec3 diffuse2_default
+
+ , int diffuse2_uaddressmode
+
+ , int diffuse2_vaddressmode
+
+ , int diffuse2_filtertype
+
+ , int diffuse2_framerange
+
+ , int diffuse2_frameoffset
+
+ , int diffuse2_frameendaction
+
+ , vec2 diffuse2_uv_scale
+
+ , vec2 diffuse2_uv_offset
+
+, MetalTexture metallic2_file , int metallic2_layer
+
+ , float metallic2_default
+
+ , int metallic2_uaddressmode
+
+ , int metallic2_vaddressmode
+
+ , int metallic2_filtertype
+
+ , int metallic2_framerange
+
+ , int metallic2_frameoffset
+
+ , int metallic2_frameendaction
+
+ , vec2 metallic2_uv_scale
+
+ , vec2 metallic2_uv_offset
+
+, MetalTexture roughness2_file , int roughness2_layer
+
+ , float roughness2_default
+
+ , int roughness2_uaddressmode
+
+ , int roughness2_vaddressmode
+
+ , int roughness2_filtertype
+
+ , int roughness2_framerange
+
+ , int roughness2_frameoffset
+
+ , int roughness2_frameendaction
+
+ , vec2 roughness2_uv_scale
+
+ , vec2 roughness2_uv_offset
+
+, MetalTexture normal2_file , int normal2_layer
+
+ , vec3 normal2_default
+
+ , int normal2_uaddressmode
+
+ , int normal2_vaddressmode
+
+ , int normal2_filtertype
+
+ , int normal2_framerange
+
+ , int normal2_frameoffset
+
+ , int normal2_frameendaction
+
+ , vec2 normal2_uv_scale
+
+ , vec2 normal2_uv_offset
+
+ , int mtlxnormalmap4_space
+
+ , float mtlxnormalmap4_scale
+
+ , float Bishop_B_base
+
+ , float Bishop_B_diffuse_roughness
+
+ , float Bishop_B_specular
+
+ , vec3 Bishop_B_specular_color
+
+ , float Bishop_B_specular_IOR
+
+ , float Bishop_B_specular_anisotropy
+
+ , float Bishop_B_specular_rotation
+
+ , float Bishop_B_transmission
+
+ , vec3 Bishop_B_transmission_color
+
+ , float Bishop_B_transmission_depth
+
+ , vec3 Bishop_B_transmission_scatter
+
+ , float Bishop_B_transmission_scatter_anisotropy
+
+ , float Bishop_B_transmission_dispersion
+
+ , float Bishop_B_transmission_extra_roughness
+
+ , float Bishop_B_subsurface
+
+ , float Bishop_B_subsurface_scale
+
+ , float Bishop_B_subsurface_anisotropy
+
+ , float Bishop_B_sheen
+
+ , vec3 Bishop_B_sheen_color
+
+ , float Bishop_B_sheen_roughness
+
+ , float Bishop_B_coat
+
+ , vec3 Bishop_B_coat_color
+
+ , float Bishop_B_coat_roughness
+
+ , float Bishop_B_coat_anisotropy
+
+ , float Bishop_B_coat_rotation
+
+ , float Bishop_B_coat_IOR
+
+ , float Bishop_B_coat_affect_color
+
+ , float Bishop_B_coat_affect_roughness
+
+ , float Bishop_B_thin_film_thickness
+
+ , float Bishop_B_thin_film_IOR
+
+ , float Bishop_B_emission
+
+ , vec3 Bishop_B_emission_color
+
+ , vec3 Bishop_B_opacity
+
+ , bool Bishop_B_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+, diffuse2_file(diffuse2_file)
+ , diffuse2_layer(diffuse2_layer)
+
+ , diffuse2_default(diffuse2_default)
+
+ , diffuse2_uaddressmode(diffuse2_uaddressmode)
+
+ , diffuse2_vaddressmode(diffuse2_vaddressmode)
+
+ , diffuse2_filtertype(diffuse2_filtertype)
+
+ , diffuse2_framerange(diffuse2_framerange)
+
+ , diffuse2_frameoffset(diffuse2_frameoffset)
+
+ , diffuse2_frameendaction(diffuse2_frameendaction)
+
+ , diffuse2_uv_scale(diffuse2_uv_scale)
+
+ , diffuse2_uv_offset(diffuse2_uv_offset)
+
+, metallic2_file(metallic2_file)
+ , metallic2_layer(metallic2_layer)
+
+ , metallic2_default(metallic2_default)
+
+ , metallic2_uaddressmode(metallic2_uaddressmode)
+
+ , metallic2_vaddressmode(metallic2_vaddressmode)
+
+ , metallic2_filtertype(metallic2_filtertype)
+
+ , metallic2_framerange(metallic2_framerange)
+
+ , metallic2_frameoffset(metallic2_frameoffset)
+
+ , metallic2_frameendaction(metallic2_frameendaction)
+
+ , metallic2_uv_scale(metallic2_uv_scale)
+
+ , metallic2_uv_offset(metallic2_uv_offset)
+
+, roughness2_file(roughness2_file)
+ , roughness2_layer(roughness2_layer)
+
+ , roughness2_default(roughness2_default)
+
+ , roughness2_uaddressmode(roughness2_uaddressmode)
+
+ , roughness2_vaddressmode(roughness2_vaddressmode)
+
+ , roughness2_filtertype(roughness2_filtertype)
+
+ , roughness2_framerange(roughness2_framerange)
+
+ , roughness2_frameoffset(roughness2_frameoffset)
+
+ , roughness2_frameendaction(roughness2_frameendaction)
+
+ , roughness2_uv_scale(roughness2_uv_scale)
+
+ , roughness2_uv_offset(roughness2_uv_offset)
+
+, normal2_file(normal2_file)
+ , normal2_layer(normal2_layer)
+
+ , normal2_default(normal2_default)
+
+ , normal2_uaddressmode(normal2_uaddressmode)
+
+ , normal2_vaddressmode(normal2_vaddressmode)
+
+ , normal2_filtertype(normal2_filtertype)
+
+ , normal2_framerange(normal2_framerange)
+
+ , normal2_frameoffset(normal2_frameoffset)
+
+ , normal2_frameendaction(normal2_frameendaction)
+
+ , normal2_uv_scale(normal2_uv_scale)
+
+ , normal2_uv_offset(normal2_uv_offset)
+
+ , mtlxnormalmap4_space(mtlxnormalmap4_space)
+
+ , mtlxnormalmap4_scale(mtlxnormalmap4_scale)
+
+ , Bishop_B_base(Bishop_B_base)
+
+ , Bishop_B_diffuse_roughness(Bishop_B_diffuse_roughness)
+
+ , Bishop_B_specular(Bishop_B_specular)
+
+ , Bishop_B_specular_color(Bishop_B_specular_color)
+
+ , Bishop_B_specular_IOR(Bishop_B_specular_IOR)
+
+ , Bishop_B_specular_anisotropy(Bishop_B_specular_anisotropy)
+
+ , Bishop_B_specular_rotation(Bishop_B_specular_rotation)
+
+ , Bishop_B_transmission(Bishop_B_transmission)
+
+ , Bishop_B_transmission_color(Bishop_B_transmission_color)
+
+ , Bishop_B_transmission_depth(Bishop_B_transmission_depth)
+
+ , Bishop_B_transmission_scatter(Bishop_B_transmission_scatter)
+
+ , Bishop_B_transmission_scatter_anisotropy(Bishop_B_transmission_scatter_anisotropy)
+
+ , Bishop_B_transmission_dispersion(Bishop_B_transmission_dispersion)
+
+ , Bishop_B_transmission_extra_roughness(Bishop_B_transmission_extra_roughness)
+
+ , Bishop_B_subsurface(Bishop_B_subsurface)
+
+ , Bishop_B_subsurface_scale(Bishop_B_subsurface_scale)
+
+ , Bishop_B_subsurface_anisotropy(Bishop_B_subsurface_anisotropy)
+
+ , Bishop_B_sheen(Bishop_B_sheen)
+
+ , Bishop_B_sheen_color(Bishop_B_sheen_color)
+
+ , Bishop_B_sheen_roughness(Bishop_B_sheen_roughness)
+
+ , Bishop_B_coat(Bishop_B_coat)
+
+ , Bishop_B_coat_color(Bishop_B_coat_color)
+
+ , Bishop_B_coat_roughness(Bishop_B_coat_roughness)
+
+ , Bishop_B_coat_anisotropy(Bishop_B_coat_anisotropy)
+
+ , Bishop_B_coat_rotation(Bishop_B_coat_rotation)
+
+ , Bishop_B_coat_IOR(Bishop_B_coat_IOR)
+
+ , Bishop_B_coat_affect_color(Bishop_B_coat_affect_color)
+
+ , Bishop_B_coat_affect_roughness(Bishop_B_coat_affect_roughness)
+
+ , Bishop_B_thin_film_thickness(Bishop_B_thin_film_thickness)
+
+ , Bishop_B_thin_film_IOR(Bishop_B_thin_film_IOR)
+
+ , Bishop_B_emission(Bishop_B_emission)
+
+ , Bishop_B_emission_color(Bishop_B_emission_color)
+
+ , Bishop_B_opacity(Bishop_B_opacity)
+
+ , Bishop_B_thin_walled(Bishop_B_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+MetalTexture diffuse2_file;
+ int diffuse2_layer;
+
+
+ vec3 diffuse2_default;
+
+
+ int diffuse2_uaddressmode;
+
+
+ int diffuse2_vaddressmode;
+
+
+ int diffuse2_filtertype;
+
+
+ int diffuse2_framerange;
+
+
+ int diffuse2_frameoffset;
+
+
+ int diffuse2_frameendaction;
+
+
+ vec2 diffuse2_uv_scale;
+
+
+ vec2 diffuse2_uv_offset;
+
+
+MetalTexture metallic2_file;
+ int metallic2_layer;
+
+
+ float metallic2_default;
+
+
+ int metallic2_uaddressmode;
+
+
+ int metallic2_vaddressmode;
+
+
+ int metallic2_filtertype;
+
+
+ int metallic2_framerange;
+
+
+ int metallic2_frameoffset;
+
+
+ int metallic2_frameendaction;
+
+
+ vec2 metallic2_uv_scale;
+
+
+ vec2 metallic2_uv_offset;
+
+
+MetalTexture roughness2_file;
+ int roughness2_layer;
+
+
+ float roughness2_default;
+
+
+ int roughness2_uaddressmode;
+
+
+ int roughness2_vaddressmode;
+
+
+ int roughness2_filtertype;
+
+
+ int roughness2_framerange;
+
+
+ int roughness2_frameoffset;
+
+
+ int roughness2_frameendaction;
+
+
+ vec2 roughness2_uv_scale;
+
+
+ vec2 roughness2_uv_offset;
+
+
+MetalTexture normal2_file;
+ int normal2_layer;
+
+
+ vec3 normal2_default;
+
+
+ int normal2_uaddressmode;
+
+
+ int normal2_vaddressmode;
+
+
+ int normal2_filtertype;
+
+
+ int normal2_framerange;
+
+
+ int normal2_frameoffset;
+
+
+ int normal2_frameendaction;
+
+
+ vec2 normal2_uv_scale;
+
+
+ vec2 normal2_uv_offset;
+
+
+ int mtlxnormalmap4_space;
+
+
+ float mtlxnormalmap4_scale;
+
+
+ float Bishop_B_base;
+
+
+ float Bishop_B_diffuse_roughness;
+
+
+ float Bishop_B_specular;
+
+
+ vec3 Bishop_B_specular_color;
+
+
+ float Bishop_B_specular_IOR;
+
+
+ float Bishop_B_specular_anisotropy;
+
+
+ float Bishop_B_specular_rotation;
+
+
+ float Bishop_B_transmission;
+
+
+ vec3 Bishop_B_transmission_color;
+
+
+ float Bishop_B_transmission_depth;
+
+
+ vec3 Bishop_B_transmission_scatter;
+
+
+ float Bishop_B_transmission_scatter_anisotropy;
+
+
+ float Bishop_B_transmission_dispersion;
+
+
+ float Bishop_B_transmission_extra_roughness;
+
+
+ float Bishop_B_subsurface;
+
+
+ float Bishop_B_subsurface_scale;
+
+
+ float Bishop_B_subsurface_anisotropy;
+
+
+ float Bishop_B_sheen;
+
+
+ vec3 Bishop_B_sheen_color;
+
+
+ float Bishop_B_sheen_roughness;
+
+
+ float Bishop_B_coat;
+
+
+ vec3 Bishop_B_coat_color;
+
+
+ float Bishop_B_coat_roughness;
+
+
+ float Bishop_B_coat_anisotropy;
+
+
+ float Bishop_B_coat_rotation;
+
+
+ float Bishop_B_coat_IOR;
+
+
+ float Bishop_B_coat_affect_color;
+
+
+ float Bishop_B_coat_affect_roughness;
+
+
+ float Bishop_B_thin_film_thickness;
+
+
+ float Bishop_B_thin_film_IOR;
+
+
+ float Bishop_B_emission;
+
+
+ vec3 Bishop_B_emission_color;
+
+
+ vec3 Bishop_B_opacity;
+
+
+ bool Bishop_B_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+ {
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+ }
+
+ void mx_image_color3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_float(MetalTexture tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread float& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_vector3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+ void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, thread vec3& out1)
+ {
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+ }
+
+ void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, thread vec3& result)
+ {
+ // Decode the normal map.
+ value = all(value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 diffuse2_out = vec3(0.0);
+ mx_image_color3(diffuse2_file, diffuse2_layer, diffuse2_default, geomprop_UV0_out1, diffuse2_uaddressmode, diffuse2_vaddressmode, diffuse2_filtertype, diffuse2_framerange, diffuse2_frameoffset, diffuse2_frameendaction, diffuse2_uv_scale, diffuse2_uv_offset, diffuse2_out);
+ float metallic2_out = 0.0;
+ mx_image_float(metallic2_file, metallic2_layer, metallic2_default, geomprop_UV0_out1, metallic2_uaddressmode, metallic2_vaddressmode, metallic2_filtertype, metallic2_framerange, metallic2_frameoffset, metallic2_frameendaction, metallic2_uv_scale, metallic2_uv_offset, metallic2_out);
+ float roughness2_out = 0.0;
+ mx_image_float(roughness2_file, roughness2_layer, roughness2_default, geomprop_UV0_out1, roughness2_uaddressmode, roughness2_vaddressmode, roughness2_filtertype, roughness2_framerange, roughness2_frameoffset, roughness2_frameendaction, roughness2_uv_scale, roughness2_uv_offset, roughness2_out);
+ vec3 normal2_out = vec3(0.0);
+ mx_image_vector3(normal2_file, normal2_layer, normal2_default, geomprop_UV0_out1, normal2_uaddressmode, normal2_vaddressmode, normal2_filtertype, normal2_framerange, normal2_frameoffset, normal2_frameendaction, normal2_uv_scale, normal2_uv_offset, normal2_out);
+ vec3 diffuse2_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse2_out, diffuse2_out_cm_out);
+ vec3 mtlxnormalmap4_out = vec3(0.0);
+ mx_normalmap(normal2_out, mtlxnormalmap4_space, mtlxnormalmap4_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap4_out);
+ surfaceshader Bishop_B_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(Bishop_B_base, diffuse2_out_cm_out, Bishop_B_diffuse_roughness, metallic2_out, Bishop_B_specular, Bishop_B_specular_color, roughness2_out, Bishop_B_specular_IOR, Bishop_B_specular_anisotropy, Bishop_B_specular_rotation, Bishop_B_transmission, Bishop_B_transmission_color, Bishop_B_transmission_depth, Bishop_B_transmission_scatter, Bishop_B_transmission_scatter_anisotropy, Bishop_B_transmission_dispersion, Bishop_B_transmission_extra_roughness, Bishop_B_subsurface, diffuse2_out_cm_out, diffuse2_out_cm_out, Bishop_B_subsurface_scale, Bishop_B_subsurface_anisotropy, Bishop_B_sheen, Bishop_B_sheen_color, Bishop_B_sheen_roughness, Bishop_B_coat, Bishop_B_coat_color, Bishop_B_coat_roughness, Bishop_B_coat_anisotropy, Bishop_B_coat_rotation, Bishop_B_coat_IOR, geomprop_Nworld_out1, Bishop_B_coat_affect_color, Bishop_B_coat_affect_roughness, Bishop_B_thin_film_thickness, Bishop_B_thin_film_IOR, Bishop_B_emission, Bishop_B_emission_color, Bishop_B_opacity, Bishop_B_thin_walled, mtlxnormalmap4_out, geomprop_Tworld_out1, Bishop_B_out);
+ material M_Bishop_B_out = Bishop_B_out;
+ out1 = float4(M_Bishop_B_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], texture2d diffuse2_file_tex [[texture(0)]], sampler diffuse2_file_sampler [[sampler(0)]]
+, texture2d metallic2_file_tex [[texture(1)]], sampler metallic2_file_sampler [[sampler(1)]]
+, texture2d roughness2_file_tex [[texture(2)]], sampler roughness2_file_sampler [[sampler(2)]]
+, texture2d normal2_file_tex [[texture(3)]], sampler normal2_file_sampler [[sampler(3)]]
+, constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(4)]], sampler u_envRadiance_sampler [[sampler(4)]]
+, texture2d u_envIrradiance_tex [[texture(5)]], sampler u_envIrradiance_sampler [[sampler(5)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+, MetalTexture {
+diffuse2_file_tex, diffuse2_file_sampler }
+ , u_pub.diffuse2_layer
+ , u_pub.diffuse2_default
+ , u_pub.diffuse2_uaddressmode
+ , u_pub.diffuse2_vaddressmode
+ , u_pub.diffuse2_filtertype
+ , u_pub.diffuse2_framerange
+ , u_pub.diffuse2_frameoffset
+ , u_pub.diffuse2_frameendaction
+ , u_pub.diffuse2_uv_scale
+ , u_pub.diffuse2_uv_offset
+, MetalTexture {
+metallic2_file_tex, metallic2_file_sampler }
+ , u_pub.metallic2_layer
+ , u_pub.metallic2_default
+ , u_pub.metallic2_uaddressmode
+ , u_pub.metallic2_vaddressmode
+ , u_pub.metallic2_filtertype
+ , u_pub.metallic2_framerange
+ , u_pub.metallic2_frameoffset
+ , u_pub.metallic2_frameendaction
+ , u_pub.metallic2_uv_scale
+ , u_pub.metallic2_uv_offset
+, MetalTexture {
+roughness2_file_tex, roughness2_file_sampler }
+ , u_pub.roughness2_layer
+ , u_pub.roughness2_default
+ , u_pub.roughness2_uaddressmode
+ , u_pub.roughness2_vaddressmode
+ , u_pub.roughness2_filtertype
+ , u_pub.roughness2_framerange
+ , u_pub.roughness2_frameoffset
+ , u_pub.roughness2_frameendaction
+ , u_pub.roughness2_uv_scale
+ , u_pub.roughness2_uv_offset
+, MetalTexture {
+normal2_file_tex, normal2_file_sampler }
+ , u_pub.normal2_layer
+ , u_pub.normal2_default
+ , u_pub.normal2_uaddressmode
+ , u_pub.normal2_vaddressmode
+ , u_pub.normal2_filtertype
+ , u_pub.normal2_framerange
+ , u_pub.normal2_frameoffset
+ , u_pub.normal2_frameendaction
+ , u_pub.normal2_uv_scale
+ , u_pub.normal2_uv_offset
+ , u_pub.mtlxnormalmap4_space
+ , u_pub.mtlxnormalmap4_scale
+ , u_pub.Bishop_B_base
+ , u_pub.Bishop_B_diffuse_roughness
+ , u_pub.Bishop_B_specular
+ , u_pub.Bishop_B_specular_color
+ , u_pub.Bishop_B_specular_IOR
+ , u_pub.Bishop_B_specular_anisotropy
+ , u_pub.Bishop_B_specular_rotation
+ , u_pub.Bishop_B_transmission
+ , u_pub.Bishop_B_transmission_color
+ , u_pub.Bishop_B_transmission_depth
+ , u_pub.Bishop_B_transmission_scatter
+ , u_pub.Bishop_B_transmission_scatter_anisotropy
+ , u_pub.Bishop_B_transmission_dispersion
+ , u_pub.Bishop_B_transmission_extra_roughness
+ , u_pub.Bishop_B_subsurface
+ , u_pub.Bishop_B_subsurface_scale
+ , u_pub.Bishop_B_subsurface_anisotropy
+ , u_pub.Bishop_B_sheen
+ , u_pub.Bishop_B_sheen_color
+ , u_pub.Bishop_B_sheen_roughness
+ , u_pub.Bishop_B_coat
+ , u_pub.Bishop_B_coat_color
+ , u_pub.Bishop_B_coat_roughness
+ , u_pub.Bishop_B_coat_anisotropy
+ , u_pub.Bishop_B_coat_rotation
+ , u_pub.Bishop_B_coat_IOR
+ , u_pub.Bishop_B_coat_affect_color
+ , u_pub.Bishop_B_coat_affect_roughness
+ , u_pub.Bishop_B_thin_film_thickness
+ , u_pub.Bishop_B_thin_film_IOR
+ , u_pub.Bishop_B_emission
+ , u_pub.Bishop_B_emission_color
+ , u_pub.Bishop_B_opacity
+ , u_pub.Bishop_B_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Bishop_B.msl.vert b/Materials/Examples/StandardSurface/M_Bishop_B.msl.vert
new file mode 100644
index 0000000000..378c23d15d
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_B.msl.vert
@@ -0,0 +1,124 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+ vec2 i_texcoord_0 [[attribute(3)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+, vec2 i_texcoord_0
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+, i_texcoord_0(i_texcoord_0)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ vec2 i_texcoord_0;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'geomprop_UV0'. Function already called in this scope.
+ // Omitted node 'diffuse2'. Function already called in this scope.
+ // Omitted node 'metallic2'. Function already called in this scope.
+ // Omitted node 'roughness2'. Function already called in this scope.
+ // Omitted node 'normal2'. Function already called in this scope.
+ // Omitted node 'diffuse2_out_cm'. Function already called in this scope.
+ // Omitted node 'mtlxnormalmap4'. Function already called in this scope.
+ // Omitted node 'Bishop_B'. Function already called in this scope.
+ // Omitted node 'M_Bishop_B'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(4) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent, i_vs.i_texcoord_0 , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Bishop_B.osl b/Materials/Examples/StandardSurface/M_Bishop_B.osl
new file mode 100644
index 0000000000..f70c118b73
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_B.osl
@@ -0,0 +1,815 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+vector2 mx_transform_uv(vector2 texcoord)
+{
+ return texcoord;
+}
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode );
+}
+
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+ out = rgb[0];
+}
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(color in, output color out)
+{
+ float bias_in2_tmp = 0.055;
+ color bias_out = in + bias_in2_tmp;
+ float linSeg_in2_tmp = 12.92;
+ color linSeg_out = in / linSeg_in2_tmp;
+ float isAboveR_value2_tmp = 0.04045;
+ float isAboveR_in1_tmp = 1;
+ float isAboveR_in2_tmp = 0;
+ float isAboveR_out = mx_ternary(in[0] > isAboveR_value2_tmp, isAboveR_in1_tmp, isAboveR_in2_tmp);
+ float isAboveG_value2_tmp = 0.04045;
+ float isAboveG_in1_tmp = 1;
+ float isAboveG_in2_tmp = 0;
+ float isAboveG_out = mx_ternary(in[1] > isAboveG_value2_tmp, isAboveG_in1_tmp, isAboveG_in2_tmp);
+ float isAboveB_value2_tmp = 0.04045;
+ float isAboveB_in1_tmp = 1;
+ float isAboveB_in2_tmp = 0;
+ float isAboveB_out = mx_ternary(in[2] > isAboveB_value2_tmp, isAboveB_in1_tmp, isAboveB_in2_tmp);
+ float max_in2_tmp = 0;
+ color max_out = max(bias_out, max_in2_tmp);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ float scale_in2_tmp = 1.055;
+ color scale_out = max_out / scale_in2_tmp;
+ float powSeg_in2_tmp = 2.4;
+ color powSeg_out = pow(scale_out, powSeg_in2_tmp);
+ color mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out = mix_out;
+}
+
+void mx_normalmap(vector value, string map_space, float normal_scale, vector N, vector U, output vector result)
+{
+ // Tangent space
+ if (map_space == "tangent")
+ {
+ vector v = value * 2.0 - 1.0;
+ vector T = normalize(U - dot(U, N) * N);
+ vector B = normalize(cross(N, T));
+ result = normalize(T * v[0] * normal_scale + B * v[1] * normal_scale + N * v[2]);
+ }
+ // Object space
+ else
+ {
+ vector n = value * 2.0 - 1.0;
+ result = normalize(n);
+ }
+}
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader M_Bishop_B
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "M_Bishop_B"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ int geomprop_UV0_index = 0
+ [[
+ string widget = "number"
+ ]],
+ textureresource diffuse2_file = {"chess_set/bishop_black_base_color.jpg", "srgb_texture"}
+ [[
+ string widget = "filename"
+ ]],
+ string diffuse2_layer = "",
+ color diffuse2_default = color(0, 0, 0),
+ string diffuse2_uaddressmode = "periodic",
+ string diffuse2_vaddressmode = "periodic",
+ string diffuse2_filtertype = "linear",
+ string diffuse2_framerange = "",
+ int diffuse2_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string diffuse2_frameendaction = "constant",
+ textureresource metallic2_file = {"chess_set/bishop_shared_metallic.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string metallic2_layer = "",
+ float metallic2_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string metallic2_uaddressmode = "periodic",
+ string metallic2_vaddressmode = "periodic",
+ string metallic2_filtertype = "linear",
+ string metallic2_framerange = "",
+ int metallic2_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string metallic2_frameendaction = "constant",
+ textureresource roughness2_file = {"chess_set/bishop_black_roughness.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string roughness2_layer = "",
+ float roughness2_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string roughness2_uaddressmode = "periodic",
+ string roughness2_vaddressmode = "periodic",
+ string roughness2_filtertype = "linear",
+ string roughness2_framerange = "",
+ int roughness2_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string roughness2_frameendaction = "constant",
+ textureresource normal2_file = {"chess_set/bishop_black_normal.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string normal2_layer = "",
+ vector normal2_default = vector(0, 0, 0),
+ string normal2_uaddressmode = "periodic",
+ string normal2_vaddressmode = "periodic",
+ string normal2_filtertype = "linear",
+ string normal2_framerange = "",
+ int normal2_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string normal2_frameendaction = "constant",
+ string mtlxnormalmap4_space = "tangent",
+ float mtlxnormalmap4_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_base = 1
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_B_specular_color = color(1, 1, 1),
+ float Bishop_B_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_B_transmission_color = color(1, 1, 1),
+ float Bishop_B_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_B_transmission_scatter = color(0, 0, 0),
+ float Bishop_B_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_subsurface_scale = 0.003
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_B_sheen_color = color(1, 1, 1),
+ float Bishop_B_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_B_coat_color = color(1, 1, 1),
+ float Bishop_B_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_B_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_B_emission_color = color(1, 1, 1),
+ color Bishop_B_opacity = color(1, 1, 1),
+ int Bishop_B_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ vector2 geomprop_UV0_out1 = vector2(u,v);
+ color diffuse2_out = color(0.0);
+ mx_image_color3(diffuse2_file, diffuse2_layer, diffuse2_default, geomprop_UV0_out1, diffuse2_uaddressmode, diffuse2_vaddressmode, diffuse2_filtertype, diffuse2_framerange, diffuse2_frameoffset, diffuse2_frameendaction, diffuse2_out);
+ float metallic2_out = 0.0;
+ mx_image_float(metallic2_file, metallic2_layer, metallic2_default, geomprop_UV0_out1, metallic2_uaddressmode, metallic2_vaddressmode, metallic2_filtertype, metallic2_framerange, metallic2_frameoffset, metallic2_frameendaction, metallic2_out);
+ float roughness2_out = 0.0;
+ mx_image_float(roughness2_file, roughness2_layer, roughness2_default, geomprop_UV0_out1, roughness2_uaddressmode, roughness2_vaddressmode, roughness2_filtertype, roughness2_framerange, roughness2_frameoffset, roughness2_frameendaction, roughness2_out);
+ vector normal2_out = vector(0.0);
+ mx_image_vector3(normal2_file, normal2_layer, normal2_default, geomprop_UV0_out1, normal2_uaddressmode, normal2_vaddressmode, normal2_filtertype, normal2_framerange, normal2_frameoffset, normal2_frameendaction, normal2_out);
+ color diffuse2_out_cm_out = color(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse2_out, diffuse2_out_cm_out);
+ vector mtlxnormalmap4_out = vector(0.0);
+ mx_normalmap(normal2_out, mtlxnormalmap4_space, mtlxnormalmap4_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap4_out);
+ surfaceshader Bishop_B_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(Bishop_B_base, diffuse2_out_cm_out, Bishop_B_diffuse_roughness, metallic2_out, Bishop_B_specular, Bishop_B_specular_color, roughness2_out, Bishop_B_specular_IOR, Bishop_B_specular_anisotropy, Bishop_B_specular_rotation, Bishop_B_transmission, Bishop_B_transmission_color, Bishop_B_transmission_depth, Bishop_B_transmission_scatter, Bishop_B_transmission_scatter_anisotropy, Bishop_B_transmission_dispersion, Bishop_B_transmission_extra_roughness, Bishop_B_subsurface, diffuse2_out_cm_out, diffuse2_out_cm_out, Bishop_B_subsurface_scale, Bishop_B_subsurface_anisotropy, Bishop_B_sheen, Bishop_B_sheen_color, Bishop_B_sheen_roughness, Bishop_B_coat, Bishop_B_coat_color, Bishop_B_coat_roughness, Bishop_B_coat_anisotropy, Bishop_B_coat_rotation, Bishop_B_coat_IOR, geomprop_Nworld_out1, Bishop_B_coat_affect_color, Bishop_B_coat_affect_roughness, Bishop_B_thin_film_thickness, Bishop_B_thin_film_IOR, Bishop_B_emission, Bishop_B_emission_color, Bishop_B_opacity, Bishop_B_thin_walled, mtlxnormalmap4_out, geomprop_Tworld_out1, Bishop_B_out);
+ MATERIAL M_Bishop_B_out = mx_surfacematerial(Bishop_B_out, displacementshader1);
+ out = M_Bishop_B_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Bishop_W.glsl.frag b/Materials/Examples/StandardSurface/M_Bishop_W.glsl.frag
new file mode 100644
index 0000000000..98f2aadcc9
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_W.glsl.frag
@@ -0,0 +1,1854 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform sampler2D diffuse3_file;
+uniform int diffuse3_layer = 0;
+uniform vec3 diffuse3_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int diffuse3_uaddressmode = 2;
+uniform int diffuse3_vaddressmode = 2;
+uniform int diffuse3_filtertype = 1;
+uniform int diffuse3_framerange = 0;
+uniform int diffuse3_frameoffset = 0;
+uniform int diffuse3_frameendaction = 0;
+uniform vec2 diffuse3_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 diffuse3_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D metallic3_file;
+uniform int metallic3_layer = 0;
+uniform float metallic3_default = 0.000000;
+uniform int metallic3_uaddressmode = 2;
+uniform int metallic3_vaddressmode = 2;
+uniform int metallic3_filtertype = 1;
+uniform int metallic3_framerange = 0;
+uniform int metallic3_frameoffset = 0;
+uniform int metallic3_frameendaction = 0;
+uniform vec2 metallic3_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 metallic3_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D roughness3_file;
+uniform int roughness3_layer = 0;
+uniform float roughness3_default = 0.000000;
+uniform int roughness3_uaddressmode = 2;
+uniform int roughness3_vaddressmode = 2;
+uniform int roughness3_filtertype = 1;
+uniform int roughness3_framerange = 0;
+uniform int roughness3_frameoffset = 0;
+uniform int roughness3_frameendaction = 0;
+uniform vec2 roughness3_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 roughness3_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D normal3_file;
+uniform int normal3_layer = 0;
+uniform vec3 normal3_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int normal3_uaddressmode = 2;
+uniform int normal3_vaddressmode = 2;
+uniform int normal3_filtertype = 1;
+uniform int normal3_framerange = 0;
+uniform int normal3_frameoffset = 0;
+uniform int normal3_frameendaction = 0;
+uniform vec2 normal3_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 normal3_uv_offset = vec2(0.000000, 0.000000);
+uniform int mtlxnormalmap5_space = 0;
+uniform float mtlxnormalmap5_scale = 1.000000;
+uniform float Bishop_W_base = 1.000000;
+uniform float Bishop_W_diffuse_roughness = 0.000000;
+uniform float Bishop_W_specular = 1.000000;
+uniform vec3 Bishop_W_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Bishop_W_specular_IOR = 1.500000;
+uniform float Bishop_W_specular_anisotropy = 0.000000;
+uniform float Bishop_W_specular_rotation = 0.000000;
+uniform float Bishop_W_transmission = 0.000000;
+uniform vec3 Bishop_W_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Bishop_W_transmission_depth = 0.000000;
+uniform vec3 Bishop_W_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float Bishop_W_transmission_scatter_anisotropy = 0.000000;
+uniform float Bishop_W_transmission_dispersion = 0.000000;
+uniform float Bishop_W_transmission_extra_roughness = 0.000000;
+uniform float Bishop_W_subsurface = 0.000000;
+uniform float Bishop_W_subsurface_scale = 0.003000;
+uniform float Bishop_W_subsurface_anisotropy = 0.000000;
+uniform float Bishop_W_sheen = 0.000000;
+uniform vec3 Bishop_W_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Bishop_W_sheen_roughness = 0.300000;
+uniform float Bishop_W_coat = 0.000000;
+uniform vec3 Bishop_W_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Bishop_W_coat_roughness = 0.100000;
+uniform float Bishop_W_coat_anisotropy = 0.000000;
+uniform float Bishop_W_coat_rotation = 0.000000;
+uniform float Bishop_W_coat_IOR = 1.500000;
+uniform float Bishop_W_coat_affect_color = 0.000000;
+uniform float Bishop_W_coat_affect_roughness = 0.000000;
+uniform float Bishop_W_thin_film_thickness = 0.000000;
+uniform float Bishop_W_thin_film_IOR = 1.500000;
+uniform float Bishop_W_emission = 0.000000;
+uniform vec3 Bishop_W_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 Bishop_W_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool Bishop_W_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+{
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+}
+
+void mx_image_color3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_float(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_vector3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, out vec3 out1)
+{
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+}
+
+void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, out vec3 result)
+{
+ // Decode the normal map.
+ value = (value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 diffuse3_out = vec3(0.0);
+ mx_image_color3(diffuse3_file, diffuse3_layer, diffuse3_default, geomprop_UV0_out1, diffuse3_uaddressmode, diffuse3_vaddressmode, diffuse3_filtertype, diffuse3_framerange, diffuse3_frameoffset, diffuse3_frameendaction, diffuse3_uv_scale, diffuse3_uv_offset, diffuse3_out);
+ float metallic3_out = 0.0;
+ mx_image_float(metallic3_file, metallic3_layer, metallic3_default, geomprop_UV0_out1, metallic3_uaddressmode, metallic3_vaddressmode, metallic3_filtertype, metallic3_framerange, metallic3_frameoffset, metallic3_frameendaction, metallic3_uv_scale, metallic3_uv_offset, metallic3_out);
+ float roughness3_out = 0.0;
+ mx_image_float(roughness3_file, roughness3_layer, roughness3_default, geomprop_UV0_out1, roughness3_uaddressmode, roughness3_vaddressmode, roughness3_filtertype, roughness3_framerange, roughness3_frameoffset, roughness3_frameendaction, roughness3_uv_scale, roughness3_uv_offset, roughness3_out);
+ vec3 normal3_out = vec3(0.0);
+ mx_image_vector3(normal3_file, normal3_layer, normal3_default, geomprop_UV0_out1, normal3_uaddressmode, normal3_vaddressmode, normal3_filtertype, normal3_framerange, normal3_frameoffset, normal3_frameendaction, normal3_uv_scale, normal3_uv_offset, normal3_out);
+ vec3 diffuse3_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse3_out, diffuse3_out_cm_out);
+ vec3 mtlxnormalmap5_out = vec3(0.0);
+ mx_normalmap(normal3_out, mtlxnormalmap5_space, mtlxnormalmap5_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap5_out);
+ surfaceshader Bishop_W_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(Bishop_W_base, diffuse3_out_cm_out, Bishop_W_diffuse_roughness, metallic3_out, Bishop_W_specular, Bishop_W_specular_color, roughness3_out, Bishop_W_specular_IOR, Bishop_W_specular_anisotropy, Bishop_W_specular_rotation, Bishop_W_transmission, Bishop_W_transmission_color, Bishop_W_transmission_depth, Bishop_W_transmission_scatter, Bishop_W_transmission_scatter_anisotropy, Bishop_W_transmission_dispersion, Bishop_W_transmission_extra_roughness, Bishop_W_subsurface, diffuse3_out_cm_out, diffuse3_out_cm_out, Bishop_W_subsurface_scale, Bishop_W_subsurface_anisotropy, Bishop_W_sheen, Bishop_W_sheen_color, Bishop_W_sheen_roughness, Bishop_W_coat, Bishop_W_coat_color, Bishop_W_coat_roughness, Bishop_W_coat_anisotropy, Bishop_W_coat_rotation, Bishop_W_coat_IOR, geomprop_Nworld_out1, Bishop_W_coat_affect_color, Bishop_W_coat_affect_roughness, Bishop_W_thin_film_thickness, Bishop_W_thin_film_IOR, Bishop_W_emission, Bishop_W_emission_color, Bishop_W_opacity, Bishop_W_thin_walled, mtlxnormalmap5_out, geomprop_Tworld_out1, Bishop_W_out);
+ material M_Bishop_W_out = Bishop_W_out;
+ out1 = vec4(M_Bishop_W_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Bishop_W.glsl.vert b/Materials/Examples/StandardSurface/M_Bishop_W.glsl.vert
new file mode 100644
index 0000000000..60b47d493e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_W.glsl.vert
@@ -0,0 +1,31 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+in vec2 i_texcoord_0;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Bishop_W.mdl b/Materials/Examples/StandardSurface/M_Bishop_W.mdl
new file mode 100644
index 0000000000..7ec5266c6b
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_W.mdl
@@ -0,0 +1,234 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+color NG_srgb_texture_to_lin_rec709_color3
+(
+ color in1 = color(0, 0, 0)
+)
+{
+ color bias_out = in1 + 0.055;
+ color linSeg_out = in1 / 12.92;
+ float isAboveR_out = mx::stdlib::mx_ifgreater_float(float3(in1).x, 0.04045, 1, 0);
+ float isAboveG_out = mx::stdlib::mx_ifgreater_float(float3(in1).y, 0.04045, 1, 0);
+ float isAboveB_out = mx::stdlib::mx_ifgreater_float(float3(in1).z, 0.04045, 1, 0);
+ color max_out = math::max(bias_out, 0);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ color scale_out = max_out / 1.055;
+ color powSeg_out = math::pow(scale_out, 2.4);
+ color mix_out = math::lerp(linSeg_out, powSeg_out, isAbove_out);
+ return mix_out;
+}
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material M_Bishop_W
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ uniform int geomprop_UV0_index = 0,
+ uniform texture_2d diffuse3_file = texture_2d("/chess_set/bishop_white_base_color.jpg", tex::gamma_linear),
+ uniform string diffuse3_layer = "",
+ color diffuse3_default = color(0, 0, 0),
+ uniform mx_addressmode_type diffuse3_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type diffuse3_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type diffuse3_filtertype = mx_filterlookup_type_linear,
+ uniform string diffuse3_framerange = "",
+ uniform int diffuse3_frameoffset = 0,
+ uniform mx_addressmode_type diffuse3_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d metallic3_file = texture_2d("/chess_set/bishop_shared_metallic.jpg", tex::gamma_linear),
+ uniform string metallic3_layer = "",
+ float metallic3_default = 0,
+ uniform mx_addressmode_type metallic3_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type metallic3_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type metallic3_filtertype = mx_filterlookup_type_linear,
+ uniform string metallic3_framerange = "",
+ uniform int metallic3_frameoffset = 0,
+ uniform mx_addressmode_type metallic3_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d roughness3_file = texture_2d("/chess_set/bishop_white_roughness.jpg", tex::gamma_linear),
+ uniform string roughness3_layer = "",
+ float roughness3_default = 0,
+ uniform mx_addressmode_type roughness3_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type roughness3_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type roughness3_filtertype = mx_filterlookup_type_linear,
+ uniform string roughness3_framerange = "",
+ uniform int roughness3_frameoffset = 0,
+ uniform mx_addressmode_type roughness3_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d normal3_file = texture_2d("/chess_set/bishop_white_normal.jpg", tex::gamma_linear),
+ uniform string normal3_layer = "",
+ float3 normal3_default = float3(0, 0, 0),
+ uniform mx_addressmode_type normal3_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type normal3_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type normal3_filtertype = mx_filterlookup_type_linear,
+ uniform string normal3_framerange = "",
+ uniform int normal3_frameoffset = 0,
+ uniform mx_addressmode_type normal3_frameendaction = mx_addressmode_type_constant,
+ uniform string mtlxnormalmap5_space = "tangent",
+ float mtlxnormalmap5_scale = 1,
+ float Bishop_W_base = 1,
+ float Bishop_W_diffuse_roughness = 0,
+ float Bishop_W_specular = 1,
+ color Bishop_W_specular_color = color(1, 1, 1),
+ uniform float Bishop_W_specular_IOR = 1.5,
+ float Bishop_W_specular_anisotropy = 0,
+ float Bishop_W_specular_rotation = 0,
+ float Bishop_W_transmission = 0,
+ color Bishop_W_transmission_color = color(1, 1, 1),
+ float Bishop_W_transmission_depth = 0,
+ color Bishop_W_transmission_scatter = color(0, 0, 0),
+ float Bishop_W_transmission_scatter_anisotropy = 0,
+ float Bishop_W_transmission_dispersion = 0,
+ float Bishop_W_transmission_extra_roughness = 0,
+ float Bishop_W_subsurface = 0,
+ float Bishop_W_subsurface_scale = 0.003,
+ float Bishop_W_subsurface_anisotropy = 0,
+ float Bishop_W_sheen = 0,
+ color Bishop_W_sheen_color = color(1, 1, 1),
+ float Bishop_W_sheen_roughness = 0.3,
+ float Bishop_W_coat = 0,
+ color Bishop_W_coat_color = color(1, 1, 1),
+ float Bishop_W_coat_roughness = 0.1,
+ float Bishop_W_coat_anisotropy = 0,
+ float Bishop_W_coat_rotation = 0,
+ uniform float Bishop_W_coat_IOR = 1.5,
+ float Bishop_W_coat_affect_color = 0,
+ float Bishop_W_coat_affect_roughness = 0,
+ float Bishop_W_thin_film_thickness = 0,
+ float Bishop_W_thin_film_IOR = 1.5,
+ float Bishop_W_emission = 0,
+ color Bishop_W_emission_color = color(1, 1, 1),
+ color Bishop_W_opacity = color(1, 1, 1),
+ bool Bishop_W_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ float2 geomprop_UV0_out1 = mx::stdlib::mx_texcoord_vector2(mxp_index:geomprop_UV0_index);
+ color diffuse3_out = mx::stdlib::mx_image_color3(diffuse3_file, diffuse3_layer, diffuse3_default, geomprop_UV0_out1, diffuse3_uaddressmode, diffuse3_vaddressmode, diffuse3_filtertype, diffuse3_framerange, diffuse3_frameoffset, diffuse3_frameendaction);
+ float metallic3_out = mx::stdlib::mx_image_float(metallic3_file, metallic3_layer, metallic3_default, geomprop_UV0_out1, metallic3_uaddressmode, metallic3_vaddressmode, metallic3_filtertype, metallic3_framerange, metallic3_frameoffset, metallic3_frameendaction);
+ float roughness3_out = mx::stdlib::mx_image_float(roughness3_file, roughness3_layer, roughness3_default, geomprop_UV0_out1, roughness3_uaddressmode, roughness3_vaddressmode, roughness3_filtertype, roughness3_framerange, roughness3_frameoffset, roughness3_frameendaction);
+ float3 normal3_out = mx::stdlib::mx_image_vector3(normal3_file, normal3_layer, normal3_default, geomprop_UV0_out1, normal3_uaddressmode, normal3_vaddressmode, normal3_filtertype, normal3_framerange, normal3_frameoffset, normal3_frameendaction);
+ color diffuse3_out_cm_out = NG_srgb_texture_to_lin_rec709_color3(diffuse3_out);
+ float3 mtlxnormalmap5_out = mx::stdlib::mx_normalmap(mxp_in:normal3_out, mxp_space:mtlxnormalmap5_space, mxp_scale:mtlxnormalmap5_scale, mxp_normal:geomprop_Nworld_out1, mxp_tangent:geomprop_Tworld_out1);
+ material Bishop_W_out = NG_standard_surface_surfaceshader_100(Bishop_W_base, diffuse3_out_cm_out, Bishop_W_diffuse_roughness, metallic3_out, Bishop_W_specular, Bishop_W_specular_color, roughness3_out, Bishop_W_specular_IOR, Bishop_W_specular_anisotropy, Bishop_W_specular_rotation, Bishop_W_transmission, Bishop_W_transmission_color, Bishop_W_transmission_depth, Bishop_W_transmission_scatter, Bishop_W_transmission_scatter_anisotropy, Bishop_W_transmission_dispersion, Bishop_W_transmission_extra_roughness, Bishop_W_subsurface, diffuse3_out_cm_out, diffuse3_out_cm_out, Bishop_W_subsurface_scale, Bishop_W_subsurface_anisotropy, Bishop_W_sheen, Bishop_W_sheen_color, Bishop_W_sheen_roughness, Bishop_W_coat, Bishop_W_coat_color, Bishop_W_coat_roughness, Bishop_W_coat_anisotropy, Bishop_W_coat_rotation, Bishop_W_coat_IOR, geomprop_Nworld_out1, Bishop_W_coat_affect_color, Bishop_W_coat_affect_roughness, Bishop_W_thin_film_thickness, Bishop_W_thin_film_IOR, Bishop_W_emission, Bishop_W_emission_color, Bishop_W_opacity, Bishop_W_thin_walled, mtlxnormalmap5_out, geomprop_Tworld_out1);
+ material M_Bishop_W_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: Bishop_W_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = M_Bishop_W_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/M_Bishop_W.msl.frag b/Materials/Examples/StandardSurface/M_Bishop_W.msl.frag
new file mode 100644
index 0000000000..6e56587629
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_W.msl.frag
@@ -0,0 +1,2784 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ int diffuse3_layer;
+ vec3 diffuse3_default;
+ int diffuse3_uaddressmode;
+ int diffuse3_vaddressmode;
+ int diffuse3_filtertype;
+ int diffuse3_framerange;
+ int diffuse3_frameoffset;
+ int diffuse3_frameendaction;
+ vec2 diffuse3_uv_scale;
+ vec2 diffuse3_uv_offset;
+ int metallic3_layer;
+ float metallic3_default;
+ int metallic3_uaddressmode;
+ int metallic3_vaddressmode;
+ int metallic3_filtertype;
+ int metallic3_framerange;
+ int metallic3_frameoffset;
+ int metallic3_frameendaction;
+ vec2 metallic3_uv_scale;
+ vec2 metallic3_uv_offset;
+ int roughness3_layer;
+ float roughness3_default;
+ int roughness3_uaddressmode;
+ int roughness3_vaddressmode;
+ int roughness3_filtertype;
+ int roughness3_framerange;
+ int roughness3_frameoffset;
+ int roughness3_frameendaction;
+ vec2 roughness3_uv_scale;
+ vec2 roughness3_uv_offset;
+ int normal3_layer;
+ vec3 normal3_default;
+ int normal3_uaddressmode;
+ int normal3_vaddressmode;
+ int normal3_filtertype;
+ int normal3_framerange;
+ int normal3_frameoffset;
+ int normal3_frameendaction;
+ vec2 normal3_uv_scale;
+ vec2 normal3_uv_offset;
+ int mtlxnormalmap5_space;
+ float mtlxnormalmap5_scale;
+ float Bishop_W_base;
+ float Bishop_W_diffuse_roughness;
+ float Bishop_W_specular;
+ vec3 Bishop_W_specular_color;
+ float Bishop_W_specular_IOR;
+ float Bishop_W_specular_anisotropy;
+ float Bishop_W_specular_rotation;
+ float Bishop_W_transmission;
+ vec3 Bishop_W_transmission_color;
+ float Bishop_W_transmission_depth;
+ vec3 Bishop_W_transmission_scatter;
+ float Bishop_W_transmission_scatter_anisotropy;
+ float Bishop_W_transmission_dispersion;
+ float Bishop_W_transmission_extra_roughness;
+ float Bishop_W_subsurface;
+ float Bishop_W_subsurface_scale;
+ float Bishop_W_subsurface_anisotropy;
+ float Bishop_W_sheen;
+ vec3 Bishop_W_sheen_color;
+ float Bishop_W_sheen_roughness;
+ float Bishop_W_coat;
+ vec3 Bishop_W_coat_color;
+ float Bishop_W_coat_roughness;
+ float Bishop_W_coat_anisotropy;
+ float Bishop_W_coat_rotation;
+ float Bishop_W_coat_IOR;
+ float Bishop_W_coat_affect_color;
+ float Bishop_W_coat_affect_roughness;
+ float Bishop_W_thin_film_thickness;
+ float Bishop_W_thin_film_IOR;
+ float Bishop_W_emission;
+ vec3 Bishop_W_emission_color;
+ vec3 Bishop_W_opacity;
+ bool Bishop_W_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec2 texcoord_0 ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+, MetalTexture diffuse3_file , int diffuse3_layer
+
+ , vec3 diffuse3_default
+
+ , int diffuse3_uaddressmode
+
+ , int diffuse3_vaddressmode
+
+ , int diffuse3_filtertype
+
+ , int diffuse3_framerange
+
+ , int diffuse3_frameoffset
+
+ , int diffuse3_frameendaction
+
+ , vec2 diffuse3_uv_scale
+
+ , vec2 diffuse3_uv_offset
+
+, MetalTexture metallic3_file , int metallic3_layer
+
+ , float metallic3_default
+
+ , int metallic3_uaddressmode
+
+ , int metallic3_vaddressmode
+
+ , int metallic3_filtertype
+
+ , int metallic3_framerange
+
+ , int metallic3_frameoffset
+
+ , int metallic3_frameendaction
+
+ , vec2 metallic3_uv_scale
+
+ , vec2 metallic3_uv_offset
+
+, MetalTexture roughness3_file , int roughness3_layer
+
+ , float roughness3_default
+
+ , int roughness3_uaddressmode
+
+ , int roughness3_vaddressmode
+
+ , int roughness3_filtertype
+
+ , int roughness3_framerange
+
+ , int roughness3_frameoffset
+
+ , int roughness3_frameendaction
+
+ , vec2 roughness3_uv_scale
+
+ , vec2 roughness3_uv_offset
+
+, MetalTexture normal3_file , int normal3_layer
+
+ , vec3 normal3_default
+
+ , int normal3_uaddressmode
+
+ , int normal3_vaddressmode
+
+ , int normal3_filtertype
+
+ , int normal3_framerange
+
+ , int normal3_frameoffset
+
+ , int normal3_frameendaction
+
+ , vec2 normal3_uv_scale
+
+ , vec2 normal3_uv_offset
+
+ , int mtlxnormalmap5_space
+
+ , float mtlxnormalmap5_scale
+
+ , float Bishop_W_base
+
+ , float Bishop_W_diffuse_roughness
+
+ , float Bishop_W_specular
+
+ , vec3 Bishop_W_specular_color
+
+ , float Bishop_W_specular_IOR
+
+ , float Bishop_W_specular_anisotropy
+
+ , float Bishop_W_specular_rotation
+
+ , float Bishop_W_transmission
+
+ , vec3 Bishop_W_transmission_color
+
+ , float Bishop_W_transmission_depth
+
+ , vec3 Bishop_W_transmission_scatter
+
+ , float Bishop_W_transmission_scatter_anisotropy
+
+ , float Bishop_W_transmission_dispersion
+
+ , float Bishop_W_transmission_extra_roughness
+
+ , float Bishop_W_subsurface
+
+ , float Bishop_W_subsurface_scale
+
+ , float Bishop_W_subsurface_anisotropy
+
+ , float Bishop_W_sheen
+
+ , vec3 Bishop_W_sheen_color
+
+ , float Bishop_W_sheen_roughness
+
+ , float Bishop_W_coat
+
+ , vec3 Bishop_W_coat_color
+
+ , float Bishop_W_coat_roughness
+
+ , float Bishop_W_coat_anisotropy
+
+ , float Bishop_W_coat_rotation
+
+ , float Bishop_W_coat_IOR
+
+ , float Bishop_W_coat_affect_color
+
+ , float Bishop_W_coat_affect_roughness
+
+ , float Bishop_W_thin_film_thickness
+
+ , float Bishop_W_thin_film_IOR
+
+ , float Bishop_W_emission
+
+ , vec3 Bishop_W_emission_color
+
+ , vec3 Bishop_W_opacity
+
+ , bool Bishop_W_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+, diffuse3_file(diffuse3_file)
+ , diffuse3_layer(diffuse3_layer)
+
+ , diffuse3_default(diffuse3_default)
+
+ , diffuse3_uaddressmode(diffuse3_uaddressmode)
+
+ , diffuse3_vaddressmode(diffuse3_vaddressmode)
+
+ , diffuse3_filtertype(diffuse3_filtertype)
+
+ , diffuse3_framerange(diffuse3_framerange)
+
+ , diffuse3_frameoffset(diffuse3_frameoffset)
+
+ , diffuse3_frameendaction(diffuse3_frameendaction)
+
+ , diffuse3_uv_scale(diffuse3_uv_scale)
+
+ , diffuse3_uv_offset(diffuse3_uv_offset)
+
+, metallic3_file(metallic3_file)
+ , metallic3_layer(metallic3_layer)
+
+ , metallic3_default(metallic3_default)
+
+ , metallic3_uaddressmode(metallic3_uaddressmode)
+
+ , metallic3_vaddressmode(metallic3_vaddressmode)
+
+ , metallic3_filtertype(metallic3_filtertype)
+
+ , metallic3_framerange(metallic3_framerange)
+
+ , metallic3_frameoffset(metallic3_frameoffset)
+
+ , metallic3_frameendaction(metallic3_frameendaction)
+
+ , metallic3_uv_scale(metallic3_uv_scale)
+
+ , metallic3_uv_offset(metallic3_uv_offset)
+
+, roughness3_file(roughness3_file)
+ , roughness3_layer(roughness3_layer)
+
+ , roughness3_default(roughness3_default)
+
+ , roughness3_uaddressmode(roughness3_uaddressmode)
+
+ , roughness3_vaddressmode(roughness3_vaddressmode)
+
+ , roughness3_filtertype(roughness3_filtertype)
+
+ , roughness3_framerange(roughness3_framerange)
+
+ , roughness3_frameoffset(roughness3_frameoffset)
+
+ , roughness3_frameendaction(roughness3_frameendaction)
+
+ , roughness3_uv_scale(roughness3_uv_scale)
+
+ , roughness3_uv_offset(roughness3_uv_offset)
+
+, normal3_file(normal3_file)
+ , normal3_layer(normal3_layer)
+
+ , normal3_default(normal3_default)
+
+ , normal3_uaddressmode(normal3_uaddressmode)
+
+ , normal3_vaddressmode(normal3_vaddressmode)
+
+ , normal3_filtertype(normal3_filtertype)
+
+ , normal3_framerange(normal3_framerange)
+
+ , normal3_frameoffset(normal3_frameoffset)
+
+ , normal3_frameendaction(normal3_frameendaction)
+
+ , normal3_uv_scale(normal3_uv_scale)
+
+ , normal3_uv_offset(normal3_uv_offset)
+
+ , mtlxnormalmap5_space(mtlxnormalmap5_space)
+
+ , mtlxnormalmap5_scale(mtlxnormalmap5_scale)
+
+ , Bishop_W_base(Bishop_W_base)
+
+ , Bishop_W_diffuse_roughness(Bishop_W_diffuse_roughness)
+
+ , Bishop_W_specular(Bishop_W_specular)
+
+ , Bishop_W_specular_color(Bishop_W_specular_color)
+
+ , Bishop_W_specular_IOR(Bishop_W_specular_IOR)
+
+ , Bishop_W_specular_anisotropy(Bishop_W_specular_anisotropy)
+
+ , Bishop_W_specular_rotation(Bishop_W_specular_rotation)
+
+ , Bishop_W_transmission(Bishop_W_transmission)
+
+ , Bishop_W_transmission_color(Bishop_W_transmission_color)
+
+ , Bishop_W_transmission_depth(Bishop_W_transmission_depth)
+
+ , Bishop_W_transmission_scatter(Bishop_W_transmission_scatter)
+
+ , Bishop_W_transmission_scatter_anisotropy(Bishop_W_transmission_scatter_anisotropy)
+
+ , Bishop_W_transmission_dispersion(Bishop_W_transmission_dispersion)
+
+ , Bishop_W_transmission_extra_roughness(Bishop_W_transmission_extra_roughness)
+
+ , Bishop_W_subsurface(Bishop_W_subsurface)
+
+ , Bishop_W_subsurface_scale(Bishop_W_subsurface_scale)
+
+ , Bishop_W_subsurface_anisotropy(Bishop_W_subsurface_anisotropy)
+
+ , Bishop_W_sheen(Bishop_W_sheen)
+
+ , Bishop_W_sheen_color(Bishop_W_sheen_color)
+
+ , Bishop_W_sheen_roughness(Bishop_W_sheen_roughness)
+
+ , Bishop_W_coat(Bishop_W_coat)
+
+ , Bishop_W_coat_color(Bishop_W_coat_color)
+
+ , Bishop_W_coat_roughness(Bishop_W_coat_roughness)
+
+ , Bishop_W_coat_anisotropy(Bishop_W_coat_anisotropy)
+
+ , Bishop_W_coat_rotation(Bishop_W_coat_rotation)
+
+ , Bishop_W_coat_IOR(Bishop_W_coat_IOR)
+
+ , Bishop_W_coat_affect_color(Bishop_W_coat_affect_color)
+
+ , Bishop_W_coat_affect_roughness(Bishop_W_coat_affect_roughness)
+
+ , Bishop_W_thin_film_thickness(Bishop_W_thin_film_thickness)
+
+ , Bishop_W_thin_film_IOR(Bishop_W_thin_film_IOR)
+
+ , Bishop_W_emission(Bishop_W_emission)
+
+ , Bishop_W_emission_color(Bishop_W_emission_color)
+
+ , Bishop_W_opacity(Bishop_W_opacity)
+
+ , Bishop_W_thin_walled(Bishop_W_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+MetalTexture diffuse3_file;
+ int diffuse3_layer;
+
+
+ vec3 diffuse3_default;
+
+
+ int diffuse3_uaddressmode;
+
+
+ int diffuse3_vaddressmode;
+
+
+ int diffuse3_filtertype;
+
+
+ int diffuse3_framerange;
+
+
+ int diffuse3_frameoffset;
+
+
+ int diffuse3_frameendaction;
+
+
+ vec2 diffuse3_uv_scale;
+
+
+ vec2 diffuse3_uv_offset;
+
+
+MetalTexture metallic3_file;
+ int metallic3_layer;
+
+
+ float metallic3_default;
+
+
+ int metallic3_uaddressmode;
+
+
+ int metallic3_vaddressmode;
+
+
+ int metallic3_filtertype;
+
+
+ int metallic3_framerange;
+
+
+ int metallic3_frameoffset;
+
+
+ int metallic3_frameendaction;
+
+
+ vec2 metallic3_uv_scale;
+
+
+ vec2 metallic3_uv_offset;
+
+
+MetalTexture roughness3_file;
+ int roughness3_layer;
+
+
+ float roughness3_default;
+
+
+ int roughness3_uaddressmode;
+
+
+ int roughness3_vaddressmode;
+
+
+ int roughness3_filtertype;
+
+
+ int roughness3_framerange;
+
+
+ int roughness3_frameoffset;
+
+
+ int roughness3_frameendaction;
+
+
+ vec2 roughness3_uv_scale;
+
+
+ vec2 roughness3_uv_offset;
+
+
+MetalTexture normal3_file;
+ int normal3_layer;
+
+
+ vec3 normal3_default;
+
+
+ int normal3_uaddressmode;
+
+
+ int normal3_vaddressmode;
+
+
+ int normal3_filtertype;
+
+
+ int normal3_framerange;
+
+
+ int normal3_frameoffset;
+
+
+ int normal3_frameendaction;
+
+
+ vec2 normal3_uv_scale;
+
+
+ vec2 normal3_uv_offset;
+
+
+ int mtlxnormalmap5_space;
+
+
+ float mtlxnormalmap5_scale;
+
+
+ float Bishop_W_base;
+
+
+ float Bishop_W_diffuse_roughness;
+
+
+ float Bishop_W_specular;
+
+
+ vec3 Bishop_W_specular_color;
+
+
+ float Bishop_W_specular_IOR;
+
+
+ float Bishop_W_specular_anisotropy;
+
+
+ float Bishop_W_specular_rotation;
+
+
+ float Bishop_W_transmission;
+
+
+ vec3 Bishop_W_transmission_color;
+
+
+ float Bishop_W_transmission_depth;
+
+
+ vec3 Bishop_W_transmission_scatter;
+
+
+ float Bishop_W_transmission_scatter_anisotropy;
+
+
+ float Bishop_W_transmission_dispersion;
+
+
+ float Bishop_W_transmission_extra_roughness;
+
+
+ float Bishop_W_subsurface;
+
+
+ float Bishop_W_subsurface_scale;
+
+
+ float Bishop_W_subsurface_anisotropy;
+
+
+ float Bishop_W_sheen;
+
+
+ vec3 Bishop_W_sheen_color;
+
+
+ float Bishop_W_sheen_roughness;
+
+
+ float Bishop_W_coat;
+
+
+ vec3 Bishop_W_coat_color;
+
+
+ float Bishop_W_coat_roughness;
+
+
+ float Bishop_W_coat_anisotropy;
+
+
+ float Bishop_W_coat_rotation;
+
+
+ float Bishop_W_coat_IOR;
+
+
+ float Bishop_W_coat_affect_color;
+
+
+ float Bishop_W_coat_affect_roughness;
+
+
+ float Bishop_W_thin_film_thickness;
+
+
+ float Bishop_W_thin_film_IOR;
+
+
+ float Bishop_W_emission;
+
+
+ vec3 Bishop_W_emission_color;
+
+
+ vec3 Bishop_W_opacity;
+
+
+ bool Bishop_W_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+ {
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+ }
+
+ void mx_image_color3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_float(MetalTexture tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread float& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_vector3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+ void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, thread vec3& out1)
+ {
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+ }
+
+ void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, thread vec3& result)
+ {
+ // Decode the normal map.
+ value = all(value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 diffuse3_out = vec3(0.0);
+ mx_image_color3(diffuse3_file, diffuse3_layer, diffuse3_default, geomprop_UV0_out1, diffuse3_uaddressmode, diffuse3_vaddressmode, diffuse3_filtertype, diffuse3_framerange, diffuse3_frameoffset, diffuse3_frameendaction, diffuse3_uv_scale, diffuse3_uv_offset, diffuse3_out);
+ float metallic3_out = 0.0;
+ mx_image_float(metallic3_file, metallic3_layer, metallic3_default, geomprop_UV0_out1, metallic3_uaddressmode, metallic3_vaddressmode, metallic3_filtertype, metallic3_framerange, metallic3_frameoffset, metallic3_frameendaction, metallic3_uv_scale, metallic3_uv_offset, metallic3_out);
+ float roughness3_out = 0.0;
+ mx_image_float(roughness3_file, roughness3_layer, roughness3_default, geomprop_UV0_out1, roughness3_uaddressmode, roughness3_vaddressmode, roughness3_filtertype, roughness3_framerange, roughness3_frameoffset, roughness3_frameendaction, roughness3_uv_scale, roughness3_uv_offset, roughness3_out);
+ vec3 normal3_out = vec3(0.0);
+ mx_image_vector3(normal3_file, normal3_layer, normal3_default, geomprop_UV0_out1, normal3_uaddressmode, normal3_vaddressmode, normal3_filtertype, normal3_framerange, normal3_frameoffset, normal3_frameendaction, normal3_uv_scale, normal3_uv_offset, normal3_out);
+ vec3 diffuse3_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse3_out, diffuse3_out_cm_out);
+ vec3 mtlxnormalmap5_out = vec3(0.0);
+ mx_normalmap(normal3_out, mtlxnormalmap5_space, mtlxnormalmap5_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap5_out);
+ surfaceshader Bishop_W_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(Bishop_W_base, diffuse3_out_cm_out, Bishop_W_diffuse_roughness, metallic3_out, Bishop_W_specular, Bishop_W_specular_color, roughness3_out, Bishop_W_specular_IOR, Bishop_W_specular_anisotropy, Bishop_W_specular_rotation, Bishop_W_transmission, Bishop_W_transmission_color, Bishop_W_transmission_depth, Bishop_W_transmission_scatter, Bishop_W_transmission_scatter_anisotropy, Bishop_W_transmission_dispersion, Bishop_W_transmission_extra_roughness, Bishop_W_subsurface, diffuse3_out_cm_out, diffuse3_out_cm_out, Bishop_W_subsurface_scale, Bishop_W_subsurface_anisotropy, Bishop_W_sheen, Bishop_W_sheen_color, Bishop_W_sheen_roughness, Bishop_W_coat, Bishop_W_coat_color, Bishop_W_coat_roughness, Bishop_W_coat_anisotropy, Bishop_W_coat_rotation, Bishop_W_coat_IOR, geomprop_Nworld_out1, Bishop_W_coat_affect_color, Bishop_W_coat_affect_roughness, Bishop_W_thin_film_thickness, Bishop_W_thin_film_IOR, Bishop_W_emission, Bishop_W_emission_color, Bishop_W_opacity, Bishop_W_thin_walled, mtlxnormalmap5_out, geomprop_Tworld_out1, Bishop_W_out);
+ material M_Bishop_W_out = Bishop_W_out;
+ out1 = float4(M_Bishop_W_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], texture2d diffuse3_file_tex [[texture(0)]], sampler diffuse3_file_sampler [[sampler(0)]]
+, texture2d metallic3_file_tex [[texture(1)]], sampler metallic3_file_sampler [[sampler(1)]]
+, texture2d roughness3_file_tex [[texture(2)]], sampler roughness3_file_sampler [[sampler(2)]]
+, texture2d normal3_file_tex [[texture(3)]], sampler normal3_file_sampler [[sampler(3)]]
+, constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(4)]], sampler u_envRadiance_sampler [[sampler(4)]]
+, texture2d u_envIrradiance_tex [[texture(5)]], sampler u_envIrradiance_sampler [[sampler(5)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+, MetalTexture {
+diffuse3_file_tex, diffuse3_file_sampler }
+ , u_pub.diffuse3_layer
+ , u_pub.diffuse3_default
+ , u_pub.diffuse3_uaddressmode
+ , u_pub.diffuse3_vaddressmode
+ , u_pub.diffuse3_filtertype
+ , u_pub.diffuse3_framerange
+ , u_pub.diffuse3_frameoffset
+ , u_pub.diffuse3_frameendaction
+ , u_pub.diffuse3_uv_scale
+ , u_pub.diffuse3_uv_offset
+, MetalTexture {
+metallic3_file_tex, metallic3_file_sampler }
+ , u_pub.metallic3_layer
+ , u_pub.metallic3_default
+ , u_pub.metallic3_uaddressmode
+ , u_pub.metallic3_vaddressmode
+ , u_pub.metallic3_filtertype
+ , u_pub.metallic3_framerange
+ , u_pub.metallic3_frameoffset
+ , u_pub.metallic3_frameendaction
+ , u_pub.metallic3_uv_scale
+ , u_pub.metallic3_uv_offset
+, MetalTexture {
+roughness3_file_tex, roughness3_file_sampler }
+ , u_pub.roughness3_layer
+ , u_pub.roughness3_default
+ , u_pub.roughness3_uaddressmode
+ , u_pub.roughness3_vaddressmode
+ , u_pub.roughness3_filtertype
+ , u_pub.roughness3_framerange
+ , u_pub.roughness3_frameoffset
+ , u_pub.roughness3_frameendaction
+ , u_pub.roughness3_uv_scale
+ , u_pub.roughness3_uv_offset
+, MetalTexture {
+normal3_file_tex, normal3_file_sampler }
+ , u_pub.normal3_layer
+ , u_pub.normal3_default
+ , u_pub.normal3_uaddressmode
+ , u_pub.normal3_vaddressmode
+ , u_pub.normal3_filtertype
+ , u_pub.normal3_framerange
+ , u_pub.normal3_frameoffset
+ , u_pub.normal3_frameendaction
+ , u_pub.normal3_uv_scale
+ , u_pub.normal3_uv_offset
+ , u_pub.mtlxnormalmap5_space
+ , u_pub.mtlxnormalmap5_scale
+ , u_pub.Bishop_W_base
+ , u_pub.Bishop_W_diffuse_roughness
+ , u_pub.Bishop_W_specular
+ , u_pub.Bishop_W_specular_color
+ , u_pub.Bishop_W_specular_IOR
+ , u_pub.Bishop_W_specular_anisotropy
+ , u_pub.Bishop_W_specular_rotation
+ , u_pub.Bishop_W_transmission
+ , u_pub.Bishop_W_transmission_color
+ , u_pub.Bishop_W_transmission_depth
+ , u_pub.Bishop_W_transmission_scatter
+ , u_pub.Bishop_W_transmission_scatter_anisotropy
+ , u_pub.Bishop_W_transmission_dispersion
+ , u_pub.Bishop_W_transmission_extra_roughness
+ , u_pub.Bishop_W_subsurface
+ , u_pub.Bishop_W_subsurface_scale
+ , u_pub.Bishop_W_subsurface_anisotropy
+ , u_pub.Bishop_W_sheen
+ , u_pub.Bishop_W_sheen_color
+ , u_pub.Bishop_W_sheen_roughness
+ , u_pub.Bishop_W_coat
+ , u_pub.Bishop_W_coat_color
+ , u_pub.Bishop_W_coat_roughness
+ , u_pub.Bishop_W_coat_anisotropy
+ , u_pub.Bishop_W_coat_rotation
+ , u_pub.Bishop_W_coat_IOR
+ , u_pub.Bishop_W_coat_affect_color
+ , u_pub.Bishop_W_coat_affect_roughness
+ , u_pub.Bishop_W_thin_film_thickness
+ , u_pub.Bishop_W_thin_film_IOR
+ , u_pub.Bishop_W_emission
+ , u_pub.Bishop_W_emission_color
+ , u_pub.Bishop_W_opacity
+ , u_pub.Bishop_W_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Bishop_W.msl.vert b/Materials/Examples/StandardSurface/M_Bishop_W.msl.vert
new file mode 100644
index 0000000000..008618b83a
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_W.msl.vert
@@ -0,0 +1,124 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+ vec2 i_texcoord_0 [[attribute(3)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+, vec2 i_texcoord_0
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+, i_texcoord_0(i_texcoord_0)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ vec2 i_texcoord_0;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'geomprop_UV0'. Function already called in this scope.
+ // Omitted node 'diffuse3'. Function already called in this scope.
+ // Omitted node 'metallic3'. Function already called in this scope.
+ // Omitted node 'roughness3'. Function already called in this scope.
+ // Omitted node 'normal3'. Function already called in this scope.
+ // Omitted node 'diffuse3_out_cm'. Function already called in this scope.
+ // Omitted node 'mtlxnormalmap5'. Function already called in this scope.
+ // Omitted node 'Bishop_W'. Function already called in this scope.
+ // Omitted node 'M_Bishop_W'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(4) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent, i_vs.i_texcoord_0 , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Bishop_W.osl b/Materials/Examples/StandardSurface/M_Bishop_W.osl
new file mode 100644
index 0000000000..fab7d5fdb1
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Bishop_W.osl
@@ -0,0 +1,815 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+vector2 mx_transform_uv(vector2 texcoord)
+{
+ return texcoord;
+}
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode );
+}
+
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+ out = rgb[0];
+}
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(color in, output color out)
+{
+ float bias_in2_tmp = 0.055;
+ color bias_out = in + bias_in2_tmp;
+ float linSeg_in2_tmp = 12.92;
+ color linSeg_out = in / linSeg_in2_tmp;
+ float isAboveR_value2_tmp = 0.04045;
+ float isAboveR_in1_tmp = 1;
+ float isAboveR_in2_tmp = 0;
+ float isAboveR_out = mx_ternary(in[0] > isAboveR_value2_tmp, isAboveR_in1_tmp, isAboveR_in2_tmp);
+ float isAboveG_value2_tmp = 0.04045;
+ float isAboveG_in1_tmp = 1;
+ float isAboveG_in2_tmp = 0;
+ float isAboveG_out = mx_ternary(in[1] > isAboveG_value2_tmp, isAboveG_in1_tmp, isAboveG_in2_tmp);
+ float isAboveB_value2_tmp = 0.04045;
+ float isAboveB_in1_tmp = 1;
+ float isAboveB_in2_tmp = 0;
+ float isAboveB_out = mx_ternary(in[2] > isAboveB_value2_tmp, isAboveB_in1_tmp, isAboveB_in2_tmp);
+ float max_in2_tmp = 0;
+ color max_out = max(bias_out, max_in2_tmp);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ float scale_in2_tmp = 1.055;
+ color scale_out = max_out / scale_in2_tmp;
+ float powSeg_in2_tmp = 2.4;
+ color powSeg_out = pow(scale_out, powSeg_in2_tmp);
+ color mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out = mix_out;
+}
+
+void mx_normalmap(vector value, string map_space, float normal_scale, vector N, vector U, output vector result)
+{
+ // Tangent space
+ if (map_space == "tangent")
+ {
+ vector v = value * 2.0 - 1.0;
+ vector T = normalize(U - dot(U, N) * N);
+ vector B = normalize(cross(N, T));
+ result = normalize(T * v[0] * normal_scale + B * v[1] * normal_scale + N * v[2]);
+ }
+ // Object space
+ else
+ {
+ vector n = value * 2.0 - 1.0;
+ result = normalize(n);
+ }
+}
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader M_Bishop_W
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "M_Bishop_W"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ int geomprop_UV0_index = 0
+ [[
+ string widget = "number"
+ ]],
+ textureresource diffuse3_file = {"chess_set/bishop_white_base_color.jpg", "srgb_texture"}
+ [[
+ string widget = "filename"
+ ]],
+ string diffuse3_layer = "",
+ color diffuse3_default = color(0, 0, 0),
+ string diffuse3_uaddressmode = "periodic",
+ string diffuse3_vaddressmode = "periodic",
+ string diffuse3_filtertype = "linear",
+ string diffuse3_framerange = "",
+ int diffuse3_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string diffuse3_frameendaction = "constant",
+ textureresource metallic3_file = {"chess_set/bishop_shared_metallic.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string metallic3_layer = "",
+ float metallic3_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string metallic3_uaddressmode = "periodic",
+ string metallic3_vaddressmode = "periodic",
+ string metallic3_filtertype = "linear",
+ string metallic3_framerange = "",
+ int metallic3_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string metallic3_frameendaction = "constant",
+ textureresource roughness3_file = {"chess_set/bishop_white_roughness.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string roughness3_layer = "",
+ float roughness3_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string roughness3_uaddressmode = "periodic",
+ string roughness3_vaddressmode = "periodic",
+ string roughness3_filtertype = "linear",
+ string roughness3_framerange = "",
+ int roughness3_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string roughness3_frameendaction = "constant",
+ textureresource normal3_file = {"chess_set/bishop_white_normal.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string normal3_layer = "",
+ vector normal3_default = vector(0, 0, 0),
+ string normal3_uaddressmode = "periodic",
+ string normal3_vaddressmode = "periodic",
+ string normal3_filtertype = "linear",
+ string normal3_framerange = "",
+ int normal3_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string normal3_frameendaction = "constant",
+ string mtlxnormalmap5_space = "tangent",
+ float mtlxnormalmap5_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_base = 1
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_W_specular_color = color(1, 1, 1),
+ float Bishop_W_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_W_transmission_color = color(1, 1, 1),
+ float Bishop_W_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_W_transmission_scatter = color(0, 0, 0),
+ float Bishop_W_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_subsurface_scale = 0.003
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_W_sheen_color = color(1, 1, 1),
+ float Bishop_W_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_W_coat_color = color(1, 1, 1),
+ float Bishop_W_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Bishop_W_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color Bishop_W_emission_color = color(1, 1, 1),
+ color Bishop_W_opacity = color(1, 1, 1),
+ int Bishop_W_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ vector2 geomprop_UV0_out1 = vector2(u,v);
+ color diffuse3_out = color(0.0);
+ mx_image_color3(diffuse3_file, diffuse3_layer, diffuse3_default, geomprop_UV0_out1, diffuse3_uaddressmode, diffuse3_vaddressmode, diffuse3_filtertype, diffuse3_framerange, diffuse3_frameoffset, diffuse3_frameendaction, diffuse3_out);
+ float metallic3_out = 0.0;
+ mx_image_float(metallic3_file, metallic3_layer, metallic3_default, geomprop_UV0_out1, metallic3_uaddressmode, metallic3_vaddressmode, metallic3_filtertype, metallic3_framerange, metallic3_frameoffset, metallic3_frameendaction, metallic3_out);
+ float roughness3_out = 0.0;
+ mx_image_float(roughness3_file, roughness3_layer, roughness3_default, geomprop_UV0_out1, roughness3_uaddressmode, roughness3_vaddressmode, roughness3_filtertype, roughness3_framerange, roughness3_frameoffset, roughness3_frameendaction, roughness3_out);
+ vector normal3_out = vector(0.0);
+ mx_image_vector3(normal3_file, normal3_layer, normal3_default, geomprop_UV0_out1, normal3_uaddressmode, normal3_vaddressmode, normal3_filtertype, normal3_framerange, normal3_frameoffset, normal3_frameendaction, normal3_out);
+ color diffuse3_out_cm_out = color(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse3_out, diffuse3_out_cm_out);
+ vector mtlxnormalmap5_out = vector(0.0);
+ mx_normalmap(normal3_out, mtlxnormalmap5_space, mtlxnormalmap5_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap5_out);
+ surfaceshader Bishop_W_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(Bishop_W_base, diffuse3_out_cm_out, Bishop_W_diffuse_roughness, metallic3_out, Bishop_W_specular, Bishop_W_specular_color, roughness3_out, Bishop_W_specular_IOR, Bishop_W_specular_anisotropy, Bishop_W_specular_rotation, Bishop_W_transmission, Bishop_W_transmission_color, Bishop_W_transmission_depth, Bishop_W_transmission_scatter, Bishop_W_transmission_scatter_anisotropy, Bishop_W_transmission_dispersion, Bishop_W_transmission_extra_roughness, Bishop_W_subsurface, diffuse3_out_cm_out, diffuse3_out_cm_out, Bishop_W_subsurface_scale, Bishop_W_subsurface_anisotropy, Bishop_W_sheen, Bishop_W_sheen_color, Bishop_W_sheen_roughness, Bishop_W_coat, Bishop_W_coat_color, Bishop_W_coat_roughness, Bishop_W_coat_anisotropy, Bishop_W_coat_rotation, Bishop_W_coat_IOR, geomprop_Nworld_out1, Bishop_W_coat_affect_color, Bishop_W_coat_affect_roughness, Bishop_W_thin_film_thickness, Bishop_W_thin_film_IOR, Bishop_W_emission, Bishop_W_emission_color, Bishop_W_opacity, Bishop_W_thin_walled, mtlxnormalmap5_out, geomprop_Tworld_out1, Bishop_W_out);
+ MATERIAL M_Bishop_W_out = mx_surfacematerial(Bishop_W_out, displacementshader1);
+ out = M_Bishop_W_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_BrickPattern.glsl.frag b/Materials/Examples/StandardSurface/M_BrickPattern.glsl.frag
new file mode 100644
index 0000000000..d419a5c990
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_BrickPattern.glsl.frag
@@ -0,0 +1,1987 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform float node_convert_1_in = 3.000000;
+uniform vec3 node_rgbtohsv_12_in = vec3(0.661876, 0.190880, 0.000000);
+uniform sampler2D node_tiledimage_float_26_file;
+uniform float node_tiledimage_float_26_default = 0.000000;
+uniform vec2 node_tiledimage_float_26_uvoffset = vec2(0.000000, 0.000000);
+uniform vec2 node_tiledimage_float_26_realworldimagesize = vec2(1.000000, 1.000000);
+uniform vec2 node_tiledimage_float_26_realworldtilesize = vec2(1.000000, 1.000000);
+uniform int node_tiledimage_float_26_filtertype = 1;
+uniform int node_tiledimage_float_26_framerange = 0;
+uniform int node_tiledimage_float_26_frameoffset = 0;
+uniform int node_tiledimage_float_26_frameendaction = 0;
+uniform sampler2D node_tiledimage_float_7_file;
+uniform float node_tiledimage_float_7_default = 0.000000;
+uniform vec2 node_tiledimage_float_7_uvoffset = vec2(0.000000, 0.000000);
+uniform vec2 node_tiledimage_float_7_realworldimagesize = vec2(1.000000, 1.000000);
+uniform vec2 node_tiledimage_float_7_realworldtilesize = vec2(1.000000, 1.000000);
+uniform int node_tiledimage_float_7_filtertype = 1;
+uniform int node_tiledimage_float_7_framerange = 0;
+uniform int node_tiledimage_float_7_frameoffset = 0;
+uniform int node_tiledimage_float_7_frameendaction = 0;
+uniform sampler2D node_tiledimage_float_24_file;
+uniform float node_tiledimage_float_24_default = 0.000000;
+uniform vec2 node_tiledimage_float_24_uvoffset = vec2(0.000000, 0.000000);
+uniform vec2 node_tiledimage_float_24_realworldimagesize = vec2(1.000000, 1.000000);
+uniform vec2 node_tiledimage_float_24_realworldtilesize = vec2(1.000000, 1.000000);
+uniform int node_tiledimage_float_24_filtertype = 1;
+uniform int node_tiledimage_float_24_framerange = 0;
+uniform int node_tiledimage_float_24_frameoffset = 0;
+uniform int node_tiledimage_float_24_frameendaction = 0;
+uniform sampler2D node_tiledimage_float_10_file;
+uniform float node_tiledimage_float_10_default = 0.000000;
+uniform vec2 node_tiledimage_float_10_uvoffset = vec2(0.000000, 0.000000);
+uniform vec2 node_tiledimage_float_10_realworldimagesize = vec2(1.000000, 1.000000);
+uniform vec2 node_tiledimage_float_10_realworldtilesize = vec2(1.000000, 1.000000);
+uniform int node_tiledimage_float_10_filtertype = 1;
+uniform int node_tiledimage_float_10_framerange = 0;
+uniform int node_tiledimage_float_10_frameoffset = 0;
+uniform int node_tiledimage_float_10_frameendaction = 0;
+uniform sampler2D node_tiledimage_float_22_file;
+uniform float node_tiledimage_float_22_default = 0.000000;
+uniform vec2 node_tiledimage_float_22_uvoffset = vec2(0.000000, 0.000000);
+uniform vec2 node_tiledimage_float_22_realworldimagesize = vec2(1.000000, 1.000000);
+uniform vec2 node_tiledimage_float_22_realworldtilesize = vec2(1.000000, 1.000000);
+uniform int node_tiledimage_float_22_filtertype = 1;
+uniform int node_tiledimage_float_22_framerange = 0;
+uniform int node_tiledimage_float_22_frameoffset = 0;
+uniform int node_tiledimage_float_22_frameendaction = 0;
+uniform sampler2D node_tiledimage_vector3_27_file;
+uniform vec3 node_tiledimage_vector3_27_default = vec3(0.000000, 0.000000, 0.000000);
+uniform vec2 node_tiledimage_vector3_27_uvoffset = vec2(0.000000, 0.000000);
+uniform vec2 node_tiledimage_vector3_27_realworldimagesize = vec2(1.000000, 1.000000);
+uniform vec2 node_tiledimage_vector3_27_realworldtilesize = vec2(1.000000, 1.000000);
+uniform int node_tiledimage_vector3_27_filtertype = 1;
+uniform int node_tiledimage_vector3_27_framerange = 0;
+uniform int node_tiledimage_vector3_27_frameoffset = 0;
+uniform int node_tiledimage_vector3_27_frameendaction = 0;
+uniform float node_multiply_25_in1 = 0.083000;
+uniform float node_multiply_20_in1 = 0.787000;
+uniform vec3 node_multiply_9_in1 = vec3(0.263273, 0.263273, 0.263273);
+uniform float node_multiply_23_in1 = 0.248000;
+uniform float node_max_1_in2 = 0.000010;
+uniform int node_normalmap_3_space = 0;
+uniform float node_normalmap_3_scale = 1.000000;
+uniform float node_divide_21_in1 = 0.853000;
+uniform float node_subtract_18_in2 = 0.350000;
+uniform float node_multiply_14_in2 = 0.083000;
+uniform float node_combine3_color3_13_in2 = 0.000000;
+uniform vec3 node_mix_6_fg = vec3(0.563720, 0.563720, 0.563720);
+uniform float node_clamp_0_low = 0.000000;
+uniform float node_clamp_0_high = 1.000000;
+uniform float N_StandardSurface_base = 1.000000;
+uniform float N_StandardSurface_diffuse_roughness = 0.000000;
+uniform float N_StandardSurface_metalness = 0.000000;
+uniform float N_StandardSurface_specular = 1.000000;
+uniform vec3 N_StandardSurface_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float N_StandardSurface_specular_IOR = 1.500000;
+uniform float N_StandardSurface_specular_anisotropy = 0.000000;
+uniform float N_StandardSurface_specular_rotation = 0.000000;
+uniform float N_StandardSurface_transmission = 0.000000;
+uniform vec3 N_StandardSurface_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float N_StandardSurface_transmission_depth = 0.000000;
+uniform vec3 N_StandardSurface_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float N_StandardSurface_transmission_scatter_anisotropy = 0.000000;
+uniform float N_StandardSurface_transmission_dispersion = 0.000000;
+uniform float N_StandardSurface_transmission_extra_roughness = 0.000000;
+uniform float N_StandardSurface_subsurface = 0.000000;
+uniform vec3 N_StandardSurface_subsurface_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 N_StandardSurface_subsurface_radius = vec3(1.000000, 1.000000, 1.000000);
+uniform float N_StandardSurface_subsurface_scale = 1.000000;
+uniform float N_StandardSurface_subsurface_anisotropy = 0.000000;
+uniform float N_StandardSurface_sheen = 0.000000;
+uniform vec3 N_StandardSurface_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float N_StandardSurface_sheen_roughness = 0.300000;
+uniform float N_StandardSurface_coat = 0.000000;
+uniform vec3 N_StandardSurface_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float N_StandardSurface_coat_roughness = 0.100000;
+uniform float N_StandardSurface_coat_anisotropy = 0.000000;
+uniform float N_StandardSurface_coat_rotation = 0.000000;
+uniform float N_StandardSurface_coat_IOR = 1.500000;
+uniform float N_StandardSurface_coat_affect_color = 0.000000;
+uniform float N_StandardSurface_coat_affect_roughness = 0.000000;
+uniform float N_StandardSurface_thin_film_thickness = 0.000000;
+uniform float N_StandardSurface_thin_film_IOR = 1.500000;
+uniform float N_StandardSurface_emission = 0.000000;
+uniform vec3 N_StandardSurface_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 N_StandardSurface_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool N_StandardSurface_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec2 texcoord_0;
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+/*
+Color transform functions.
+
+These funcions 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);
+}
+
+void mx_rgbtohsv_color3(vec3 _in, out vec3 result)
+{
+ result = mx_rgbtohsv(_in);
+}
+
+vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+{
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+}
+
+void mx_image_float(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+void NG_tiledimage_float(sampler2D file, float default1, vec2 texcoord1, vec2 uvtiling, vec2 uvoffset, vec2 realworldimagesize, vec2 realworldtilesize, int filtertype, int framerange, int frameoffset, int frameendaction, out float out1)
+{
+ vec2 N_mult_float_out = texcoord1 * uvtiling;
+ vec2 N_sub_float_out = N_mult_float_out - uvoffset;
+ vec2 N_divtilesize_float_out = N_sub_float_out / realworldimagesize;
+ vec2 N_multtilesize_float_out = N_divtilesize_float_out * realworldtilesize;
+ float N_img_float_out = 0.0;
+ mx_image_float(file, 0, default1, N_multtilesize_float_out, 2, 2, filtertype, framerange, frameoffset, frameendaction, vec2(1.000000, 1.000000), vec2(0.000000, 0.000000), N_img_float_out);
+ out1 = N_img_float_out;
+}
+
+
+void mx_image_vector3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+void NG_tiledimage_vector3(sampler2D file, vec3 default1, vec2 texcoord1, vec2 uvtiling, vec2 uvoffset, vec2 realworldimagesize, vec2 realworldtilesize, int filtertype, int framerange, int frameoffset, int frameendaction, out vec3 out1)
+{
+ vec2 N_mult_vector3_out = texcoord1 * uvtiling;
+ vec2 N_sub_vector3_out = N_mult_vector3_out - uvoffset;
+ vec2 N_divtilesize_vector3_out = N_sub_vector3_out / realworldimagesize;
+ vec2 N_multtilesize_vector3_out = N_divtilesize_vector3_out * realworldtilesize;
+ vec3 N_img_vector3_out = vec3(0.0);
+ mx_image_vector3(file, 0, default1, N_multtilesize_vector3_out, 2, 2, filtertype, framerange, frameoffset, frameendaction, vec2(1.000000, 1.000000), vec2(0.000000, 0.000000), N_img_vector3_out);
+ out1 = N_img_vector3_out;
+}
+
+void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, out vec3 result)
+{
+ // Decode the normal map.
+ value = (value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+}
+
+
+void mx_hsvtorgb_color3(vec3 _in, out vec3 result)
+{
+ result = mx_hsvtorgb(_in);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec2 node_convert_1_out = vec2(node_convert_1_in, node_convert_1_in);
+ vec3 node_rgbtohsv_12_out = vec3(0.0);
+ mx_rgbtohsv_color3(node_rgbtohsv_12_in, node_rgbtohsv_12_out);
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ float node_tiledimage_float_26_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_26_file, node_tiledimage_float_26_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_26_uvoffset, node_tiledimage_float_26_realworldimagesize, node_tiledimage_float_26_realworldtilesize, node_tiledimage_float_26_filtertype, node_tiledimage_float_26_framerange, node_tiledimage_float_26_frameoffset, node_tiledimage_float_26_frameendaction, node_tiledimage_float_26_out);
+ float node_tiledimage_float_7_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_7_file, node_tiledimage_float_7_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_7_uvoffset, node_tiledimage_float_7_realworldimagesize, node_tiledimage_float_7_realworldtilesize, node_tiledimage_float_7_filtertype, node_tiledimage_float_7_framerange, node_tiledimage_float_7_frameoffset, node_tiledimage_float_7_frameendaction, node_tiledimage_float_7_out);
+ float node_tiledimage_float_24_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_24_file, node_tiledimage_float_24_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_24_uvoffset, node_tiledimage_float_24_realworldimagesize, node_tiledimage_float_24_realworldtilesize, node_tiledimage_float_24_filtertype, node_tiledimage_float_24_framerange, node_tiledimage_float_24_frameoffset, node_tiledimage_float_24_frameendaction, node_tiledimage_float_24_out);
+ float node_tiledimage_float_10_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_10_file, node_tiledimage_float_10_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_10_uvoffset, node_tiledimage_float_10_realworldimagesize, node_tiledimage_float_10_realworldtilesize, node_tiledimage_float_10_filtertype, node_tiledimage_float_10_framerange, node_tiledimage_float_10_frameoffset, node_tiledimage_float_10_frameendaction, node_tiledimage_float_10_out);
+ float node_tiledimage_float_22_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_22_file, node_tiledimage_float_22_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_22_uvoffset, node_tiledimage_float_22_realworldimagesize, node_tiledimage_float_22_realworldtilesize, node_tiledimage_float_22_filtertype, node_tiledimage_float_22_framerange, node_tiledimage_float_22_frameoffset, node_tiledimage_float_22_frameendaction, node_tiledimage_float_22_out);
+ vec3 node_tiledimage_vector3_27_out = vec3(0.0);
+ NG_tiledimage_vector3(node_tiledimage_vector3_27_file, node_tiledimage_vector3_27_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_vector3_27_uvoffset, node_tiledimage_vector3_27_realworldimagesize, node_tiledimage_vector3_27_realworldtilesize, node_tiledimage_vector3_27_filtertype, node_tiledimage_vector3_27_framerange, node_tiledimage_vector3_27_frameoffset, node_tiledimage_vector3_27_frameendaction, node_tiledimage_vector3_27_out);
+ float node_multiply_25_out = node_multiply_25_in1 * node_tiledimage_float_26_out;
+ float node_multiply_20_out = node_multiply_20_in1 * node_tiledimage_float_26_out;
+ vec3 node_multiply_9_out = node_multiply_9_in1 * node_tiledimage_float_7_out;
+ float node_multiply_23_out = node_multiply_23_in1 * node_tiledimage_float_24_out;
+ float node_max_1_out = max(node_tiledimage_float_10_out, node_max_1_in2);
+ vec3 node_normalmap_3_out = vec3(0.0);
+ mx_normalmap(node_tiledimage_vector3_27_out, node_normalmap_3_space, node_normalmap_3_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, node_normalmap_3_out);
+ float node_add_19_out = node_multiply_25_out + node_tiledimage_float_7_out;
+ float node_divide_21_out = node_divide_21_in1 / node_max_1_out;
+ float node_subtract_18_out = node_add_19_out - node_subtract_18_in2;
+ float node_multiply_15_out = node_add_19_out * node_multiply_20_out;
+ float node_multiply_1_out = node_divide_21_out * node_tiledimage_float_22_out;
+ float node_multiply_14_out = node_subtract_18_out * node_multiply_14_in2;
+ vec3 node_combine3_color3_13_out = vec3(node_multiply_14_out, node_combine3_color3_13_in2, node_multiply_15_out);
+ vec3 node_add_16_out = node_combine3_color3_13_out + node_rgbtohsv_12_out;
+ vec3 node_hsvtorgb_17_out = vec3(0.0);
+ mx_hsvtorgb_color3(node_add_16_out, node_hsvtorgb_17_out);
+ vec3 node_mix_6_out = mix(node_hsvtorgb_17_out, node_mix_6_fg, node_multiply_23_out);
+ vec3 node_multiply_5_out = node_mix_6_out * node_tiledimage_float_7_out;
+ vec3 node_mix_8_out = mix(node_multiply_9_out, node_multiply_5_out, node_tiledimage_float_10_out);
+ vec3 node_clamp_0_out = clamp(node_mix_8_out, node_clamp_0_low, node_clamp_0_high);
+ surfaceshader N_StandardSurface_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(N_StandardSurface_base, node_clamp_0_out, N_StandardSurface_diffuse_roughness, N_StandardSurface_metalness, N_StandardSurface_specular, N_StandardSurface_specular_color, node_multiply_1_out, N_StandardSurface_specular_IOR, N_StandardSurface_specular_anisotropy, N_StandardSurface_specular_rotation, N_StandardSurface_transmission, N_StandardSurface_transmission_color, N_StandardSurface_transmission_depth, N_StandardSurface_transmission_scatter, N_StandardSurface_transmission_scatter_anisotropy, N_StandardSurface_transmission_dispersion, N_StandardSurface_transmission_extra_roughness, N_StandardSurface_subsurface, N_StandardSurface_subsurface_color, N_StandardSurface_subsurface_radius, N_StandardSurface_subsurface_scale, N_StandardSurface_subsurface_anisotropy, N_StandardSurface_sheen, N_StandardSurface_sheen_color, N_StandardSurface_sheen_roughness, N_StandardSurface_coat, N_StandardSurface_coat_color, N_StandardSurface_coat_roughness, N_StandardSurface_coat_anisotropy, N_StandardSurface_coat_rotation, N_StandardSurface_coat_IOR, geomprop_Nworld_out1, N_StandardSurface_coat_affect_color, N_StandardSurface_coat_affect_roughness, N_StandardSurface_thin_film_thickness, N_StandardSurface_thin_film_IOR, N_StandardSurface_emission, N_StandardSurface_emission_color, N_StandardSurface_opacity, N_StandardSurface_thin_walled, node_normalmap_3_out, geomprop_Tworld_out1, N_StandardSurface_out);
+ material M_BrickPattern_out = N_StandardSurface_out;
+ out1 = vec4(M_BrickPattern_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/M_BrickPattern.glsl.vert b/Materials/Examples/StandardSurface/M_BrickPattern.glsl.vert
new file mode 100644
index 0000000000..05e3b627ff
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_BrickPattern.glsl.vert
@@ -0,0 +1,51 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec2 i_texcoord_0;
+in vec3 i_normal;
+in vec3 i_tangent;
+
+out VertexData
+{
+ vec2 texcoord_0;
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.texcoord_0 = i_texcoord_0;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ // Omitted node 'N_mult_float'. Function already called in this scope.
+ // Omitted node 'N_sub_float'. Function already called in this scope.
+ // Omitted node 'N_divtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_multtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_img_float'. Function already called in this scope.
+ // Omitted node 'N_mult_float'. Function already called in this scope.
+ // Omitted node 'N_sub_float'. Function already called in this scope.
+ // Omitted node 'N_divtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_multtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_img_float'. Function already called in this scope.
+ // Omitted node 'N_mult_float'. Function already called in this scope.
+ // Omitted node 'N_sub_float'. Function already called in this scope.
+ // Omitted node 'N_divtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_multtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_img_float'. Function already called in this scope.
+ // Omitted node 'N_mult_float'. Function already called in this scope.
+ // Omitted node 'N_sub_float'. Function already called in this scope.
+ // Omitted node 'N_divtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_multtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_img_float'. Function already called in this scope.
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_BrickPattern.mdl b/Materials/Examples/StandardSurface/M_BrickPattern.mdl
new file mode 100644
index 0000000000..9c9f4ddc4e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_BrickPattern.mdl
@@ -0,0 +1,318 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+float NG_tiledimage_float
+(
+ uniform texture_2d file = texture_2d(),
+ float default1 = 0,
+ float2 texcoord = float2(state::texture_coordinate(0).x, state::texture_coordinate(0).y),
+ float2 uvtiling = float2(1, 1),
+ float2 uvoffset = float2(0, 0),
+ float2 realworldimagesize = float2(1, 1),
+ float2 realworldtilesize = float2(1, 1),
+ uniform mx_filterlookup_type filtertype = mx_filterlookup_type_linear,
+ uniform string framerange = "",
+ uniform int frameoffset = 0,
+ uniform mx_addressmode_type frameendaction = mx_addressmode_type_constant
+)
+{
+ float2 N_mult_float_out = texcoord * uvtiling;
+ float2 N_sub_float_out = N_mult_float_out - uvoffset;
+ float2 N_divtilesize_float_out = N_sub_float_out / realworldimagesize;
+ float2 N_multtilesize_float_out = N_divtilesize_float_out * realworldtilesize;
+ float N_img_float_out = mx::stdlib::mx_image_float(file, "", default1, N_multtilesize_float_out, mx_addressmode_type_periodic, mx_addressmode_type_periodic, filtertype, framerange, frameoffset, frameendaction);
+ return N_img_float_out;
+}
+
+float3 NG_tiledimage_vector3
+(
+ uniform texture_2d file = texture_2d(),
+ float3 default1 = float3(0, 0, 0),
+ float2 texcoord = float2(state::texture_coordinate(0).x, state::texture_coordinate(0).y),
+ float2 uvtiling = float2(1, 1),
+ float2 uvoffset = float2(0, 0),
+ float2 realworldimagesize = float2(1, 1),
+ float2 realworldtilesize = float2(1, 1),
+ uniform mx_filterlookup_type filtertype = mx_filterlookup_type_linear,
+ uniform string framerange = "",
+ uniform int frameoffset = 0,
+ uniform mx_addressmode_type frameendaction = mx_addressmode_type_constant
+)
+{
+ float2 N_mult_vector3_out = texcoord * uvtiling;
+ float2 N_sub_vector3_out = N_mult_vector3_out - uvoffset;
+ float2 N_divtilesize_vector3_out = N_sub_vector3_out / realworldimagesize;
+ float2 N_multtilesize_vector3_out = N_divtilesize_vector3_out * realworldtilesize;
+ float3 N_img_vector3_out = mx::stdlib::mx_image_vector3(file, "", default1, N_multtilesize_vector3_out, mx_addressmode_type_periodic, mx_addressmode_type_periodic, filtertype, framerange, frameoffset, frameendaction);
+ return N_img_vector3_out;
+}
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material M_BrickPattern
+(
+ material displacementshader = material(),
+ uniform int geomprop_UV0_index = 0,
+ float node_convert_1_in = 3,
+ color node_rgbtohsv_12_in = color(0.661876, 0.19088, 0),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ uniform texture_2d node_tiledimage_float_26_file = texture_2d("../../../Images/brick_variation_mask.jpg", tex::gamma_linear),
+ float node_tiledimage_float_26_default = 0,
+ float2 node_tiledimage_float_26_uvoffset = float2(0, 0),
+ float2 node_tiledimage_float_26_realworldimagesize = float2(1, 1),
+ float2 node_tiledimage_float_26_realworldtilesize = float2(1, 1),
+ uniform mx_filterlookup_type node_tiledimage_float_26_filtertype = mx_filterlookup_type_linear,
+ uniform string node_tiledimage_float_26_framerange = "",
+ uniform int node_tiledimage_float_26_frameoffset = 0,
+ uniform mx_addressmode_type node_tiledimage_float_26_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d node_tiledimage_float_7_file = texture_2d("../../../Images/brick_base_gray.jpg", tex::gamma_linear),
+ float node_tiledimage_float_7_default = 0,
+ float2 node_tiledimage_float_7_uvoffset = float2(0, 0),
+ float2 node_tiledimage_float_7_realworldimagesize = float2(1, 1),
+ float2 node_tiledimage_float_7_realworldtilesize = float2(1, 1),
+ uniform mx_filterlookup_type node_tiledimage_float_7_filtertype = mx_filterlookup_type_linear,
+ uniform string node_tiledimage_float_7_framerange = "",
+ uniform int node_tiledimage_float_7_frameoffset = 0,
+ uniform mx_addressmode_type node_tiledimage_float_7_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d node_tiledimage_float_24_file = texture_2d("../../../Images/brick_dirt_mask.jpg", tex::gamma_linear),
+ float node_tiledimage_float_24_default = 0,
+ float2 node_tiledimage_float_24_uvoffset = float2(0, 0),
+ float2 node_tiledimage_float_24_realworldimagesize = float2(1, 1),
+ float2 node_tiledimage_float_24_realworldtilesize = float2(1, 1),
+ uniform mx_filterlookup_type node_tiledimage_float_24_filtertype = mx_filterlookup_type_linear,
+ uniform string node_tiledimage_float_24_framerange = "",
+ uniform int node_tiledimage_float_24_frameoffset = 0,
+ uniform mx_addressmode_type node_tiledimage_float_24_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d node_tiledimage_float_10_file = texture_2d("../../../Images/brick_mask.jpg", tex::gamma_linear),
+ float node_tiledimage_float_10_default = 0,
+ float2 node_tiledimage_float_10_uvoffset = float2(0, 0),
+ float2 node_tiledimage_float_10_realworldimagesize = float2(1, 1),
+ float2 node_tiledimage_float_10_realworldtilesize = float2(1, 1),
+ uniform mx_filterlookup_type node_tiledimage_float_10_filtertype = mx_filterlookup_type_linear,
+ uniform string node_tiledimage_float_10_framerange = "",
+ uniform int node_tiledimage_float_10_frameoffset = 0,
+ uniform mx_addressmode_type node_tiledimage_float_10_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d node_tiledimage_float_22_file = texture_2d("../../../Images/brick_roughness.jpg", tex::gamma_linear),
+ float node_tiledimage_float_22_default = 0,
+ float2 node_tiledimage_float_22_uvoffset = float2(0, 0),
+ float2 node_tiledimage_float_22_realworldimagesize = float2(1, 1),
+ float2 node_tiledimage_float_22_realworldtilesize = float2(1, 1),
+ uniform mx_filterlookup_type node_tiledimage_float_22_filtertype = mx_filterlookup_type_linear,
+ uniform string node_tiledimage_float_22_framerange = "",
+ uniform int node_tiledimage_float_22_frameoffset = 0,
+ uniform mx_addressmode_type node_tiledimage_float_22_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d node_tiledimage_vector3_27_file = texture_2d("../../../Images/brick_normal.jpg", tex::gamma_linear),
+ float3 node_tiledimage_vector3_27_default = float3(0, 0, 0),
+ float2 node_tiledimage_vector3_27_uvoffset = float2(0, 0),
+ float2 node_tiledimage_vector3_27_realworldimagesize = float2(1, 1),
+ float2 node_tiledimage_vector3_27_realworldtilesize = float2(1, 1),
+ uniform mx_filterlookup_type node_tiledimage_vector3_27_filtertype = mx_filterlookup_type_linear,
+ uniform string node_tiledimage_vector3_27_framerange = "",
+ uniform int node_tiledimage_vector3_27_frameoffset = 0,
+ uniform mx_addressmode_type node_tiledimage_vector3_27_frameendaction = mx_addressmode_type_constant,
+ float node_multiply_25_in1 = 0.083,
+ float node_multiply_20_in1 = 0.787,
+ color node_multiply_9_in1 = color(0.263273, 0.263273, 0.263273),
+ float node_multiply_23_in1 = 0.248,
+ float node_max_1_in2 = 1e-05,
+ uniform string node_normalmap_3_space = "tangent",
+ float node_normalmap_3_scale = 1,
+ float node_divide_21_in1 = 0.853,
+ float node_subtract_18_in2 = 0.35,
+ float node_multiply_14_in2 = 0.083,
+ float node_combine3_color3_13_in2 = 0,
+ color node_mix_6_fg = color(0.56372, 0.56372, 0.56372),
+ float node_clamp_0_low = 0,
+ float node_clamp_0_high = 1,
+ float N_StandardSurface_base = 1,
+ float N_StandardSurface_diffuse_roughness = 0,
+ float N_StandardSurface_metalness = 0,
+ float N_StandardSurface_specular = 1,
+ color N_StandardSurface_specular_color = color(1, 1, 1),
+ uniform float N_StandardSurface_specular_IOR = 1.5,
+ float N_StandardSurface_specular_anisotropy = 0,
+ float N_StandardSurface_specular_rotation = 0,
+ float N_StandardSurface_transmission = 0,
+ color N_StandardSurface_transmission_color = color(1, 1, 1),
+ float N_StandardSurface_transmission_depth = 0,
+ color N_StandardSurface_transmission_scatter = color(0, 0, 0),
+ float N_StandardSurface_transmission_scatter_anisotropy = 0,
+ float N_StandardSurface_transmission_dispersion = 0,
+ float N_StandardSurface_transmission_extra_roughness = 0,
+ float N_StandardSurface_subsurface = 0,
+ color N_StandardSurface_subsurface_color = color(1, 1, 1),
+ color N_StandardSurface_subsurface_radius = color(1, 1, 1),
+ float N_StandardSurface_subsurface_scale = 1,
+ float N_StandardSurface_subsurface_anisotropy = 0,
+ float N_StandardSurface_sheen = 0,
+ color N_StandardSurface_sheen_color = color(1, 1, 1),
+ float N_StandardSurface_sheen_roughness = 0.3,
+ float N_StandardSurface_coat = 0,
+ color N_StandardSurface_coat_color = color(1, 1, 1),
+ float N_StandardSurface_coat_roughness = 0.1,
+ float N_StandardSurface_coat_anisotropy = 0,
+ float N_StandardSurface_coat_rotation = 0,
+ uniform float N_StandardSurface_coat_IOR = 1.5,
+ float N_StandardSurface_coat_affect_color = 0,
+ float N_StandardSurface_coat_affect_roughness = 0,
+ float N_StandardSurface_thin_film_thickness = 0,
+ float N_StandardSurface_thin_film_IOR = 1.5,
+ float N_StandardSurface_emission = 0,
+ color N_StandardSurface_emission_color = color(1, 1, 1),
+ color N_StandardSurface_opacity = color(1, 1, 1),
+ bool N_StandardSurface_thin_walled = false
+)
+= let
+{
+ float2 geomprop_UV0_out1 = mx::stdlib::mx_texcoord_vector2(mxp_index:geomprop_UV0_index);
+ float2 node_convert_1_out = float2(node_convert_1_in, node_convert_1_in);
+ color node_rgbtohsv_12_out = mx::stdlib::mx_rgbtohsv_color3(node_rgbtohsv_12_in);
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ float node_tiledimage_float_26_out = NG_tiledimage_float(node_tiledimage_float_26_file, node_tiledimage_float_26_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_26_uvoffset, node_tiledimage_float_26_realworldimagesize, node_tiledimage_float_26_realworldtilesize, node_tiledimage_float_26_filtertype, node_tiledimage_float_26_framerange, node_tiledimage_float_26_frameoffset, node_tiledimage_float_26_frameendaction);
+ float node_tiledimage_float_7_out = NG_tiledimage_float(node_tiledimage_float_7_file, node_tiledimage_float_7_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_7_uvoffset, node_tiledimage_float_7_realworldimagesize, node_tiledimage_float_7_realworldtilesize, node_tiledimage_float_7_filtertype, node_tiledimage_float_7_framerange, node_tiledimage_float_7_frameoffset, node_tiledimage_float_7_frameendaction);
+ float node_tiledimage_float_24_out = NG_tiledimage_float(node_tiledimage_float_24_file, node_tiledimage_float_24_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_24_uvoffset, node_tiledimage_float_24_realworldimagesize, node_tiledimage_float_24_realworldtilesize, node_tiledimage_float_24_filtertype, node_tiledimage_float_24_framerange, node_tiledimage_float_24_frameoffset, node_tiledimage_float_24_frameendaction);
+ float node_tiledimage_float_10_out = NG_tiledimage_float(node_tiledimage_float_10_file, node_tiledimage_float_10_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_10_uvoffset, node_tiledimage_float_10_realworldimagesize, node_tiledimage_float_10_realworldtilesize, node_tiledimage_float_10_filtertype, node_tiledimage_float_10_framerange, node_tiledimage_float_10_frameoffset, node_tiledimage_float_10_frameendaction);
+ float node_tiledimage_float_22_out = NG_tiledimage_float(node_tiledimage_float_22_file, node_tiledimage_float_22_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_22_uvoffset, node_tiledimage_float_22_realworldimagesize, node_tiledimage_float_22_realworldtilesize, node_tiledimage_float_22_filtertype, node_tiledimage_float_22_framerange, node_tiledimage_float_22_frameoffset, node_tiledimage_float_22_frameendaction);
+ float3 node_tiledimage_vector3_27_out = NG_tiledimage_vector3(node_tiledimage_vector3_27_file, node_tiledimage_vector3_27_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_vector3_27_uvoffset, node_tiledimage_vector3_27_realworldimagesize, node_tiledimage_vector3_27_realworldtilesize, node_tiledimage_vector3_27_filtertype, node_tiledimage_vector3_27_framerange, node_tiledimage_vector3_27_frameoffset, node_tiledimage_vector3_27_frameendaction);
+ float node_multiply_25_out = node_multiply_25_in1 * node_tiledimage_float_26_out;
+ float node_multiply_20_out = node_multiply_20_in1 * node_tiledimage_float_26_out;
+ color node_multiply_9_out = node_multiply_9_in1 * node_tiledimage_float_7_out;
+ float node_multiply_23_out = node_multiply_23_in1 * node_tiledimage_float_24_out;
+ float node_max_1_out = math::max(node_tiledimage_float_10_out, node_max_1_in2);
+ float3 node_normalmap_3_out = mx::stdlib::mx_normalmap(mxp_in:node_tiledimage_vector3_27_out, mxp_space:node_normalmap_3_space, mxp_scale:node_normalmap_3_scale, mxp_normal:geomprop_Nworld_out1, mxp_tangent:geomprop_Tworld_out1);
+ float node_add_19_out = node_multiply_25_out + node_tiledimage_float_7_out;
+ float node_divide_21_out = node_divide_21_in1 / node_max_1_out;
+ float node_subtract_18_out = node_add_19_out - node_subtract_18_in2;
+ float node_multiply_15_out = node_add_19_out * node_multiply_20_out;
+ float node_multiply_1_out = node_divide_21_out * node_tiledimage_float_22_out;
+ float node_multiply_14_out = node_subtract_18_out * node_multiply_14_in2;
+ color node_combine3_color3_13_out = color(node_multiply_14_out, node_combine3_color3_13_in2, node_multiply_15_out);
+ color node_add_16_out = node_combine3_color3_13_out + node_rgbtohsv_12_out;
+ color node_hsvtorgb_17_out = mx::stdlib::mx_hsvtorgb_color3(node_add_16_out);
+ color node_mix_6_out = math::lerp(node_hsvtorgb_17_out, node_mix_6_fg, node_multiply_23_out);
+ color node_multiply_5_out = node_mix_6_out * node_tiledimage_float_7_out;
+ color node_mix_8_out = math::lerp(node_multiply_9_out, node_multiply_5_out, node_tiledimage_float_10_out);
+ color node_clamp_0_out = math::clamp(node_mix_8_out, node_clamp_0_low, node_clamp_0_high);
+ material N_StandardSurface_out = NG_standard_surface_surfaceshader_100(N_StandardSurface_base, node_clamp_0_out, N_StandardSurface_diffuse_roughness, N_StandardSurface_metalness, N_StandardSurface_specular, N_StandardSurface_specular_color, node_multiply_1_out, N_StandardSurface_specular_IOR, N_StandardSurface_specular_anisotropy, N_StandardSurface_specular_rotation, N_StandardSurface_transmission, N_StandardSurface_transmission_color, N_StandardSurface_transmission_depth, N_StandardSurface_transmission_scatter, N_StandardSurface_transmission_scatter_anisotropy, N_StandardSurface_transmission_dispersion, N_StandardSurface_transmission_extra_roughness, N_StandardSurface_subsurface, N_StandardSurface_subsurface_color, N_StandardSurface_subsurface_radius, N_StandardSurface_subsurface_scale, N_StandardSurface_subsurface_anisotropy, N_StandardSurface_sheen, N_StandardSurface_sheen_color, N_StandardSurface_sheen_roughness, N_StandardSurface_coat, N_StandardSurface_coat_color, N_StandardSurface_coat_roughness, N_StandardSurface_coat_anisotropy, N_StandardSurface_coat_rotation, N_StandardSurface_coat_IOR, geomprop_Nworld_out1, N_StandardSurface_coat_affect_color, N_StandardSurface_coat_affect_roughness, N_StandardSurface_thin_film_thickness, N_StandardSurface_thin_film_IOR, N_StandardSurface_emission, N_StandardSurface_emission_color, N_StandardSurface_opacity, N_StandardSurface_thin_walled, node_normalmap_3_out, geomprop_Tworld_out1);
+ material M_BrickPattern_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: N_StandardSurface_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = M_BrickPattern_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/M_BrickPattern.msl.frag b/Materials/Examples/StandardSurface/M_BrickPattern.msl.frag
new file mode 100644
index 0000000000..bbdae977fc
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_BrickPattern.msl.frag
@@ -0,0 +1,3125 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ float node_convert_1_in;
+ vec3 node_rgbtohsv_12_in;
+ float node_tiledimage_float_26_default;
+ vec2 node_tiledimage_float_26_uvoffset;
+ vec2 node_tiledimage_float_26_realworldimagesize;
+ vec2 node_tiledimage_float_26_realworldtilesize;
+ int node_tiledimage_float_26_filtertype;
+ int node_tiledimage_float_26_framerange;
+ int node_tiledimage_float_26_frameoffset;
+ int node_tiledimage_float_26_frameendaction;
+ float node_tiledimage_float_7_default;
+ vec2 node_tiledimage_float_7_uvoffset;
+ vec2 node_tiledimage_float_7_realworldimagesize;
+ vec2 node_tiledimage_float_7_realworldtilesize;
+ int node_tiledimage_float_7_filtertype;
+ int node_tiledimage_float_7_framerange;
+ int node_tiledimage_float_7_frameoffset;
+ int node_tiledimage_float_7_frameendaction;
+ float node_tiledimage_float_24_default;
+ vec2 node_tiledimage_float_24_uvoffset;
+ vec2 node_tiledimage_float_24_realworldimagesize;
+ vec2 node_tiledimage_float_24_realworldtilesize;
+ int node_tiledimage_float_24_filtertype;
+ int node_tiledimage_float_24_framerange;
+ int node_tiledimage_float_24_frameoffset;
+ int node_tiledimage_float_24_frameendaction;
+ float node_tiledimage_float_10_default;
+ vec2 node_tiledimage_float_10_uvoffset;
+ vec2 node_tiledimage_float_10_realworldimagesize;
+ vec2 node_tiledimage_float_10_realworldtilesize;
+ int node_tiledimage_float_10_filtertype;
+ int node_tiledimage_float_10_framerange;
+ int node_tiledimage_float_10_frameoffset;
+ int node_tiledimage_float_10_frameendaction;
+ float node_tiledimage_float_22_default;
+ vec2 node_tiledimage_float_22_uvoffset;
+ vec2 node_tiledimage_float_22_realworldimagesize;
+ vec2 node_tiledimage_float_22_realworldtilesize;
+ int node_tiledimage_float_22_filtertype;
+ int node_tiledimage_float_22_framerange;
+ int node_tiledimage_float_22_frameoffset;
+ int node_tiledimage_float_22_frameendaction;
+ vec3 node_tiledimage_vector3_27_default;
+ vec2 node_tiledimage_vector3_27_uvoffset;
+ vec2 node_tiledimage_vector3_27_realworldimagesize;
+ vec2 node_tiledimage_vector3_27_realworldtilesize;
+ int node_tiledimage_vector3_27_filtertype;
+ int node_tiledimage_vector3_27_framerange;
+ int node_tiledimage_vector3_27_frameoffset;
+ int node_tiledimage_vector3_27_frameendaction;
+ float node_multiply_25_in1;
+ float node_multiply_20_in1;
+ vec3 node_multiply_9_in1;
+ float node_multiply_23_in1;
+ float node_max_1_in2;
+ int node_normalmap_3_space;
+ float node_normalmap_3_scale;
+ float node_divide_21_in1;
+ float node_subtract_18_in2;
+ float node_multiply_14_in2;
+ float node_combine3_color3_13_in2;
+ vec3 node_mix_6_fg;
+ float node_clamp_0_low;
+ float node_clamp_0_high;
+ float N_StandardSurface_base;
+ float N_StandardSurface_diffuse_roughness;
+ float N_StandardSurface_metalness;
+ float N_StandardSurface_specular;
+ vec3 N_StandardSurface_specular_color;
+ float N_StandardSurface_specular_IOR;
+ float N_StandardSurface_specular_anisotropy;
+ float N_StandardSurface_specular_rotation;
+ float N_StandardSurface_transmission;
+ vec3 N_StandardSurface_transmission_color;
+ float N_StandardSurface_transmission_depth;
+ vec3 N_StandardSurface_transmission_scatter;
+ float N_StandardSurface_transmission_scatter_anisotropy;
+ float N_StandardSurface_transmission_dispersion;
+ float N_StandardSurface_transmission_extra_roughness;
+ float N_StandardSurface_subsurface;
+ vec3 N_StandardSurface_subsurface_color;
+ vec3 N_StandardSurface_subsurface_radius;
+ float N_StandardSurface_subsurface_scale;
+ float N_StandardSurface_subsurface_anisotropy;
+ float N_StandardSurface_sheen;
+ vec3 N_StandardSurface_sheen_color;
+ float N_StandardSurface_sheen_roughness;
+ float N_StandardSurface_coat;
+ vec3 N_StandardSurface_coat_color;
+ float N_StandardSurface_coat_roughness;
+ float N_StandardSurface_coat_anisotropy;
+ float N_StandardSurface_coat_rotation;
+ float N_StandardSurface_coat_IOR;
+ float N_StandardSurface_coat_affect_color;
+ float N_StandardSurface_coat_affect_roughness;
+ float N_StandardSurface_thin_film_thickness;
+ float N_StandardSurface_thin_film_IOR;
+ float N_StandardSurface_emission;
+ vec3 N_StandardSurface_emission_color;
+ vec3 N_StandardSurface_opacity;
+ bool N_StandardSurface_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec2 texcoord_0 ;
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+ , float node_convert_1_in
+
+ , vec3 node_rgbtohsv_12_in
+
+, MetalTexture node_tiledimage_float_26_file , float node_tiledimage_float_26_default
+
+ , vec2 node_tiledimage_float_26_uvoffset
+
+ , vec2 node_tiledimage_float_26_realworldimagesize
+
+ , vec2 node_tiledimage_float_26_realworldtilesize
+
+ , int node_tiledimage_float_26_filtertype
+
+ , int node_tiledimage_float_26_framerange
+
+ , int node_tiledimage_float_26_frameoffset
+
+ , int node_tiledimage_float_26_frameendaction
+
+, MetalTexture node_tiledimage_float_7_file , float node_tiledimage_float_7_default
+
+ , vec2 node_tiledimage_float_7_uvoffset
+
+ , vec2 node_tiledimage_float_7_realworldimagesize
+
+ , vec2 node_tiledimage_float_7_realworldtilesize
+
+ , int node_tiledimage_float_7_filtertype
+
+ , int node_tiledimage_float_7_framerange
+
+ , int node_tiledimage_float_7_frameoffset
+
+ , int node_tiledimage_float_7_frameendaction
+
+, MetalTexture node_tiledimage_float_24_file , float node_tiledimage_float_24_default
+
+ , vec2 node_tiledimage_float_24_uvoffset
+
+ , vec2 node_tiledimage_float_24_realworldimagesize
+
+ , vec2 node_tiledimage_float_24_realworldtilesize
+
+ , int node_tiledimage_float_24_filtertype
+
+ , int node_tiledimage_float_24_framerange
+
+ , int node_tiledimage_float_24_frameoffset
+
+ , int node_tiledimage_float_24_frameendaction
+
+, MetalTexture node_tiledimage_float_10_file , float node_tiledimage_float_10_default
+
+ , vec2 node_tiledimage_float_10_uvoffset
+
+ , vec2 node_tiledimage_float_10_realworldimagesize
+
+ , vec2 node_tiledimage_float_10_realworldtilesize
+
+ , int node_tiledimage_float_10_filtertype
+
+ , int node_tiledimage_float_10_framerange
+
+ , int node_tiledimage_float_10_frameoffset
+
+ , int node_tiledimage_float_10_frameendaction
+
+, MetalTexture node_tiledimage_float_22_file , float node_tiledimage_float_22_default
+
+ , vec2 node_tiledimage_float_22_uvoffset
+
+ , vec2 node_tiledimage_float_22_realworldimagesize
+
+ , vec2 node_tiledimage_float_22_realworldtilesize
+
+ , int node_tiledimage_float_22_filtertype
+
+ , int node_tiledimage_float_22_framerange
+
+ , int node_tiledimage_float_22_frameoffset
+
+ , int node_tiledimage_float_22_frameendaction
+
+, MetalTexture node_tiledimage_vector3_27_file , vec3 node_tiledimage_vector3_27_default
+
+ , vec2 node_tiledimage_vector3_27_uvoffset
+
+ , vec2 node_tiledimage_vector3_27_realworldimagesize
+
+ , vec2 node_tiledimage_vector3_27_realworldtilesize
+
+ , int node_tiledimage_vector3_27_filtertype
+
+ , int node_tiledimage_vector3_27_framerange
+
+ , int node_tiledimage_vector3_27_frameoffset
+
+ , int node_tiledimage_vector3_27_frameendaction
+
+ , float node_multiply_25_in1
+
+ , float node_multiply_20_in1
+
+ , vec3 node_multiply_9_in1
+
+ , float node_multiply_23_in1
+
+ , float node_max_1_in2
+
+ , int node_normalmap_3_space
+
+ , float node_normalmap_3_scale
+
+ , float node_divide_21_in1
+
+ , float node_subtract_18_in2
+
+ , float node_multiply_14_in2
+
+ , float node_combine3_color3_13_in2
+
+ , vec3 node_mix_6_fg
+
+ , float node_clamp_0_low
+
+ , float node_clamp_0_high
+
+ , float N_StandardSurface_base
+
+ , float N_StandardSurface_diffuse_roughness
+
+ , float N_StandardSurface_metalness
+
+ , float N_StandardSurface_specular
+
+ , vec3 N_StandardSurface_specular_color
+
+ , float N_StandardSurface_specular_IOR
+
+ , float N_StandardSurface_specular_anisotropy
+
+ , float N_StandardSurface_specular_rotation
+
+ , float N_StandardSurface_transmission
+
+ , vec3 N_StandardSurface_transmission_color
+
+ , float N_StandardSurface_transmission_depth
+
+ , vec3 N_StandardSurface_transmission_scatter
+
+ , float N_StandardSurface_transmission_scatter_anisotropy
+
+ , float N_StandardSurface_transmission_dispersion
+
+ , float N_StandardSurface_transmission_extra_roughness
+
+ , float N_StandardSurface_subsurface
+
+ , vec3 N_StandardSurface_subsurface_color
+
+ , vec3 N_StandardSurface_subsurface_radius
+
+ , float N_StandardSurface_subsurface_scale
+
+ , float N_StandardSurface_subsurface_anisotropy
+
+ , float N_StandardSurface_sheen
+
+ , vec3 N_StandardSurface_sheen_color
+
+ , float N_StandardSurface_sheen_roughness
+
+ , float N_StandardSurface_coat
+
+ , vec3 N_StandardSurface_coat_color
+
+ , float N_StandardSurface_coat_roughness
+
+ , float N_StandardSurface_coat_anisotropy
+
+ , float N_StandardSurface_coat_rotation
+
+ , float N_StandardSurface_coat_IOR
+
+ , float N_StandardSurface_coat_affect_color
+
+ , float N_StandardSurface_coat_affect_roughness
+
+ , float N_StandardSurface_thin_film_thickness
+
+ , float N_StandardSurface_thin_film_IOR
+
+ , float N_StandardSurface_emission
+
+ , vec3 N_StandardSurface_emission_color
+
+ , vec3 N_StandardSurface_opacity
+
+ , bool N_StandardSurface_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+ , node_convert_1_in(node_convert_1_in)
+
+ , node_rgbtohsv_12_in(node_rgbtohsv_12_in)
+
+, node_tiledimage_float_26_file(node_tiledimage_float_26_file)
+ , node_tiledimage_float_26_default(node_tiledimage_float_26_default)
+
+ , node_tiledimage_float_26_uvoffset(node_tiledimage_float_26_uvoffset)
+
+ , node_tiledimage_float_26_realworldimagesize(node_tiledimage_float_26_realworldimagesize)
+
+ , node_tiledimage_float_26_realworldtilesize(node_tiledimage_float_26_realworldtilesize)
+
+ , node_tiledimage_float_26_filtertype(node_tiledimage_float_26_filtertype)
+
+ , node_tiledimage_float_26_framerange(node_tiledimage_float_26_framerange)
+
+ , node_tiledimage_float_26_frameoffset(node_tiledimage_float_26_frameoffset)
+
+ , node_tiledimage_float_26_frameendaction(node_tiledimage_float_26_frameendaction)
+
+, node_tiledimage_float_7_file(node_tiledimage_float_7_file)
+ , node_tiledimage_float_7_default(node_tiledimage_float_7_default)
+
+ , node_tiledimage_float_7_uvoffset(node_tiledimage_float_7_uvoffset)
+
+ , node_tiledimage_float_7_realworldimagesize(node_tiledimage_float_7_realworldimagesize)
+
+ , node_tiledimage_float_7_realworldtilesize(node_tiledimage_float_7_realworldtilesize)
+
+ , node_tiledimage_float_7_filtertype(node_tiledimage_float_7_filtertype)
+
+ , node_tiledimage_float_7_framerange(node_tiledimage_float_7_framerange)
+
+ , node_tiledimage_float_7_frameoffset(node_tiledimage_float_7_frameoffset)
+
+ , node_tiledimage_float_7_frameendaction(node_tiledimage_float_7_frameendaction)
+
+, node_tiledimage_float_24_file(node_tiledimage_float_24_file)
+ , node_tiledimage_float_24_default(node_tiledimage_float_24_default)
+
+ , node_tiledimage_float_24_uvoffset(node_tiledimage_float_24_uvoffset)
+
+ , node_tiledimage_float_24_realworldimagesize(node_tiledimage_float_24_realworldimagesize)
+
+ , node_tiledimage_float_24_realworldtilesize(node_tiledimage_float_24_realworldtilesize)
+
+ , node_tiledimage_float_24_filtertype(node_tiledimage_float_24_filtertype)
+
+ , node_tiledimage_float_24_framerange(node_tiledimage_float_24_framerange)
+
+ , node_tiledimage_float_24_frameoffset(node_tiledimage_float_24_frameoffset)
+
+ , node_tiledimage_float_24_frameendaction(node_tiledimage_float_24_frameendaction)
+
+, node_tiledimage_float_10_file(node_tiledimage_float_10_file)
+ , node_tiledimage_float_10_default(node_tiledimage_float_10_default)
+
+ , node_tiledimage_float_10_uvoffset(node_tiledimage_float_10_uvoffset)
+
+ , node_tiledimage_float_10_realworldimagesize(node_tiledimage_float_10_realworldimagesize)
+
+ , node_tiledimage_float_10_realworldtilesize(node_tiledimage_float_10_realworldtilesize)
+
+ , node_tiledimage_float_10_filtertype(node_tiledimage_float_10_filtertype)
+
+ , node_tiledimage_float_10_framerange(node_tiledimage_float_10_framerange)
+
+ , node_tiledimage_float_10_frameoffset(node_tiledimage_float_10_frameoffset)
+
+ , node_tiledimage_float_10_frameendaction(node_tiledimage_float_10_frameendaction)
+
+, node_tiledimage_float_22_file(node_tiledimage_float_22_file)
+ , node_tiledimage_float_22_default(node_tiledimage_float_22_default)
+
+ , node_tiledimage_float_22_uvoffset(node_tiledimage_float_22_uvoffset)
+
+ , node_tiledimage_float_22_realworldimagesize(node_tiledimage_float_22_realworldimagesize)
+
+ , node_tiledimage_float_22_realworldtilesize(node_tiledimage_float_22_realworldtilesize)
+
+ , node_tiledimage_float_22_filtertype(node_tiledimage_float_22_filtertype)
+
+ , node_tiledimage_float_22_framerange(node_tiledimage_float_22_framerange)
+
+ , node_tiledimage_float_22_frameoffset(node_tiledimage_float_22_frameoffset)
+
+ , node_tiledimage_float_22_frameendaction(node_tiledimage_float_22_frameendaction)
+
+, node_tiledimage_vector3_27_file(node_tiledimage_vector3_27_file)
+ , node_tiledimage_vector3_27_default(node_tiledimage_vector3_27_default)
+
+ , node_tiledimage_vector3_27_uvoffset(node_tiledimage_vector3_27_uvoffset)
+
+ , node_tiledimage_vector3_27_realworldimagesize(node_tiledimage_vector3_27_realworldimagesize)
+
+ , node_tiledimage_vector3_27_realworldtilesize(node_tiledimage_vector3_27_realworldtilesize)
+
+ , node_tiledimage_vector3_27_filtertype(node_tiledimage_vector3_27_filtertype)
+
+ , node_tiledimage_vector3_27_framerange(node_tiledimage_vector3_27_framerange)
+
+ , node_tiledimage_vector3_27_frameoffset(node_tiledimage_vector3_27_frameoffset)
+
+ , node_tiledimage_vector3_27_frameendaction(node_tiledimage_vector3_27_frameendaction)
+
+ , node_multiply_25_in1(node_multiply_25_in1)
+
+ , node_multiply_20_in1(node_multiply_20_in1)
+
+ , node_multiply_9_in1(node_multiply_9_in1)
+
+ , node_multiply_23_in1(node_multiply_23_in1)
+
+ , node_max_1_in2(node_max_1_in2)
+
+ , node_normalmap_3_space(node_normalmap_3_space)
+
+ , node_normalmap_3_scale(node_normalmap_3_scale)
+
+ , node_divide_21_in1(node_divide_21_in1)
+
+ , node_subtract_18_in2(node_subtract_18_in2)
+
+ , node_multiply_14_in2(node_multiply_14_in2)
+
+ , node_combine3_color3_13_in2(node_combine3_color3_13_in2)
+
+ , node_mix_6_fg(node_mix_6_fg)
+
+ , node_clamp_0_low(node_clamp_0_low)
+
+ , node_clamp_0_high(node_clamp_0_high)
+
+ , N_StandardSurface_base(N_StandardSurface_base)
+
+ , N_StandardSurface_diffuse_roughness(N_StandardSurface_diffuse_roughness)
+
+ , N_StandardSurface_metalness(N_StandardSurface_metalness)
+
+ , N_StandardSurface_specular(N_StandardSurface_specular)
+
+ , N_StandardSurface_specular_color(N_StandardSurface_specular_color)
+
+ , N_StandardSurface_specular_IOR(N_StandardSurface_specular_IOR)
+
+ , N_StandardSurface_specular_anisotropy(N_StandardSurface_specular_anisotropy)
+
+ , N_StandardSurface_specular_rotation(N_StandardSurface_specular_rotation)
+
+ , N_StandardSurface_transmission(N_StandardSurface_transmission)
+
+ , N_StandardSurface_transmission_color(N_StandardSurface_transmission_color)
+
+ , N_StandardSurface_transmission_depth(N_StandardSurface_transmission_depth)
+
+ , N_StandardSurface_transmission_scatter(N_StandardSurface_transmission_scatter)
+
+ , N_StandardSurface_transmission_scatter_anisotropy(N_StandardSurface_transmission_scatter_anisotropy)
+
+ , N_StandardSurface_transmission_dispersion(N_StandardSurface_transmission_dispersion)
+
+ , N_StandardSurface_transmission_extra_roughness(N_StandardSurface_transmission_extra_roughness)
+
+ , N_StandardSurface_subsurface(N_StandardSurface_subsurface)
+
+ , N_StandardSurface_subsurface_color(N_StandardSurface_subsurface_color)
+
+ , N_StandardSurface_subsurface_radius(N_StandardSurface_subsurface_radius)
+
+ , N_StandardSurface_subsurface_scale(N_StandardSurface_subsurface_scale)
+
+ , N_StandardSurface_subsurface_anisotropy(N_StandardSurface_subsurface_anisotropy)
+
+ , N_StandardSurface_sheen(N_StandardSurface_sheen)
+
+ , N_StandardSurface_sheen_color(N_StandardSurface_sheen_color)
+
+ , N_StandardSurface_sheen_roughness(N_StandardSurface_sheen_roughness)
+
+ , N_StandardSurface_coat(N_StandardSurface_coat)
+
+ , N_StandardSurface_coat_color(N_StandardSurface_coat_color)
+
+ , N_StandardSurface_coat_roughness(N_StandardSurface_coat_roughness)
+
+ , N_StandardSurface_coat_anisotropy(N_StandardSurface_coat_anisotropy)
+
+ , N_StandardSurface_coat_rotation(N_StandardSurface_coat_rotation)
+
+ , N_StandardSurface_coat_IOR(N_StandardSurface_coat_IOR)
+
+ , N_StandardSurface_coat_affect_color(N_StandardSurface_coat_affect_color)
+
+ , N_StandardSurface_coat_affect_roughness(N_StandardSurface_coat_affect_roughness)
+
+ , N_StandardSurface_thin_film_thickness(N_StandardSurface_thin_film_thickness)
+
+ , N_StandardSurface_thin_film_IOR(N_StandardSurface_thin_film_IOR)
+
+ , N_StandardSurface_emission(N_StandardSurface_emission)
+
+ , N_StandardSurface_emission_color(N_StandardSurface_emission_color)
+
+ , N_StandardSurface_opacity(N_StandardSurface_opacity)
+
+ , N_StandardSurface_thin_walled(N_StandardSurface_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+ float node_convert_1_in;
+
+
+ vec3 node_rgbtohsv_12_in;
+
+
+MetalTexture node_tiledimage_float_26_file;
+ float node_tiledimage_float_26_default;
+
+
+ vec2 node_tiledimage_float_26_uvoffset;
+
+
+ vec2 node_tiledimage_float_26_realworldimagesize;
+
+
+ vec2 node_tiledimage_float_26_realworldtilesize;
+
+
+ int node_tiledimage_float_26_filtertype;
+
+
+ int node_tiledimage_float_26_framerange;
+
+
+ int node_tiledimage_float_26_frameoffset;
+
+
+ int node_tiledimage_float_26_frameendaction;
+
+
+MetalTexture node_tiledimage_float_7_file;
+ float node_tiledimage_float_7_default;
+
+
+ vec2 node_tiledimage_float_7_uvoffset;
+
+
+ vec2 node_tiledimage_float_7_realworldimagesize;
+
+
+ vec2 node_tiledimage_float_7_realworldtilesize;
+
+
+ int node_tiledimage_float_7_filtertype;
+
+
+ int node_tiledimage_float_7_framerange;
+
+
+ int node_tiledimage_float_7_frameoffset;
+
+
+ int node_tiledimage_float_7_frameendaction;
+
+
+MetalTexture node_tiledimage_float_24_file;
+ float node_tiledimage_float_24_default;
+
+
+ vec2 node_tiledimage_float_24_uvoffset;
+
+
+ vec2 node_tiledimage_float_24_realworldimagesize;
+
+
+ vec2 node_tiledimage_float_24_realworldtilesize;
+
+
+ int node_tiledimage_float_24_filtertype;
+
+
+ int node_tiledimage_float_24_framerange;
+
+
+ int node_tiledimage_float_24_frameoffset;
+
+
+ int node_tiledimage_float_24_frameendaction;
+
+
+MetalTexture node_tiledimage_float_10_file;
+ float node_tiledimage_float_10_default;
+
+
+ vec2 node_tiledimage_float_10_uvoffset;
+
+
+ vec2 node_tiledimage_float_10_realworldimagesize;
+
+
+ vec2 node_tiledimage_float_10_realworldtilesize;
+
+
+ int node_tiledimage_float_10_filtertype;
+
+
+ int node_tiledimage_float_10_framerange;
+
+
+ int node_tiledimage_float_10_frameoffset;
+
+
+ int node_tiledimage_float_10_frameendaction;
+
+
+MetalTexture node_tiledimage_float_22_file;
+ float node_tiledimage_float_22_default;
+
+
+ vec2 node_tiledimage_float_22_uvoffset;
+
+
+ vec2 node_tiledimage_float_22_realworldimagesize;
+
+
+ vec2 node_tiledimage_float_22_realworldtilesize;
+
+
+ int node_tiledimage_float_22_filtertype;
+
+
+ int node_tiledimage_float_22_framerange;
+
+
+ int node_tiledimage_float_22_frameoffset;
+
+
+ int node_tiledimage_float_22_frameendaction;
+
+
+MetalTexture node_tiledimage_vector3_27_file;
+ vec3 node_tiledimage_vector3_27_default;
+
+
+ vec2 node_tiledimage_vector3_27_uvoffset;
+
+
+ vec2 node_tiledimage_vector3_27_realworldimagesize;
+
+
+ vec2 node_tiledimage_vector3_27_realworldtilesize;
+
+
+ int node_tiledimage_vector3_27_filtertype;
+
+
+ int node_tiledimage_vector3_27_framerange;
+
+
+ int node_tiledimage_vector3_27_frameoffset;
+
+
+ int node_tiledimage_vector3_27_frameendaction;
+
+
+ float node_multiply_25_in1;
+
+
+ float node_multiply_20_in1;
+
+
+ vec3 node_multiply_9_in1;
+
+
+ float node_multiply_23_in1;
+
+
+ float node_max_1_in2;
+
+
+ int node_normalmap_3_space;
+
+
+ float node_normalmap_3_scale;
+
+
+ float node_divide_21_in1;
+
+
+ float node_subtract_18_in2;
+
+
+ float node_multiply_14_in2;
+
+
+ float node_combine3_color3_13_in2;
+
+
+ vec3 node_mix_6_fg;
+
+
+ float node_clamp_0_low;
+
+
+ float node_clamp_0_high;
+
+
+ float N_StandardSurface_base;
+
+
+ float N_StandardSurface_diffuse_roughness;
+
+
+ float N_StandardSurface_metalness;
+
+
+ float N_StandardSurface_specular;
+
+
+ vec3 N_StandardSurface_specular_color;
+
+
+ float N_StandardSurface_specular_IOR;
+
+
+ float N_StandardSurface_specular_anisotropy;
+
+
+ float N_StandardSurface_specular_rotation;
+
+
+ float N_StandardSurface_transmission;
+
+
+ vec3 N_StandardSurface_transmission_color;
+
+
+ float N_StandardSurface_transmission_depth;
+
+
+ vec3 N_StandardSurface_transmission_scatter;
+
+
+ float N_StandardSurface_transmission_scatter_anisotropy;
+
+
+ float N_StandardSurface_transmission_dispersion;
+
+
+ float N_StandardSurface_transmission_extra_roughness;
+
+
+ float N_StandardSurface_subsurface;
+
+
+ vec3 N_StandardSurface_subsurface_color;
+
+
+ vec3 N_StandardSurface_subsurface_radius;
+
+
+ float N_StandardSurface_subsurface_scale;
+
+
+ float N_StandardSurface_subsurface_anisotropy;
+
+
+ float N_StandardSurface_sheen;
+
+
+ vec3 N_StandardSurface_sheen_color;
+
+
+ float N_StandardSurface_sheen_roughness;
+
+
+ float N_StandardSurface_coat;
+
+
+ vec3 N_StandardSurface_coat_color;
+
+
+ float N_StandardSurface_coat_roughness;
+
+
+ float N_StandardSurface_coat_anisotropy;
+
+
+ float N_StandardSurface_coat_rotation;
+
+
+ float N_StandardSurface_coat_IOR;
+
+
+ float N_StandardSurface_coat_affect_color;
+
+
+ float N_StandardSurface_coat_affect_roughness;
+
+
+ float N_StandardSurface_thin_film_thickness;
+
+
+ float N_StandardSurface_thin_film_IOR;
+
+
+ float N_StandardSurface_emission;
+
+
+ vec3 N_StandardSurface_emission_color;
+
+
+ vec3 N_StandardSurface_opacity;
+
+
+ bool N_StandardSurface_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ /*
+ Color transform functions.
+
+ These funcions 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);
+ }
+
+ void mx_rgbtohsv_color3(vec3 _in, thread vec3& result)
+ {
+ result = mx_rgbtohsv(_in);
+ }
+
+ vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+ {
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+ }
+
+ void mx_image_float(MetalTexture tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread float& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+ void NG_tiledimage_float(MetalTexture file, float default1, vec2 texcoord1, vec2 uvtiling, vec2 uvoffset, vec2 realworldimagesize, vec2 realworldtilesize, int filtertype, int framerange, int frameoffset, int frameendaction, thread float& out1)
+ {
+ vec2 N_mult_float_out = texcoord1 * uvtiling;
+ vec2 N_sub_float_out = N_mult_float_out - uvoffset;
+ vec2 N_divtilesize_float_out = N_sub_float_out / realworldimagesize;
+ vec2 N_multtilesize_float_out = N_divtilesize_float_out * realworldtilesize;
+ float N_img_float_out = 0.0;
+ mx_image_float(file, 0, default1, N_multtilesize_float_out, 2, 2, filtertype, framerange, frameoffset, frameendaction, vec2(1.000000, 1.000000), vec2(0.000000, 0.000000), N_img_float_out);
+ out1 = N_img_float_out;
+ }
+
+
+ void mx_image_vector3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+ void NG_tiledimage_vector3(MetalTexture file, vec3 default1, vec2 texcoord1, vec2 uvtiling, vec2 uvoffset, vec2 realworldimagesize, vec2 realworldtilesize, int filtertype, int framerange, int frameoffset, int frameendaction, thread vec3& out1)
+ {
+ vec2 N_mult_vector3_out = texcoord1 * uvtiling;
+ vec2 N_sub_vector3_out = N_mult_vector3_out - uvoffset;
+ vec2 N_divtilesize_vector3_out = N_sub_vector3_out / realworldimagesize;
+ vec2 N_multtilesize_vector3_out = N_divtilesize_vector3_out * realworldtilesize;
+ vec3 N_img_vector3_out = vec3(0.0);
+ mx_image_vector3(file, 0, default1, N_multtilesize_vector3_out, 2, 2, filtertype, framerange, frameoffset, frameendaction, vec2(1.000000, 1.000000), vec2(0.000000, 0.000000), N_img_vector3_out);
+ out1 = N_img_vector3_out;
+ }
+
+ void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, thread vec3& result)
+ {
+ // Decode the normal map.
+ value = all(value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+ }
+
+
+ void mx_hsvtorgb_color3(vec3 _in, thread vec3& result)
+ {
+ result = mx_hsvtorgb(_in);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec2 node_convert_1_out = vec2(node_convert_1_in, node_convert_1_in);
+ vec3 node_rgbtohsv_12_out = vec3(0.0);
+ mx_rgbtohsv_color3(node_rgbtohsv_12_in, node_rgbtohsv_12_out);
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ float node_tiledimage_float_26_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_26_file, node_tiledimage_float_26_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_26_uvoffset, node_tiledimage_float_26_realworldimagesize, node_tiledimage_float_26_realworldtilesize, node_tiledimage_float_26_filtertype, node_tiledimage_float_26_framerange, node_tiledimage_float_26_frameoffset, node_tiledimage_float_26_frameendaction, node_tiledimage_float_26_out);
+ float node_tiledimage_float_7_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_7_file, node_tiledimage_float_7_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_7_uvoffset, node_tiledimage_float_7_realworldimagesize, node_tiledimage_float_7_realworldtilesize, node_tiledimage_float_7_filtertype, node_tiledimage_float_7_framerange, node_tiledimage_float_7_frameoffset, node_tiledimage_float_7_frameendaction, node_tiledimage_float_7_out);
+ float node_tiledimage_float_24_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_24_file, node_tiledimage_float_24_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_24_uvoffset, node_tiledimage_float_24_realworldimagesize, node_tiledimage_float_24_realworldtilesize, node_tiledimage_float_24_filtertype, node_tiledimage_float_24_framerange, node_tiledimage_float_24_frameoffset, node_tiledimage_float_24_frameendaction, node_tiledimage_float_24_out);
+ float node_tiledimage_float_10_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_10_file, node_tiledimage_float_10_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_10_uvoffset, node_tiledimage_float_10_realworldimagesize, node_tiledimage_float_10_realworldtilesize, node_tiledimage_float_10_filtertype, node_tiledimage_float_10_framerange, node_tiledimage_float_10_frameoffset, node_tiledimage_float_10_frameendaction, node_tiledimage_float_10_out);
+ float node_tiledimage_float_22_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_22_file, node_tiledimage_float_22_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_22_uvoffset, node_tiledimage_float_22_realworldimagesize, node_tiledimage_float_22_realworldtilesize, node_tiledimage_float_22_filtertype, node_tiledimage_float_22_framerange, node_tiledimage_float_22_frameoffset, node_tiledimage_float_22_frameendaction, node_tiledimage_float_22_out);
+ vec3 node_tiledimage_vector3_27_out = vec3(0.0);
+ NG_tiledimage_vector3(node_tiledimage_vector3_27_file, node_tiledimage_vector3_27_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_vector3_27_uvoffset, node_tiledimage_vector3_27_realworldimagesize, node_tiledimage_vector3_27_realworldtilesize, node_tiledimage_vector3_27_filtertype, node_tiledimage_vector3_27_framerange, node_tiledimage_vector3_27_frameoffset, node_tiledimage_vector3_27_frameendaction, node_tiledimage_vector3_27_out);
+ float node_multiply_25_out = node_multiply_25_in1 * node_tiledimage_float_26_out;
+ float node_multiply_20_out = node_multiply_20_in1 * node_tiledimage_float_26_out;
+ vec3 node_multiply_9_out = node_multiply_9_in1 * node_tiledimage_float_7_out;
+ float node_multiply_23_out = node_multiply_23_in1 * node_tiledimage_float_24_out;
+ float node_max_1_out = max(node_tiledimage_float_10_out, node_max_1_in2);
+ vec3 node_normalmap_3_out = vec3(0.0);
+ mx_normalmap(node_tiledimage_vector3_27_out, node_normalmap_3_space, node_normalmap_3_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, node_normalmap_3_out);
+ float node_add_19_out = node_multiply_25_out + node_tiledimage_float_7_out;
+ float node_divide_21_out = node_divide_21_in1 / node_max_1_out;
+ float node_subtract_18_out = node_add_19_out - node_subtract_18_in2;
+ float node_multiply_15_out = node_add_19_out * node_multiply_20_out;
+ float node_multiply_1_out = node_divide_21_out * node_tiledimage_float_22_out;
+ float node_multiply_14_out = node_subtract_18_out * node_multiply_14_in2;
+ vec3 node_combine3_color3_13_out = vec3(node_multiply_14_out, node_combine3_color3_13_in2, node_multiply_15_out);
+ vec3 node_add_16_out = node_combine3_color3_13_out + node_rgbtohsv_12_out;
+ vec3 node_hsvtorgb_17_out = vec3(0.0);
+ mx_hsvtorgb_color3(node_add_16_out, node_hsvtorgb_17_out);
+ vec3 node_mix_6_out = mix(node_hsvtorgb_17_out, node_mix_6_fg, node_multiply_23_out);
+ vec3 node_multiply_5_out = node_mix_6_out * node_tiledimage_float_7_out;
+ vec3 node_mix_8_out = mix(node_multiply_9_out, node_multiply_5_out, node_tiledimage_float_10_out);
+ vec3 node_clamp_0_out = clamp(node_mix_8_out, node_clamp_0_low, node_clamp_0_high);
+ surfaceshader N_StandardSurface_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(N_StandardSurface_base, node_clamp_0_out, N_StandardSurface_diffuse_roughness, N_StandardSurface_metalness, N_StandardSurface_specular, N_StandardSurface_specular_color, node_multiply_1_out, N_StandardSurface_specular_IOR, N_StandardSurface_specular_anisotropy, N_StandardSurface_specular_rotation, N_StandardSurface_transmission, N_StandardSurface_transmission_color, N_StandardSurface_transmission_depth, N_StandardSurface_transmission_scatter, N_StandardSurface_transmission_scatter_anisotropy, N_StandardSurface_transmission_dispersion, N_StandardSurface_transmission_extra_roughness, N_StandardSurface_subsurface, N_StandardSurface_subsurface_color, N_StandardSurface_subsurface_radius, N_StandardSurface_subsurface_scale, N_StandardSurface_subsurface_anisotropy, N_StandardSurface_sheen, N_StandardSurface_sheen_color, N_StandardSurface_sheen_roughness, N_StandardSurface_coat, N_StandardSurface_coat_color, N_StandardSurface_coat_roughness, N_StandardSurface_coat_anisotropy, N_StandardSurface_coat_rotation, N_StandardSurface_coat_IOR, geomprop_Nworld_out1, N_StandardSurface_coat_affect_color, N_StandardSurface_coat_affect_roughness, N_StandardSurface_thin_film_thickness, N_StandardSurface_thin_film_IOR, N_StandardSurface_emission, N_StandardSurface_emission_color, N_StandardSurface_opacity, N_StandardSurface_thin_walled, node_normalmap_3_out, geomprop_Tworld_out1, N_StandardSurface_out);
+ material M_BrickPattern_out = N_StandardSurface_out;
+ out1 = float4(M_BrickPattern_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], texture2d node_tiledimage_float_26_file_tex [[texture(0)]], sampler node_tiledimage_float_26_file_sampler [[sampler(0)]]
+, texture2d node_tiledimage_float_7_file_tex [[texture(1)]], sampler node_tiledimage_float_7_file_sampler [[sampler(1)]]
+, texture2d node_tiledimage_float_24_file_tex [[texture(2)]], sampler node_tiledimage_float_24_file_sampler [[sampler(2)]]
+, texture2d node_tiledimage_float_10_file_tex [[texture(3)]], sampler node_tiledimage_float_10_file_sampler [[sampler(3)]]
+, texture2d node_tiledimage_float_22_file_tex [[texture(4)]], sampler node_tiledimage_float_22_file_sampler [[sampler(4)]]
+, texture2d node_tiledimage_vector3_27_file_tex [[texture(5)]], sampler node_tiledimage_vector3_27_file_sampler [[sampler(5)]]
+, constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(6)]], sampler u_envRadiance_sampler [[sampler(6)]]
+, texture2d u_envIrradiance_tex [[texture(7)]], sampler u_envIrradiance_sampler [[sampler(7)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+ , u_pub.node_convert_1_in
+ , u_pub.node_rgbtohsv_12_in
+, MetalTexture {
+node_tiledimage_float_26_file_tex, node_tiledimage_float_26_file_sampler }
+ , u_pub.node_tiledimage_float_26_default
+ , u_pub.node_tiledimage_float_26_uvoffset
+ , u_pub.node_tiledimage_float_26_realworldimagesize
+ , u_pub.node_tiledimage_float_26_realworldtilesize
+ , u_pub.node_tiledimage_float_26_filtertype
+ , u_pub.node_tiledimage_float_26_framerange
+ , u_pub.node_tiledimage_float_26_frameoffset
+ , u_pub.node_tiledimage_float_26_frameendaction
+, MetalTexture {
+node_tiledimage_float_7_file_tex, node_tiledimage_float_7_file_sampler }
+ , u_pub.node_tiledimage_float_7_default
+ , u_pub.node_tiledimage_float_7_uvoffset
+ , u_pub.node_tiledimage_float_7_realworldimagesize
+ , u_pub.node_tiledimage_float_7_realworldtilesize
+ , u_pub.node_tiledimage_float_7_filtertype
+ , u_pub.node_tiledimage_float_7_framerange
+ , u_pub.node_tiledimage_float_7_frameoffset
+ , u_pub.node_tiledimage_float_7_frameendaction
+, MetalTexture {
+node_tiledimage_float_24_file_tex, node_tiledimage_float_24_file_sampler }
+ , u_pub.node_tiledimage_float_24_default
+ , u_pub.node_tiledimage_float_24_uvoffset
+ , u_pub.node_tiledimage_float_24_realworldimagesize
+ , u_pub.node_tiledimage_float_24_realworldtilesize
+ , u_pub.node_tiledimage_float_24_filtertype
+ , u_pub.node_tiledimage_float_24_framerange
+ , u_pub.node_tiledimage_float_24_frameoffset
+ , u_pub.node_tiledimage_float_24_frameendaction
+, MetalTexture {
+node_tiledimage_float_10_file_tex, node_tiledimage_float_10_file_sampler }
+ , u_pub.node_tiledimage_float_10_default
+ , u_pub.node_tiledimage_float_10_uvoffset
+ , u_pub.node_tiledimage_float_10_realworldimagesize
+ , u_pub.node_tiledimage_float_10_realworldtilesize
+ , u_pub.node_tiledimage_float_10_filtertype
+ , u_pub.node_tiledimage_float_10_framerange
+ , u_pub.node_tiledimage_float_10_frameoffset
+ , u_pub.node_tiledimage_float_10_frameendaction
+, MetalTexture {
+node_tiledimage_float_22_file_tex, node_tiledimage_float_22_file_sampler }
+ , u_pub.node_tiledimage_float_22_default
+ , u_pub.node_tiledimage_float_22_uvoffset
+ , u_pub.node_tiledimage_float_22_realworldimagesize
+ , u_pub.node_tiledimage_float_22_realworldtilesize
+ , u_pub.node_tiledimage_float_22_filtertype
+ , u_pub.node_tiledimage_float_22_framerange
+ , u_pub.node_tiledimage_float_22_frameoffset
+ , u_pub.node_tiledimage_float_22_frameendaction
+, MetalTexture {
+node_tiledimage_vector3_27_file_tex, node_tiledimage_vector3_27_file_sampler }
+ , u_pub.node_tiledimage_vector3_27_default
+ , u_pub.node_tiledimage_vector3_27_uvoffset
+ , u_pub.node_tiledimage_vector3_27_realworldimagesize
+ , u_pub.node_tiledimage_vector3_27_realworldtilesize
+ , u_pub.node_tiledimage_vector3_27_filtertype
+ , u_pub.node_tiledimage_vector3_27_framerange
+ , u_pub.node_tiledimage_vector3_27_frameoffset
+ , u_pub.node_tiledimage_vector3_27_frameendaction
+ , u_pub.node_multiply_25_in1
+ , u_pub.node_multiply_20_in1
+ , u_pub.node_multiply_9_in1
+ , u_pub.node_multiply_23_in1
+ , u_pub.node_max_1_in2
+ , u_pub.node_normalmap_3_space
+ , u_pub.node_normalmap_3_scale
+ , u_pub.node_divide_21_in1
+ , u_pub.node_subtract_18_in2
+ , u_pub.node_multiply_14_in2
+ , u_pub.node_combine3_color3_13_in2
+ , u_pub.node_mix_6_fg
+ , u_pub.node_clamp_0_low
+ , u_pub.node_clamp_0_high
+ , u_pub.N_StandardSurface_base
+ , u_pub.N_StandardSurface_diffuse_roughness
+ , u_pub.N_StandardSurface_metalness
+ , u_pub.N_StandardSurface_specular
+ , u_pub.N_StandardSurface_specular_color
+ , u_pub.N_StandardSurface_specular_IOR
+ , u_pub.N_StandardSurface_specular_anisotropy
+ , u_pub.N_StandardSurface_specular_rotation
+ , u_pub.N_StandardSurface_transmission
+ , u_pub.N_StandardSurface_transmission_color
+ , u_pub.N_StandardSurface_transmission_depth
+ , u_pub.N_StandardSurface_transmission_scatter
+ , u_pub.N_StandardSurface_transmission_scatter_anisotropy
+ , u_pub.N_StandardSurface_transmission_dispersion
+ , u_pub.N_StandardSurface_transmission_extra_roughness
+ , u_pub.N_StandardSurface_subsurface
+ , u_pub.N_StandardSurface_subsurface_color
+ , u_pub.N_StandardSurface_subsurface_radius
+ , u_pub.N_StandardSurface_subsurface_scale
+ , u_pub.N_StandardSurface_subsurface_anisotropy
+ , u_pub.N_StandardSurface_sheen
+ , u_pub.N_StandardSurface_sheen_color
+ , u_pub.N_StandardSurface_sheen_roughness
+ , u_pub.N_StandardSurface_coat
+ , u_pub.N_StandardSurface_coat_color
+ , u_pub.N_StandardSurface_coat_roughness
+ , u_pub.N_StandardSurface_coat_anisotropy
+ , u_pub.N_StandardSurface_coat_rotation
+ , u_pub.N_StandardSurface_coat_IOR
+ , u_pub.N_StandardSurface_coat_affect_color
+ , u_pub.N_StandardSurface_coat_affect_roughness
+ , u_pub.N_StandardSurface_thin_film_thickness
+ , u_pub.N_StandardSurface_thin_film_IOR
+ , u_pub.N_StandardSurface_emission
+ , u_pub.N_StandardSurface_emission_color
+ , u_pub.N_StandardSurface_opacity
+ , u_pub.N_StandardSurface_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/M_BrickPattern.msl.vert b/Materials/Examples/StandardSurface/M_BrickPattern.msl.vert
new file mode 100644
index 0000000000..5332e709f9
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_BrickPattern.msl.vert
@@ -0,0 +1,165 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec2 i_texcoord_0 [[attribute(1)]];
+ vec3 i_normal [[attribute(2)]];
+ vec3 i_tangent [[attribute(3)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec2 texcoord_0;
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec2 i_texcoord_0
+, vec3 i_normal
+, vec3 i_tangent
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_texcoord_0(i_texcoord_0)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec2 i_texcoord_0;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.texcoord_0 = i_texcoord_0;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ // Omitted node 'N_mult_float'. Function already called in this scope.
+ // Omitted node 'N_sub_float'. Function already called in this scope.
+ // Omitted node 'N_divtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_multtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_img_float'. Function already called in this scope.
+ // Omitted node 'N_mult_float'. Function already called in this scope.
+ // Omitted node 'N_sub_float'. Function already called in this scope.
+ // Omitted node 'N_divtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_multtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_img_float'. Function already called in this scope.
+ // Omitted node 'N_mult_float'. Function already called in this scope.
+ // Omitted node 'N_sub_float'. Function already called in this scope.
+ // Omitted node 'N_divtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_multtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_img_float'. Function already called in this scope.
+ // Omitted node 'N_mult_float'. Function already called in this scope.
+ // Omitted node 'N_sub_float'. Function already called in this scope.
+ // Omitted node 'N_divtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_multtilesize_float'. Function already called in this scope.
+ // Omitted node 'N_img_float'. Function already called in this scope.
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_UV0'. Function already called in this scope.
+ // Omitted node 'node_convert_1'. Function already called in this scope.
+ // Omitted node 'node_rgbtohsv_12'. Function already called in this scope.
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'node_tiledimage_float_26'. Function already called in this scope.
+ // Omitted node 'node_tiledimage_float_7'. Function already called in this scope.
+ // Omitted node 'node_tiledimage_float_24'. Function already called in this scope.
+ // Omitted node 'node_tiledimage_float_10'. Function already called in this scope.
+ // Omitted node 'node_tiledimage_float_22'. Function already called in this scope.
+ // Omitted node 'node_tiledimage_vector3_27'. Function already called in this scope.
+ // Omitted node 'node_multiply_25'. Function already called in this scope.
+ // Omitted node 'node_multiply_20'. Function already called in this scope.
+ // Omitted node 'node_multiply_9'. Function already called in this scope.
+ // Omitted node 'node_multiply_23'. Function already called in this scope.
+ // Omitted node 'node_max_1'. Function already called in this scope.
+ // Omitted node 'node_normalmap_3'. Function already called in this scope.
+ // Omitted node 'node_add_19'. Function already called in this scope.
+ // Omitted node 'node_divide_21'. Function already called in this scope.
+ // Omitted node 'node_subtract_18'. Function already called in this scope.
+ // Omitted node 'node_multiply_15'. Function already called in this scope.
+ // Omitted node 'node_multiply_1'. Function already called in this scope.
+ // Omitted node 'node_multiply_14'. Function already called in this scope.
+ // Omitted node 'node_combine3_color3_13'. Function already called in this scope.
+ // Omitted node 'node_add_16'. Function already called in this scope.
+ // Omitted node 'node_hsvtorgb_17'. Function already called in this scope.
+ // Omitted node 'node_mix_6'. Function already called in this scope.
+ // Omitted node 'node_multiply_5'. Function already called in this scope.
+ // Omitted node 'node_mix_8'. Function already called in this scope.
+ // Omitted node 'node_clamp_0'. Function already called in this scope.
+ // Omitted node 'N_StandardSurface'. Function already called in this scope.
+ // Omitted node 'M_BrickPattern'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(4) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_texcoord_0, i_vs.i_normal, i_vs.i_tangent , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_BrickPattern.osl b/Materials/Examples/StandardSurface/M_BrickPattern.osl
new file mode 100644
index 0000000000..4c14ed101a
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_BrickPattern.osl
@@ -0,0 +1,917 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+void mx_rgbtohsv_color3(vector _in, output vector result)
+{
+ result = transformc("rgb","hsv", _in);
+}
+
+vector2 mx_transform_uv(vector2 texcoord)
+{
+ return texcoord;
+}
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+ out = rgb[0];
+}
+
+void NG_tiledimage_float(textureresource file, float default1, vector2 texcoord, vector2 uvtiling, vector2 uvoffset, vector2 realworldimagesize, vector2 realworldtilesize, string filtertype, string framerange, int frameoffset, string frameendaction, output float out)
+{
+ vector2 N_mult_float_out = texcoord * uvtiling;
+ vector2 N_sub_float_out = N_mult_float_out - uvoffset;
+ vector2 N_divtilesize_float_out = N_sub_float_out / realworldimagesize;
+ vector2 N_multtilesize_float_out = N_divtilesize_float_out * realworldtilesize;
+ float N_img_float_out = 0.0;
+ mx_image_float(file, "", default1, N_multtilesize_float_out, "periodic", "periodic", filtertype, framerange, frameoffset, frameendaction, N_img_float_out);
+ out = N_img_float_out;
+}
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+}
+
+void NG_tiledimage_vector3(textureresource file, vector default1, vector2 texcoord, vector2 uvtiling, vector2 uvoffset, vector2 realworldimagesize, vector2 realworldtilesize, string filtertype, string framerange, int frameoffset, string frameendaction, output vector out)
+{
+ vector2 N_mult_vector3_out = texcoord * uvtiling;
+ vector2 N_sub_vector3_out = N_mult_vector3_out - uvoffset;
+ vector2 N_divtilesize_vector3_out = N_sub_vector3_out / realworldimagesize;
+ vector2 N_multtilesize_vector3_out = N_divtilesize_vector3_out * realworldtilesize;
+ vector N_img_vector3_out = vector(0.0);
+ mx_image_vector3(file, "", default1, N_multtilesize_vector3_out, "periodic", "periodic", filtertype, framerange, frameoffset, frameendaction, N_img_vector3_out);
+ out = N_img_vector3_out;
+}
+
+void mx_normalmap(vector value, string map_space, float normal_scale, vector N, vector U, output vector result)
+{
+ // Tangent space
+ if (map_space == "tangent")
+ {
+ vector v = value * 2.0 - 1.0;
+ vector T = normalize(U - dot(U, N) * N);
+ vector B = normalize(cross(N, T));
+ result = normalize(T * v[0] * normal_scale + B * v[1] * normal_scale + N * v[2]);
+ }
+ // Object space
+ else
+ {
+ vector n = value * 2.0 - 1.0;
+ result = normalize(n);
+ }
+}
+
+void mx_hsvtorgb_color3(vector _in, output vector result)
+{
+ result = transformc("hsv","rgb", _in);
+}
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader M_BrickPattern
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "M_BrickPattern"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ int geomprop_UV0_index = 0
+ [[
+ string widget = "number"
+ ]],
+ float node_convert_1_in = 3
+ [[
+ string widget = "number"
+ ]],
+ color node_rgbtohsv_12_in = color(0.661876, 0.19088, 0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ textureresource node_tiledimage_float_26_file = {"../../../Images/brick_variation_mask.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ float node_tiledimage_float_26_default = 0
+ [[
+ string widget = "number"
+ ]],
+ vector2 node_tiledimage_float_26_uvoffset = {0, 0},
+ vector2 node_tiledimage_float_26_realworldimagesize = {1, 1},
+ vector2 node_tiledimage_float_26_realworldtilesize = {1, 1},
+ string node_tiledimage_float_26_filtertype = "linear",
+ string node_tiledimage_float_26_framerange = "",
+ int node_tiledimage_float_26_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string node_tiledimage_float_26_frameendaction = "constant",
+ textureresource node_tiledimage_float_7_file = {"../../../Images/brick_base_gray.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ float node_tiledimage_float_7_default = 0
+ [[
+ string widget = "number"
+ ]],
+ vector2 node_tiledimage_float_7_uvoffset = {0, 0},
+ vector2 node_tiledimage_float_7_realworldimagesize = {1, 1},
+ vector2 node_tiledimage_float_7_realworldtilesize = {1, 1},
+ string node_tiledimage_float_7_filtertype = "linear",
+ string node_tiledimage_float_7_framerange = "",
+ int node_tiledimage_float_7_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string node_tiledimage_float_7_frameendaction = "constant",
+ textureresource node_tiledimage_float_24_file = {"../../../Images/brick_dirt_mask.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ float node_tiledimage_float_24_default = 0
+ [[
+ string widget = "number"
+ ]],
+ vector2 node_tiledimage_float_24_uvoffset = {0, 0},
+ vector2 node_tiledimage_float_24_realworldimagesize = {1, 1},
+ vector2 node_tiledimage_float_24_realworldtilesize = {1, 1},
+ string node_tiledimage_float_24_filtertype = "linear",
+ string node_tiledimage_float_24_framerange = "",
+ int node_tiledimage_float_24_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string node_tiledimage_float_24_frameendaction = "constant",
+ textureresource node_tiledimage_float_10_file = {"../../../Images/brick_mask.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ float node_tiledimage_float_10_default = 0
+ [[
+ string widget = "number"
+ ]],
+ vector2 node_tiledimage_float_10_uvoffset = {0, 0},
+ vector2 node_tiledimage_float_10_realworldimagesize = {1, 1},
+ vector2 node_tiledimage_float_10_realworldtilesize = {1, 1},
+ string node_tiledimage_float_10_filtertype = "linear",
+ string node_tiledimage_float_10_framerange = "",
+ int node_tiledimage_float_10_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string node_tiledimage_float_10_frameendaction = "constant",
+ textureresource node_tiledimage_float_22_file = {"../../../Images/brick_roughness.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ float node_tiledimage_float_22_default = 0
+ [[
+ string widget = "number"
+ ]],
+ vector2 node_tiledimage_float_22_uvoffset = {0, 0},
+ vector2 node_tiledimage_float_22_realworldimagesize = {1, 1},
+ vector2 node_tiledimage_float_22_realworldtilesize = {1, 1},
+ string node_tiledimage_float_22_filtertype = "linear",
+ string node_tiledimage_float_22_framerange = "",
+ int node_tiledimage_float_22_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string node_tiledimage_float_22_frameendaction = "constant",
+ textureresource node_tiledimage_vector3_27_file = {"../../../Images/brick_normal.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ vector node_tiledimage_vector3_27_default = vector(0, 0, 0),
+ vector2 node_tiledimage_vector3_27_uvoffset = {0, 0},
+ vector2 node_tiledimage_vector3_27_realworldimagesize = {1, 1},
+ vector2 node_tiledimage_vector3_27_realworldtilesize = {1, 1},
+ string node_tiledimage_vector3_27_filtertype = "linear",
+ string node_tiledimage_vector3_27_framerange = "",
+ int node_tiledimage_vector3_27_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string node_tiledimage_vector3_27_frameendaction = "constant",
+ float node_multiply_25_in1 = 0.083
+ [[
+ string widget = "number"
+ ]],
+ float node_multiply_20_in1 = 0.787
+ [[
+ string widget = "number"
+ ]],
+ color node_multiply_9_in1 = color(0.263273, 0.263273, 0.263273),
+ float node_multiply_23_in1 = 0.248
+ [[
+ string widget = "number"
+ ]],
+ float node_max_1_in2 = 1e-05
+ [[
+ string widget = "number"
+ ]],
+ string node_normalmap_3_space = "tangent",
+ float node_normalmap_3_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float node_divide_21_in1 = 0.853
+ [[
+ string widget = "number"
+ ]],
+ float node_subtract_18_in2 = 0.35
+ [[
+ string widget = "number"
+ ]],
+ float node_multiply_14_in2 = 0.083
+ [[
+ string widget = "number"
+ ]],
+ float node_combine3_color3_13_in2 = 0
+ [[
+ string widget = "number"
+ ]],
+ color node_mix_6_fg = color(0.56372, 0.56372, 0.56372),
+ float node_clamp_0_low = 0
+ [[
+ string widget = "number"
+ ]],
+ float node_clamp_0_high = 1
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_base = 1
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_metalness = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color N_StandardSurface_specular_color = color(1, 1, 1),
+ float N_StandardSurface_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color N_StandardSurface_transmission_color = color(1, 1, 1),
+ float N_StandardSurface_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color N_StandardSurface_transmission_scatter = color(0, 0, 0),
+ float N_StandardSurface_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ color N_StandardSurface_subsurface_color = color(1, 1, 1),
+ color N_StandardSurface_subsurface_radius = color(1, 1, 1),
+ float N_StandardSurface_subsurface_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color N_StandardSurface_sheen_color = color(1, 1, 1),
+ float N_StandardSurface_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color N_StandardSurface_coat_color = color(1, 1, 1),
+ float N_StandardSurface_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float N_StandardSurface_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color N_StandardSurface_emission_color = color(1, 1, 1),
+ color N_StandardSurface_opacity = color(1, 1, 1),
+ int N_StandardSurface_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector2 geomprop_UV0_out1 = vector2(u,v);
+ vector2 node_convert_1_out = vector2(node_convert_1_in, node_convert_1_in);
+ color node_rgbtohsv_12_out = color(0.0);
+ mx_rgbtohsv_color3(node_rgbtohsv_12_in, node_rgbtohsv_12_out);
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ float node_tiledimage_float_26_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_26_file, node_tiledimage_float_26_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_26_uvoffset, node_tiledimage_float_26_realworldimagesize, node_tiledimage_float_26_realworldtilesize, node_tiledimage_float_26_filtertype, node_tiledimage_float_26_framerange, node_tiledimage_float_26_frameoffset, node_tiledimage_float_26_frameendaction, node_tiledimage_float_26_out);
+ float node_tiledimage_float_7_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_7_file, node_tiledimage_float_7_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_7_uvoffset, node_tiledimage_float_7_realworldimagesize, node_tiledimage_float_7_realworldtilesize, node_tiledimage_float_7_filtertype, node_tiledimage_float_7_framerange, node_tiledimage_float_7_frameoffset, node_tiledimage_float_7_frameendaction, node_tiledimage_float_7_out);
+ float node_tiledimage_float_24_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_24_file, node_tiledimage_float_24_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_24_uvoffset, node_tiledimage_float_24_realworldimagesize, node_tiledimage_float_24_realworldtilesize, node_tiledimage_float_24_filtertype, node_tiledimage_float_24_framerange, node_tiledimage_float_24_frameoffset, node_tiledimage_float_24_frameendaction, node_tiledimage_float_24_out);
+ float node_tiledimage_float_10_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_10_file, node_tiledimage_float_10_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_10_uvoffset, node_tiledimage_float_10_realworldimagesize, node_tiledimage_float_10_realworldtilesize, node_tiledimage_float_10_filtertype, node_tiledimage_float_10_framerange, node_tiledimage_float_10_frameoffset, node_tiledimage_float_10_frameendaction, node_tiledimage_float_10_out);
+ float node_tiledimage_float_22_out = 0.0;
+ NG_tiledimage_float(node_tiledimage_float_22_file, node_tiledimage_float_22_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_float_22_uvoffset, node_tiledimage_float_22_realworldimagesize, node_tiledimage_float_22_realworldtilesize, node_tiledimage_float_22_filtertype, node_tiledimage_float_22_framerange, node_tiledimage_float_22_frameoffset, node_tiledimage_float_22_frameendaction, node_tiledimage_float_22_out);
+ vector node_tiledimage_vector3_27_out = vector(0.0);
+ NG_tiledimage_vector3(node_tiledimage_vector3_27_file, node_tiledimage_vector3_27_default, geomprop_UV0_out1, node_convert_1_out, node_tiledimage_vector3_27_uvoffset, node_tiledimage_vector3_27_realworldimagesize, node_tiledimage_vector3_27_realworldtilesize, node_tiledimage_vector3_27_filtertype, node_tiledimage_vector3_27_framerange, node_tiledimage_vector3_27_frameoffset, node_tiledimage_vector3_27_frameendaction, node_tiledimage_vector3_27_out);
+ float node_multiply_25_out = node_multiply_25_in1 * node_tiledimage_float_26_out;
+ float node_multiply_20_out = node_multiply_20_in1 * node_tiledimage_float_26_out;
+ color node_multiply_9_out = node_multiply_9_in1 * node_tiledimage_float_7_out;
+ float node_multiply_23_out = node_multiply_23_in1 * node_tiledimage_float_24_out;
+ float node_max_1_out = max(node_tiledimage_float_10_out, node_max_1_in2);
+ vector node_normalmap_3_out = vector(0.0);
+ mx_normalmap(node_tiledimage_vector3_27_out, node_normalmap_3_space, node_normalmap_3_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, node_normalmap_3_out);
+ float node_add_19_out = node_multiply_25_out + node_tiledimage_float_7_out;
+ float node_divide_21_out = node_divide_21_in1 / node_max_1_out;
+ float node_subtract_18_out = node_add_19_out - node_subtract_18_in2;
+ float node_multiply_15_out = node_add_19_out * node_multiply_20_out;
+ float node_multiply_1_out = node_divide_21_out * node_tiledimage_float_22_out;
+ float node_multiply_14_out = node_subtract_18_out * node_multiply_14_in2;
+ color node_combine3_color3_13_out = color(node_multiply_14_out, node_combine3_color3_13_in2, node_multiply_15_out);
+ color node_add_16_out = node_combine3_color3_13_out + node_rgbtohsv_12_out;
+ color node_hsvtorgb_17_out = color(0.0);
+ mx_hsvtorgb_color3(node_add_16_out, node_hsvtorgb_17_out);
+ color node_mix_6_out = mix(node_hsvtorgb_17_out, node_mix_6_fg, node_multiply_23_out);
+ color node_multiply_5_out = node_mix_6_out * node_tiledimage_float_7_out;
+ color node_mix_8_out = mix(node_multiply_9_out, node_multiply_5_out, node_tiledimage_float_10_out);
+ color node_clamp_0_out = clamp(node_mix_8_out, node_clamp_0_low, node_clamp_0_high);
+ surfaceshader N_StandardSurface_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(N_StandardSurface_base, node_clamp_0_out, N_StandardSurface_diffuse_roughness, N_StandardSurface_metalness, N_StandardSurface_specular, N_StandardSurface_specular_color, node_multiply_1_out, N_StandardSurface_specular_IOR, N_StandardSurface_specular_anisotropy, N_StandardSurface_specular_rotation, N_StandardSurface_transmission, N_StandardSurface_transmission_color, N_StandardSurface_transmission_depth, N_StandardSurface_transmission_scatter, N_StandardSurface_transmission_scatter_anisotropy, N_StandardSurface_transmission_dispersion, N_StandardSurface_transmission_extra_roughness, N_StandardSurface_subsurface, N_StandardSurface_subsurface_color, N_StandardSurface_subsurface_radius, N_StandardSurface_subsurface_scale, N_StandardSurface_subsurface_anisotropy, N_StandardSurface_sheen, N_StandardSurface_sheen_color, N_StandardSurface_sheen_roughness, N_StandardSurface_coat, N_StandardSurface_coat_color, N_StandardSurface_coat_roughness, N_StandardSurface_coat_anisotropy, N_StandardSurface_coat_rotation, N_StandardSurface_coat_IOR, geomprop_Nworld_out1, N_StandardSurface_coat_affect_color, N_StandardSurface_coat_affect_roughness, N_StandardSurface_thin_film_thickness, N_StandardSurface_thin_film_IOR, N_StandardSurface_emission, N_StandardSurface_emission_color, N_StandardSurface_opacity, N_StandardSurface_thin_walled, node_normalmap_3_out, geomprop_Tworld_out1, N_StandardSurface_out);
+ MATERIAL M_BrickPattern_out = mx_surfacematerial(N_StandardSurface_out, displacementshader1);
+ out = M_BrickPattern_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Castle_B.glsl.frag b/Materials/Examples/StandardSurface/M_Castle_B.glsl.frag
new file mode 100644
index 0000000000..03fe3343f7
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_B.glsl.frag
@@ -0,0 +1,1854 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform sampler2D diffuse6_file;
+uniform int diffuse6_layer = 0;
+uniform vec3 diffuse6_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int diffuse6_uaddressmode = 2;
+uniform int diffuse6_vaddressmode = 2;
+uniform int diffuse6_filtertype = 1;
+uniform int diffuse6_framerange = 0;
+uniform int diffuse6_frameoffset = 0;
+uniform int diffuse6_frameendaction = 0;
+uniform vec2 diffuse6_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 diffuse6_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D metallic6_file;
+uniform int metallic6_layer = 0;
+uniform float metallic6_default = 0.000000;
+uniform int metallic6_uaddressmode = 2;
+uniform int metallic6_vaddressmode = 2;
+uniform int metallic6_filtertype = 1;
+uniform int metallic6_framerange = 0;
+uniform int metallic6_frameoffset = 0;
+uniform int metallic6_frameendaction = 0;
+uniform vec2 metallic6_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 metallic6_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D roughness6_file;
+uniform int roughness6_layer = 0;
+uniform float roughness6_default = 0.000000;
+uniform int roughness6_uaddressmode = 2;
+uniform int roughness6_vaddressmode = 2;
+uniform int roughness6_filtertype = 1;
+uniform int roughness6_framerange = 0;
+uniform int roughness6_frameoffset = 0;
+uniform int roughness6_frameendaction = 0;
+uniform vec2 roughness6_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 roughness6_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D normal6_file;
+uniform int normal6_layer = 0;
+uniform vec3 normal6_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int normal6_uaddressmode = 2;
+uniform int normal6_vaddressmode = 2;
+uniform int normal6_filtertype = 1;
+uniform int normal6_framerange = 0;
+uniform int normal6_frameoffset = 0;
+uniform int normal6_frameendaction = 0;
+uniform vec2 normal6_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 normal6_uv_offset = vec2(0.000000, 0.000000);
+uniform int mtlxnormalmap8_space = 0;
+uniform float mtlxnormalmap8_scale = 1.000000;
+uniform float Castle_B_base = 1.000000;
+uniform float Castle_B_diffuse_roughness = 0.000000;
+uniform float Castle_B_specular = 1.000000;
+uniform vec3 Castle_B_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Castle_B_specular_IOR = 1.500000;
+uniform float Castle_B_specular_anisotropy = 0.000000;
+uniform float Castle_B_specular_rotation = 0.000000;
+uniform float Castle_B_transmission = 0.000000;
+uniform vec3 Castle_B_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Castle_B_transmission_depth = 0.000000;
+uniform vec3 Castle_B_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float Castle_B_transmission_scatter_anisotropy = 0.000000;
+uniform float Castle_B_transmission_dispersion = 0.000000;
+uniform float Castle_B_transmission_extra_roughness = 0.000000;
+uniform float Castle_B_subsurface = 0.000000;
+uniform float Castle_B_subsurface_scale = 0.003000;
+uniform float Castle_B_subsurface_anisotropy = 0.000000;
+uniform float Castle_B_sheen = 0.000000;
+uniform vec3 Castle_B_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Castle_B_sheen_roughness = 0.300000;
+uniform float Castle_B_coat = 0.000000;
+uniform vec3 Castle_B_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Castle_B_coat_roughness = 0.100000;
+uniform float Castle_B_coat_anisotropy = 0.000000;
+uniform float Castle_B_coat_rotation = 0.000000;
+uniform float Castle_B_coat_IOR = 1.500000;
+uniform float Castle_B_coat_affect_color = 0.000000;
+uniform float Castle_B_coat_affect_roughness = 0.000000;
+uniform float Castle_B_thin_film_thickness = 0.000000;
+uniform float Castle_B_thin_film_IOR = 1.500000;
+uniform float Castle_B_emission = 0.000000;
+uniform vec3 Castle_B_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 Castle_B_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool Castle_B_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+{
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+}
+
+void mx_image_color3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_float(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_vector3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, out vec3 out1)
+{
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+}
+
+void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, out vec3 result)
+{
+ // Decode the normal map.
+ value = (value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 diffuse6_out = vec3(0.0);
+ mx_image_color3(diffuse6_file, diffuse6_layer, diffuse6_default, geomprop_UV0_out1, diffuse6_uaddressmode, diffuse6_vaddressmode, diffuse6_filtertype, diffuse6_framerange, diffuse6_frameoffset, diffuse6_frameendaction, diffuse6_uv_scale, diffuse6_uv_offset, diffuse6_out);
+ float metallic6_out = 0.0;
+ mx_image_float(metallic6_file, metallic6_layer, metallic6_default, geomprop_UV0_out1, metallic6_uaddressmode, metallic6_vaddressmode, metallic6_filtertype, metallic6_framerange, metallic6_frameoffset, metallic6_frameendaction, metallic6_uv_scale, metallic6_uv_offset, metallic6_out);
+ float roughness6_out = 0.0;
+ mx_image_float(roughness6_file, roughness6_layer, roughness6_default, geomprop_UV0_out1, roughness6_uaddressmode, roughness6_vaddressmode, roughness6_filtertype, roughness6_framerange, roughness6_frameoffset, roughness6_frameendaction, roughness6_uv_scale, roughness6_uv_offset, roughness6_out);
+ vec3 normal6_out = vec3(0.0);
+ mx_image_vector3(normal6_file, normal6_layer, normal6_default, geomprop_UV0_out1, normal6_uaddressmode, normal6_vaddressmode, normal6_filtertype, normal6_framerange, normal6_frameoffset, normal6_frameendaction, normal6_uv_scale, normal6_uv_offset, normal6_out);
+ vec3 diffuse6_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse6_out, diffuse6_out_cm_out);
+ vec3 mtlxnormalmap8_out = vec3(0.0);
+ mx_normalmap(normal6_out, mtlxnormalmap8_space, mtlxnormalmap8_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap8_out);
+ surfaceshader Castle_B_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(Castle_B_base, diffuse6_out_cm_out, Castle_B_diffuse_roughness, metallic6_out, Castle_B_specular, Castle_B_specular_color, roughness6_out, Castle_B_specular_IOR, Castle_B_specular_anisotropy, Castle_B_specular_rotation, Castle_B_transmission, Castle_B_transmission_color, Castle_B_transmission_depth, Castle_B_transmission_scatter, Castle_B_transmission_scatter_anisotropy, Castle_B_transmission_dispersion, Castle_B_transmission_extra_roughness, Castle_B_subsurface, diffuse6_out_cm_out, diffuse6_out_cm_out, Castle_B_subsurface_scale, Castle_B_subsurface_anisotropy, Castle_B_sheen, Castle_B_sheen_color, Castle_B_sheen_roughness, Castle_B_coat, Castle_B_coat_color, Castle_B_coat_roughness, Castle_B_coat_anisotropy, Castle_B_coat_rotation, Castle_B_coat_IOR, geomprop_Nworld_out1, Castle_B_coat_affect_color, Castle_B_coat_affect_roughness, Castle_B_thin_film_thickness, Castle_B_thin_film_IOR, Castle_B_emission, Castle_B_emission_color, Castle_B_opacity, Castle_B_thin_walled, mtlxnormalmap8_out, geomprop_Tworld_out1, Castle_B_out);
+ material M_Castle_B_out = Castle_B_out;
+ out1 = vec4(M_Castle_B_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Castle_B.glsl.vert b/Materials/Examples/StandardSurface/M_Castle_B.glsl.vert
new file mode 100644
index 0000000000..60b47d493e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_B.glsl.vert
@@ -0,0 +1,31 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+in vec2 i_texcoord_0;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Castle_B.mdl b/Materials/Examples/StandardSurface/M_Castle_B.mdl
new file mode 100644
index 0000000000..3bc4afe2c5
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_B.mdl
@@ -0,0 +1,234 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+color NG_srgb_texture_to_lin_rec709_color3
+(
+ color in1 = color(0, 0, 0)
+)
+{
+ color bias_out = in1 + 0.055;
+ color linSeg_out = in1 / 12.92;
+ float isAboveR_out = mx::stdlib::mx_ifgreater_float(float3(in1).x, 0.04045, 1, 0);
+ float isAboveG_out = mx::stdlib::mx_ifgreater_float(float3(in1).y, 0.04045, 1, 0);
+ float isAboveB_out = mx::stdlib::mx_ifgreater_float(float3(in1).z, 0.04045, 1, 0);
+ color max_out = math::max(bias_out, 0);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ color scale_out = max_out / 1.055;
+ color powSeg_out = math::pow(scale_out, 2.4);
+ color mix_out = math::lerp(linSeg_out, powSeg_out, isAbove_out);
+ return mix_out;
+}
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material M_Castle_B
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ uniform int geomprop_UV0_index = 0,
+ uniform texture_2d diffuse6_file = texture_2d("/chess_set/castle_black_base_color.jpg", tex::gamma_linear),
+ uniform string diffuse6_layer = "",
+ color diffuse6_default = color(0, 0, 0),
+ uniform mx_addressmode_type diffuse6_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type diffuse6_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type diffuse6_filtertype = mx_filterlookup_type_linear,
+ uniform string diffuse6_framerange = "",
+ uniform int diffuse6_frameoffset = 0,
+ uniform mx_addressmode_type diffuse6_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d metallic6_file = texture_2d("/chess_set/castle_shared_metallic.jpg", tex::gamma_linear),
+ uniform string metallic6_layer = "",
+ float metallic6_default = 0,
+ uniform mx_addressmode_type metallic6_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type metallic6_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type metallic6_filtertype = mx_filterlookup_type_linear,
+ uniform string metallic6_framerange = "",
+ uniform int metallic6_frameoffset = 0,
+ uniform mx_addressmode_type metallic6_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d roughness6_file = texture_2d("/chess_set/castle_shared_roughness.jpg", tex::gamma_linear),
+ uniform string roughness6_layer = "",
+ float roughness6_default = 0,
+ uniform mx_addressmode_type roughness6_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type roughness6_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type roughness6_filtertype = mx_filterlookup_type_linear,
+ uniform string roughness6_framerange = "",
+ uniform int roughness6_frameoffset = 0,
+ uniform mx_addressmode_type roughness6_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d normal6_file = texture_2d("/chess_set/castle_shared_normal.jpg", tex::gamma_linear),
+ uniform string normal6_layer = "",
+ float3 normal6_default = float3(0, 0, 0),
+ uniform mx_addressmode_type normal6_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type normal6_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type normal6_filtertype = mx_filterlookup_type_linear,
+ uniform string normal6_framerange = "",
+ uniform int normal6_frameoffset = 0,
+ uniform mx_addressmode_type normal6_frameendaction = mx_addressmode_type_constant,
+ uniform string mtlxnormalmap8_space = "tangent",
+ float mtlxnormalmap8_scale = 1,
+ float Castle_B_base = 1,
+ float Castle_B_diffuse_roughness = 0,
+ float Castle_B_specular = 1,
+ color Castle_B_specular_color = color(1, 1, 1),
+ uniform float Castle_B_specular_IOR = 1.5,
+ float Castle_B_specular_anisotropy = 0,
+ float Castle_B_specular_rotation = 0,
+ float Castle_B_transmission = 0,
+ color Castle_B_transmission_color = color(1, 1, 1),
+ float Castle_B_transmission_depth = 0,
+ color Castle_B_transmission_scatter = color(0, 0, 0),
+ float Castle_B_transmission_scatter_anisotropy = 0,
+ float Castle_B_transmission_dispersion = 0,
+ float Castle_B_transmission_extra_roughness = 0,
+ float Castle_B_subsurface = 0,
+ float Castle_B_subsurface_scale = 0.003,
+ float Castle_B_subsurface_anisotropy = 0,
+ float Castle_B_sheen = 0,
+ color Castle_B_sheen_color = color(1, 1, 1),
+ float Castle_B_sheen_roughness = 0.3,
+ float Castle_B_coat = 0,
+ color Castle_B_coat_color = color(1, 1, 1),
+ float Castle_B_coat_roughness = 0.1,
+ float Castle_B_coat_anisotropy = 0,
+ float Castle_B_coat_rotation = 0,
+ uniform float Castle_B_coat_IOR = 1.5,
+ float Castle_B_coat_affect_color = 0,
+ float Castle_B_coat_affect_roughness = 0,
+ float Castle_B_thin_film_thickness = 0,
+ float Castle_B_thin_film_IOR = 1.5,
+ float Castle_B_emission = 0,
+ color Castle_B_emission_color = color(1, 1, 1),
+ color Castle_B_opacity = color(1, 1, 1),
+ bool Castle_B_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ float2 geomprop_UV0_out1 = mx::stdlib::mx_texcoord_vector2(mxp_index:geomprop_UV0_index);
+ color diffuse6_out = mx::stdlib::mx_image_color3(diffuse6_file, diffuse6_layer, diffuse6_default, geomprop_UV0_out1, diffuse6_uaddressmode, diffuse6_vaddressmode, diffuse6_filtertype, diffuse6_framerange, diffuse6_frameoffset, diffuse6_frameendaction);
+ float metallic6_out = mx::stdlib::mx_image_float(metallic6_file, metallic6_layer, metallic6_default, geomprop_UV0_out1, metallic6_uaddressmode, metallic6_vaddressmode, metallic6_filtertype, metallic6_framerange, metallic6_frameoffset, metallic6_frameendaction);
+ float roughness6_out = mx::stdlib::mx_image_float(roughness6_file, roughness6_layer, roughness6_default, geomprop_UV0_out1, roughness6_uaddressmode, roughness6_vaddressmode, roughness6_filtertype, roughness6_framerange, roughness6_frameoffset, roughness6_frameendaction);
+ float3 normal6_out = mx::stdlib::mx_image_vector3(normal6_file, normal6_layer, normal6_default, geomprop_UV0_out1, normal6_uaddressmode, normal6_vaddressmode, normal6_filtertype, normal6_framerange, normal6_frameoffset, normal6_frameendaction);
+ color diffuse6_out_cm_out = NG_srgb_texture_to_lin_rec709_color3(diffuse6_out);
+ float3 mtlxnormalmap8_out = mx::stdlib::mx_normalmap(mxp_in:normal6_out, mxp_space:mtlxnormalmap8_space, mxp_scale:mtlxnormalmap8_scale, mxp_normal:geomprop_Nworld_out1, mxp_tangent:geomprop_Tworld_out1);
+ material Castle_B_out = NG_standard_surface_surfaceshader_100(Castle_B_base, diffuse6_out_cm_out, Castle_B_diffuse_roughness, metallic6_out, Castle_B_specular, Castle_B_specular_color, roughness6_out, Castle_B_specular_IOR, Castle_B_specular_anisotropy, Castle_B_specular_rotation, Castle_B_transmission, Castle_B_transmission_color, Castle_B_transmission_depth, Castle_B_transmission_scatter, Castle_B_transmission_scatter_anisotropy, Castle_B_transmission_dispersion, Castle_B_transmission_extra_roughness, Castle_B_subsurface, diffuse6_out_cm_out, diffuse6_out_cm_out, Castle_B_subsurface_scale, Castle_B_subsurface_anisotropy, Castle_B_sheen, Castle_B_sheen_color, Castle_B_sheen_roughness, Castle_B_coat, Castle_B_coat_color, Castle_B_coat_roughness, Castle_B_coat_anisotropy, Castle_B_coat_rotation, Castle_B_coat_IOR, geomprop_Nworld_out1, Castle_B_coat_affect_color, Castle_B_coat_affect_roughness, Castle_B_thin_film_thickness, Castle_B_thin_film_IOR, Castle_B_emission, Castle_B_emission_color, Castle_B_opacity, Castle_B_thin_walled, mtlxnormalmap8_out, geomprop_Tworld_out1);
+ material M_Castle_B_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: Castle_B_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = M_Castle_B_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/M_Castle_B.msl.frag b/Materials/Examples/StandardSurface/M_Castle_B.msl.frag
new file mode 100644
index 0000000000..9d477da9f1
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_B.msl.frag
@@ -0,0 +1,2784 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ int diffuse6_layer;
+ vec3 diffuse6_default;
+ int diffuse6_uaddressmode;
+ int diffuse6_vaddressmode;
+ int diffuse6_filtertype;
+ int diffuse6_framerange;
+ int diffuse6_frameoffset;
+ int diffuse6_frameendaction;
+ vec2 diffuse6_uv_scale;
+ vec2 diffuse6_uv_offset;
+ int metallic6_layer;
+ float metallic6_default;
+ int metallic6_uaddressmode;
+ int metallic6_vaddressmode;
+ int metallic6_filtertype;
+ int metallic6_framerange;
+ int metallic6_frameoffset;
+ int metallic6_frameendaction;
+ vec2 metallic6_uv_scale;
+ vec2 metallic6_uv_offset;
+ int roughness6_layer;
+ float roughness6_default;
+ int roughness6_uaddressmode;
+ int roughness6_vaddressmode;
+ int roughness6_filtertype;
+ int roughness6_framerange;
+ int roughness6_frameoffset;
+ int roughness6_frameendaction;
+ vec2 roughness6_uv_scale;
+ vec2 roughness6_uv_offset;
+ int normal6_layer;
+ vec3 normal6_default;
+ int normal6_uaddressmode;
+ int normal6_vaddressmode;
+ int normal6_filtertype;
+ int normal6_framerange;
+ int normal6_frameoffset;
+ int normal6_frameendaction;
+ vec2 normal6_uv_scale;
+ vec2 normal6_uv_offset;
+ int mtlxnormalmap8_space;
+ float mtlxnormalmap8_scale;
+ float Castle_B_base;
+ float Castle_B_diffuse_roughness;
+ float Castle_B_specular;
+ vec3 Castle_B_specular_color;
+ float Castle_B_specular_IOR;
+ float Castle_B_specular_anisotropy;
+ float Castle_B_specular_rotation;
+ float Castle_B_transmission;
+ vec3 Castle_B_transmission_color;
+ float Castle_B_transmission_depth;
+ vec3 Castle_B_transmission_scatter;
+ float Castle_B_transmission_scatter_anisotropy;
+ float Castle_B_transmission_dispersion;
+ float Castle_B_transmission_extra_roughness;
+ float Castle_B_subsurface;
+ float Castle_B_subsurface_scale;
+ float Castle_B_subsurface_anisotropy;
+ float Castle_B_sheen;
+ vec3 Castle_B_sheen_color;
+ float Castle_B_sheen_roughness;
+ float Castle_B_coat;
+ vec3 Castle_B_coat_color;
+ float Castle_B_coat_roughness;
+ float Castle_B_coat_anisotropy;
+ float Castle_B_coat_rotation;
+ float Castle_B_coat_IOR;
+ float Castle_B_coat_affect_color;
+ float Castle_B_coat_affect_roughness;
+ float Castle_B_thin_film_thickness;
+ float Castle_B_thin_film_IOR;
+ float Castle_B_emission;
+ vec3 Castle_B_emission_color;
+ vec3 Castle_B_opacity;
+ bool Castle_B_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec2 texcoord_0 ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+, MetalTexture diffuse6_file , int diffuse6_layer
+
+ , vec3 diffuse6_default
+
+ , int diffuse6_uaddressmode
+
+ , int diffuse6_vaddressmode
+
+ , int diffuse6_filtertype
+
+ , int diffuse6_framerange
+
+ , int diffuse6_frameoffset
+
+ , int diffuse6_frameendaction
+
+ , vec2 diffuse6_uv_scale
+
+ , vec2 diffuse6_uv_offset
+
+, MetalTexture metallic6_file , int metallic6_layer
+
+ , float metallic6_default
+
+ , int metallic6_uaddressmode
+
+ , int metallic6_vaddressmode
+
+ , int metallic6_filtertype
+
+ , int metallic6_framerange
+
+ , int metallic6_frameoffset
+
+ , int metallic6_frameendaction
+
+ , vec2 metallic6_uv_scale
+
+ , vec2 metallic6_uv_offset
+
+, MetalTexture roughness6_file , int roughness6_layer
+
+ , float roughness6_default
+
+ , int roughness6_uaddressmode
+
+ , int roughness6_vaddressmode
+
+ , int roughness6_filtertype
+
+ , int roughness6_framerange
+
+ , int roughness6_frameoffset
+
+ , int roughness6_frameendaction
+
+ , vec2 roughness6_uv_scale
+
+ , vec2 roughness6_uv_offset
+
+, MetalTexture normal6_file , int normal6_layer
+
+ , vec3 normal6_default
+
+ , int normal6_uaddressmode
+
+ , int normal6_vaddressmode
+
+ , int normal6_filtertype
+
+ , int normal6_framerange
+
+ , int normal6_frameoffset
+
+ , int normal6_frameendaction
+
+ , vec2 normal6_uv_scale
+
+ , vec2 normal6_uv_offset
+
+ , int mtlxnormalmap8_space
+
+ , float mtlxnormalmap8_scale
+
+ , float Castle_B_base
+
+ , float Castle_B_diffuse_roughness
+
+ , float Castle_B_specular
+
+ , vec3 Castle_B_specular_color
+
+ , float Castle_B_specular_IOR
+
+ , float Castle_B_specular_anisotropy
+
+ , float Castle_B_specular_rotation
+
+ , float Castle_B_transmission
+
+ , vec3 Castle_B_transmission_color
+
+ , float Castle_B_transmission_depth
+
+ , vec3 Castle_B_transmission_scatter
+
+ , float Castle_B_transmission_scatter_anisotropy
+
+ , float Castle_B_transmission_dispersion
+
+ , float Castle_B_transmission_extra_roughness
+
+ , float Castle_B_subsurface
+
+ , float Castle_B_subsurface_scale
+
+ , float Castle_B_subsurface_anisotropy
+
+ , float Castle_B_sheen
+
+ , vec3 Castle_B_sheen_color
+
+ , float Castle_B_sheen_roughness
+
+ , float Castle_B_coat
+
+ , vec3 Castle_B_coat_color
+
+ , float Castle_B_coat_roughness
+
+ , float Castle_B_coat_anisotropy
+
+ , float Castle_B_coat_rotation
+
+ , float Castle_B_coat_IOR
+
+ , float Castle_B_coat_affect_color
+
+ , float Castle_B_coat_affect_roughness
+
+ , float Castle_B_thin_film_thickness
+
+ , float Castle_B_thin_film_IOR
+
+ , float Castle_B_emission
+
+ , vec3 Castle_B_emission_color
+
+ , vec3 Castle_B_opacity
+
+ , bool Castle_B_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+, diffuse6_file(diffuse6_file)
+ , diffuse6_layer(diffuse6_layer)
+
+ , diffuse6_default(diffuse6_default)
+
+ , diffuse6_uaddressmode(diffuse6_uaddressmode)
+
+ , diffuse6_vaddressmode(diffuse6_vaddressmode)
+
+ , diffuse6_filtertype(diffuse6_filtertype)
+
+ , diffuse6_framerange(diffuse6_framerange)
+
+ , diffuse6_frameoffset(diffuse6_frameoffset)
+
+ , diffuse6_frameendaction(diffuse6_frameendaction)
+
+ , diffuse6_uv_scale(diffuse6_uv_scale)
+
+ , diffuse6_uv_offset(diffuse6_uv_offset)
+
+, metallic6_file(metallic6_file)
+ , metallic6_layer(metallic6_layer)
+
+ , metallic6_default(metallic6_default)
+
+ , metallic6_uaddressmode(metallic6_uaddressmode)
+
+ , metallic6_vaddressmode(metallic6_vaddressmode)
+
+ , metallic6_filtertype(metallic6_filtertype)
+
+ , metallic6_framerange(metallic6_framerange)
+
+ , metallic6_frameoffset(metallic6_frameoffset)
+
+ , metallic6_frameendaction(metallic6_frameendaction)
+
+ , metallic6_uv_scale(metallic6_uv_scale)
+
+ , metallic6_uv_offset(metallic6_uv_offset)
+
+, roughness6_file(roughness6_file)
+ , roughness6_layer(roughness6_layer)
+
+ , roughness6_default(roughness6_default)
+
+ , roughness6_uaddressmode(roughness6_uaddressmode)
+
+ , roughness6_vaddressmode(roughness6_vaddressmode)
+
+ , roughness6_filtertype(roughness6_filtertype)
+
+ , roughness6_framerange(roughness6_framerange)
+
+ , roughness6_frameoffset(roughness6_frameoffset)
+
+ , roughness6_frameendaction(roughness6_frameendaction)
+
+ , roughness6_uv_scale(roughness6_uv_scale)
+
+ , roughness6_uv_offset(roughness6_uv_offset)
+
+, normal6_file(normal6_file)
+ , normal6_layer(normal6_layer)
+
+ , normal6_default(normal6_default)
+
+ , normal6_uaddressmode(normal6_uaddressmode)
+
+ , normal6_vaddressmode(normal6_vaddressmode)
+
+ , normal6_filtertype(normal6_filtertype)
+
+ , normal6_framerange(normal6_framerange)
+
+ , normal6_frameoffset(normal6_frameoffset)
+
+ , normal6_frameendaction(normal6_frameendaction)
+
+ , normal6_uv_scale(normal6_uv_scale)
+
+ , normal6_uv_offset(normal6_uv_offset)
+
+ , mtlxnormalmap8_space(mtlxnormalmap8_space)
+
+ , mtlxnormalmap8_scale(mtlxnormalmap8_scale)
+
+ , Castle_B_base(Castle_B_base)
+
+ , Castle_B_diffuse_roughness(Castle_B_diffuse_roughness)
+
+ , Castle_B_specular(Castle_B_specular)
+
+ , Castle_B_specular_color(Castle_B_specular_color)
+
+ , Castle_B_specular_IOR(Castle_B_specular_IOR)
+
+ , Castle_B_specular_anisotropy(Castle_B_specular_anisotropy)
+
+ , Castle_B_specular_rotation(Castle_B_specular_rotation)
+
+ , Castle_B_transmission(Castle_B_transmission)
+
+ , Castle_B_transmission_color(Castle_B_transmission_color)
+
+ , Castle_B_transmission_depth(Castle_B_transmission_depth)
+
+ , Castle_B_transmission_scatter(Castle_B_transmission_scatter)
+
+ , Castle_B_transmission_scatter_anisotropy(Castle_B_transmission_scatter_anisotropy)
+
+ , Castle_B_transmission_dispersion(Castle_B_transmission_dispersion)
+
+ , Castle_B_transmission_extra_roughness(Castle_B_transmission_extra_roughness)
+
+ , Castle_B_subsurface(Castle_B_subsurface)
+
+ , Castle_B_subsurface_scale(Castle_B_subsurface_scale)
+
+ , Castle_B_subsurface_anisotropy(Castle_B_subsurface_anisotropy)
+
+ , Castle_B_sheen(Castle_B_sheen)
+
+ , Castle_B_sheen_color(Castle_B_sheen_color)
+
+ , Castle_B_sheen_roughness(Castle_B_sheen_roughness)
+
+ , Castle_B_coat(Castle_B_coat)
+
+ , Castle_B_coat_color(Castle_B_coat_color)
+
+ , Castle_B_coat_roughness(Castle_B_coat_roughness)
+
+ , Castle_B_coat_anisotropy(Castle_B_coat_anisotropy)
+
+ , Castle_B_coat_rotation(Castle_B_coat_rotation)
+
+ , Castle_B_coat_IOR(Castle_B_coat_IOR)
+
+ , Castle_B_coat_affect_color(Castle_B_coat_affect_color)
+
+ , Castle_B_coat_affect_roughness(Castle_B_coat_affect_roughness)
+
+ , Castle_B_thin_film_thickness(Castle_B_thin_film_thickness)
+
+ , Castle_B_thin_film_IOR(Castle_B_thin_film_IOR)
+
+ , Castle_B_emission(Castle_B_emission)
+
+ , Castle_B_emission_color(Castle_B_emission_color)
+
+ , Castle_B_opacity(Castle_B_opacity)
+
+ , Castle_B_thin_walled(Castle_B_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+MetalTexture diffuse6_file;
+ int diffuse6_layer;
+
+
+ vec3 diffuse6_default;
+
+
+ int diffuse6_uaddressmode;
+
+
+ int diffuse6_vaddressmode;
+
+
+ int diffuse6_filtertype;
+
+
+ int diffuse6_framerange;
+
+
+ int diffuse6_frameoffset;
+
+
+ int diffuse6_frameendaction;
+
+
+ vec2 diffuse6_uv_scale;
+
+
+ vec2 diffuse6_uv_offset;
+
+
+MetalTexture metallic6_file;
+ int metallic6_layer;
+
+
+ float metallic6_default;
+
+
+ int metallic6_uaddressmode;
+
+
+ int metallic6_vaddressmode;
+
+
+ int metallic6_filtertype;
+
+
+ int metallic6_framerange;
+
+
+ int metallic6_frameoffset;
+
+
+ int metallic6_frameendaction;
+
+
+ vec2 metallic6_uv_scale;
+
+
+ vec2 metallic6_uv_offset;
+
+
+MetalTexture roughness6_file;
+ int roughness6_layer;
+
+
+ float roughness6_default;
+
+
+ int roughness6_uaddressmode;
+
+
+ int roughness6_vaddressmode;
+
+
+ int roughness6_filtertype;
+
+
+ int roughness6_framerange;
+
+
+ int roughness6_frameoffset;
+
+
+ int roughness6_frameendaction;
+
+
+ vec2 roughness6_uv_scale;
+
+
+ vec2 roughness6_uv_offset;
+
+
+MetalTexture normal6_file;
+ int normal6_layer;
+
+
+ vec3 normal6_default;
+
+
+ int normal6_uaddressmode;
+
+
+ int normal6_vaddressmode;
+
+
+ int normal6_filtertype;
+
+
+ int normal6_framerange;
+
+
+ int normal6_frameoffset;
+
+
+ int normal6_frameendaction;
+
+
+ vec2 normal6_uv_scale;
+
+
+ vec2 normal6_uv_offset;
+
+
+ int mtlxnormalmap8_space;
+
+
+ float mtlxnormalmap8_scale;
+
+
+ float Castle_B_base;
+
+
+ float Castle_B_diffuse_roughness;
+
+
+ float Castle_B_specular;
+
+
+ vec3 Castle_B_specular_color;
+
+
+ float Castle_B_specular_IOR;
+
+
+ float Castle_B_specular_anisotropy;
+
+
+ float Castle_B_specular_rotation;
+
+
+ float Castle_B_transmission;
+
+
+ vec3 Castle_B_transmission_color;
+
+
+ float Castle_B_transmission_depth;
+
+
+ vec3 Castle_B_transmission_scatter;
+
+
+ float Castle_B_transmission_scatter_anisotropy;
+
+
+ float Castle_B_transmission_dispersion;
+
+
+ float Castle_B_transmission_extra_roughness;
+
+
+ float Castle_B_subsurface;
+
+
+ float Castle_B_subsurface_scale;
+
+
+ float Castle_B_subsurface_anisotropy;
+
+
+ float Castle_B_sheen;
+
+
+ vec3 Castle_B_sheen_color;
+
+
+ float Castle_B_sheen_roughness;
+
+
+ float Castle_B_coat;
+
+
+ vec3 Castle_B_coat_color;
+
+
+ float Castle_B_coat_roughness;
+
+
+ float Castle_B_coat_anisotropy;
+
+
+ float Castle_B_coat_rotation;
+
+
+ float Castle_B_coat_IOR;
+
+
+ float Castle_B_coat_affect_color;
+
+
+ float Castle_B_coat_affect_roughness;
+
+
+ float Castle_B_thin_film_thickness;
+
+
+ float Castle_B_thin_film_IOR;
+
+
+ float Castle_B_emission;
+
+
+ vec3 Castle_B_emission_color;
+
+
+ vec3 Castle_B_opacity;
+
+
+ bool Castle_B_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+ {
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+ }
+
+ void mx_image_color3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_float(MetalTexture tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread float& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_vector3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+ void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, thread vec3& out1)
+ {
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+ }
+
+ void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, thread vec3& result)
+ {
+ // Decode the normal map.
+ value = all(value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 diffuse6_out = vec3(0.0);
+ mx_image_color3(diffuse6_file, diffuse6_layer, diffuse6_default, geomprop_UV0_out1, diffuse6_uaddressmode, diffuse6_vaddressmode, diffuse6_filtertype, diffuse6_framerange, diffuse6_frameoffset, diffuse6_frameendaction, diffuse6_uv_scale, diffuse6_uv_offset, diffuse6_out);
+ float metallic6_out = 0.0;
+ mx_image_float(metallic6_file, metallic6_layer, metallic6_default, geomprop_UV0_out1, metallic6_uaddressmode, metallic6_vaddressmode, metallic6_filtertype, metallic6_framerange, metallic6_frameoffset, metallic6_frameendaction, metallic6_uv_scale, metallic6_uv_offset, metallic6_out);
+ float roughness6_out = 0.0;
+ mx_image_float(roughness6_file, roughness6_layer, roughness6_default, geomprop_UV0_out1, roughness6_uaddressmode, roughness6_vaddressmode, roughness6_filtertype, roughness6_framerange, roughness6_frameoffset, roughness6_frameendaction, roughness6_uv_scale, roughness6_uv_offset, roughness6_out);
+ vec3 normal6_out = vec3(0.0);
+ mx_image_vector3(normal6_file, normal6_layer, normal6_default, geomprop_UV0_out1, normal6_uaddressmode, normal6_vaddressmode, normal6_filtertype, normal6_framerange, normal6_frameoffset, normal6_frameendaction, normal6_uv_scale, normal6_uv_offset, normal6_out);
+ vec3 diffuse6_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse6_out, diffuse6_out_cm_out);
+ vec3 mtlxnormalmap8_out = vec3(0.0);
+ mx_normalmap(normal6_out, mtlxnormalmap8_space, mtlxnormalmap8_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap8_out);
+ surfaceshader Castle_B_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(Castle_B_base, diffuse6_out_cm_out, Castle_B_diffuse_roughness, metallic6_out, Castle_B_specular, Castle_B_specular_color, roughness6_out, Castle_B_specular_IOR, Castle_B_specular_anisotropy, Castle_B_specular_rotation, Castle_B_transmission, Castle_B_transmission_color, Castle_B_transmission_depth, Castle_B_transmission_scatter, Castle_B_transmission_scatter_anisotropy, Castle_B_transmission_dispersion, Castle_B_transmission_extra_roughness, Castle_B_subsurface, diffuse6_out_cm_out, diffuse6_out_cm_out, Castle_B_subsurface_scale, Castle_B_subsurface_anisotropy, Castle_B_sheen, Castle_B_sheen_color, Castle_B_sheen_roughness, Castle_B_coat, Castle_B_coat_color, Castle_B_coat_roughness, Castle_B_coat_anisotropy, Castle_B_coat_rotation, Castle_B_coat_IOR, geomprop_Nworld_out1, Castle_B_coat_affect_color, Castle_B_coat_affect_roughness, Castle_B_thin_film_thickness, Castle_B_thin_film_IOR, Castle_B_emission, Castle_B_emission_color, Castle_B_opacity, Castle_B_thin_walled, mtlxnormalmap8_out, geomprop_Tworld_out1, Castle_B_out);
+ material M_Castle_B_out = Castle_B_out;
+ out1 = float4(M_Castle_B_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], texture2d diffuse6_file_tex [[texture(0)]], sampler diffuse6_file_sampler [[sampler(0)]]
+, texture2d metallic6_file_tex [[texture(1)]], sampler metallic6_file_sampler [[sampler(1)]]
+, texture2d roughness6_file_tex [[texture(2)]], sampler roughness6_file_sampler [[sampler(2)]]
+, texture2d normal6_file_tex [[texture(3)]], sampler normal6_file_sampler [[sampler(3)]]
+, constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(4)]], sampler u_envRadiance_sampler [[sampler(4)]]
+, texture2d u_envIrradiance_tex [[texture(5)]], sampler u_envIrradiance_sampler [[sampler(5)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+, MetalTexture {
+diffuse6_file_tex, diffuse6_file_sampler }
+ , u_pub.diffuse6_layer
+ , u_pub.diffuse6_default
+ , u_pub.diffuse6_uaddressmode
+ , u_pub.diffuse6_vaddressmode
+ , u_pub.diffuse6_filtertype
+ , u_pub.diffuse6_framerange
+ , u_pub.diffuse6_frameoffset
+ , u_pub.diffuse6_frameendaction
+ , u_pub.diffuse6_uv_scale
+ , u_pub.diffuse6_uv_offset
+, MetalTexture {
+metallic6_file_tex, metallic6_file_sampler }
+ , u_pub.metallic6_layer
+ , u_pub.metallic6_default
+ , u_pub.metallic6_uaddressmode
+ , u_pub.metallic6_vaddressmode
+ , u_pub.metallic6_filtertype
+ , u_pub.metallic6_framerange
+ , u_pub.metallic6_frameoffset
+ , u_pub.metallic6_frameendaction
+ , u_pub.metallic6_uv_scale
+ , u_pub.metallic6_uv_offset
+, MetalTexture {
+roughness6_file_tex, roughness6_file_sampler }
+ , u_pub.roughness6_layer
+ , u_pub.roughness6_default
+ , u_pub.roughness6_uaddressmode
+ , u_pub.roughness6_vaddressmode
+ , u_pub.roughness6_filtertype
+ , u_pub.roughness6_framerange
+ , u_pub.roughness6_frameoffset
+ , u_pub.roughness6_frameendaction
+ , u_pub.roughness6_uv_scale
+ , u_pub.roughness6_uv_offset
+, MetalTexture {
+normal6_file_tex, normal6_file_sampler }
+ , u_pub.normal6_layer
+ , u_pub.normal6_default
+ , u_pub.normal6_uaddressmode
+ , u_pub.normal6_vaddressmode
+ , u_pub.normal6_filtertype
+ , u_pub.normal6_framerange
+ , u_pub.normal6_frameoffset
+ , u_pub.normal6_frameendaction
+ , u_pub.normal6_uv_scale
+ , u_pub.normal6_uv_offset
+ , u_pub.mtlxnormalmap8_space
+ , u_pub.mtlxnormalmap8_scale
+ , u_pub.Castle_B_base
+ , u_pub.Castle_B_diffuse_roughness
+ , u_pub.Castle_B_specular
+ , u_pub.Castle_B_specular_color
+ , u_pub.Castle_B_specular_IOR
+ , u_pub.Castle_B_specular_anisotropy
+ , u_pub.Castle_B_specular_rotation
+ , u_pub.Castle_B_transmission
+ , u_pub.Castle_B_transmission_color
+ , u_pub.Castle_B_transmission_depth
+ , u_pub.Castle_B_transmission_scatter
+ , u_pub.Castle_B_transmission_scatter_anisotropy
+ , u_pub.Castle_B_transmission_dispersion
+ , u_pub.Castle_B_transmission_extra_roughness
+ , u_pub.Castle_B_subsurface
+ , u_pub.Castle_B_subsurface_scale
+ , u_pub.Castle_B_subsurface_anisotropy
+ , u_pub.Castle_B_sheen
+ , u_pub.Castle_B_sheen_color
+ , u_pub.Castle_B_sheen_roughness
+ , u_pub.Castle_B_coat
+ , u_pub.Castle_B_coat_color
+ , u_pub.Castle_B_coat_roughness
+ , u_pub.Castle_B_coat_anisotropy
+ , u_pub.Castle_B_coat_rotation
+ , u_pub.Castle_B_coat_IOR
+ , u_pub.Castle_B_coat_affect_color
+ , u_pub.Castle_B_coat_affect_roughness
+ , u_pub.Castle_B_thin_film_thickness
+ , u_pub.Castle_B_thin_film_IOR
+ , u_pub.Castle_B_emission
+ , u_pub.Castle_B_emission_color
+ , u_pub.Castle_B_opacity
+ , u_pub.Castle_B_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Castle_B.msl.vert b/Materials/Examples/StandardSurface/M_Castle_B.msl.vert
new file mode 100644
index 0000000000..17376f7eee
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_B.msl.vert
@@ -0,0 +1,124 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+ vec2 i_texcoord_0 [[attribute(3)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+, vec2 i_texcoord_0
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+, i_texcoord_0(i_texcoord_0)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ vec2 i_texcoord_0;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'geomprop_UV0'. Function already called in this scope.
+ // Omitted node 'diffuse6'. Function already called in this scope.
+ // Omitted node 'metallic6'. Function already called in this scope.
+ // Omitted node 'roughness6'. Function already called in this scope.
+ // Omitted node 'normal6'. Function already called in this scope.
+ // Omitted node 'diffuse6_out_cm'. Function already called in this scope.
+ // Omitted node 'mtlxnormalmap8'. Function already called in this scope.
+ // Omitted node 'Castle_B'. Function already called in this scope.
+ // Omitted node 'M_Castle_B'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(4) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent, i_vs.i_texcoord_0 , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Castle_B.osl b/Materials/Examples/StandardSurface/M_Castle_B.osl
new file mode 100644
index 0000000000..1d6b22d005
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_B.osl
@@ -0,0 +1,815 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+vector2 mx_transform_uv(vector2 texcoord)
+{
+ return texcoord;
+}
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode );
+}
+
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+ out = rgb[0];
+}
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(color in, output color out)
+{
+ float bias_in2_tmp = 0.055;
+ color bias_out = in + bias_in2_tmp;
+ float linSeg_in2_tmp = 12.92;
+ color linSeg_out = in / linSeg_in2_tmp;
+ float isAboveR_value2_tmp = 0.04045;
+ float isAboveR_in1_tmp = 1;
+ float isAboveR_in2_tmp = 0;
+ float isAboveR_out = mx_ternary(in[0] > isAboveR_value2_tmp, isAboveR_in1_tmp, isAboveR_in2_tmp);
+ float isAboveG_value2_tmp = 0.04045;
+ float isAboveG_in1_tmp = 1;
+ float isAboveG_in2_tmp = 0;
+ float isAboveG_out = mx_ternary(in[1] > isAboveG_value2_tmp, isAboveG_in1_tmp, isAboveG_in2_tmp);
+ float isAboveB_value2_tmp = 0.04045;
+ float isAboveB_in1_tmp = 1;
+ float isAboveB_in2_tmp = 0;
+ float isAboveB_out = mx_ternary(in[2] > isAboveB_value2_tmp, isAboveB_in1_tmp, isAboveB_in2_tmp);
+ float max_in2_tmp = 0;
+ color max_out = max(bias_out, max_in2_tmp);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ float scale_in2_tmp = 1.055;
+ color scale_out = max_out / scale_in2_tmp;
+ float powSeg_in2_tmp = 2.4;
+ color powSeg_out = pow(scale_out, powSeg_in2_tmp);
+ color mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out = mix_out;
+}
+
+void mx_normalmap(vector value, string map_space, float normal_scale, vector N, vector U, output vector result)
+{
+ // Tangent space
+ if (map_space == "tangent")
+ {
+ vector v = value * 2.0 - 1.0;
+ vector T = normalize(U - dot(U, N) * N);
+ vector B = normalize(cross(N, T));
+ result = normalize(T * v[0] * normal_scale + B * v[1] * normal_scale + N * v[2]);
+ }
+ // Object space
+ else
+ {
+ vector n = value * 2.0 - 1.0;
+ result = normalize(n);
+ }
+}
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader M_Castle_B
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "M_Castle_B"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ int geomprop_UV0_index = 0
+ [[
+ string widget = "number"
+ ]],
+ textureresource diffuse6_file = {"chess_set/castle_black_base_color.jpg", "srgb_texture"}
+ [[
+ string widget = "filename"
+ ]],
+ string diffuse6_layer = "",
+ color diffuse6_default = color(0, 0, 0),
+ string diffuse6_uaddressmode = "periodic",
+ string diffuse6_vaddressmode = "periodic",
+ string diffuse6_filtertype = "linear",
+ string diffuse6_framerange = "",
+ int diffuse6_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string diffuse6_frameendaction = "constant",
+ textureresource metallic6_file = {"chess_set/castle_shared_metallic.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string metallic6_layer = "",
+ float metallic6_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string metallic6_uaddressmode = "periodic",
+ string metallic6_vaddressmode = "periodic",
+ string metallic6_filtertype = "linear",
+ string metallic6_framerange = "",
+ int metallic6_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string metallic6_frameendaction = "constant",
+ textureresource roughness6_file = {"chess_set/castle_shared_roughness.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string roughness6_layer = "",
+ float roughness6_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string roughness6_uaddressmode = "periodic",
+ string roughness6_vaddressmode = "periodic",
+ string roughness6_filtertype = "linear",
+ string roughness6_framerange = "",
+ int roughness6_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string roughness6_frameendaction = "constant",
+ textureresource normal6_file = {"chess_set/castle_shared_normal.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string normal6_layer = "",
+ vector normal6_default = vector(0, 0, 0),
+ string normal6_uaddressmode = "periodic",
+ string normal6_vaddressmode = "periodic",
+ string normal6_filtertype = "linear",
+ string normal6_framerange = "",
+ int normal6_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string normal6_frameendaction = "constant",
+ string mtlxnormalmap8_space = "tangent",
+ float mtlxnormalmap8_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_base = 1
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color Castle_B_specular_color = color(1, 1, 1),
+ float Castle_B_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color Castle_B_transmission_color = color(1, 1, 1),
+ float Castle_B_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color Castle_B_transmission_scatter = color(0, 0, 0),
+ float Castle_B_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_subsurface_scale = 0.003
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color Castle_B_sheen_color = color(1, 1, 1),
+ float Castle_B_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color Castle_B_coat_color = color(1, 1, 1),
+ float Castle_B_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Castle_B_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color Castle_B_emission_color = color(1, 1, 1),
+ color Castle_B_opacity = color(1, 1, 1),
+ int Castle_B_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ vector2 geomprop_UV0_out1 = vector2(u,v);
+ color diffuse6_out = color(0.0);
+ mx_image_color3(diffuse6_file, diffuse6_layer, diffuse6_default, geomprop_UV0_out1, diffuse6_uaddressmode, diffuse6_vaddressmode, diffuse6_filtertype, diffuse6_framerange, diffuse6_frameoffset, diffuse6_frameendaction, diffuse6_out);
+ float metallic6_out = 0.0;
+ mx_image_float(metallic6_file, metallic6_layer, metallic6_default, geomprop_UV0_out1, metallic6_uaddressmode, metallic6_vaddressmode, metallic6_filtertype, metallic6_framerange, metallic6_frameoffset, metallic6_frameendaction, metallic6_out);
+ float roughness6_out = 0.0;
+ mx_image_float(roughness6_file, roughness6_layer, roughness6_default, geomprop_UV0_out1, roughness6_uaddressmode, roughness6_vaddressmode, roughness6_filtertype, roughness6_framerange, roughness6_frameoffset, roughness6_frameendaction, roughness6_out);
+ vector normal6_out = vector(0.0);
+ mx_image_vector3(normal6_file, normal6_layer, normal6_default, geomprop_UV0_out1, normal6_uaddressmode, normal6_vaddressmode, normal6_filtertype, normal6_framerange, normal6_frameoffset, normal6_frameendaction, normal6_out);
+ color diffuse6_out_cm_out = color(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse6_out, diffuse6_out_cm_out);
+ vector mtlxnormalmap8_out = vector(0.0);
+ mx_normalmap(normal6_out, mtlxnormalmap8_space, mtlxnormalmap8_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap8_out);
+ surfaceshader Castle_B_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(Castle_B_base, diffuse6_out_cm_out, Castle_B_diffuse_roughness, metallic6_out, Castle_B_specular, Castle_B_specular_color, roughness6_out, Castle_B_specular_IOR, Castle_B_specular_anisotropy, Castle_B_specular_rotation, Castle_B_transmission, Castle_B_transmission_color, Castle_B_transmission_depth, Castle_B_transmission_scatter, Castle_B_transmission_scatter_anisotropy, Castle_B_transmission_dispersion, Castle_B_transmission_extra_roughness, Castle_B_subsurface, diffuse6_out_cm_out, diffuse6_out_cm_out, Castle_B_subsurface_scale, Castle_B_subsurface_anisotropy, Castle_B_sheen, Castle_B_sheen_color, Castle_B_sheen_roughness, Castle_B_coat, Castle_B_coat_color, Castle_B_coat_roughness, Castle_B_coat_anisotropy, Castle_B_coat_rotation, Castle_B_coat_IOR, geomprop_Nworld_out1, Castle_B_coat_affect_color, Castle_B_coat_affect_roughness, Castle_B_thin_film_thickness, Castle_B_thin_film_IOR, Castle_B_emission, Castle_B_emission_color, Castle_B_opacity, Castle_B_thin_walled, mtlxnormalmap8_out, geomprop_Tworld_out1, Castle_B_out);
+ MATERIAL M_Castle_B_out = mx_surfacematerial(Castle_B_out, displacementshader1);
+ out = M_Castle_B_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Castle_W.glsl.frag b/Materials/Examples/StandardSurface/M_Castle_W.glsl.frag
new file mode 100644
index 0000000000..c16f80ad45
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_W.glsl.frag
@@ -0,0 +1,1854 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform sampler2D diffuse7_file;
+uniform int diffuse7_layer = 0;
+uniform vec3 diffuse7_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int diffuse7_uaddressmode = 2;
+uniform int diffuse7_vaddressmode = 2;
+uniform int diffuse7_filtertype = 1;
+uniform int diffuse7_framerange = 0;
+uniform int diffuse7_frameoffset = 0;
+uniform int diffuse7_frameendaction = 0;
+uniform vec2 diffuse7_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 diffuse7_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D metallic7_file;
+uniform int metallic7_layer = 0;
+uniform float metallic7_default = 0.000000;
+uniform int metallic7_uaddressmode = 2;
+uniform int metallic7_vaddressmode = 2;
+uniform int metallic7_filtertype = 1;
+uniform int metallic7_framerange = 0;
+uniform int metallic7_frameoffset = 0;
+uniform int metallic7_frameendaction = 0;
+uniform vec2 metallic7_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 metallic7_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D roughness7_file;
+uniform int roughness7_layer = 0;
+uniform float roughness7_default = 0.000000;
+uniform int roughness7_uaddressmode = 2;
+uniform int roughness7_vaddressmode = 2;
+uniform int roughness7_filtertype = 1;
+uniform int roughness7_framerange = 0;
+uniform int roughness7_frameoffset = 0;
+uniform int roughness7_frameendaction = 0;
+uniform vec2 roughness7_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 roughness7_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D normal7_file;
+uniform int normal7_layer = 0;
+uniform vec3 normal7_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int normal7_uaddressmode = 2;
+uniform int normal7_vaddressmode = 2;
+uniform int normal7_filtertype = 1;
+uniform int normal7_framerange = 0;
+uniform int normal7_frameoffset = 0;
+uniform int normal7_frameendaction = 0;
+uniform vec2 normal7_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 normal7_uv_offset = vec2(0.000000, 0.000000);
+uniform int mtlxnormalmap9_space = 0;
+uniform float mtlxnormalmap9_scale = 1.000000;
+uniform float Castle_W_base = 1.000000;
+uniform float Castle_W_diffuse_roughness = 0.000000;
+uniform float Castle_W_specular = 1.000000;
+uniform vec3 Castle_W_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Castle_W_specular_IOR = 1.500000;
+uniform float Castle_W_specular_anisotropy = 0.000000;
+uniform float Castle_W_specular_rotation = 0.000000;
+uniform float Castle_W_transmission = 0.000000;
+uniform vec3 Castle_W_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Castle_W_transmission_depth = 0.000000;
+uniform vec3 Castle_W_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float Castle_W_transmission_scatter_anisotropy = 0.000000;
+uniform float Castle_W_transmission_dispersion = 0.000000;
+uniform float Castle_W_transmission_extra_roughness = 0.000000;
+uniform float Castle_W_subsurface = 0.000000;
+uniform float Castle_W_subsurface_scale = 0.003000;
+uniform float Castle_W_subsurface_anisotropy = 0.000000;
+uniform float Castle_W_sheen = 0.000000;
+uniform vec3 Castle_W_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Castle_W_sheen_roughness = 0.300000;
+uniform float Castle_W_coat = 0.000000;
+uniform vec3 Castle_W_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Castle_W_coat_roughness = 0.100000;
+uniform float Castle_W_coat_anisotropy = 0.000000;
+uniform float Castle_W_coat_rotation = 0.000000;
+uniform float Castle_W_coat_IOR = 1.500000;
+uniform float Castle_W_coat_affect_color = 0.000000;
+uniform float Castle_W_coat_affect_roughness = 0.000000;
+uniform float Castle_W_thin_film_thickness = 0.000000;
+uniform float Castle_W_thin_film_IOR = 1.500000;
+uniform float Castle_W_emission = 0.000000;
+uniform vec3 Castle_W_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 Castle_W_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool Castle_W_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+{
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+}
+
+void mx_image_color3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_float(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_vector3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, out vec3 out1)
+{
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+}
+
+void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, out vec3 result)
+{
+ // Decode the normal map.
+ value = (value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 diffuse7_out = vec3(0.0);
+ mx_image_color3(diffuse7_file, diffuse7_layer, diffuse7_default, geomprop_UV0_out1, diffuse7_uaddressmode, diffuse7_vaddressmode, diffuse7_filtertype, diffuse7_framerange, diffuse7_frameoffset, diffuse7_frameendaction, diffuse7_uv_scale, diffuse7_uv_offset, diffuse7_out);
+ float metallic7_out = 0.0;
+ mx_image_float(metallic7_file, metallic7_layer, metallic7_default, geomprop_UV0_out1, metallic7_uaddressmode, metallic7_vaddressmode, metallic7_filtertype, metallic7_framerange, metallic7_frameoffset, metallic7_frameendaction, metallic7_uv_scale, metallic7_uv_offset, metallic7_out);
+ float roughness7_out = 0.0;
+ mx_image_float(roughness7_file, roughness7_layer, roughness7_default, geomprop_UV0_out1, roughness7_uaddressmode, roughness7_vaddressmode, roughness7_filtertype, roughness7_framerange, roughness7_frameoffset, roughness7_frameendaction, roughness7_uv_scale, roughness7_uv_offset, roughness7_out);
+ vec3 normal7_out = vec3(0.0);
+ mx_image_vector3(normal7_file, normal7_layer, normal7_default, geomprop_UV0_out1, normal7_uaddressmode, normal7_vaddressmode, normal7_filtertype, normal7_framerange, normal7_frameoffset, normal7_frameendaction, normal7_uv_scale, normal7_uv_offset, normal7_out);
+ vec3 diffuse7_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse7_out, diffuse7_out_cm_out);
+ vec3 mtlxnormalmap9_out = vec3(0.0);
+ mx_normalmap(normal7_out, mtlxnormalmap9_space, mtlxnormalmap9_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap9_out);
+ surfaceshader Castle_W_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(Castle_W_base, diffuse7_out_cm_out, Castle_W_diffuse_roughness, metallic7_out, Castle_W_specular, Castle_W_specular_color, roughness7_out, Castle_W_specular_IOR, Castle_W_specular_anisotropy, Castle_W_specular_rotation, Castle_W_transmission, Castle_W_transmission_color, Castle_W_transmission_depth, Castle_W_transmission_scatter, Castle_W_transmission_scatter_anisotropy, Castle_W_transmission_dispersion, Castle_W_transmission_extra_roughness, Castle_W_subsurface, diffuse7_out_cm_out, diffuse7_out_cm_out, Castle_W_subsurface_scale, Castle_W_subsurface_anisotropy, Castle_W_sheen, Castle_W_sheen_color, Castle_W_sheen_roughness, Castle_W_coat, Castle_W_coat_color, Castle_W_coat_roughness, Castle_W_coat_anisotropy, Castle_W_coat_rotation, Castle_W_coat_IOR, geomprop_Nworld_out1, Castle_W_coat_affect_color, Castle_W_coat_affect_roughness, Castle_W_thin_film_thickness, Castle_W_thin_film_IOR, Castle_W_emission, Castle_W_emission_color, Castle_W_opacity, Castle_W_thin_walled, mtlxnormalmap9_out, geomprop_Tworld_out1, Castle_W_out);
+ material M_Castle_W_out = Castle_W_out;
+ out1 = vec4(M_Castle_W_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Castle_W.glsl.vert b/Materials/Examples/StandardSurface/M_Castle_W.glsl.vert
new file mode 100644
index 0000000000..60b47d493e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_W.glsl.vert
@@ -0,0 +1,31 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+in vec2 i_texcoord_0;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Castle_W.mdl b/Materials/Examples/StandardSurface/M_Castle_W.mdl
new file mode 100644
index 0000000000..0a34e30e7e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_W.mdl
@@ -0,0 +1,234 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+color NG_srgb_texture_to_lin_rec709_color3
+(
+ color in1 = color(0, 0, 0)
+)
+{
+ color bias_out = in1 + 0.055;
+ color linSeg_out = in1 / 12.92;
+ float isAboveR_out = mx::stdlib::mx_ifgreater_float(float3(in1).x, 0.04045, 1, 0);
+ float isAboveG_out = mx::stdlib::mx_ifgreater_float(float3(in1).y, 0.04045, 1, 0);
+ float isAboveB_out = mx::stdlib::mx_ifgreater_float(float3(in1).z, 0.04045, 1, 0);
+ color max_out = math::max(bias_out, 0);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ color scale_out = max_out / 1.055;
+ color powSeg_out = math::pow(scale_out, 2.4);
+ color mix_out = math::lerp(linSeg_out, powSeg_out, isAbove_out);
+ return mix_out;
+}
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material M_Castle_W
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ uniform int geomprop_UV0_index = 0,
+ uniform texture_2d diffuse7_file = texture_2d("/chess_set/castle_white_base_color.jpg", tex::gamma_linear),
+ uniform string diffuse7_layer = "",
+ color diffuse7_default = color(0, 0, 0),
+ uniform mx_addressmode_type diffuse7_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type diffuse7_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type diffuse7_filtertype = mx_filterlookup_type_linear,
+ uniform string diffuse7_framerange = "",
+ uniform int diffuse7_frameoffset = 0,
+ uniform mx_addressmode_type diffuse7_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d metallic7_file = texture_2d("/chess_set/castle_shared_metallic.jpg", tex::gamma_linear),
+ uniform string metallic7_layer = "",
+ float metallic7_default = 0,
+ uniform mx_addressmode_type metallic7_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type metallic7_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type metallic7_filtertype = mx_filterlookup_type_linear,
+ uniform string metallic7_framerange = "",
+ uniform int metallic7_frameoffset = 0,
+ uniform mx_addressmode_type metallic7_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d roughness7_file = texture_2d("/chess_set/castle_shared_roughness.jpg", tex::gamma_linear),
+ uniform string roughness7_layer = "",
+ float roughness7_default = 0,
+ uniform mx_addressmode_type roughness7_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type roughness7_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type roughness7_filtertype = mx_filterlookup_type_linear,
+ uniform string roughness7_framerange = "",
+ uniform int roughness7_frameoffset = 0,
+ uniform mx_addressmode_type roughness7_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d normal7_file = texture_2d("/chess_set/castle_shared_normal.jpg", tex::gamma_linear),
+ uniform string normal7_layer = "",
+ float3 normal7_default = float3(0, 0, 0),
+ uniform mx_addressmode_type normal7_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type normal7_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type normal7_filtertype = mx_filterlookup_type_linear,
+ uniform string normal7_framerange = "",
+ uniform int normal7_frameoffset = 0,
+ uniform mx_addressmode_type normal7_frameendaction = mx_addressmode_type_constant,
+ uniform string mtlxnormalmap9_space = "tangent",
+ float mtlxnormalmap9_scale = 1,
+ float Castle_W_base = 1,
+ float Castle_W_diffuse_roughness = 0,
+ float Castle_W_specular = 1,
+ color Castle_W_specular_color = color(1, 1, 1),
+ uniform float Castle_W_specular_IOR = 1.5,
+ float Castle_W_specular_anisotropy = 0,
+ float Castle_W_specular_rotation = 0,
+ float Castle_W_transmission = 0,
+ color Castle_W_transmission_color = color(1, 1, 1),
+ float Castle_W_transmission_depth = 0,
+ color Castle_W_transmission_scatter = color(0, 0, 0),
+ float Castle_W_transmission_scatter_anisotropy = 0,
+ float Castle_W_transmission_dispersion = 0,
+ float Castle_W_transmission_extra_roughness = 0,
+ float Castle_W_subsurface = 0,
+ float Castle_W_subsurface_scale = 0.003,
+ float Castle_W_subsurface_anisotropy = 0,
+ float Castle_W_sheen = 0,
+ color Castle_W_sheen_color = color(1, 1, 1),
+ float Castle_W_sheen_roughness = 0.3,
+ float Castle_W_coat = 0,
+ color Castle_W_coat_color = color(1, 1, 1),
+ float Castle_W_coat_roughness = 0.1,
+ float Castle_W_coat_anisotropy = 0,
+ float Castle_W_coat_rotation = 0,
+ uniform float Castle_W_coat_IOR = 1.5,
+ float Castle_W_coat_affect_color = 0,
+ float Castle_W_coat_affect_roughness = 0,
+ float Castle_W_thin_film_thickness = 0,
+ float Castle_W_thin_film_IOR = 1.5,
+ float Castle_W_emission = 0,
+ color Castle_W_emission_color = color(1, 1, 1),
+ color Castle_W_opacity = color(1, 1, 1),
+ bool Castle_W_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ float2 geomprop_UV0_out1 = mx::stdlib::mx_texcoord_vector2(mxp_index:geomprop_UV0_index);
+ color diffuse7_out = mx::stdlib::mx_image_color3(diffuse7_file, diffuse7_layer, diffuse7_default, geomprop_UV0_out1, diffuse7_uaddressmode, diffuse7_vaddressmode, diffuse7_filtertype, diffuse7_framerange, diffuse7_frameoffset, diffuse7_frameendaction);
+ float metallic7_out = mx::stdlib::mx_image_float(metallic7_file, metallic7_layer, metallic7_default, geomprop_UV0_out1, metallic7_uaddressmode, metallic7_vaddressmode, metallic7_filtertype, metallic7_framerange, metallic7_frameoffset, metallic7_frameendaction);
+ float roughness7_out = mx::stdlib::mx_image_float(roughness7_file, roughness7_layer, roughness7_default, geomprop_UV0_out1, roughness7_uaddressmode, roughness7_vaddressmode, roughness7_filtertype, roughness7_framerange, roughness7_frameoffset, roughness7_frameendaction);
+ float3 normal7_out = mx::stdlib::mx_image_vector3(normal7_file, normal7_layer, normal7_default, geomprop_UV0_out1, normal7_uaddressmode, normal7_vaddressmode, normal7_filtertype, normal7_framerange, normal7_frameoffset, normal7_frameendaction);
+ color diffuse7_out_cm_out = NG_srgb_texture_to_lin_rec709_color3(diffuse7_out);
+ float3 mtlxnormalmap9_out = mx::stdlib::mx_normalmap(mxp_in:normal7_out, mxp_space:mtlxnormalmap9_space, mxp_scale:mtlxnormalmap9_scale, mxp_normal:geomprop_Nworld_out1, mxp_tangent:geomprop_Tworld_out1);
+ material Castle_W_out = NG_standard_surface_surfaceshader_100(Castle_W_base, diffuse7_out_cm_out, Castle_W_diffuse_roughness, metallic7_out, Castle_W_specular, Castle_W_specular_color, roughness7_out, Castle_W_specular_IOR, Castle_W_specular_anisotropy, Castle_W_specular_rotation, Castle_W_transmission, Castle_W_transmission_color, Castle_W_transmission_depth, Castle_W_transmission_scatter, Castle_W_transmission_scatter_anisotropy, Castle_W_transmission_dispersion, Castle_W_transmission_extra_roughness, Castle_W_subsurface, diffuse7_out_cm_out, diffuse7_out_cm_out, Castle_W_subsurface_scale, Castle_W_subsurface_anisotropy, Castle_W_sheen, Castle_W_sheen_color, Castle_W_sheen_roughness, Castle_W_coat, Castle_W_coat_color, Castle_W_coat_roughness, Castle_W_coat_anisotropy, Castle_W_coat_rotation, Castle_W_coat_IOR, geomprop_Nworld_out1, Castle_W_coat_affect_color, Castle_W_coat_affect_roughness, Castle_W_thin_film_thickness, Castle_W_thin_film_IOR, Castle_W_emission, Castle_W_emission_color, Castle_W_opacity, Castle_W_thin_walled, mtlxnormalmap9_out, geomprop_Tworld_out1);
+ material M_Castle_W_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: Castle_W_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = M_Castle_W_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/M_Castle_W.msl.frag b/Materials/Examples/StandardSurface/M_Castle_W.msl.frag
new file mode 100644
index 0000000000..8c2b9fa906
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_W.msl.frag
@@ -0,0 +1,2784 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ int diffuse7_layer;
+ vec3 diffuse7_default;
+ int diffuse7_uaddressmode;
+ int diffuse7_vaddressmode;
+ int diffuse7_filtertype;
+ int diffuse7_framerange;
+ int diffuse7_frameoffset;
+ int diffuse7_frameendaction;
+ vec2 diffuse7_uv_scale;
+ vec2 diffuse7_uv_offset;
+ int metallic7_layer;
+ float metallic7_default;
+ int metallic7_uaddressmode;
+ int metallic7_vaddressmode;
+ int metallic7_filtertype;
+ int metallic7_framerange;
+ int metallic7_frameoffset;
+ int metallic7_frameendaction;
+ vec2 metallic7_uv_scale;
+ vec2 metallic7_uv_offset;
+ int roughness7_layer;
+ float roughness7_default;
+ int roughness7_uaddressmode;
+ int roughness7_vaddressmode;
+ int roughness7_filtertype;
+ int roughness7_framerange;
+ int roughness7_frameoffset;
+ int roughness7_frameendaction;
+ vec2 roughness7_uv_scale;
+ vec2 roughness7_uv_offset;
+ int normal7_layer;
+ vec3 normal7_default;
+ int normal7_uaddressmode;
+ int normal7_vaddressmode;
+ int normal7_filtertype;
+ int normal7_framerange;
+ int normal7_frameoffset;
+ int normal7_frameendaction;
+ vec2 normal7_uv_scale;
+ vec2 normal7_uv_offset;
+ int mtlxnormalmap9_space;
+ float mtlxnormalmap9_scale;
+ float Castle_W_base;
+ float Castle_W_diffuse_roughness;
+ float Castle_W_specular;
+ vec3 Castle_W_specular_color;
+ float Castle_W_specular_IOR;
+ float Castle_W_specular_anisotropy;
+ float Castle_W_specular_rotation;
+ float Castle_W_transmission;
+ vec3 Castle_W_transmission_color;
+ float Castle_W_transmission_depth;
+ vec3 Castle_W_transmission_scatter;
+ float Castle_W_transmission_scatter_anisotropy;
+ float Castle_W_transmission_dispersion;
+ float Castle_W_transmission_extra_roughness;
+ float Castle_W_subsurface;
+ float Castle_W_subsurface_scale;
+ float Castle_W_subsurface_anisotropy;
+ float Castle_W_sheen;
+ vec3 Castle_W_sheen_color;
+ float Castle_W_sheen_roughness;
+ float Castle_W_coat;
+ vec3 Castle_W_coat_color;
+ float Castle_W_coat_roughness;
+ float Castle_W_coat_anisotropy;
+ float Castle_W_coat_rotation;
+ float Castle_W_coat_IOR;
+ float Castle_W_coat_affect_color;
+ float Castle_W_coat_affect_roughness;
+ float Castle_W_thin_film_thickness;
+ float Castle_W_thin_film_IOR;
+ float Castle_W_emission;
+ vec3 Castle_W_emission_color;
+ vec3 Castle_W_opacity;
+ bool Castle_W_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec2 texcoord_0 ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+, MetalTexture diffuse7_file , int diffuse7_layer
+
+ , vec3 diffuse7_default
+
+ , int diffuse7_uaddressmode
+
+ , int diffuse7_vaddressmode
+
+ , int diffuse7_filtertype
+
+ , int diffuse7_framerange
+
+ , int diffuse7_frameoffset
+
+ , int diffuse7_frameendaction
+
+ , vec2 diffuse7_uv_scale
+
+ , vec2 diffuse7_uv_offset
+
+, MetalTexture metallic7_file , int metallic7_layer
+
+ , float metallic7_default
+
+ , int metallic7_uaddressmode
+
+ , int metallic7_vaddressmode
+
+ , int metallic7_filtertype
+
+ , int metallic7_framerange
+
+ , int metallic7_frameoffset
+
+ , int metallic7_frameendaction
+
+ , vec2 metallic7_uv_scale
+
+ , vec2 metallic7_uv_offset
+
+, MetalTexture roughness7_file , int roughness7_layer
+
+ , float roughness7_default
+
+ , int roughness7_uaddressmode
+
+ , int roughness7_vaddressmode
+
+ , int roughness7_filtertype
+
+ , int roughness7_framerange
+
+ , int roughness7_frameoffset
+
+ , int roughness7_frameendaction
+
+ , vec2 roughness7_uv_scale
+
+ , vec2 roughness7_uv_offset
+
+, MetalTexture normal7_file , int normal7_layer
+
+ , vec3 normal7_default
+
+ , int normal7_uaddressmode
+
+ , int normal7_vaddressmode
+
+ , int normal7_filtertype
+
+ , int normal7_framerange
+
+ , int normal7_frameoffset
+
+ , int normal7_frameendaction
+
+ , vec2 normal7_uv_scale
+
+ , vec2 normal7_uv_offset
+
+ , int mtlxnormalmap9_space
+
+ , float mtlxnormalmap9_scale
+
+ , float Castle_W_base
+
+ , float Castle_W_diffuse_roughness
+
+ , float Castle_W_specular
+
+ , vec3 Castle_W_specular_color
+
+ , float Castle_W_specular_IOR
+
+ , float Castle_W_specular_anisotropy
+
+ , float Castle_W_specular_rotation
+
+ , float Castle_W_transmission
+
+ , vec3 Castle_W_transmission_color
+
+ , float Castle_W_transmission_depth
+
+ , vec3 Castle_W_transmission_scatter
+
+ , float Castle_W_transmission_scatter_anisotropy
+
+ , float Castle_W_transmission_dispersion
+
+ , float Castle_W_transmission_extra_roughness
+
+ , float Castle_W_subsurface
+
+ , float Castle_W_subsurface_scale
+
+ , float Castle_W_subsurface_anisotropy
+
+ , float Castle_W_sheen
+
+ , vec3 Castle_W_sheen_color
+
+ , float Castle_W_sheen_roughness
+
+ , float Castle_W_coat
+
+ , vec3 Castle_W_coat_color
+
+ , float Castle_W_coat_roughness
+
+ , float Castle_W_coat_anisotropy
+
+ , float Castle_W_coat_rotation
+
+ , float Castle_W_coat_IOR
+
+ , float Castle_W_coat_affect_color
+
+ , float Castle_W_coat_affect_roughness
+
+ , float Castle_W_thin_film_thickness
+
+ , float Castle_W_thin_film_IOR
+
+ , float Castle_W_emission
+
+ , vec3 Castle_W_emission_color
+
+ , vec3 Castle_W_opacity
+
+ , bool Castle_W_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+, diffuse7_file(diffuse7_file)
+ , diffuse7_layer(diffuse7_layer)
+
+ , diffuse7_default(diffuse7_default)
+
+ , diffuse7_uaddressmode(diffuse7_uaddressmode)
+
+ , diffuse7_vaddressmode(diffuse7_vaddressmode)
+
+ , diffuse7_filtertype(diffuse7_filtertype)
+
+ , diffuse7_framerange(diffuse7_framerange)
+
+ , diffuse7_frameoffset(diffuse7_frameoffset)
+
+ , diffuse7_frameendaction(diffuse7_frameendaction)
+
+ , diffuse7_uv_scale(diffuse7_uv_scale)
+
+ , diffuse7_uv_offset(diffuse7_uv_offset)
+
+, metallic7_file(metallic7_file)
+ , metallic7_layer(metallic7_layer)
+
+ , metallic7_default(metallic7_default)
+
+ , metallic7_uaddressmode(metallic7_uaddressmode)
+
+ , metallic7_vaddressmode(metallic7_vaddressmode)
+
+ , metallic7_filtertype(metallic7_filtertype)
+
+ , metallic7_framerange(metallic7_framerange)
+
+ , metallic7_frameoffset(metallic7_frameoffset)
+
+ , metallic7_frameendaction(metallic7_frameendaction)
+
+ , metallic7_uv_scale(metallic7_uv_scale)
+
+ , metallic7_uv_offset(metallic7_uv_offset)
+
+, roughness7_file(roughness7_file)
+ , roughness7_layer(roughness7_layer)
+
+ , roughness7_default(roughness7_default)
+
+ , roughness7_uaddressmode(roughness7_uaddressmode)
+
+ , roughness7_vaddressmode(roughness7_vaddressmode)
+
+ , roughness7_filtertype(roughness7_filtertype)
+
+ , roughness7_framerange(roughness7_framerange)
+
+ , roughness7_frameoffset(roughness7_frameoffset)
+
+ , roughness7_frameendaction(roughness7_frameendaction)
+
+ , roughness7_uv_scale(roughness7_uv_scale)
+
+ , roughness7_uv_offset(roughness7_uv_offset)
+
+, normal7_file(normal7_file)
+ , normal7_layer(normal7_layer)
+
+ , normal7_default(normal7_default)
+
+ , normal7_uaddressmode(normal7_uaddressmode)
+
+ , normal7_vaddressmode(normal7_vaddressmode)
+
+ , normal7_filtertype(normal7_filtertype)
+
+ , normal7_framerange(normal7_framerange)
+
+ , normal7_frameoffset(normal7_frameoffset)
+
+ , normal7_frameendaction(normal7_frameendaction)
+
+ , normal7_uv_scale(normal7_uv_scale)
+
+ , normal7_uv_offset(normal7_uv_offset)
+
+ , mtlxnormalmap9_space(mtlxnormalmap9_space)
+
+ , mtlxnormalmap9_scale(mtlxnormalmap9_scale)
+
+ , Castle_W_base(Castle_W_base)
+
+ , Castle_W_diffuse_roughness(Castle_W_diffuse_roughness)
+
+ , Castle_W_specular(Castle_W_specular)
+
+ , Castle_W_specular_color(Castle_W_specular_color)
+
+ , Castle_W_specular_IOR(Castle_W_specular_IOR)
+
+ , Castle_W_specular_anisotropy(Castle_W_specular_anisotropy)
+
+ , Castle_W_specular_rotation(Castle_W_specular_rotation)
+
+ , Castle_W_transmission(Castle_W_transmission)
+
+ , Castle_W_transmission_color(Castle_W_transmission_color)
+
+ , Castle_W_transmission_depth(Castle_W_transmission_depth)
+
+ , Castle_W_transmission_scatter(Castle_W_transmission_scatter)
+
+ , Castle_W_transmission_scatter_anisotropy(Castle_W_transmission_scatter_anisotropy)
+
+ , Castle_W_transmission_dispersion(Castle_W_transmission_dispersion)
+
+ , Castle_W_transmission_extra_roughness(Castle_W_transmission_extra_roughness)
+
+ , Castle_W_subsurface(Castle_W_subsurface)
+
+ , Castle_W_subsurface_scale(Castle_W_subsurface_scale)
+
+ , Castle_W_subsurface_anisotropy(Castle_W_subsurface_anisotropy)
+
+ , Castle_W_sheen(Castle_W_sheen)
+
+ , Castle_W_sheen_color(Castle_W_sheen_color)
+
+ , Castle_W_sheen_roughness(Castle_W_sheen_roughness)
+
+ , Castle_W_coat(Castle_W_coat)
+
+ , Castle_W_coat_color(Castle_W_coat_color)
+
+ , Castle_W_coat_roughness(Castle_W_coat_roughness)
+
+ , Castle_W_coat_anisotropy(Castle_W_coat_anisotropy)
+
+ , Castle_W_coat_rotation(Castle_W_coat_rotation)
+
+ , Castle_W_coat_IOR(Castle_W_coat_IOR)
+
+ , Castle_W_coat_affect_color(Castle_W_coat_affect_color)
+
+ , Castle_W_coat_affect_roughness(Castle_W_coat_affect_roughness)
+
+ , Castle_W_thin_film_thickness(Castle_W_thin_film_thickness)
+
+ , Castle_W_thin_film_IOR(Castle_W_thin_film_IOR)
+
+ , Castle_W_emission(Castle_W_emission)
+
+ , Castle_W_emission_color(Castle_W_emission_color)
+
+ , Castle_W_opacity(Castle_W_opacity)
+
+ , Castle_W_thin_walled(Castle_W_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+MetalTexture diffuse7_file;
+ int diffuse7_layer;
+
+
+ vec3 diffuse7_default;
+
+
+ int diffuse7_uaddressmode;
+
+
+ int diffuse7_vaddressmode;
+
+
+ int diffuse7_filtertype;
+
+
+ int diffuse7_framerange;
+
+
+ int diffuse7_frameoffset;
+
+
+ int diffuse7_frameendaction;
+
+
+ vec2 diffuse7_uv_scale;
+
+
+ vec2 diffuse7_uv_offset;
+
+
+MetalTexture metallic7_file;
+ int metallic7_layer;
+
+
+ float metallic7_default;
+
+
+ int metallic7_uaddressmode;
+
+
+ int metallic7_vaddressmode;
+
+
+ int metallic7_filtertype;
+
+
+ int metallic7_framerange;
+
+
+ int metallic7_frameoffset;
+
+
+ int metallic7_frameendaction;
+
+
+ vec2 metallic7_uv_scale;
+
+
+ vec2 metallic7_uv_offset;
+
+
+MetalTexture roughness7_file;
+ int roughness7_layer;
+
+
+ float roughness7_default;
+
+
+ int roughness7_uaddressmode;
+
+
+ int roughness7_vaddressmode;
+
+
+ int roughness7_filtertype;
+
+
+ int roughness7_framerange;
+
+
+ int roughness7_frameoffset;
+
+
+ int roughness7_frameendaction;
+
+
+ vec2 roughness7_uv_scale;
+
+
+ vec2 roughness7_uv_offset;
+
+
+MetalTexture normal7_file;
+ int normal7_layer;
+
+
+ vec3 normal7_default;
+
+
+ int normal7_uaddressmode;
+
+
+ int normal7_vaddressmode;
+
+
+ int normal7_filtertype;
+
+
+ int normal7_framerange;
+
+
+ int normal7_frameoffset;
+
+
+ int normal7_frameendaction;
+
+
+ vec2 normal7_uv_scale;
+
+
+ vec2 normal7_uv_offset;
+
+
+ int mtlxnormalmap9_space;
+
+
+ float mtlxnormalmap9_scale;
+
+
+ float Castle_W_base;
+
+
+ float Castle_W_diffuse_roughness;
+
+
+ float Castle_W_specular;
+
+
+ vec3 Castle_W_specular_color;
+
+
+ float Castle_W_specular_IOR;
+
+
+ float Castle_W_specular_anisotropy;
+
+
+ float Castle_W_specular_rotation;
+
+
+ float Castle_W_transmission;
+
+
+ vec3 Castle_W_transmission_color;
+
+
+ float Castle_W_transmission_depth;
+
+
+ vec3 Castle_W_transmission_scatter;
+
+
+ float Castle_W_transmission_scatter_anisotropy;
+
+
+ float Castle_W_transmission_dispersion;
+
+
+ float Castle_W_transmission_extra_roughness;
+
+
+ float Castle_W_subsurface;
+
+
+ float Castle_W_subsurface_scale;
+
+
+ float Castle_W_subsurface_anisotropy;
+
+
+ float Castle_W_sheen;
+
+
+ vec3 Castle_W_sheen_color;
+
+
+ float Castle_W_sheen_roughness;
+
+
+ float Castle_W_coat;
+
+
+ vec3 Castle_W_coat_color;
+
+
+ float Castle_W_coat_roughness;
+
+
+ float Castle_W_coat_anisotropy;
+
+
+ float Castle_W_coat_rotation;
+
+
+ float Castle_W_coat_IOR;
+
+
+ float Castle_W_coat_affect_color;
+
+
+ float Castle_W_coat_affect_roughness;
+
+
+ float Castle_W_thin_film_thickness;
+
+
+ float Castle_W_thin_film_IOR;
+
+
+ float Castle_W_emission;
+
+
+ vec3 Castle_W_emission_color;
+
+
+ vec3 Castle_W_opacity;
+
+
+ bool Castle_W_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+ {
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+ }
+
+ void mx_image_color3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_float(MetalTexture tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread float& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_vector3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+ void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, thread vec3& out1)
+ {
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+ }
+
+ void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, thread vec3& result)
+ {
+ // Decode the normal map.
+ value = all(value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 diffuse7_out = vec3(0.0);
+ mx_image_color3(diffuse7_file, diffuse7_layer, diffuse7_default, geomprop_UV0_out1, diffuse7_uaddressmode, diffuse7_vaddressmode, diffuse7_filtertype, diffuse7_framerange, diffuse7_frameoffset, diffuse7_frameendaction, diffuse7_uv_scale, diffuse7_uv_offset, diffuse7_out);
+ float metallic7_out = 0.0;
+ mx_image_float(metallic7_file, metallic7_layer, metallic7_default, geomprop_UV0_out1, metallic7_uaddressmode, metallic7_vaddressmode, metallic7_filtertype, metallic7_framerange, metallic7_frameoffset, metallic7_frameendaction, metallic7_uv_scale, metallic7_uv_offset, metallic7_out);
+ float roughness7_out = 0.0;
+ mx_image_float(roughness7_file, roughness7_layer, roughness7_default, geomprop_UV0_out1, roughness7_uaddressmode, roughness7_vaddressmode, roughness7_filtertype, roughness7_framerange, roughness7_frameoffset, roughness7_frameendaction, roughness7_uv_scale, roughness7_uv_offset, roughness7_out);
+ vec3 normal7_out = vec3(0.0);
+ mx_image_vector3(normal7_file, normal7_layer, normal7_default, geomprop_UV0_out1, normal7_uaddressmode, normal7_vaddressmode, normal7_filtertype, normal7_framerange, normal7_frameoffset, normal7_frameendaction, normal7_uv_scale, normal7_uv_offset, normal7_out);
+ vec3 diffuse7_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse7_out, diffuse7_out_cm_out);
+ vec3 mtlxnormalmap9_out = vec3(0.0);
+ mx_normalmap(normal7_out, mtlxnormalmap9_space, mtlxnormalmap9_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap9_out);
+ surfaceshader Castle_W_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(Castle_W_base, diffuse7_out_cm_out, Castle_W_diffuse_roughness, metallic7_out, Castle_W_specular, Castle_W_specular_color, roughness7_out, Castle_W_specular_IOR, Castle_W_specular_anisotropy, Castle_W_specular_rotation, Castle_W_transmission, Castle_W_transmission_color, Castle_W_transmission_depth, Castle_W_transmission_scatter, Castle_W_transmission_scatter_anisotropy, Castle_W_transmission_dispersion, Castle_W_transmission_extra_roughness, Castle_W_subsurface, diffuse7_out_cm_out, diffuse7_out_cm_out, Castle_W_subsurface_scale, Castle_W_subsurface_anisotropy, Castle_W_sheen, Castle_W_sheen_color, Castle_W_sheen_roughness, Castle_W_coat, Castle_W_coat_color, Castle_W_coat_roughness, Castle_W_coat_anisotropy, Castle_W_coat_rotation, Castle_W_coat_IOR, geomprop_Nworld_out1, Castle_W_coat_affect_color, Castle_W_coat_affect_roughness, Castle_W_thin_film_thickness, Castle_W_thin_film_IOR, Castle_W_emission, Castle_W_emission_color, Castle_W_opacity, Castle_W_thin_walled, mtlxnormalmap9_out, geomprop_Tworld_out1, Castle_W_out);
+ material M_Castle_W_out = Castle_W_out;
+ out1 = float4(M_Castle_W_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], texture2d diffuse7_file_tex [[texture(0)]], sampler diffuse7_file_sampler [[sampler(0)]]
+, texture2d metallic7_file_tex [[texture(1)]], sampler metallic7_file_sampler [[sampler(1)]]
+, texture2d roughness7_file_tex [[texture(2)]], sampler roughness7_file_sampler [[sampler(2)]]
+, texture2d normal7_file_tex [[texture(3)]], sampler normal7_file_sampler [[sampler(3)]]
+, constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(4)]], sampler u_envRadiance_sampler [[sampler(4)]]
+, texture2d u_envIrradiance_tex [[texture(5)]], sampler u_envIrradiance_sampler [[sampler(5)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+, MetalTexture {
+diffuse7_file_tex, diffuse7_file_sampler }
+ , u_pub.diffuse7_layer
+ , u_pub.diffuse7_default
+ , u_pub.diffuse7_uaddressmode
+ , u_pub.diffuse7_vaddressmode
+ , u_pub.diffuse7_filtertype
+ , u_pub.diffuse7_framerange
+ , u_pub.diffuse7_frameoffset
+ , u_pub.diffuse7_frameendaction
+ , u_pub.diffuse7_uv_scale
+ , u_pub.diffuse7_uv_offset
+, MetalTexture {
+metallic7_file_tex, metallic7_file_sampler }
+ , u_pub.metallic7_layer
+ , u_pub.metallic7_default
+ , u_pub.metallic7_uaddressmode
+ , u_pub.metallic7_vaddressmode
+ , u_pub.metallic7_filtertype
+ , u_pub.metallic7_framerange
+ , u_pub.metallic7_frameoffset
+ , u_pub.metallic7_frameendaction
+ , u_pub.metallic7_uv_scale
+ , u_pub.metallic7_uv_offset
+, MetalTexture {
+roughness7_file_tex, roughness7_file_sampler }
+ , u_pub.roughness7_layer
+ , u_pub.roughness7_default
+ , u_pub.roughness7_uaddressmode
+ , u_pub.roughness7_vaddressmode
+ , u_pub.roughness7_filtertype
+ , u_pub.roughness7_framerange
+ , u_pub.roughness7_frameoffset
+ , u_pub.roughness7_frameendaction
+ , u_pub.roughness7_uv_scale
+ , u_pub.roughness7_uv_offset
+, MetalTexture {
+normal7_file_tex, normal7_file_sampler }
+ , u_pub.normal7_layer
+ , u_pub.normal7_default
+ , u_pub.normal7_uaddressmode
+ , u_pub.normal7_vaddressmode
+ , u_pub.normal7_filtertype
+ , u_pub.normal7_framerange
+ , u_pub.normal7_frameoffset
+ , u_pub.normal7_frameendaction
+ , u_pub.normal7_uv_scale
+ , u_pub.normal7_uv_offset
+ , u_pub.mtlxnormalmap9_space
+ , u_pub.mtlxnormalmap9_scale
+ , u_pub.Castle_W_base
+ , u_pub.Castle_W_diffuse_roughness
+ , u_pub.Castle_W_specular
+ , u_pub.Castle_W_specular_color
+ , u_pub.Castle_W_specular_IOR
+ , u_pub.Castle_W_specular_anisotropy
+ , u_pub.Castle_W_specular_rotation
+ , u_pub.Castle_W_transmission
+ , u_pub.Castle_W_transmission_color
+ , u_pub.Castle_W_transmission_depth
+ , u_pub.Castle_W_transmission_scatter
+ , u_pub.Castle_W_transmission_scatter_anisotropy
+ , u_pub.Castle_W_transmission_dispersion
+ , u_pub.Castle_W_transmission_extra_roughness
+ , u_pub.Castle_W_subsurface
+ , u_pub.Castle_W_subsurface_scale
+ , u_pub.Castle_W_subsurface_anisotropy
+ , u_pub.Castle_W_sheen
+ , u_pub.Castle_W_sheen_color
+ , u_pub.Castle_W_sheen_roughness
+ , u_pub.Castle_W_coat
+ , u_pub.Castle_W_coat_color
+ , u_pub.Castle_W_coat_roughness
+ , u_pub.Castle_W_coat_anisotropy
+ , u_pub.Castle_W_coat_rotation
+ , u_pub.Castle_W_coat_IOR
+ , u_pub.Castle_W_coat_affect_color
+ , u_pub.Castle_W_coat_affect_roughness
+ , u_pub.Castle_W_thin_film_thickness
+ , u_pub.Castle_W_thin_film_IOR
+ , u_pub.Castle_W_emission
+ , u_pub.Castle_W_emission_color
+ , u_pub.Castle_W_opacity
+ , u_pub.Castle_W_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Castle_W.msl.vert b/Materials/Examples/StandardSurface/M_Castle_W.msl.vert
new file mode 100644
index 0000000000..dbf51a96bf
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_W.msl.vert
@@ -0,0 +1,124 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+ vec2 i_texcoord_0 [[attribute(3)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+, vec2 i_texcoord_0
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+, i_texcoord_0(i_texcoord_0)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ vec2 i_texcoord_0;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'geomprop_UV0'. Function already called in this scope.
+ // Omitted node 'diffuse7'. Function already called in this scope.
+ // Omitted node 'metallic7'. Function already called in this scope.
+ // Omitted node 'roughness7'. Function already called in this scope.
+ // Omitted node 'normal7'. Function already called in this scope.
+ // Omitted node 'diffuse7_out_cm'. Function already called in this scope.
+ // Omitted node 'mtlxnormalmap9'. Function already called in this scope.
+ // Omitted node 'Castle_W'. Function already called in this scope.
+ // Omitted node 'M_Castle_W'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(4) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent, i_vs.i_texcoord_0 , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Castle_W.osl b/Materials/Examples/StandardSurface/M_Castle_W.osl
new file mode 100644
index 0000000000..58641f05e0
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Castle_W.osl
@@ -0,0 +1,815 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+vector2 mx_transform_uv(vector2 texcoord)
+{
+ return texcoord;
+}
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode );
+}
+
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+ out = rgb[0];
+}
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(color in, output color out)
+{
+ float bias_in2_tmp = 0.055;
+ color bias_out = in + bias_in2_tmp;
+ float linSeg_in2_tmp = 12.92;
+ color linSeg_out = in / linSeg_in2_tmp;
+ float isAboveR_value2_tmp = 0.04045;
+ float isAboveR_in1_tmp = 1;
+ float isAboveR_in2_tmp = 0;
+ float isAboveR_out = mx_ternary(in[0] > isAboveR_value2_tmp, isAboveR_in1_tmp, isAboveR_in2_tmp);
+ float isAboveG_value2_tmp = 0.04045;
+ float isAboveG_in1_tmp = 1;
+ float isAboveG_in2_tmp = 0;
+ float isAboveG_out = mx_ternary(in[1] > isAboveG_value2_tmp, isAboveG_in1_tmp, isAboveG_in2_tmp);
+ float isAboveB_value2_tmp = 0.04045;
+ float isAboveB_in1_tmp = 1;
+ float isAboveB_in2_tmp = 0;
+ float isAboveB_out = mx_ternary(in[2] > isAboveB_value2_tmp, isAboveB_in1_tmp, isAboveB_in2_tmp);
+ float max_in2_tmp = 0;
+ color max_out = max(bias_out, max_in2_tmp);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ float scale_in2_tmp = 1.055;
+ color scale_out = max_out / scale_in2_tmp;
+ float powSeg_in2_tmp = 2.4;
+ color powSeg_out = pow(scale_out, powSeg_in2_tmp);
+ color mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out = mix_out;
+}
+
+void mx_normalmap(vector value, string map_space, float normal_scale, vector N, vector U, output vector result)
+{
+ // Tangent space
+ if (map_space == "tangent")
+ {
+ vector v = value * 2.0 - 1.0;
+ vector T = normalize(U - dot(U, N) * N);
+ vector B = normalize(cross(N, T));
+ result = normalize(T * v[0] * normal_scale + B * v[1] * normal_scale + N * v[2]);
+ }
+ // Object space
+ else
+ {
+ vector n = value * 2.0 - 1.0;
+ result = normalize(n);
+ }
+}
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader M_Castle_W
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "M_Castle_W"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ int geomprop_UV0_index = 0
+ [[
+ string widget = "number"
+ ]],
+ textureresource diffuse7_file = {"chess_set/castle_white_base_color.jpg", "srgb_texture"}
+ [[
+ string widget = "filename"
+ ]],
+ string diffuse7_layer = "",
+ color diffuse7_default = color(0, 0, 0),
+ string diffuse7_uaddressmode = "periodic",
+ string diffuse7_vaddressmode = "periodic",
+ string diffuse7_filtertype = "linear",
+ string diffuse7_framerange = "",
+ int diffuse7_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string diffuse7_frameendaction = "constant",
+ textureresource metallic7_file = {"chess_set/castle_shared_metallic.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string metallic7_layer = "",
+ float metallic7_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string metallic7_uaddressmode = "periodic",
+ string metallic7_vaddressmode = "periodic",
+ string metallic7_filtertype = "linear",
+ string metallic7_framerange = "",
+ int metallic7_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string metallic7_frameendaction = "constant",
+ textureresource roughness7_file = {"chess_set/castle_shared_roughness.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string roughness7_layer = "",
+ float roughness7_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string roughness7_uaddressmode = "periodic",
+ string roughness7_vaddressmode = "periodic",
+ string roughness7_filtertype = "linear",
+ string roughness7_framerange = "",
+ int roughness7_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string roughness7_frameendaction = "constant",
+ textureresource normal7_file = {"chess_set/castle_shared_normal.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string normal7_layer = "",
+ vector normal7_default = vector(0, 0, 0),
+ string normal7_uaddressmode = "periodic",
+ string normal7_vaddressmode = "periodic",
+ string normal7_filtertype = "linear",
+ string normal7_framerange = "",
+ int normal7_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string normal7_frameendaction = "constant",
+ string mtlxnormalmap9_space = "tangent",
+ float mtlxnormalmap9_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_base = 1
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color Castle_W_specular_color = color(1, 1, 1),
+ float Castle_W_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color Castle_W_transmission_color = color(1, 1, 1),
+ float Castle_W_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color Castle_W_transmission_scatter = color(0, 0, 0),
+ float Castle_W_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_subsurface_scale = 0.003
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color Castle_W_sheen_color = color(1, 1, 1),
+ float Castle_W_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color Castle_W_coat_color = color(1, 1, 1),
+ float Castle_W_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Castle_W_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color Castle_W_emission_color = color(1, 1, 1),
+ color Castle_W_opacity = color(1, 1, 1),
+ int Castle_W_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ vector2 geomprop_UV0_out1 = vector2(u,v);
+ color diffuse7_out = color(0.0);
+ mx_image_color3(diffuse7_file, diffuse7_layer, diffuse7_default, geomprop_UV0_out1, diffuse7_uaddressmode, diffuse7_vaddressmode, diffuse7_filtertype, diffuse7_framerange, diffuse7_frameoffset, diffuse7_frameendaction, diffuse7_out);
+ float metallic7_out = 0.0;
+ mx_image_float(metallic7_file, metallic7_layer, metallic7_default, geomprop_UV0_out1, metallic7_uaddressmode, metallic7_vaddressmode, metallic7_filtertype, metallic7_framerange, metallic7_frameoffset, metallic7_frameendaction, metallic7_out);
+ float roughness7_out = 0.0;
+ mx_image_float(roughness7_file, roughness7_layer, roughness7_default, geomprop_UV0_out1, roughness7_uaddressmode, roughness7_vaddressmode, roughness7_filtertype, roughness7_framerange, roughness7_frameoffset, roughness7_frameendaction, roughness7_out);
+ vector normal7_out = vector(0.0);
+ mx_image_vector3(normal7_file, normal7_layer, normal7_default, geomprop_UV0_out1, normal7_uaddressmode, normal7_vaddressmode, normal7_filtertype, normal7_framerange, normal7_frameoffset, normal7_frameendaction, normal7_out);
+ color diffuse7_out_cm_out = color(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(diffuse7_out, diffuse7_out_cm_out);
+ vector mtlxnormalmap9_out = vector(0.0);
+ mx_normalmap(normal7_out, mtlxnormalmap9_space, mtlxnormalmap9_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap9_out);
+ surfaceshader Castle_W_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(Castle_W_base, diffuse7_out_cm_out, Castle_W_diffuse_roughness, metallic7_out, Castle_W_specular, Castle_W_specular_color, roughness7_out, Castle_W_specular_IOR, Castle_W_specular_anisotropy, Castle_W_specular_rotation, Castle_W_transmission, Castle_W_transmission_color, Castle_W_transmission_depth, Castle_W_transmission_scatter, Castle_W_transmission_scatter_anisotropy, Castle_W_transmission_dispersion, Castle_W_transmission_extra_roughness, Castle_W_subsurface, diffuse7_out_cm_out, diffuse7_out_cm_out, Castle_W_subsurface_scale, Castle_W_subsurface_anisotropy, Castle_W_sheen, Castle_W_sheen_color, Castle_W_sheen_roughness, Castle_W_coat, Castle_W_coat_color, Castle_W_coat_roughness, Castle_W_coat_anisotropy, Castle_W_coat_rotation, Castle_W_coat_IOR, geomprop_Nworld_out1, Castle_W_coat_affect_color, Castle_W_coat_affect_roughness, Castle_W_thin_film_thickness, Castle_W_thin_film_IOR, Castle_W_emission, Castle_W_emission_color, Castle_W_opacity, Castle_W_thin_walled, mtlxnormalmap9_out, geomprop_Tworld_out1, Castle_W_out);
+ MATERIAL M_Castle_W_out = mx_surfacematerial(Castle_W_out, displacementshader1);
+ out = M_Castle_W_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Chessboard.glsl.frag b/Materials/Examples/StandardSurface/M_Chessboard.glsl.frag
new file mode 100644
index 0000000000..ec31961319
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Chessboard.glsl.frag
@@ -0,0 +1,1854 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform sampler2D mtlximage13_file;
+uniform int mtlximage13_layer = 0;
+uniform vec3 mtlximage13_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int mtlximage13_uaddressmode = 2;
+uniform int mtlximage13_vaddressmode = 2;
+uniform int mtlximage13_filtertype = 1;
+uniform int mtlximage13_framerange = 0;
+uniform int mtlximage13_frameoffset = 0;
+uniform int mtlximage13_frameendaction = 0;
+uniform vec2 mtlximage13_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage13_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D mtlximage16_file;
+uniform int mtlximage16_layer = 0;
+uniform float mtlximage16_default = 0.000000;
+uniform int mtlximage16_uaddressmode = 2;
+uniform int mtlximage16_vaddressmode = 2;
+uniform int mtlximage16_filtertype = 1;
+uniform int mtlximage16_framerange = 0;
+uniform int mtlximage16_frameoffset = 0;
+uniform int mtlximage16_frameendaction = 0;
+uniform vec2 mtlximage16_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage16_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D mtlximage17_file;
+uniform int mtlximage17_layer = 0;
+uniform float mtlximage17_default = 0.000000;
+uniform int mtlximage17_uaddressmode = 2;
+uniform int mtlximage17_vaddressmode = 2;
+uniform int mtlximage17_filtertype = 1;
+uniform int mtlximage17_framerange = 0;
+uniform int mtlximage17_frameoffset = 0;
+uniform int mtlximage17_frameendaction = 0;
+uniform vec2 mtlximage17_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage17_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D mtlximage15_file;
+uniform int mtlximage15_layer = 0;
+uniform vec3 mtlximage15_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int mtlximage15_uaddressmode = 2;
+uniform int mtlximage15_vaddressmode = 2;
+uniform int mtlximage15_filtertype = 1;
+uniform int mtlximage15_framerange = 0;
+uniform int mtlximage15_frameoffset = 0;
+uniform int mtlximage15_frameendaction = 0;
+uniform vec2 mtlximage15_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage15_uv_offset = vec2(0.000000, 0.000000);
+uniform int mtlxnormalmap12_space = 0;
+uniform float mtlxnormalmap12_scale = 1.000000;
+uniform float Chessboard_base = 1.000000;
+uniform float Chessboard_diffuse_roughness = 0.000000;
+uniform float Chessboard_specular = 1.000000;
+uniform vec3 Chessboard_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Chessboard_specular_IOR = 1.500000;
+uniform float Chessboard_specular_anisotropy = 0.000000;
+uniform float Chessboard_specular_rotation = 0.000000;
+uniform float Chessboard_transmission = 0.000000;
+uniform vec3 Chessboard_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Chessboard_transmission_depth = 0.000000;
+uniform vec3 Chessboard_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float Chessboard_transmission_scatter_anisotropy = 0.000000;
+uniform float Chessboard_transmission_dispersion = 0.000000;
+uniform float Chessboard_transmission_extra_roughness = 0.000000;
+uniform float Chessboard_subsurface = 0.000000;
+uniform float Chessboard_subsurface_scale = 0.003000;
+uniform float Chessboard_subsurface_anisotropy = 0.000000;
+uniform float Chessboard_sheen = 0.000000;
+uniform vec3 Chessboard_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Chessboard_sheen_roughness = 0.300000;
+uniform float Chessboard_coat = 0.000000;
+uniform vec3 Chessboard_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float Chessboard_coat_roughness = 0.100000;
+uniform float Chessboard_coat_anisotropy = 0.000000;
+uniform float Chessboard_coat_rotation = 0.000000;
+uniform float Chessboard_coat_IOR = 1.500000;
+uniform float Chessboard_coat_affect_color = 0.000000;
+uniform float Chessboard_coat_affect_roughness = 0.000000;
+uniform float Chessboard_thin_film_thickness = 0.000000;
+uniform float Chessboard_thin_film_IOR = 1.500000;
+uniform float Chessboard_emission = 0.000000;
+uniform vec3 Chessboard_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 Chessboard_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool Chessboard_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+{
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+}
+
+void mx_image_color3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_float(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_vector3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, out vec3 out1)
+{
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+}
+
+void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, out vec3 result)
+{
+ // Decode the normal map.
+ value = (value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 mtlximage13_out = vec3(0.0);
+ mx_image_color3(mtlximage13_file, mtlximage13_layer, mtlximage13_default, geomprop_UV0_out1, mtlximage13_uaddressmode, mtlximage13_vaddressmode, mtlximage13_filtertype, mtlximage13_framerange, mtlximage13_frameoffset, mtlximage13_frameendaction, mtlximage13_uv_scale, mtlximage13_uv_offset, mtlximage13_out);
+ float mtlximage16_out = 0.0;
+ mx_image_float(mtlximage16_file, mtlximage16_layer, mtlximage16_default, geomprop_UV0_out1, mtlximage16_uaddressmode, mtlximage16_vaddressmode, mtlximage16_filtertype, mtlximage16_framerange, mtlximage16_frameoffset, mtlximage16_frameendaction, mtlximage16_uv_scale, mtlximage16_uv_offset, mtlximage16_out);
+ float mtlximage17_out = 0.0;
+ mx_image_float(mtlximage17_file, mtlximage17_layer, mtlximage17_default, geomprop_UV0_out1, mtlximage17_uaddressmode, mtlximage17_vaddressmode, mtlximage17_filtertype, mtlximage17_framerange, mtlximage17_frameoffset, mtlximage17_frameendaction, mtlximage17_uv_scale, mtlximage17_uv_offset, mtlximage17_out);
+ vec3 mtlximage15_out = vec3(0.0);
+ mx_image_vector3(mtlximage15_file, mtlximage15_layer, mtlximage15_default, geomprop_UV0_out1, mtlximage15_uaddressmode, mtlximage15_vaddressmode, mtlximage15_filtertype, mtlximage15_framerange, mtlximage15_frameoffset, mtlximage15_frameendaction, mtlximage15_uv_scale, mtlximage15_uv_offset, mtlximage15_out);
+ vec3 mtlximage13_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(mtlximage13_out, mtlximage13_out_cm_out);
+ vec3 mtlxnormalmap12_out = vec3(0.0);
+ mx_normalmap(mtlximage15_out, mtlxnormalmap12_space, mtlxnormalmap12_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap12_out);
+ surfaceshader Chessboard_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(Chessboard_base, mtlximage13_out_cm_out, Chessboard_diffuse_roughness, mtlximage16_out, Chessboard_specular, Chessboard_specular_color, mtlximage17_out, Chessboard_specular_IOR, Chessboard_specular_anisotropy, Chessboard_specular_rotation, Chessboard_transmission, Chessboard_transmission_color, Chessboard_transmission_depth, Chessboard_transmission_scatter, Chessboard_transmission_scatter_anisotropy, Chessboard_transmission_dispersion, Chessboard_transmission_extra_roughness, Chessboard_subsurface, mtlximage13_out_cm_out, mtlximage13_out_cm_out, Chessboard_subsurface_scale, Chessboard_subsurface_anisotropy, Chessboard_sheen, Chessboard_sheen_color, Chessboard_sheen_roughness, Chessboard_coat, Chessboard_coat_color, Chessboard_coat_roughness, Chessboard_coat_anisotropy, Chessboard_coat_rotation, Chessboard_coat_IOR, geomprop_Nworld_out1, Chessboard_coat_affect_color, Chessboard_coat_affect_roughness, Chessboard_thin_film_thickness, Chessboard_thin_film_IOR, Chessboard_emission, Chessboard_emission_color, Chessboard_opacity, Chessboard_thin_walled, mtlxnormalmap12_out, geomprop_Tworld_out1, Chessboard_out);
+ material M_Chessboard_out = Chessboard_out;
+ out1 = vec4(M_Chessboard_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Chessboard.glsl.vert b/Materials/Examples/StandardSurface/M_Chessboard.glsl.vert
new file mode 100644
index 0000000000..60b47d493e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Chessboard.glsl.vert
@@ -0,0 +1,31 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+in vec2 i_texcoord_0;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Chessboard.mdl b/Materials/Examples/StandardSurface/M_Chessboard.mdl
new file mode 100644
index 0000000000..1186d6de6d
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Chessboard.mdl
@@ -0,0 +1,234 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+color NG_srgb_texture_to_lin_rec709_color3
+(
+ color in1 = color(0, 0, 0)
+)
+{
+ color bias_out = in1 + 0.055;
+ color linSeg_out = in1 / 12.92;
+ float isAboveR_out = mx::stdlib::mx_ifgreater_float(float3(in1).x, 0.04045, 1, 0);
+ float isAboveG_out = mx::stdlib::mx_ifgreater_float(float3(in1).y, 0.04045, 1, 0);
+ float isAboveB_out = mx::stdlib::mx_ifgreater_float(float3(in1).z, 0.04045, 1, 0);
+ color max_out = math::max(bias_out, 0);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ color scale_out = max_out / 1.055;
+ color powSeg_out = math::pow(scale_out, 2.4);
+ color mix_out = math::lerp(linSeg_out, powSeg_out, isAbove_out);
+ return mix_out;
+}
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material M_Chessboard
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ uniform int geomprop_UV0_index = 0,
+ uniform texture_2d mtlximage13_file = texture_2d("/chess_set/chessboard_base_color.jpg", tex::gamma_linear),
+ uniform string mtlximage13_layer = "",
+ color mtlximage13_default = color(0, 0, 0),
+ uniform mx_addressmode_type mtlximage13_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage13_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage13_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage13_framerange = "",
+ uniform int mtlximage13_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage13_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d mtlximage16_file = texture_2d("/chess_set/chessboard_metallic.jpg", tex::gamma_linear),
+ uniform string mtlximage16_layer = "",
+ float mtlximage16_default = 0,
+ uniform mx_addressmode_type mtlximage16_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage16_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage16_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage16_framerange = "",
+ uniform int mtlximage16_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage16_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d mtlximage17_file = texture_2d("/chess_set/chessboard_roughness.jpg", tex::gamma_linear),
+ uniform string mtlximage17_layer = "",
+ float mtlximage17_default = 0,
+ uniform mx_addressmode_type mtlximage17_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage17_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage17_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage17_framerange = "",
+ uniform int mtlximage17_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage17_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d mtlximage15_file = texture_2d("/chess_set/chessboard_normal.jpg", tex::gamma_linear),
+ uniform string mtlximage15_layer = "",
+ float3 mtlximage15_default = float3(0, 0, 0),
+ uniform mx_addressmode_type mtlximage15_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage15_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage15_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage15_framerange = "",
+ uniform int mtlximage15_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage15_frameendaction = mx_addressmode_type_constant,
+ uniform string mtlxnormalmap12_space = "tangent",
+ float mtlxnormalmap12_scale = 1,
+ float Chessboard_base = 1,
+ float Chessboard_diffuse_roughness = 0,
+ float Chessboard_specular = 1,
+ color Chessboard_specular_color = color(1, 1, 1),
+ uniform float Chessboard_specular_IOR = 1.5,
+ float Chessboard_specular_anisotropy = 0,
+ float Chessboard_specular_rotation = 0,
+ float Chessboard_transmission = 0,
+ color Chessboard_transmission_color = color(1, 1, 1),
+ float Chessboard_transmission_depth = 0,
+ color Chessboard_transmission_scatter = color(0, 0, 0),
+ float Chessboard_transmission_scatter_anisotropy = 0,
+ float Chessboard_transmission_dispersion = 0,
+ float Chessboard_transmission_extra_roughness = 0,
+ float Chessboard_subsurface = 0,
+ float Chessboard_subsurface_scale = 0.003,
+ float Chessboard_subsurface_anisotropy = 0,
+ float Chessboard_sheen = 0,
+ color Chessboard_sheen_color = color(1, 1, 1),
+ float Chessboard_sheen_roughness = 0.3,
+ float Chessboard_coat = 0,
+ color Chessboard_coat_color = color(1, 1, 1),
+ float Chessboard_coat_roughness = 0.1,
+ float Chessboard_coat_anisotropy = 0,
+ float Chessboard_coat_rotation = 0,
+ uniform float Chessboard_coat_IOR = 1.5,
+ float Chessboard_coat_affect_color = 0,
+ float Chessboard_coat_affect_roughness = 0,
+ float Chessboard_thin_film_thickness = 0,
+ float Chessboard_thin_film_IOR = 1.5,
+ float Chessboard_emission = 0,
+ color Chessboard_emission_color = color(1, 1, 1),
+ color Chessboard_opacity = color(1, 1, 1),
+ bool Chessboard_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ float2 geomprop_UV0_out1 = mx::stdlib::mx_texcoord_vector2(mxp_index:geomprop_UV0_index);
+ color mtlximage13_out = mx::stdlib::mx_image_color3(mtlximage13_file, mtlximage13_layer, mtlximage13_default, geomprop_UV0_out1, mtlximage13_uaddressmode, mtlximage13_vaddressmode, mtlximage13_filtertype, mtlximage13_framerange, mtlximage13_frameoffset, mtlximage13_frameendaction);
+ float mtlximage16_out = mx::stdlib::mx_image_float(mtlximage16_file, mtlximage16_layer, mtlximage16_default, geomprop_UV0_out1, mtlximage16_uaddressmode, mtlximage16_vaddressmode, mtlximage16_filtertype, mtlximage16_framerange, mtlximage16_frameoffset, mtlximage16_frameendaction);
+ float mtlximage17_out = mx::stdlib::mx_image_float(mtlximage17_file, mtlximage17_layer, mtlximage17_default, geomprop_UV0_out1, mtlximage17_uaddressmode, mtlximage17_vaddressmode, mtlximage17_filtertype, mtlximage17_framerange, mtlximage17_frameoffset, mtlximage17_frameendaction);
+ float3 mtlximage15_out = mx::stdlib::mx_image_vector3(mtlximage15_file, mtlximage15_layer, mtlximage15_default, geomprop_UV0_out1, mtlximage15_uaddressmode, mtlximage15_vaddressmode, mtlximage15_filtertype, mtlximage15_framerange, mtlximage15_frameoffset, mtlximage15_frameendaction);
+ color mtlximage13_out_cm_out = NG_srgb_texture_to_lin_rec709_color3(mtlximage13_out);
+ float3 mtlxnormalmap12_out = mx::stdlib::mx_normalmap(mxp_in:mtlximage15_out, mxp_space:mtlxnormalmap12_space, mxp_scale:mtlxnormalmap12_scale, mxp_normal:geomprop_Nworld_out1, mxp_tangent:geomprop_Tworld_out1);
+ material Chessboard_out = NG_standard_surface_surfaceshader_100(Chessboard_base, mtlximage13_out_cm_out, Chessboard_diffuse_roughness, mtlximage16_out, Chessboard_specular, Chessboard_specular_color, mtlximage17_out, Chessboard_specular_IOR, Chessboard_specular_anisotropy, Chessboard_specular_rotation, Chessboard_transmission, Chessboard_transmission_color, Chessboard_transmission_depth, Chessboard_transmission_scatter, Chessboard_transmission_scatter_anisotropy, Chessboard_transmission_dispersion, Chessboard_transmission_extra_roughness, Chessboard_subsurface, mtlximage13_out_cm_out, mtlximage13_out_cm_out, Chessboard_subsurface_scale, Chessboard_subsurface_anisotropy, Chessboard_sheen, Chessboard_sheen_color, Chessboard_sheen_roughness, Chessboard_coat, Chessboard_coat_color, Chessboard_coat_roughness, Chessboard_coat_anisotropy, Chessboard_coat_rotation, Chessboard_coat_IOR, geomprop_Nworld_out1, Chessboard_coat_affect_color, Chessboard_coat_affect_roughness, Chessboard_thin_film_thickness, Chessboard_thin_film_IOR, Chessboard_emission, Chessboard_emission_color, Chessboard_opacity, Chessboard_thin_walled, mtlxnormalmap12_out, geomprop_Tworld_out1);
+ material M_Chessboard_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: Chessboard_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = M_Chessboard_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/M_Chessboard.msl.frag b/Materials/Examples/StandardSurface/M_Chessboard.msl.frag
new file mode 100644
index 0000000000..7f5fb7452c
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Chessboard.msl.frag
@@ -0,0 +1,2784 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ int mtlximage13_layer;
+ vec3 mtlximage13_default;
+ int mtlximage13_uaddressmode;
+ int mtlximage13_vaddressmode;
+ int mtlximage13_filtertype;
+ int mtlximage13_framerange;
+ int mtlximage13_frameoffset;
+ int mtlximage13_frameendaction;
+ vec2 mtlximage13_uv_scale;
+ vec2 mtlximage13_uv_offset;
+ int mtlximage16_layer;
+ float mtlximage16_default;
+ int mtlximage16_uaddressmode;
+ int mtlximage16_vaddressmode;
+ int mtlximage16_filtertype;
+ int mtlximage16_framerange;
+ int mtlximage16_frameoffset;
+ int mtlximage16_frameendaction;
+ vec2 mtlximage16_uv_scale;
+ vec2 mtlximage16_uv_offset;
+ int mtlximage17_layer;
+ float mtlximage17_default;
+ int mtlximage17_uaddressmode;
+ int mtlximage17_vaddressmode;
+ int mtlximage17_filtertype;
+ int mtlximage17_framerange;
+ int mtlximage17_frameoffset;
+ int mtlximage17_frameendaction;
+ vec2 mtlximage17_uv_scale;
+ vec2 mtlximage17_uv_offset;
+ int mtlximage15_layer;
+ vec3 mtlximage15_default;
+ int mtlximage15_uaddressmode;
+ int mtlximage15_vaddressmode;
+ int mtlximage15_filtertype;
+ int mtlximage15_framerange;
+ int mtlximage15_frameoffset;
+ int mtlximage15_frameendaction;
+ vec2 mtlximage15_uv_scale;
+ vec2 mtlximage15_uv_offset;
+ int mtlxnormalmap12_space;
+ float mtlxnormalmap12_scale;
+ float Chessboard_base;
+ float Chessboard_diffuse_roughness;
+ float Chessboard_specular;
+ vec3 Chessboard_specular_color;
+ float Chessboard_specular_IOR;
+ float Chessboard_specular_anisotropy;
+ float Chessboard_specular_rotation;
+ float Chessboard_transmission;
+ vec3 Chessboard_transmission_color;
+ float Chessboard_transmission_depth;
+ vec3 Chessboard_transmission_scatter;
+ float Chessboard_transmission_scatter_anisotropy;
+ float Chessboard_transmission_dispersion;
+ float Chessboard_transmission_extra_roughness;
+ float Chessboard_subsurface;
+ float Chessboard_subsurface_scale;
+ float Chessboard_subsurface_anisotropy;
+ float Chessboard_sheen;
+ vec3 Chessboard_sheen_color;
+ float Chessboard_sheen_roughness;
+ float Chessboard_coat;
+ vec3 Chessboard_coat_color;
+ float Chessboard_coat_roughness;
+ float Chessboard_coat_anisotropy;
+ float Chessboard_coat_rotation;
+ float Chessboard_coat_IOR;
+ float Chessboard_coat_affect_color;
+ float Chessboard_coat_affect_roughness;
+ float Chessboard_thin_film_thickness;
+ float Chessboard_thin_film_IOR;
+ float Chessboard_emission;
+ vec3 Chessboard_emission_color;
+ vec3 Chessboard_opacity;
+ bool Chessboard_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec2 texcoord_0 ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+, MetalTexture mtlximage13_file , int mtlximage13_layer
+
+ , vec3 mtlximage13_default
+
+ , int mtlximage13_uaddressmode
+
+ , int mtlximage13_vaddressmode
+
+ , int mtlximage13_filtertype
+
+ , int mtlximage13_framerange
+
+ , int mtlximage13_frameoffset
+
+ , int mtlximage13_frameendaction
+
+ , vec2 mtlximage13_uv_scale
+
+ , vec2 mtlximage13_uv_offset
+
+, MetalTexture mtlximage16_file , int mtlximage16_layer
+
+ , float mtlximage16_default
+
+ , int mtlximage16_uaddressmode
+
+ , int mtlximage16_vaddressmode
+
+ , int mtlximage16_filtertype
+
+ , int mtlximage16_framerange
+
+ , int mtlximage16_frameoffset
+
+ , int mtlximage16_frameendaction
+
+ , vec2 mtlximage16_uv_scale
+
+ , vec2 mtlximage16_uv_offset
+
+, MetalTexture mtlximage17_file , int mtlximage17_layer
+
+ , float mtlximage17_default
+
+ , int mtlximage17_uaddressmode
+
+ , int mtlximage17_vaddressmode
+
+ , int mtlximage17_filtertype
+
+ , int mtlximage17_framerange
+
+ , int mtlximage17_frameoffset
+
+ , int mtlximage17_frameendaction
+
+ , vec2 mtlximage17_uv_scale
+
+ , vec2 mtlximage17_uv_offset
+
+, MetalTexture mtlximage15_file , int mtlximage15_layer
+
+ , vec3 mtlximage15_default
+
+ , int mtlximage15_uaddressmode
+
+ , int mtlximage15_vaddressmode
+
+ , int mtlximage15_filtertype
+
+ , int mtlximage15_framerange
+
+ , int mtlximage15_frameoffset
+
+ , int mtlximage15_frameendaction
+
+ , vec2 mtlximage15_uv_scale
+
+ , vec2 mtlximage15_uv_offset
+
+ , int mtlxnormalmap12_space
+
+ , float mtlxnormalmap12_scale
+
+ , float Chessboard_base
+
+ , float Chessboard_diffuse_roughness
+
+ , float Chessboard_specular
+
+ , vec3 Chessboard_specular_color
+
+ , float Chessboard_specular_IOR
+
+ , float Chessboard_specular_anisotropy
+
+ , float Chessboard_specular_rotation
+
+ , float Chessboard_transmission
+
+ , vec3 Chessboard_transmission_color
+
+ , float Chessboard_transmission_depth
+
+ , vec3 Chessboard_transmission_scatter
+
+ , float Chessboard_transmission_scatter_anisotropy
+
+ , float Chessboard_transmission_dispersion
+
+ , float Chessboard_transmission_extra_roughness
+
+ , float Chessboard_subsurface
+
+ , float Chessboard_subsurface_scale
+
+ , float Chessboard_subsurface_anisotropy
+
+ , float Chessboard_sheen
+
+ , vec3 Chessboard_sheen_color
+
+ , float Chessboard_sheen_roughness
+
+ , float Chessboard_coat
+
+ , vec3 Chessboard_coat_color
+
+ , float Chessboard_coat_roughness
+
+ , float Chessboard_coat_anisotropy
+
+ , float Chessboard_coat_rotation
+
+ , float Chessboard_coat_IOR
+
+ , float Chessboard_coat_affect_color
+
+ , float Chessboard_coat_affect_roughness
+
+ , float Chessboard_thin_film_thickness
+
+ , float Chessboard_thin_film_IOR
+
+ , float Chessboard_emission
+
+ , vec3 Chessboard_emission_color
+
+ , vec3 Chessboard_opacity
+
+ , bool Chessboard_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+, mtlximage13_file(mtlximage13_file)
+ , mtlximage13_layer(mtlximage13_layer)
+
+ , mtlximage13_default(mtlximage13_default)
+
+ , mtlximage13_uaddressmode(mtlximage13_uaddressmode)
+
+ , mtlximage13_vaddressmode(mtlximage13_vaddressmode)
+
+ , mtlximage13_filtertype(mtlximage13_filtertype)
+
+ , mtlximage13_framerange(mtlximage13_framerange)
+
+ , mtlximage13_frameoffset(mtlximage13_frameoffset)
+
+ , mtlximage13_frameendaction(mtlximage13_frameendaction)
+
+ , mtlximage13_uv_scale(mtlximage13_uv_scale)
+
+ , mtlximage13_uv_offset(mtlximage13_uv_offset)
+
+, mtlximage16_file(mtlximage16_file)
+ , mtlximage16_layer(mtlximage16_layer)
+
+ , mtlximage16_default(mtlximage16_default)
+
+ , mtlximage16_uaddressmode(mtlximage16_uaddressmode)
+
+ , mtlximage16_vaddressmode(mtlximage16_vaddressmode)
+
+ , mtlximage16_filtertype(mtlximage16_filtertype)
+
+ , mtlximage16_framerange(mtlximage16_framerange)
+
+ , mtlximage16_frameoffset(mtlximage16_frameoffset)
+
+ , mtlximage16_frameendaction(mtlximage16_frameendaction)
+
+ , mtlximage16_uv_scale(mtlximage16_uv_scale)
+
+ , mtlximage16_uv_offset(mtlximage16_uv_offset)
+
+, mtlximage17_file(mtlximage17_file)
+ , mtlximage17_layer(mtlximage17_layer)
+
+ , mtlximage17_default(mtlximage17_default)
+
+ , mtlximage17_uaddressmode(mtlximage17_uaddressmode)
+
+ , mtlximage17_vaddressmode(mtlximage17_vaddressmode)
+
+ , mtlximage17_filtertype(mtlximage17_filtertype)
+
+ , mtlximage17_framerange(mtlximage17_framerange)
+
+ , mtlximage17_frameoffset(mtlximage17_frameoffset)
+
+ , mtlximage17_frameendaction(mtlximage17_frameendaction)
+
+ , mtlximage17_uv_scale(mtlximage17_uv_scale)
+
+ , mtlximage17_uv_offset(mtlximage17_uv_offset)
+
+, mtlximage15_file(mtlximage15_file)
+ , mtlximage15_layer(mtlximage15_layer)
+
+ , mtlximage15_default(mtlximage15_default)
+
+ , mtlximage15_uaddressmode(mtlximage15_uaddressmode)
+
+ , mtlximage15_vaddressmode(mtlximage15_vaddressmode)
+
+ , mtlximage15_filtertype(mtlximage15_filtertype)
+
+ , mtlximage15_framerange(mtlximage15_framerange)
+
+ , mtlximage15_frameoffset(mtlximage15_frameoffset)
+
+ , mtlximage15_frameendaction(mtlximage15_frameendaction)
+
+ , mtlximage15_uv_scale(mtlximage15_uv_scale)
+
+ , mtlximage15_uv_offset(mtlximage15_uv_offset)
+
+ , mtlxnormalmap12_space(mtlxnormalmap12_space)
+
+ , mtlxnormalmap12_scale(mtlxnormalmap12_scale)
+
+ , Chessboard_base(Chessboard_base)
+
+ , Chessboard_diffuse_roughness(Chessboard_diffuse_roughness)
+
+ , Chessboard_specular(Chessboard_specular)
+
+ , Chessboard_specular_color(Chessboard_specular_color)
+
+ , Chessboard_specular_IOR(Chessboard_specular_IOR)
+
+ , Chessboard_specular_anisotropy(Chessboard_specular_anisotropy)
+
+ , Chessboard_specular_rotation(Chessboard_specular_rotation)
+
+ , Chessboard_transmission(Chessboard_transmission)
+
+ , Chessboard_transmission_color(Chessboard_transmission_color)
+
+ , Chessboard_transmission_depth(Chessboard_transmission_depth)
+
+ , Chessboard_transmission_scatter(Chessboard_transmission_scatter)
+
+ , Chessboard_transmission_scatter_anisotropy(Chessboard_transmission_scatter_anisotropy)
+
+ , Chessboard_transmission_dispersion(Chessboard_transmission_dispersion)
+
+ , Chessboard_transmission_extra_roughness(Chessboard_transmission_extra_roughness)
+
+ , Chessboard_subsurface(Chessboard_subsurface)
+
+ , Chessboard_subsurface_scale(Chessboard_subsurface_scale)
+
+ , Chessboard_subsurface_anisotropy(Chessboard_subsurface_anisotropy)
+
+ , Chessboard_sheen(Chessboard_sheen)
+
+ , Chessboard_sheen_color(Chessboard_sheen_color)
+
+ , Chessboard_sheen_roughness(Chessboard_sheen_roughness)
+
+ , Chessboard_coat(Chessboard_coat)
+
+ , Chessboard_coat_color(Chessboard_coat_color)
+
+ , Chessboard_coat_roughness(Chessboard_coat_roughness)
+
+ , Chessboard_coat_anisotropy(Chessboard_coat_anisotropy)
+
+ , Chessboard_coat_rotation(Chessboard_coat_rotation)
+
+ , Chessboard_coat_IOR(Chessboard_coat_IOR)
+
+ , Chessboard_coat_affect_color(Chessboard_coat_affect_color)
+
+ , Chessboard_coat_affect_roughness(Chessboard_coat_affect_roughness)
+
+ , Chessboard_thin_film_thickness(Chessboard_thin_film_thickness)
+
+ , Chessboard_thin_film_IOR(Chessboard_thin_film_IOR)
+
+ , Chessboard_emission(Chessboard_emission)
+
+ , Chessboard_emission_color(Chessboard_emission_color)
+
+ , Chessboard_opacity(Chessboard_opacity)
+
+ , Chessboard_thin_walled(Chessboard_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+MetalTexture mtlximage13_file;
+ int mtlximage13_layer;
+
+
+ vec3 mtlximage13_default;
+
+
+ int mtlximage13_uaddressmode;
+
+
+ int mtlximage13_vaddressmode;
+
+
+ int mtlximage13_filtertype;
+
+
+ int mtlximage13_framerange;
+
+
+ int mtlximage13_frameoffset;
+
+
+ int mtlximage13_frameendaction;
+
+
+ vec2 mtlximage13_uv_scale;
+
+
+ vec2 mtlximage13_uv_offset;
+
+
+MetalTexture mtlximage16_file;
+ int mtlximage16_layer;
+
+
+ float mtlximage16_default;
+
+
+ int mtlximage16_uaddressmode;
+
+
+ int mtlximage16_vaddressmode;
+
+
+ int mtlximage16_filtertype;
+
+
+ int mtlximage16_framerange;
+
+
+ int mtlximage16_frameoffset;
+
+
+ int mtlximage16_frameendaction;
+
+
+ vec2 mtlximage16_uv_scale;
+
+
+ vec2 mtlximage16_uv_offset;
+
+
+MetalTexture mtlximage17_file;
+ int mtlximage17_layer;
+
+
+ float mtlximage17_default;
+
+
+ int mtlximage17_uaddressmode;
+
+
+ int mtlximage17_vaddressmode;
+
+
+ int mtlximage17_filtertype;
+
+
+ int mtlximage17_framerange;
+
+
+ int mtlximage17_frameoffset;
+
+
+ int mtlximage17_frameendaction;
+
+
+ vec2 mtlximage17_uv_scale;
+
+
+ vec2 mtlximage17_uv_offset;
+
+
+MetalTexture mtlximage15_file;
+ int mtlximage15_layer;
+
+
+ vec3 mtlximage15_default;
+
+
+ int mtlximage15_uaddressmode;
+
+
+ int mtlximage15_vaddressmode;
+
+
+ int mtlximage15_filtertype;
+
+
+ int mtlximage15_framerange;
+
+
+ int mtlximage15_frameoffset;
+
+
+ int mtlximage15_frameendaction;
+
+
+ vec2 mtlximage15_uv_scale;
+
+
+ vec2 mtlximage15_uv_offset;
+
+
+ int mtlxnormalmap12_space;
+
+
+ float mtlxnormalmap12_scale;
+
+
+ float Chessboard_base;
+
+
+ float Chessboard_diffuse_roughness;
+
+
+ float Chessboard_specular;
+
+
+ vec3 Chessboard_specular_color;
+
+
+ float Chessboard_specular_IOR;
+
+
+ float Chessboard_specular_anisotropy;
+
+
+ float Chessboard_specular_rotation;
+
+
+ float Chessboard_transmission;
+
+
+ vec3 Chessboard_transmission_color;
+
+
+ float Chessboard_transmission_depth;
+
+
+ vec3 Chessboard_transmission_scatter;
+
+
+ float Chessboard_transmission_scatter_anisotropy;
+
+
+ float Chessboard_transmission_dispersion;
+
+
+ float Chessboard_transmission_extra_roughness;
+
+
+ float Chessboard_subsurface;
+
+
+ float Chessboard_subsurface_scale;
+
+
+ float Chessboard_subsurface_anisotropy;
+
+
+ float Chessboard_sheen;
+
+
+ vec3 Chessboard_sheen_color;
+
+
+ float Chessboard_sheen_roughness;
+
+
+ float Chessboard_coat;
+
+
+ vec3 Chessboard_coat_color;
+
+
+ float Chessboard_coat_roughness;
+
+
+ float Chessboard_coat_anisotropy;
+
+
+ float Chessboard_coat_rotation;
+
+
+ float Chessboard_coat_IOR;
+
+
+ float Chessboard_coat_affect_color;
+
+
+ float Chessboard_coat_affect_roughness;
+
+
+ float Chessboard_thin_film_thickness;
+
+
+ float Chessboard_thin_film_IOR;
+
+
+ float Chessboard_emission;
+
+
+ vec3 Chessboard_emission_color;
+
+
+ vec3 Chessboard_opacity;
+
+
+ bool Chessboard_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+ {
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+ }
+
+ void mx_image_color3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_float(MetalTexture tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread float& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_vector3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+ void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, thread vec3& out1)
+ {
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+ }
+
+ void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, thread vec3& result)
+ {
+ // Decode the normal map.
+ value = all(value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 mtlximage13_out = vec3(0.0);
+ mx_image_color3(mtlximage13_file, mtlximage13_layer, mtlximage13_default, geomprop_UV0_out1, mtlximage13_uaddressmode, mtlximage13_vaddressmode, mtlximage13_filtertype, mtlximage13_framerange, mtlximage13_frameoffset, mtlximage13_frameendaction, mtlximage13_uv_scale, mtlximage13_uv_offset, mtlximage13_out);
+ float mtlximage16_out = 0.0;
+ mx_image_float(mtlximage16_file, mtlximage16_layer, mtlximage16_default, geomprop_UV0_out1, mtlximage16_uaddressmode, mtlximage16_vaddressmode, mtlximage16_filtertype, mtlximage16_framerange, mtlximage16_frameoffset, mtlximage16_frameendaction, mtlximage16_uv_scale, mtlximage16_uv_offset, mtlximage16_out);
+ float mtlximage17_out = 0.0;
+ mx_image_float(mtlximage17_file, mtlximage17_layer, mtlximage17_default, geomprop_UV0_out1, mtlximage17_uaddressmode, mtlximage17_vaddressmode, mtlximage17_filtertype, mtlximage17_framerange, mtlximage17_frameoffset, mtlximage17_frameendaction, mtlximage17_uv_scale, mtlximage17_uv_offset, mtlximage17_out);
+ vec3 mtlximage15_out = vec3(0.0);
+ mx_image_vector3(mtlximage15_file, mtlximage15_layer, mtlximage15_default, geomprop_UV0_out1, mtlximage15_uaddressmode, mtlximage15_vaddressmode, mtlximage15_filtertype, mtlximage15_framerange, mtlximage15_frameoffset, mtlximage15_frameendaction, mtlximage15_uv_scale, mtlximage15_uv_offset, mtlximage15_out);
+ vec3 mtlximage13_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(mtlximage13_out, mtlximage13_out_cm_out);
+ vec3 mtlxnormalmap12_out = vec3(0.0);
+ mx_normalmap(mtlximage15_out, mtlxnormalmap12_space, mtlxnormalmap12_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap12_out);
+ surfaceshader Chessboard_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(Chessboard_base, mtlximage13_out_cm_out, Chessboard_diffuse_roughness, mtlximage16_out, Chessboard_specular, Chessboard_specular_color, mtlximage17_out, Chessboard_specular_IOR, Chessboard_specular_anisotropy, Chessboard_specular_rotation, Chessboard_transmission, Chessboard_transmission_color, Chessboard_transmission_depth, Chessboard_transmission_scatter, Chessboard_transmission_scatter_anisotropy, Chessboard_transmission_dispersion, Chessboard_transmission_extra_roughness, Chessboard_subsurface, mtlximage13_out_cm_out, mtlximage13_out_cm_out, Chessboard_subsurface_scale, Chessboard_subsurface_anisotropy, Chessboard_sheen, Chessboard_sheen_color, Chessboard_sheen_roughness, Chessboard_coat, Chessboard_coat_color, Chessboard_coat_roughness, Chessboard_coat_anisotropy, Chessboard_coat_rotation, Chessboard_coat_IOR, geomprop_Nworld_out1, Chessboard_coat_affect_color, Chessboard_coat_affect_roughness, Chessboard_thin_film_thickness, Chessboard_thin_film_IOR, Chessboard_emission, Chessboard_emission_color, Chessboard_opacity, Chessboard_thin_walled, mtlxnormalmap12_out, geomprop_Tworld_out1, Chessboard_out);
+ material M_Chessboard_out = Chessboard_out;
+ out1 = float4(M_Chessboard_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], texture2d mtlximage13_file_tex [[texture(0)]], sampler mtlximage13_file_sampler [[sampler(0)]]
+, texture2d mtlximage16_file_tex [[texture(1)]], sampler mtlximage16_file_sampler [[sampler(1)]]
+, texture2d mtlximage17_file_tex [[texture(2)]], sampler mtlximage17_file_sampler [[sampler(2)]]
+, texture2d mtlximage15_file_tex [[texture(3)]], sampler mtlximage15_file_sampler [[sampler(3)]]
+, constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(4)]], sampler u_envRadiance_sampler [[sampler(4)]]
+, texture2d u_envIrradiance_tex [[texture(5)]], sampler u_envIrradiance_sampler [[sampler(5)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+, MetalTexture {
+mtlximage13_file_tex, mtlximage13_file_sampler }
+ , u_pub.mtlximage13_layer
+ , u_pub.mtlximage13_default
+ , u_pub.mtlximage13_uaddressmode
+ , u_pub.mtlximage13_vaddressmode
+ , u_pub.mtlximage13_filtertype
+ , u_pub.mtlximage13_framerange
+ , u_pub.mtlximage13_frameoffset
+ , u_pub.mtlximage13_frameendaction
+ , u_pub.mtlximage13_uv_scale
+ , u_pub.mtlximage13_uv_offset
+, MetalTexture {
+mtlximage16_file_tex, mtlximage16_file_sampler }
+ , u_pub.mtlximage16_layer
+ , u_pub.mtlximage16_default
+ , u_pub.mtlximage16_uaddressmode
+ , u_pub.mtlximage16_vaddressmode
+ , u_pub.mtlximage16_filtertype
+ , u_pub.mtlximage16_framerange
+ , u_pub.mtlximage16_frameoffset
+ , u_pub.mtlximage16_frameendaction
+ , u_pub.mtlximage16_uv_scale
+ , u_pub.mtlximage16_uv_offset
+, MetalTexture {
+mtlximage17_file_tex, mtlximage17_file_sampler }
+ , u_pub.mtlximage17_layer
+ , u_pub.mtlximage17_default
+ , u_pub.mtlximage17_uaddressmode
+ , u_pub.mtlximage17_vaddressmode
+ , u_pub.mtlximage17_filtertype
+ , u_pub.mtlximage17_framerange
+ , u_pub.mtlximage17_frameoffset
+ , u_pub.mtlximage17_frameendaction
+ , u_pub.mtlximage17_uv_scale
+ , u_pub.mtlximage17_uv_offset
+, MetalTexture {
+mtlximage15_file_tex, mtlximage15_file_sampler }
+ , u_pub.mtlximage15_layer
+ , u_pub.mtlximage15_default
+ , u_pub.mtlximage15_uaddressmode
+ , u_pub.mtlximage15_vaddressmode
+ , u_pub.mtlximage15_filtertype
+ , u_pub.mtlximage15_framerange
+ , u_pub.mtlximage15_frameoffset
+ , u_pub.mtlximage15_frameendaction
+ , u_pub.mtlximage15_uv_scale
+ , u_pub.mtlximage15_uv_offset
+ , u_pub.mtlxnormalmap12_space
+ , u_pub.mtlxnormalmap12_scale
+ , u_pub.Chessboard_base
+ , u_pub.Chessboard_diffuse_roughness
+ , u_pub.Chessboard_specular
+ , u_pub.Chessboard_specular_color
+ , u_pub.Chessboard_specular_IOR
+ , u_pub.Chessboard_specular_anisotropy
+ , u_pub.Chessboard_specular_rotation
+ , u_pub.Chessboard_transmission
+ , u_pub.Chessboard_transmission_color
+ , u_pub.Chessboard_transmission_depth
+ , u_pub.Chessboard_transmission_scatter
+ , u_pub.Chessboard_transmission_scatter_anisotropy
+ , u_pub.Chessboard_transmission_dispersion
+ , u_pub.Chessboard_transmission_extra_roughness
+ , u_pub.Chessboard_subsurface
+ , u_pub.Chessboard_subsurface_scale
+ , u_pub.Chessboard_subsurface_anisotropy
+ , u_pub.Chessboard_sheen
+ , u_pub.Chessboard_sheen_color
+ , u_pub.Chessboard_sheen_roughness
+ , u_pub.Chessboard_coat
+ , u_pub.Chessboard_coat_color
+ , u_pub.Chessboard_coat_roughness
+ , u_pub.Chessboard_coat_anisotropy
+ , u_pub.Chessboard_coat_rotation
+ , u_pub.Chessboard_coat_IOR
+ , u_pub.Chessboard_coat_affect_color
+ , u_pub.Chessboard_coat_affect_roughness
+ , u_pub.Chessboard_thin_film_thickness
+ , u_pub.Chessboard_thin_film_IOR
+ , u_pub.Chessboard_emission
+ , u_pub.Chessboard_emission_color
+ , u_pub.Chessboard_opacity
+ , u_pub.Chessboard_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Chessboard.msl.vert b/Materials/Examples/StandardSurface/M_Chessboard.msl.vert
new file mode 100644
index 0000000000..bf89212a19
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Chessboard.msl.vert
@@ -0,0 +1,124 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+ vec2 i_texcoord_0 [[attribute(3)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+, vec2 i_texcoord_0
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+, i_texcoord_0(i_texcoord_0)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ vec2 i_texcoord_0;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'geomprop_UV0'. Function already called in this scope.
+ // Omitted node 'mtlximage13'. Function already called in this scope.
+ // Omitted node 'mtlximage16'. Function already called in this scope.
+ // Omitted node 'mtlximage17'. Function already called in this scope.
+ // Omitted node 'mtlximage15'. Function already called in this scope.
+ // Omitted node 'mtlximage13_out_cm'. Function already called in this scope.
+ // Omitted node 'mtlxnormalmap12'. Function already called in this scope.
+ // Omitted node 'Chessboard'. Function already called in this scope.
+ // Omitted node 'M_Chessboard'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(4) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent, i_vs.i_texcoord_0 , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_Chessboard.osl b/Materials/Examples/StandardSurface/M_Chessboard.osl
new file mode 100644
index 0000000000..6ddeaa6351
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_Chessboard.osl
@@ -0,0 +1,815 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+vector2 mx_transform_uv(vector2 texcoord)
+{
+ return texcoord;
+}
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode );
+}
+
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+ out = rgb[0];
+}
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(color in, output color out)
+{
+ float bias_in2_tmp = 0.055;
+ color bias_out = in + bias_in2_tmp;
+ float linSeg_in2_tmp = 12.92;
+ color linSeg_out = in / linSeg_in2_tmp;
+ float isAboveR_value2_tmp = 0.04045;
+ float isAboveR_in1_tmp = 1;
+ float isAboveR_in2_tmp = 0;
+ float isAboveR_out = mx_ternary(in[0] > isAboveR_value2_tmp, isAboveR_in1_tmp, isAboveR_in2_tmp);
+ float isAboveG_value2_tmp = 0.04045;
+ float isAboveG_in1_tmp = 1;
+ float isAboveG_in2_tmp = 0;
+ float isAboveG_out = mx_ternary(in[1] > isAboveG_value2_tmp, isAboveG_in1_tmp, isAboveG_in2_tmp);
+ float isAboveB_value2_tmp = 0.04045;
+ float isAboveB_in1_tmp = 1;
+ float isAboveB_in2_tmp = 0;
+ float isAboveB_out = mx_ternary(in[2] > isAboveB_value2_tmp, isAboveB_in1_tmp, isAboveB_in2_tmp);
+ float max_in2_tmp = 0;
+ color max_out = max(bias_out, max_in2_tmp);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ float scale_in2_tmp = 1.055;
+ color scale_out = max_out / scale_in2_tmp;
+ float powSeg_in2_tmp = 2.4;
+ color powSeg_out = pow(scale_out, powSeg_in2_tmp);
+ color mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out = mix_out;
+}
+
+void mx_normalmap(vector value, string map_space, float normal_scale, vector N, vector U, output vector result)
+{
+ // Tangent space
+ if (map_space == "tangent")
+ {
+ vector v = value * 2.0 - 1.0;
+ vector T = normalize(U - dot(U, N) * N);
+ vector B = normalize(cross(N, T));
+ result = normalize(T * v[0] * normal_scale + B * v[1] * normal_scale + N * v[2]);
+ }
+ // Object space
+ else
+ {
+ vector n = value * 2.0 - 1.0;
+ result = normalize(n);
+ }
+}
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader M_Chessboard
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "M_Chessboard"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ int geomprop_UV0_index = 0
+ [[
+ string widget = "number"
+ ]],
+ textureresource mtlximage13_file = {"chess_set/chessboard_base_color.jpg", "srgb_texture"}
+ [[
+ string widget = "filename"
+ ]],
+ string mtlximage13_layer = "",
+ color mtlximage13_default = color(0, 0, 0),
+ string mtlximage13_uaddressmode = "periodic",
+ string mtlximage13_vaddressmode = "periodic",
+ string mtlximage13_filtertype = "linear",
+ string mtlximage13_framerange = "",
+ int mtlximage13_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage13_frameendaction = "constant",
+ textureresource mtlximage16_file = {"chess_set/chessboard_metallic.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string mtlximage16_layer = "",
+ float mtlximage16_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage16_uaddressmode = "periodic",
+ string mtlximage16_vaddressmode = "periodic",
+ string mtlximage16_filtertype = "linear",
+ string mtlximage16_framerange = "",
+ int mtlximage16_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage16_frameendaction = "constant",
+ textureresource mtlximage17_file = {"chess_set/chessboard_roughness.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string mtlximage17_layer = "",
+ float mtlximage17_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage17_uaddressmode = "periodic",
+ string mtlximage17_vaddressmode = "periodic",
+ string mtlximage17_filtertype = "linear",
+ string mtlximage17_framerange = "",
+ int mtlximage17_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage17_frameendaction = "constant",
+ textureresource mtlximage15_file = {"chess_set/chessboard_normal.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string mtlximage15_layer = "",
+ vector mtlximage15_default = vector(0, 0, 0),
+ string mtlximage15_uaddressmode = "periodic",
+ string mtlximage15_vaddressmode = "periodic",
+ string mtlximage15_filtertype = "linear",
+ string mtlximage15_framerange = "",
+ int mtlximage15_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage15_frameendaction = "constant",
+ string mtlxnormalmap12_space = "tangent",
+ float mtlxnormalmap12_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_base = 1
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color Chessboard_specular_color = color(1, 1, 1),
+ float Chessboard_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color Chessboard_transmission_color = color(1, 1, 1),
+ float Chessboard_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color Chessboard_transmission_scatter = color(0, 0, 0),
+ float Chessboard_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_subsurface = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_subsurface_scale = 0.003
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color Chessboard_sheen_color = color(1, 1, 1),
+ float Chessboard_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color Chessboard_coat_color = color(1, 1, 1),
+ float Chessboard_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float Chessboard_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color Chessboard_emission_color = color(1, 1, 1),
+ color Chessboard_opacity = color(1, 1, 1),
+ int Chessboard_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ vector2 geomprop_UV0_out1 = vector2(u,v);
+ color mtlximage13_out = color(0.0);
+ mx_image_color3(mtlximage13_file, mtlximage13_layer, mtlximage13_default, geomprop_UV0_out1, mtlximage13_uaddressmode, mtlximage13_vaddressmode, mtlximage13_filtertype, mtlximage13_framerange, mtlximage13_frameoffset, mtlximage13_frameendaction, mtlximage13_out);
+ float mtlximage16_out = 0.0;
+ mx_image_float(mtlximage16_file, mtlximage16_layer, mtlximage16_default, geomprop_UV0_out1, mtlximage16_uaddressmode, mtlximage16_vaddressmode, mtlximage16_filtertype, mtlximage16_framerange, mtlximage16_frameoffset, mtlximage16_frameendaction, mtlximage16_out);
+ float mtlximage17_out = 0.0;
+ mx_image_float(mtlximage17_file, mtlximage17_layer, mtlximage17_default, geomprop_UV0_out1, mtlximage17_uaddressmode, mtlximage17_vaddressmode, mtlximage17_filtertype, mtlximage17_framerange, mtlximage17_frameoffset, mtlximage17_frameendaction, mtlximage17_out);
+ vector mtlximage15_out = vector(0.0);
+ mx_image_vector3(mtlximage15_file, mtlximage15_layer, mtlximage15_default, geomprop_UV0_out1, mtlximage15_uaddressmode, mtlximage15_vaddressmode, mtlximage15_filtertype, mtlximage15_framerange, mtlximage15_frameoffset, mtlximage15_frameendaction, mtlximage15_out);
+ color mtlximage13_out_cm_out = color(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(mtlximage13_out, mtlximage13_out_cm_out);
+ vector mtlxnormalmap12_out = vector(0.0);
+ mx_normalmap(mtlximage15_out, mtlxnormalmap12_space, mtlxnormalmap12_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap12_out);
+ surfaceshader Chessboard_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(Chessboard_base, mtlximage13_out_cm_out, Chessboard_diffuse_roughness, mtlximage16_out, Chessboard_specular, Chessboard_specular_color, mtlximage17_out, Chessboard_specular_IOR, Chessboard_specular_anisotropy, Chessboard_specular_rotation, Chessboard_transmission, Chessboard_transmission_color, Chessboard_transmission_depth, Chessboard_transmission_scatter, Chessboard_transmission_scatter_anisotropy, Chessboard_transmission_dispersion, Chessboard_transmission_extra_roughness, Chessboard_subsurface, mtlximage13_out_cm_out, mtlximage13_out_cm_out, Chessboard_subsurface_scale, Chessboard_subsurface_anisotropy, Chessboard_sheen, Chessboard_sheen_color, Chessboard_sheen_roughness, Chessboard_coat, Chessboard_coat_color, Chessboard_coat_roughness, Chessboard_coat_anisotropy, Chessboard_coat_rotation, Chessboard_coat_IOR, geomprop_Nworld_out1, Chessboard_coat_affect_color, Chessboard_coat_affect_roughness, Chessboard_thin_film_thickness, Chessboard_thin_film_IOR, Chessboard_emission, Chessboard_emission_color, Chessboard_opacity, Chessboard_thin_walled, mtlxnormalmap12_out, geomprop_Tworld_out1, Chessboard_out);
+ MATERIAL M_Chessboard_out = mx_surfacematerial(Chessboard_out, displacementshader1);
+ out = M_Chessboard_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_King_B.glsl.frag b/Materials/Examples/StandardSurface/M_King_B.glsl.frag
new file mode 100644
index 0000000000..191322e375
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_King_B.glsl.frag
@@ -0,0 +1,1866 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform sampler2D mtlximage1_file;
+uniform int mtlximage1_layer = 0;
+uniform vec3 mtlximage1_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int mtlximage1_uaddressmode = 2;
+uniform int mtlximage1_vaddressmode = 2;
+uniform int mtlximage1_filtertype = 1;
+uniform int mtlximage1_framerange = 0;
+uniform int mtlximage1_frameoffset = 0;
+uniform int mtlximage1_frameendaction = 0;
+uniform vec2 mtlximage1_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage1_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D mtlximage2_file;
+uniform int mtlximage2_layer = 0;
+uniform float mtlximage2_default = 0.000000;
+uniform int mtlximage2_uaddressmode = 2;
+uniform int mtlximage2_vaddressmode = 2;
+uniform int mtlximage2_filtertype = 1;
+uniform int mtlximage2_framerange = 0;
+uniform int mtlximage2_frameoffset = 0;
+uniform int mtlximage2_frameendaction = 0;
+uniform vec2 mtlximage2_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage2_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D mtlximage4_file;
+uniform int mtlximage4_layer = 0;
+uniform float mtlximage4_default = 0.000000;
+uniform int mtlximage4_uaddressmode = 2;
+uniform int mtlximage4_vaddressmode = 2;
+uniform int mtlximage4_filtertype = 1;
+uniform int mtlximage4_framerange = 0;
+uniform int mtlximage4_frameoffset = 0;
+uniform int mtlximage4_frameendaction = 0;
+uniform vec2 mtlximage4_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage4_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D mtlximage3_file;
+uniform int mtlximage3_layer = 0;
+uniform float mtlximage3_default = 0.000000;
+uniform int mtlximage3_uaddressmode = 2;
+uniform int mtlximage3_vaddressmode = 2;
+uniform int mtlximage3_filtertype = 1;
+uniform int mtlximage3_framerange = 0;
+uniform int mtlximage3_frameoffset = 0;
+uniform int mtlximage3_frameendaction = 0;
+uniform vec2 mtlximage3_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage3_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D mtlximage6_file;
+uniform int mtlximage6_layer = 0;
+uniform vec3 mtlximage6_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int mtlximage6_uaddressmode = 2;
+uniform int mtlximage6_vaddressmode = 2;
+uniform int mtlximage6_filtertype = 1;
+uniform int mtlximage6_framerange = 0;
+uniform int mtlximage6_frameoffset = 0;
+uniform int mtlximage6_frameendaction = 0;
+uniform vec2 mtlximage6_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage6_uv_offset = vec2(0.000000, 0.000000);
+uniform int mtlxnormalmap1_space = 0;
+uniform float mtlxnormalmap1_scale = 1.000000;
+uniform float King_B_base = 1.000000;
+uniform float King_B_diffuse_roughness = 0.000000;
+uniform float King_B_specular = 1.000000;
+uniform vec3 King_B_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float King_B_specular_IOR = 1.500000;
+uniform float King_B_specular_anisotropy = 0.000000;
+uniform float King_B_specular_rotation = 0.000000;
+uniform float King_B_transmission = 0.000000;
+uniform vec3 King_B_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float King_B_transmission_depth = 0.000000;
+uniform vec3 King_B_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float King_B_transmission_scatter_anisotropy = 0.000000;
+uniform float King_B_transmission_dispersion = 0.000000;
+uniform float King_B_transmission_extra_roughness = 0.000000;
+uniform float King_B_subsurface_scale = 0.003000;
+uniform float King_B_subsurface_anisotropy = 0.000000;
+uniform float King_B_sheen = 0.000000;
+uniform vec3 King_B_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float King_B_sheen_roughness = 0.300000;
+uniform float King_B_coat = 0.000000;
+uniform vec3 King_B_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float King_B_coat_roughness = 0.100000;
+uniform float King_B_coat_anisotropy = 0.000000;
+uniform float King_B_coat_rotation = 0.000000;
+uniform float King_B_coat_IOR = 1.500000;
+uniform float King_B_coat_affect_color = 0.000000;
+uniform float King_B_coat_affect_roughness = 0.000000;
+uniform float King_B_thin_film_thickness = 0.000000;
+uniform float King_B_thin_film_IOR = 1.500000;
+uniform float King_B_emission = 0.000000;
+uniform vec3 King_B_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 King_B_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool King_B_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+{
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+}
+
+void mx_image_color3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_float(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_vector3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, out vec3 out1)
+{
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+}
+
+void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, out vec3 result)
+{
+ // Decode the normal map.
+ value = (value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 mtlximage1_out = vec3(0.0);
+ mx_image_color3(mtlximage1_file, mtlximage1_layer, mtlximage1_default, geomprop_UV0_out1, mtlximage1_uaddressmode, mtlximage1_vaddressmode, mtlximage1_filtertype, mtlximage1_framerange, mtlximage1_frameoffset, mtlximage1_frameendaction, mtlximage1_uv_scale, mtlximage1_uv_offset, mtlximage1_out);
+ float mtlximage2_out = 0.0;
+ mx_image_float(mtlximage2_file, mtlximage2_layer, mtlximage2_default, geomprop_UV0_out1, mtlximage2_uaddressmode, mtlximage2_vaddressmode, mtlximage2_filtertype, mtlximage2_framerange, mtlximage2_frameoffset, mtlximage2_frameendaction, mtlximage2_uv_scale, mtlximage2_uv_offset, mtlximage2_out);
+ float mtlximage4_out = 0.0;
+ mx_image_float(mtlximage4_file, mtlximage4_layer, mtlximage4_default, geomprop_UV0_out1, mtlximage4_uaddressmode, mtlximage4_vaddressmode, mtlximage4_filtertype, mtlximage4_framerange, mtlximage4_frameoffset, mtlximage4_frameendaction, mtlximage4_uv_scale, mtlximage4_uv_offset, mtlximage4_out);
+ float mtlximage3_out = 0.0;
+ mx_image_float(mtlximage3_file, mtlximage3_layer, mtlximage3_default, geomprop_UV0_out1, mtlximage3_uaddressmode, mtlximage3_vaddressmode, mtlximage3_filtertype, mtlximage3_framerange, mtlximage3_frameoffset, mtlximage3_frameendaction, mtlximage3_uv_scale, mtlximage3_uv_offset, mtlximage3_out);
+ vec3 mtlximage6_out = vec3(0.0);
+ mx_image_vector3(mtlximage6_file, mtlximage6_layer, mtlximage6_default, geomprop_UV0_out1, mtlximage6_uaddressmode, mtlximage6_vaddressmode, mtlximage6_filtertype, mtlximage6_framerange, mtlximage6_frameoffset, mtlximage6_frameendaction, mtlximage6_uv_scale, mtlximage6_uv_offset, mtlximage6_out);
+ vec3 mtlximage1_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(mtlximage1_out, mtlximage1_out_cm_out);
+ vec3 mtlxnormalmap1_out = vec3(0.0);
+ mx_normalmap(mtlximage6_out, mtlxnormalmap1_space, mtlxnormalmap1_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap1_out);
+ surfaceshader King_B_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(King_B_base, mtlximage1_out_cm_out, King_B_diffuse_roughness, mtlximage2_out, King_B_specular, King_B_specular_color, mtlximage4_out, King_B_specular_IOR, King_B_specular_anisotropy, King_B_specular_rotation, King_B_transmission, King_B_transmission_color, King_B_transmission_depth, King_B_transmission_scatter, King_B_transmission_scatter_anisotropy, King_B_transmission_dispersion, King_B_transmission_extra_roughness, mtlximage3_out, mtlximage1_out_cm_out, mtlximage1_out_cm_out, King_B_subsurface_scale, King_B_subsurface_anisotropy, King_B_sheen, King_B_sheen_color, King_B_sheen_roughness, King_B_coat, King_B_coat_color, King_B_coat_roughness, King_B_coat_anisotropy, King_B_coat_rotation, King_B_coat_IOR, geomprop_Nworld_out1, King_B_coat_affect_color, King_B_coat_affect_roughness, King_B_thin_film_thickness, King_B_thin_film_IOR, King_B_emission, King_B_emission_color, King_B_opacity, King_B_thin_walled, mtlxnormalmap1_out, geomprop_Tworld_out1, King_B_out);
+ material M_King_B_out = King_B_out;
+ out1 = vec4(M_King_B_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/M_King_B.glsl.vert b/Materials/Examples/StandardSurface/M_King_B.glsl.vert
new file mode 100644
index 0000000000..60b47d493e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_King_B.glsl.vert
@@ -0,0 +1,31 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+in vec2 i_texcoord_0;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_King_B.mdl b/Materials/Examples/StandardSurface/M_King_B.mdl
new file mode 100644
index 0000000000..6dd75082e1
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_King_B.mdl
@@ -0,0 +1,243 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+color NG_srgb_texture_to_lin_rec709_color3
+(
+ color in1 = color(0, 0, 0)
+)
+{
+ color bias_out = in1 + 0.055;
+ color linSeg_out = in1 / 12.92;
+ float isAboveR_out = mx::stdlib::mx_ifgreater_float(float3(in1).x, 0.04045, 1, 0);
+ float isAboveG_out = mx::stdlib::mx_ifgreater_float(float3(in1).y, 0.04045, 1, 0);
+ float isAboveB_out = mx::stdlib::mx_ifgreater_float(float3(in1).z, 0.04045, 1, 0);
+ color max_out = math::max(bias_out, 0);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ color scale_out = max_out / 1.055;
+ color powSeg_out = math::pow(scale_out, 2.4);
+ color mix_out = math::lerp(linSeg_out, powSeg_out, isAbove_out);
+ return mix_out;
+}
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material M_King_B
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ uniform int geomprop_UV0_index = 0,
+ uniform texture_2d mtlximage1_file = texture_2d("/chess_set/king_black_base_color.jpg", tex::gamma_linear),
+ uniform string mtlximage1_layer = "",
+ color mtlximage1_default = color(0, 0, 0),
+ uniform mx_addressmode_type mtlximage1_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage1_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage1_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage1_framerange = "",
+ uniform int mtlximage1_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage1_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d mtlximage2_file = texture_2d("/chess_set/king_shared_metallic.jpg", tex::gamma_linear),
+ uniform string mtlximage2_layer = "",
+ float mtlximage2_default = 0,
+ uniform mx_addressmode_type mtlximage2_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage2_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage2_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage2_framerange = "",
+ uniform int mtlximage2_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage2_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d mtlximage4_file = texture_2d("/chess_set/king_black_roughness.jpg", tex::gamma_linear),
+ uniform string mtlximage4_layer = "",
+ float mtlximage4_default = 0,
+ uniform mx_addressmode_type mtlximage4_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage4_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage4_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage4_framerange = "",
+ uniform int mtlximage4_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage4_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d mtlximage3_file = texture_2d("/chess_set/king_shared_scattering.jpg", tex::gamma_linear),
+ uniform string mtlximage3_layer = "",
+ float mtlximage3_default = 0,
+ uniform mx_addressmode_type mtlximage3_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage3_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage3_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage3_framerange = "",
+ uniform int mtlximage3_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage3_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d mtlximage6_file = texture_2d("/chess_set/king_black_normal.jpg", tex::gamma_linear),
+ uniform string mtlximage6_layer = "",
+ float3 mtlximage6_default = float3(0, 0, 0),
+ uniform mx_addressmode_type mtlximage6_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage6_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage6_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage6_framerange = "",
+ uniform int mtlximage6_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage6_frameendaction = mx_addressmode_type_constant,
+ uniform string mtlxnormalmap1_space = "tangent",
+ float mtlxnormalmap1_scale = 1,
+ float King_B_base = 1,
+ float King_B_diffuse_roughness = 0,
+ float King_B_specular = 1,
+ color King_B_specular_color = color(1, 1, 1),
+ uniform float King_B_specular_IOR = 1.5,
+ float King_B_specular_anisotropy = 0,
+ float King_B_specular_rotation = 0,
+ float King_B_transmission = 0,
+ color King_B_transmission_color = color(1, 1, 1),
+ float King_B_transmission_depth = 0,
+ color King_B_transmission_scatter = color(0, 0, 0),
+ float King_B_transmission_scatter_anisotropy = 0,
+ float King_B_transmission_dispersion = 0,
+ float King_B_transmission_extra_roughness = 0,
+ float King_B_subsurface_scale = 0.003,
+ float King_B_subsurface_anisotropy = 0,
+ float King_B_sheen = 0,
+ color King_B_sheen_color = color(1, 1, 1),
+ float King_B_sheen_roughness = 0.3,
+ float King_B_coat = 0,
+ color King_B_coat_color = color(1, 1, 1),
+ float King_B_coat_roughness = 0.1,
+ float King_B_coat_anisotropy = 0,
+ float King_B_coat_rotation = 0,
+ uniform float King_B_coat_IOR = 1.5,
+ float King_B_coat_affect_color = 0,
+ float King_B_coat_affect_roughness = 0,
+ float King_B_thin_film_thickness = 0,
+ float King_B_thin_film_IOR = 1.5,
+ float King_B_emission = 0,
+ color King_B_emission_color = color(1, 1, 1),
+ color King_B_opacity = color(1, 1, 1),
+ bool King_B_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ float2 geomprop_UV0_out1 = mx::stdlib::mx_texcoord_vector2(mxp_index:geomprop_UV0_index);
+ color mtlximage1_out = mx::stdlib::mx_image_color3(mtlximage1_file, mtlximage1_layer, mtlximage1_default, geomprop_UV0_out1, mtlximage1_uaddressmode, mtlximage1_vaddressmode, mtlximage1_filtertype, mtlximage1_framerange, mtlximage1_frameoffset, mtlximage1_frameendaction);
+ float mtlximage2_out = mx::stdlib::mx_image_float(mtlximage2_file, mtlximage2_layer, mtlximage2_default, geomprop_UV0_out1, mtlximage2_uaddressmode, mtlximage2_vaddressmode, mtlximage2_filtertype, mtlximage2_framerange, mtlximage2_frameoffset, mtlximage2_frameendaction);
+ float mtlximage4_out = mx::stdlib::mx_image_float(mtlximage4_file, mtlximage4_layer, mtlximage4_default, geomprop_UV0_out1, mtlximage4_uaddressmode, mtlximage4_vaddressmode, mtlximage4_filtertype, mtlximage4_framerange, mtlximage4_frameoffset, mtlximage4_frameendaction);
+ float mtlximage3_out = mx::stdlib::mx_image_float(mtlximage3_file, mtlximage3_layer, mtlximage3_default, geomprop_UV0_out1, mtlximage3_uaddressmode, mtlximage3_vaddressmode, mtlximage3_filtertype, mtlximage3_framerange, mtlximage3_frameoffset, mtlximage3_frameendaction);
+ float3 mtlximage6_out = mx::stdlib::mx_image_vector3(mtlximage6_file, mtlximage6_layer, mtlximage6_default, geomprop_UV0_out1, mtlximage6_uaddressmode, mtlximage6_vaddressmode, mtlximage6_filtertype, mtlximage6_framerange, mtlximage6_frameoffset, mtlximage6_frameendaction);
+ color mtlximage1_out_cm_out = NG_srgb_texture_to_lin_rec709_color3(mtlximage1_out);
+ float3 mtlxnormalmap1_out = mx::stdlib::mx_normalmap(mxp_in:mtlximage6_out, mxp_space:mtlxnormalmap1_space, mxp_scale:mtlxnormalmap1_scale, mxp_normal:geomprop_Nworld_out1, mxp_tangent:geomprop_Tworld_out1);
+ material King_B_out = NG_standard_surface_surfaceshader_100(King_B_base, mtlximage1_out_cm_out, King_B_diffuse_roughness, mtlximage2_out, King_B_specular, King_B_specular_color, mtlximage4_out, King_B_specular_IOR, King_B_specular_anisotropy, King_B_specular_rotation, King_B_transmission, King_B_transmission_color, King_B_transmission_depth, King_B_transmission_scatter, King_B_transmission_scatter_anisotropy, King_B_transmission_dispersion, King_B_transmission_extra_roughness, mtlximage3_out, mtlximage1_out_cm_out, mtlximage1_out_cm_out, King_B_subsurface_scale, King_B_subsurface_anisotropy, King_B_sheen, King_B_sheen_color, King_B_sheen_roughness, King_B_coat, King_B_coat_color, King_B_coat_roughness, King_B_coat_anisotropy, King_B_coat_rotation, King_B_coat_IOR, geomprop_Nworld_out1, King_B_coat_affect_color, King_B_coat_affect_roughness, King_B_thin_film_thickness, King_B_thin_film_IOR, King_B_emission, King_B_emission_color, King_B_opacity, King_B_thin_walled, mtlxnormalmap1_out, geomprop_Tworld_out1);
+ material M_King_B_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: King_B_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = M_King_B_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/M_King_B.msl.frag b/Materials/Examples/StandardSurface/M_King_B.msl.frag
new file mode 100644
index 0000000000..4e6508eb78
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_King_B.msl.frag
@@ -0,0 +1,2872 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ int mtlximage1_layer;
+ vec3 mtlximage1_default;
+ int mtlximage1_uaddressmode;
+ int mtlximage1_vaddressmode;
+ int mtlximage1_filtertype;
+ int mtlximage1_framerange;
+ int mtlximage1_frameoffset;
+ int mtlximage1_frameendaction;
+ vec2 mtlximage1_uv_scale;
+ vec2 mtlximage1_uv_offset;
+ int mtlximage2_layer;
+ float mtlximage2_default;
+ int mtlximage2_uaddressmode;
+ int mtlximage2_vaddressmode;
+ int mtlximage2_filtertype;
+ int mtlximage2_framerange;
+ int mtlximage2_frameoffset;
+ int mtlximage2_frameendaction;
+ vec2 mtlximage2_uv_scale;
+ vec2 mtlximage2_uv_offset;
+ int mtlximage4_layer;
+ float mtlximage4_default;
+ int mtlximage4_uaddressmode;
+ int mtlximage4_vaddressmode;
+ int mtlximage4_filtertype;
+ int mtlximage4_framerange;
+ int mtlximage4_frameoffset;
+ int mtlximage4_frameendaction;
+ vec2 mtlximage4_uv_scale;
+ vec2 mtlximage4_uv_offset;
+ int mtlximage3_layer;
+ float mtlximage3_default;
+ int mtlximage3_uaddressmode;
+ int mtlximage3_vaddressmode;
+ int mtlximage3_filtertype;
+ int mtlximage3_framerange;
+ int mtlximage3_frameoffset;
+ int mtlximage3_frameendaction;
+ vec2 mtlximage3_uv_scale;
+ vec2 mtlximage3_uv_offset;
+ int mtlximage6_layer;
+ vec3 mtlximage6_default;
+ int mtlximage6_uaddressmode;
+ int mtlximage6_vaddressmode;
+ int mtlximage6_filtertype;
+ int mtlximage6_framerange;
+ int mtlximage6_frameoffset;
+ int mtlximage6_frameendaction;
+ vec2 mtlximage6_uv_scale;
+ vec2 mtlximage6_uv_offset;
+ int mtlxnormalmap1_space;
+ float mtlxnormalmap1_scale;
+ float King_B_base;
+ float King_B_diffuse_roughness;
+ float King_B_specular;
+ vec3 King_B_specular_color;
+ float King_B_specular_IOR;
+ float King_B_specular_anisotropy;
+ float King_B_specular_rotation;
+ float King_B_transmission;
+ vec3 King_B_transmission_color;
+ float King_B_transmission_depth;
+ vec3 King_B_transmission_scatter;
+ float King_B_transmission_scatter_anisotropy;
+ float King_B_transmission_dispersion;
+ float King_B_transmission_extra_roughness;
+ float King_B_subsurface_scale;
+ float King_B_subsurface_anisotropy;
+ float King_B_sheen;
+ vec3 King_B_sheen_color;
+ float King_B_sheen_roughness;
+ float King_B_coat;
+ vec3 King_B_coat_color;
+ float King_B_coat_roughness;
+ float King_B_coat_anisotropy;
+ float King_B_coat_rotation;
+ float King_B_coat_IOR;
+ float King_B_coat_affect_color;
+ float King_B_coat_affect_roughness;
+ float King_B_thin_film_thickness;
+ float King_B_thin_film_IOR;
+ float King_B_emission;
+ vec3 King_B_emission_color;
+ vec3 King_B_opacity;
+ bool King_B_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec2 texcoord_0 ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+, MetalTexture mtlximage1_file , int mtlximage1_layer
+
+ , vec3 mtlximage1_default
+
+ , int mtlximage1_uaddressmode
+
+ , int mtlximage1_vaddressmode
+
+ , int mtlximage1_filtertype
+
+ , int mtlximage1_framerange
+
+ , int mtlximage1_frameoffset
+
+ , int mtlximage1_frameendaction
+
+ , vec2 mtlximage1_uv_scale
+
+ , vec2 mtlximage1_uv_offset
+
+, MetalTexture mtlximage2_file , int mtlximage2_layer
+
+ , float mtlximage2_default
+
+ , int mtlximage2_uaddressmode
+
+ , int mtlximage2_vaddressmode
+
+ , int mtlximage2_filtertype
+
+ , int mtlximage2_framerange
+
+ , int mtlximage2_frameoffset
+
+ , int mtlximage2_frameendaction
+
+ , vec2 mtlximage2_uv_scale
+
+ , vec2 mtlximage2_uv_offset
+
+, MetalTexture mtlximage4_file , int mtlximage4_layer
+
+ , float mtlximage4_default
+
+ , int mtlximage4_uaddressmode
+
+ , int mtlximage4_vaddressmode
+
+ , int mtlximage4_filtertype
+
+ , int mtlximage4_framerange
+
+ , int mtlximage4_frameoffset
+
+ , int mtlximage4_frameendaction
+
+ , vec2 mtlximage4_uv_scale
+
+ , vec2 mtlximage4_uv_offset
+
+, MetalTexture mtlximage3_file , int mtlximage3_layer
+
+ , float mtlximage3_default
+
+ , int mtlximage3_uaddressmode
+
+ , int mtlximage3_vaddressmode
+
+ , int mtlximage3_filtertype
+
+ , int mtlximage3_framerange
+
+ , int mtlximage3_frameoffset
+
+ , int mtlximage3_frameendaction
+
+ , vec2 mtlximage3_uv_scale
+
+ , vec2 mtlximage3_uv_offset
+
+, MetalTexture mtlximage6_file , int mtlximage6_layer
+
+ , vec3 mtlximage6_default
+
+ , int mtlximage6_uaddressmode
+
+ , int mtlximage6_vaddressmode
+
+ , int mtlximage6_filtertype
+
+ , int mtlximage6_framerange
+
+ , int mtlximage6_frameoffset
+
+ , int mtlximage6_frameendaction
+
+ , vec2 mtlximage6_uv_scale
+
+ , vec2 mtlximage6_uv_offset
+
+ , int mtlxnormalmap1_space
+
+ , float mtlxnormalmap1_scale
+
+ , float King_B_base
+
+ , float King_B_diffuse_roughness
+
+ , float King_B_specular
+
+ , vec3 King_B_specular_color
+
+ , float King_B_specular_IOR
+
+ , float King_B_specular_anisotropy
+
+ , float King_B_specular_rotation
+
+ , float King_B_transmission
+
+ , vec3 King_B_transmission_color
+
+ , float King_B_transmission_depth
+
+ , vec3 King_B_transmission_scatter
+
+ , float King_B_transmission_scatter_anisotropy
+
+ , float King_B_transmission_dispersion
+
+ , float King_B_transmission_extra_roughness
+
+ , float King_B_subsurface_scale
+
+ , float King_B_subsurface_anisotropy
+
+ , float King_B_sheen
+
+ , vec3 King_B_sheen_color
+
+ , float King_B_sheen_roughness
+
+ , float King_B_coat
+
+ , vec3 King_B_coat_color
+
+ , float King_B_coat_roughness
+
+ , float King_B_coat_anisotropy
+
+ , float King_B_coat_rotation
+
+ , float King_B_coat_IOR
+
+ , float King_B_coat_affect_color
+
+ , float King_B_coat_affect_roughness
+
+ , float King_B_thin_film_thickness
+
+ , float King_B_thin_film_IOR
+
+ , float King_B_emission
+
+ , vec3 King_B_emission_color
+
+ , vec3 King_B_opacity
+
+ , bool King_B_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+, mtlximage1_file(mtlximage1_file)
+ , mtlximage1_layer(mtlximage1_layer)
+
+ , mtlximage1_default(mtlximage1_default)
+
+ , mtlximage1_uaddressmode(mtlximage1_uaddressmode)
+
+ , mtlximage1_vaddressmode(mtlximage1_vaddressmode)
+
+ , mtlximage1_filtertype(mtlximage1_filtertype)
+
+ , mtlximage1_framerange(mtlximage1_framerange)
+
+ , mtlximage1_frameoffset(mtlximage1_frameoffset)
+
+ , mtlximage1_frameendaction(mtlximage1_frameendaction)
+
+ , mtlximage1_uv_scale(mtlximage1_uv_scale)
+
+ , mtlximage1_uv_offset(mtlximage1_uv_offset)
+
+, mtlximage2_file(mtlximage2_file)
+ , mtlximage2_layer(mtlximage2_layer)
+
+ , mtlximage2_default(mtlximage2_default)
+
+ , mtlximage2_uaddressmode(mtlximage2_uaddressmode)
+
+ , mtlximage2_vaddressmode(mtlximage2_vaddressmode)
+
+ , mtlximage2_filtertype(mtlximage2_filtertype)
+
+ , mtlximage2_framerange(mtlximage2_framerange)
+
+ , mtlximage2_frameoffset(mtlximage2_frameoffset)
+
+ , mtlximage2_frameendaction(mtlximage2_frameendaction)
+
+ , mtlximage2_uv_scale(mtlximage2_uv_scale)
+
+ , mtlximage2_uv_offset(mtlximage2_uv_offset)
+
+, mtlximage4_file(mtlximage4_file)
+ , mtlximage4_layer(mtlximage4_layer)
+
+ , mtlximage4_default(mtlximage4_default)
+
+ , mtlximage4_uaddressmode(mtlximage4_uaddressmode)
+
+ , mtlximage4_vaddressmode(mtlximage4_vaddressmode)
+
+ , mtlximage4_filtertype(mtlximage4_filtertype)
+
+ , mtlximage4_framerange(mtlximage4_framerange)
+
+ , mtlximage4_frameoffset(mtlximage4_frameoffset)
+
+ , mtlximage4_frameendaction(mtlximage4_frameendaction)
+
+ , mtlximage4_uv_scale(mtlximage4_uv_scale)
+
+ , mtlximage4_uv_offset(mtlximage4_uv_offset)
+
+, mtlximage3_file(mtlximage3_file)
+ , mtlximage3_layer(mtlximage3_layer)
+
+ , mtlximage3_default(mtlximage3_default)
+
+ , mtlximage3_uaddressmode(mtlximage3_uaddressmode)
+
+ , mtlximage3_vaddressmode(mtlximage3_vaddressmode)
+
+ , mtlximage3_filtertype(mtlximage3_filtertype)
+
+ , mtlximage3_framerange(mtlximage3_framerange)
+
+ , mtlximage3_frameoffset(mtlximage3_frameoffset)
+
+ , mtlximage3_frameendaction(mtlximage3_frameendaction)
+
+ , mtlximage3_uv_scale(mtlximage3_uv_scale)
+
+ , mtlximage3_uv_offset(mtlximage3_uv_offset)
+
+, mtlximage6_file(mtlximage6_file)
+ , mtlximage6_layer(mtlximage6_layer)
+
+ , mtlximage6_default(mtlximage6_default)
+
+ , mtlximage6_uaddressmode(mtlximage6_uaddressmode)
+
+ , mtlximage6_vaddressmode(mtlximage6_vaddressmode)
+
+ , mtlximage6_filtertype(mtlximage6_filtertype)
+
+ , mtlximage6_framerange(mtlximage6_framerange)
+
+ , mtlximage6_frameoffset(mtlximage6_frameoffset)
+
+ , mtlximage6_frameendaction(mtlximage6_frameendaction)
+
+ , mtlximage6_uv_scale(mtlximage6_uv_scale)
+
+ , mtlximage6_uv_offset(mtlximage6_uv_offset)
+
+ , mtlxnormalmap1_space(mtlxnormalmap1_space)
+
+ , mtlxnormalmap1_scale(mtlxnormalmap1_scale)
+
+ , King_B_base(King_B_base)
+
+ , King_B_diffuse_roughness(King_B_diffuse_roughness)
+
+ , King_B_specular(King_B_specular)
+
+ , King_B_specular_color(King_B_specular_color)
+
+ , King_B_specular_IOR(King_B_specular_IOR)
+
+ , King_B_specular_anisotropy(King_B_specular_anisotropy)
+
+ , King_B_specular_rotation(King_B_specular_rotation)
+
+ , King_B_transmission(King_B_transmission)
+
+ , King_B_transmission_color(King_B_transmission_color)
+
+ , King_B_transmission_depth(King_B_transmission_depth)
+
+ , King_B_transmission_scatter(King_B_transmission_scatter)
+
+ , King_B_transmission_scatter_anisotropy(King_B_transmission_scatter_anisotropy)
+
+ , King_B_transmission_dispersion(King_B_transmission_dispersion)
+
+ , King_B_transmission_extra_roughness(King_B_transmission_extra_roughness)
+
+ , King_B_subsurface_scale(King_B_subsurface_scale)
+
+ , King_B_subsurface_anisotropy(King_B_subsurface_anisotropy)
+
+ , King_B_sheen(King_B_sheen)
+
+ , King_B_sheen_color(King_B_sheen_color)
+
+ , King_B_sheen_roughness(King_B_sheen_roughness)
+
+ , King_B_coat(King_B_coat)
+
+ , King_B_coat_color(King_B_coat_color)
+
+ , King_B_coat_roughness(King_B_coat_roughness)
+
+ , King_B_coat_anisotropy(King_B_coat_anisotropy)
+
+ , King_B_coat_rotation(King_B_coat_rotation)
+
+ , King_B_coat_IOR(King_B_coat_IOR)
+
+ , King_B_coat_affect_color(King_B_coat_affect_color)
+
+ , King_B_coat_affect_roughness(King_B_coat_affect_roughness)
+
+ , King_B_thin_film_thickness(King_B_thin_film_thickness)
+
+ , King_B_thin_film_IOR(King_B_thin_film_IOR)
+
+ , King_B_emission(King_B_emission)
+
+ , King_B_emission_color(King_B_emission_color)
+
+ , King_B_opacity(King_B_opacity)
+
+ , King_B_thin_walled(King_B_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template
+ T atan(T y, T x) { return ::atan2(y, x); }
+
+ #define lessThan(a, b) ((a) < (b))
+ #define lessThanEqual(a, b) ((a) <= (b))
+ #define greaterThan(a, b) ((a) > (b))
+ #define greaterThanEqual(a, b) ((a) >= (b))
+ #define equal(a, b) ((a) == (b))
+ #define notEqual(a, b) ((a) != (b))
+
+ #endif
+
+ #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;
+ }
+
+ // 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+ }
+
+ // Fresnel model options.
+ const int FRESNEL_MODEL_DIELECTRIC = 0;
+ const int FRESNEL_MODEL_CONDUCTOR = 1;
+ const int FRESNEL_MODEL_SCHLICK = 2;
+ const int FRESNEL_MODEL_AIRY = 3;
+ const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+ // 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);
+
+ // Parameters for Fresnel calculations.
+ struct FresnelData
+ {
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+ #ifdef __METAL__
+ FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+ #endif
+
+ };
+
+ // 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+ }
+
+ // 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+ {
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+ }
+
+ // https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+ float mx_fresnel_dielectric(float cosTheta, float ior)
+ {
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float n, thread float& Rp, thread float& Rs)
+ {
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+ }
+
+ void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, thread float& Rp, thread float& Rs)
+ {
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, thread vec3& Rp, thread vec3& Rs)
+ {
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+ }
+
+ void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& Rp, thread vec3& Rs)
+ {
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+ }
+
+ 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);
+ }
+
+ // Phase shift due to a dielectric material
+ void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, thread float& phiP, thread float& phiS)
+ {
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+ }
+
+ // Phase shift due to a conducting material
+ void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, thread vec3& phiP, thread vec3& phiS)
+ {
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+ }
+
+ // Evaluation XYZ sensitivity curves in Fourier space
+ 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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+ {
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+ }
+
+ FresnelData mx_init_fresnel_data(int model)
+ {
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+ }
+
+ FresnelData mx_init_fresnel_dielectric(float ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+ {
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+ }
+
+ vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+ {
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+ }
+
+ // 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, MetalTexture envSampler)
+ {
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+ }
+
+ // 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);
+ }
+
+ 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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+ }
+
+ vec3 mx_environment_irradiance(vec3 N)
+ {
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+ }
+
+
+ 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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+ }
+
+ vec4 gl_FragCoord;
+ VertexData vd;
+
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+
+ displacementshader displacementshader1;
+
+
+MetalTexture mtlximage1_file;
+ int mtlximage1_layer;
+
+
+ vec3 mtlximage1_default;
+
+
+ int mtlximage1_uaddressmode;
+
+
+ int mtlximage1_vaddressmode;
+
+
+ int mtlximage1_filtertype;
+
+
+ int mtlximage1_framerange;
+
+
+ int mtlximage1_frameoffset;
+
+
+ int mtlximage1_frameendaction;
+
+
+ vec2 mtlximage1_uv_scale;
+
+
+ vec2 mtlximage1_uv_offset;
+
+
+MetalTexture mtlximage2_file;
+ int mtlximage2_layer;
+
+
+ float mtlximage2_default;
+
+
+ int mtlximage2_uaddressmode;
+
+
+ int mtlximage2_vaddressmode;
+
+
+ int mtlximage2_filtertype;
+
+
+ int mtlximage2_framerange;
+
+
+ int mtlximage2_frameoffset;
+
+
+ int mtlximage2_frameendaction;
+
+
+ vec2 mtlximage2_uv_scale;
+
+
+ vec2 mtlximage2_uv_offset;
+
+
+MetalTexture mtlximage4_file;
+ int mtlximage4_layer;
+
+
+ float mtlximage4_default;
+
+
+ int mtlximage4_uaddressmode;
+
+
+ int mtlximage4_vaddressmode;
+
+
+ int mtlximage4_filtertype;
+
+
+ int mtlximage4_framerange;
+
+
+ int mtlximage4_frameoffset;
+
+
+ int mtlximage4_frameendaction;
+
+
+ vec2 mtlximage4_uv_scale;
+
+
+ vec2 mtlximage4_uv_offset;
+
+
+MetalTexture mtlximage3_file;
+ int mtlximage3_layer;
+
+
+ float mtlximage3_default;
+
+
+ int mtlximage3_uaddressmode;
+
+
+ int mtlximage3_vaddressmode;
+
+
+ int mtlximage3_filtertype;
+
+
+ int mtlximage3_framerange;
+
+
+ int mtlximage3_frameoffset;
+
+
+ int mtlximage3_frameendaction;
+
+
+ vec2 mtlximage3_uv_scale;
+
+
+ vec2 mtlximage3_uv_offset;
+
+
+MetalTexture mtlximage6_file;
+ int mtlximage6_layer;
+
+
+ vec3 mtlximage6_default;
+
+
+ int mtlximage6_uaddressmode;
+
+
+ int mtlximage6_vaddressmode;
+
+
+ int mtlximage6_filtertype;
+
+
+ int mtlximage6_framerange;
+
+
+ int mtlximage6_frameoffset;
+
+
+ int mtlximage6_frameendaction;
+
+
+ vec2 mtlximage6_uv_scale;
+
+
+ vec2 mtlximage6_uv_offset;
+
+
+ int mtlxnormalmap1_space;
+
+
+ float mtlxnormalmap1_scale;
+
+
+ float King_B_base;
+
+
+ float King_B_diffuse_roughness;
+
+
+ float King_B_specular;
+
+
+ vec3 King_B_specular_color;
+
+
+ float King_B_specular_IOR;
+
+
+ float King_B_specular_anisotropy;
+
+
+ float King_B_specular_rotation;
+
+
+ float King_B_transmission;
+
+
+ vec3 King_B_transmission_color;
+
+
+ float King_B_transmission_depth;
+
+
+ vec3 King_B_transmission_scatter;
+
+
+ float King_B_transmission_scatter_anisotropy;
+
+
+ float King_B_transmission_dispersion;
+
+
+ float King_B_transmission_extra_roughness;
+
+
+ float King_B_subsurface_scale;
+
+
+ float King_B_subsurface_anisotropy;
+
+
+ float King_B_sheen;
+
+
+ vec3 King_B_sheen_color;
+
+
+ float King_B_sheen_roughness;
+
+
+ float King_B_coat;
+
+
+ vec3 King_B_coat_color;
+
+
+ float King_B_coat_roughness;
+
+
+ float King_B_coat_anisotropy;
+
+
+ float King_B_coat_rotation;
+
+
+ float King_B_coat_IOR;
+
+
+ float King_B_coat_affect_color;
+
+
+ float King_B_coat_affect_roughness;
+
+
+ float King_B_thin_film_thickness;
+
+
+ float King_B_thin_film_IOR;
+
+
+ float King_B_emission;
+
+
+ vec3 King_B_emission_color;
+
+
+ vec3 King_B_opacity;
+
+
+ bool King_B_thin_walled;
+
+
+ mat4 u_envMatrix;
+
+
+MetalTexture u_envRadiance;
+ int u_envRadianceMips;
+
+
+ int u_envRadianceSamples;
+
+
+MetalTexture u_envIrradiance;
+ bool u_refractionTwoSided;
+
+
+ vec3 u_viewPosition;
+
+
+ int u_numActiveLightSources;
+
+ vec4 out1;
+ int numActiveLightSources()
+ {
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+ }
+
+ void sampleLightSource(LightData light, float3 position, thread lightshader& result)
+ {
+ result.intensity = float3(0.0);
+ result.direction = float3(0.0);
+ }
+
+ vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+ {
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+ }
+
+ void mx_image_color3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_float(MetalTexture tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread float& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+
+ void mx_image_vector3(MetalTexture tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, thread vec3& result)
+ {
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+ }
+
+ void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, thread vec3& out1)
+ {
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+ }
+
+ void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, thread vec3& result)
+ {
+ // Decode the normal map.
+ value = all(value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+ }
+
+ void mx_roughness_anisotropy(float roughness, float anisotropy, thread 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;
+ }
+ }
+
+
+ // http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+ }
+
+ void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // We need to include NdotL from the light integral here
+ // as in this case it's not cancelled thread by& the BRDF denominator.
+ bsdf.response = fr * NdotL * occlusion * weight;
+ }
+
+ void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+ }
+
+ void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, thread vec3& result)
+ {
+ result = vec3(dot(_in, lumacoeffs));
+ }
+
+ mat4 mx_rotationMatrix(vec3 axis, float angle)
+ {
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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, thread vec3& result)
+ {
+ float rotationRadians = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+ }
+
+ void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, thread vec3& ior, thread 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);
+ }
+
+ void mx_uniform_edf(vec3 N, vec3 L, vec3 color, thread EDF& result)
+ {
+ result = color;
+ }
+
+
+ void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, thread EDF& result)
+ {
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+ }
+
+
+ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+ }
+
+ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, thread BSDF& bsdf)
+ {
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+ }
+
+
+ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+ }
+
+ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+ }
+
+ // We fake diffuse transmission by using diffuse reflection from the opposite side.
+ // So this BTDF is really a BRDF.
+ void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+ }
+
+ void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ // Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+ // based on https://mimosa-pudica.net/improved-oren-nayar.html.
+ float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+ }
+
+ // https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+ // Section 5.3
+ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+ {
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+ }
+
+ void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+ }
+
+ void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+
+ void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+ }
+
+ void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, thread BSDF& bsdf)
+ {
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+ }
+
+ void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, thread surfaceshader& out1)
+ {
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader{float3(0.0),float3(0.0)};
+ {
+ float3 N = normalize(vd.normalWorld);
+ float3 V = normalize(u_viewPosition - vd.positionWorld);
+ float3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ float3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF specular_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF translucent_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF selected_subsurface_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ BSDF subsurface_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF{float3(0.0),float3(1.0), 0.0, 0.0};
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(float3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+ }
+
+ PixelOutputs FragmentMain()
+ {
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 mtlximage1_out = vec3(0.0);
+ mx_image_color3(mtlximage1_file, mtlximage1_layer, mtlximage1_default, geomprop_UV0_out1, mtlximage1_uaddressmode, mtlximage1_vaddressmode, mtlximage1_filtertype, mtlximage1_framerange, mtlximage1_frameoffset, mtlximage1_frameendaction, mtlximage1_uv_scale, mtlximage1_uv_offset, mtlximage1_out);
+ float mtlximage2_out = 0.0;
+ mx_image_float(mtlximage2_file, mtlximage2_layer, mtlximage2_default, geomprop_UV0_out1, mtlximage2_uaddressmode, mtlximage2_vaddressmode, mtlximage2_filtertype, mtlximage2_framerange, mtlximage2_frameoffset, mtlximage2_frameendaction, mtlximage2_uv_scale, mtlximage2_uv_offset, mtlximage2_out);
+ float mtlximage4_out = 0.0;
+ mx_image_float(mtlximage4_file, mtlximage4_layer, mtlximage4_default, geomprop_UV0_out1, mtlximage4_uaddressmode, mtlximage4_vaddressmode, mtlximage4_filtertype, mtlximage4_framerange, mtlximage4_frameoffset, mtlximage4_frameendaction, mtlximage4_uv_scale, mtlximage4_uv_offset, mtlximage4_out);
+ float mtlximage3_out = 0.0;
+ mx_image_float(mtlximage3_file, mtlximage3_layer, mtlximage3_default, geomprop_UV0_out1, mtlximage3_uaddressmode, mtlximage3_vaddressmode, mtlximage3_filtertype, mtlximage3_framerange, mtlximage3_frameoffset, mtlximage3_frameendaction, mtlximage3_uv_scale, mtlximage3_uv_offset, mtlximage3_out);
+ vec3 mtlximage6_out = vec3(0.0);
+ mx_image_vector3(mtlximage6_file, mtlximage6_layer, mtlximage6_default, geomprop_UV0_out1, mtlximage6_uaddressmode, mtlximage6_vaddressmode, mtlximage6_filtertype, mtlximage6_framerange, mtlximage6_frameoffset, mtlximage6_frameendaction, mtlximage6_uv_scale, mtlximage6_uv_offset, mtlximage6_out);
+ vec3 mtlximage1_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(mtlximage1_out, mtlximage1_out_cm_out);
+ vec3 mtlxnormalmap1_out = vec3(0.0);
+ mx_normalmap(mtlximage6_out, mtlxnormalmap1_space, mtlxnormalmap1_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap1_out);
+ surfaceshader King_B_out = surfaceshader{float3(0.0),float3(0.0)};
+ NG_standard_surface_surfaceshader_100(King_B_base, mtlximage1_out_cm_out, King_B_diffuse_roughness, mtlximage2_out, King_B_specular, King_B_specular_color, mtlximage4_out, King_B_specular_IOR, King_B_specular_anisotropy, King_B_specular_rotation, King_B_transmission, King_B_transmission_color, King_B_transmission_depth, King_B_transmission_scatter, King_B_transmission_scatter_anisotropy, King_B_transmission_dispersion, King_B_transmission_extra_roughness, mtlximage3_out, mtlximage1_out_cm_out, mtlximage1_out_cm_out, King_B_subsurface_scale, King_B_subsurface_anisotropy, King_B_sheen, King_B_sheen_color, King_B_sheen_roughness, King_B_coat, King_B_coat_color, King_B_coat_roughness, King_B_coat_anisotropy, King_B_coat_rotation, King_B_coat_IOR, geomprop_Nworld_out1, King_B_coat_affect_color, King_B_coat_affect_roughness, King_B_thin_film_thickness, King_B_thin_film_IOR, King_B_emission, King_B_emission_color, King_B_opacity, King_B_thin_walled, mtlxnormalmap1_out, geomprop_Tworld_out1, King_B_out);
+ material M_King_B_out = King_B_out;
+ out1 = float4(M_King_B_out.color, 1.0);
+return PixelOutputs{out1 };
+ }
+
+};
+fragment PixelOutputs FragmentMain(
+VertexData vd [[ stage_in ]], constant LightData_pixel& u_lightData[[ buffer(0) ]], texture2d mtlximage1_file_tex [[texture(0)]], sampler mtlximage1_file_sampler [[sampler(0)]]
+, texture2d mtlximage2_file_tex [[texture(1)]], sampler mtlximage2_file_sampler [[sampler(1)]]
+, texture2d mtlximage4_file_tex [[texture(2)]], sampler mtlximage4_file_sampler [[sampler(2)]]
+, texture2d mtlximage3_file_tex [[texture(3)]], sampler mtlximage3_file_sampler [[sampler(3)]]
+, texture2d mtlximage6_file_tex [[texture(4)]], sampler mtlximage6_file_sampler [[sampler(4)]]
+, constant PublicUniforms& u_pub[[ buffer(1) ]], texture2d u_envRadiance_tex [[texture(5)]], sampler u_envRadiance_sampler [[sampler(5)]]
+, texture2d u_envIrradiance_tex [[texture(6)]], sampler u_envIrradiance_sampler [[sampler(6)]]
+, constant PrivateUniforms& u_prv[[ buffer(2) ]])
+{
+ GlobalContext ctx {vd, u_lightData.u_lightData
+ , u_pub.displacementshader1
+, MetalTexture {
+mtlximage1_file_tex, mtlximage1_file_sampler }
+ , u_pub.mtlximage1_layer
+ , u_pub.mtlximage1_default
+ , u_pub.mtlximage1_uaddressmode
+ , u_pub.mtlximage1_vaddressmode
+ , u_pub.mtlximage1_filtertype
+ , u_pub.mtlximage1_framerange
+ , u_pub.mtlximage1_frameoffset
+ , u_pub.mtlximage1_frameendaction
+ , u_pub.mtlximage1_uv_scale
+ , u_pub.mtlximage1_uv_offset
+, MetalTexture {
+mtlximage2_file_tex, mtlximage2_file_sampler }
+ , u_pub.mtlximage2_layer
+ , u_pub.mtlximage2_default
+ , u_pub.mtlximage2_uaddressmode
+ , u_pub.mtlximage2_vaddressmode
+ , u_pub.mtlximage2_filtertype
+ , u_pub.mtlximage2_framerange
+ , u_pub.mtlximage2_frameoffset
+ , u_pub.mtlximage2_frameendaction
+ , u_pub.mtlximage2_uv_scale
+ , u_pub.mtlximage2_uv_offset
+, MetalTexture {
+mtlximage4_file_tex, mtlximage4_file_sampler }
+ , u_pub.mtlximage4_layer
+ , u_pub.mtlximage4_default
+ , u_pub.mtlximage4_uaddressmode
+ , u_pub.mtlximage4_vaddressmode
+ , u_pub.mtlximage4_filtertype
+ , u_pub.mtlximage4_framerange
+ , u_pub.mtlximage4_frameoffset
+ , u_pub.mtlximage4_frameendaction
+ , u_pub.mtlximage4_uv_scale
+ , u_pub.mtlximage4_uv_offset
+, MetalTexture {
+mtlximage3_file_tex, mtlximage3_file_sampler }
+ , u_pub.mtlximage3_layer
+ , u_pub.mtlximage3_default
+ , u_pub.mtlximage3_uaddressmode
+ , u_pub.mtlximage3_vaddressmode
+ , u_pub.mtlximage3_filtertype
+ , u_pub.mtlximage3_framerange
+ , u_pub.mtlximage3_frameoffset
+ , u_pub.mtlximage3_frameendaction
+ , u_pub.mtlximage3_uv_scale
+ , u_pub.mtlximage3_uv_offset
+, MetalTexture {
+mtlximage6_file_tex, mtlximage6_file_sampler }
+ , u_pub.mtlximage6_layer
+ , u_pub.mtlximage6_default
+ , u_pub.mtlximage6_uaddressmode
+ , u_pub.mtlximage6_vaddressmode
+ , u_pub.mtlximage6_filtertype
+ , u_pub.mtlximage6_framerange
+ , u_pub.mtlximage6_frameoffset
+ , u_pub.mtlximage6_frameendaction
+ , u_pub.mtlximage6_uv_scale
+ , u_pub.mtlximage6_uv_offset
+ , u_pub.mtlxnormalmap1_space
+ , u_pub.mtlxnormalmap1_scale
+ , u_pub.King_B_base
+ , u_pub.King_B_diffuse_roughness
+ , u_pub.King_B_specular
+ , u_pub.King_B_specular_color
+ , u_pub.King_B_specular_IOR
+ , u_pub.King_B_specular_anisotropy
+ , u_pub.King_B_specular_rotation
+ , u_pub.King_B_transmission
+ , u_pub.King_B_transmission_color
+ , u_pub.King_B_transmission_depth
+ , u_pub.King_B_transmission_scatter
+ , u_pub.King_B_transmission_scatter_anisotropy
+ , u_pub.King_B_transmission_dispersion
+ , u_pub.King_B_transmission_extra_roughness
+ , u_pub.King_B_subsurface_scale
+ , u_pub.King_B_subsurface_anisotropy
+ , u_pub.King_B_sheen
+ , u_pub.King_B_sheen_color
+ , u_pub.King_B_sheen_roughness
+ , u_pub.King_B_coat
+ , u_pub.King_B_coat_color
+ , u_pub.King_B_coat_roughness
+ , u_pub.King_B_coat_anisotropy
+ , u_pub.King_B_coat_rotation
+ , u_pub.King_B_coat_IOR
+ , u_pub.King_B_coat_affect_color
+ , u_pub.King_B_coat_affect_roughness
+ , u_pub.King_B_thin_film_thickness
+ , u_pub.King_B_thin_film_IOR
+ , u_pub.King_B_emission
+ , u_pub.King_B_emission_color
+ , u_pub.King_B_opacity
+ , u_pub.King_B_thin_walled
+ , u_prv.u_envMatrix
+, MetalTexture {
+u_envRadiance_tex, u_envRadiance_sampler }
+ , u_prv.u_envRadianceMips
+ , u_prv.u_envRadianceSamples
+, MetalTexture {
+u_envIrradiance_tex, u_envIrradiance_sampler }
+ , u_prv.u_refractionTwoSided
+ , u_prv.u_viewPosition
+ , u_prv.u_numActiveLightSources
+ };
+ return ctx.FragmentMain();
+}
+
diff --git a/Materials/Examples/StandardSurface/M_King_B.msl.vert b/Materials/Examples/StandardSurface/M_King_B.msl.vert
new file mode 100644
index 0000000000..3fe61e750f
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_King_B.msl.vert
@@ -0,0 +1,125 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_worldMatrix;
+ mat4 u_viewProjectionMatrix;
+ mat4 u_worldInverseTransposeMatrix;
+};
+
+// Inputs block: VertexInputs
+struct VertexInputs
+{
+ vec3 i_position [[attribute(0)]];
+ vec3 i_normal [[attribute(1)]];
+ vec3 i_tangent [[attribute(2)]];
+ vec2 i_texcoord_0 [[attribute(3)]];
+};
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+};
+
+struct GlobalContext
+{
+ GlobalContext(
+ vec3 i_position
+, vec3 i_normal
+, vec3 i_tangent
+, vec2 i_texcoord_0
+ , mat4 u_worldMatrix
+
+ , mat4 u_viewProjectionMatrix
+
+ , mat4 u_worldInverseTransposeMatrix
+
+ ) :
+ i_position(i_position)
+, i_normal(i_normal)
+, i_tangent(i_tangent)
+, i_texcoord_0(i_texcoord_0)
+ , u_worldMatrix(u_worldMatrix)
+
+ , u_viewProjectionMatrix(u_viewProjectionMatrix)
+
+ , u_worldInverseTransposeMatrix(u_worldInverseTransposeMatrix)
+
+ {}
+ vec3 i_position;
+
+ vec3 i_normal;
+
+ vec3 i_tangent;
+
+ vec2 i_texcoord_0;
+
+ mat4 u_worldMatrix;
+
+
+ mat4 u_viewProjectionMatrix;
+
+
+ mat4 u_worldInverseTransposeMatrix;
+
+ VertexData VertexMain()
+ {
+ VertexData vd;
+ float4 hPositionWorld = u_worldMatrix * float4(i_position, 1.0);
+ vd.pos = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * float4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * float4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+
+ return vd;
+ // Omitted node 'geomprop_Nworld'. Function already called in this scope.
+ // Omitted node 'geomprop_Tworld'. Function already called in this scope.
+ // Omitted node 'geomprop_UV0'. Function already called in this scope.
+ // Omitted node 'mtlximage1'. Function already called in this scope.
+ // Omitted node 'mtlximage2'. Function already called in this scope.
+ // Omitted node 'mtlximage4'. Function already called in this scope.
+ // Omitted node 'mtlximage3'. Function already called in this scope.
+ // Omitted node 'mtlximage6'. Function already called in this scope.
+ // Omitted node 'mtlximage1_out_cm'. Function already called in this scope.
+ // Omitted node 'mtlxnormalmap1'. Function already called in this scope.
+ // Omitted node 'King_B'. Function already called in this scope.
+ // Omitted node 'M_King_B'. Function already called in this scope.
+ }
+
+};
+vertex VertexData VertexMain(
+VertexInputs i_vs [[ stage_in ]], constant PrivateUniforms& u_prv[[ buffer(4) ]])
+{
+ GlobalContext ctx {i_vs.i_position, i_vs.i_normal, i_vs.i_tangent, i_vs.i_texcoord_0 , u_prv.u_worldMatrix
+ , u_prv.u_viewProjectionMatrix
+ , u_prv.u_worldInverseTransposeMatrix
+ };
+ VertexData out = ctx.VertexMain();
+ out.pos.y = -out.pos.y;
+ return out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_King_B.osl b/Materials/Examples/StandardSurface/M_King_B.osl
new file mode 100644
index 0000000000..2bca341633
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_King_B.osl
@@ -0,0 +1,831 @@
+#include "mx_funcs.h"
+
+#define true 1
+#define false 0
+struct textureresource { string filename; string colorspace; };
+struct BSDF { closure color response; color throughput; float thickness; float ior; };
+#define EDF closure color
+#define VDF closure color
+struct surfaceshader { closure color bsdf; closure color edf; float opacity; };
+#define volumeshader closure color
+#define displacementshader vector
+#define lightshader closure color
+#define MATERIAL closure color
+
+#define M_FLOAT_EPS 1e-8
+
+vector2 mx_transform_uv(vector2 texcoord)
+{
+ return texcoord;
+}
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode );
+}
+
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+ out = rgb[0];
+}
+
+
+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, "missingcolor", missingColor, "swrap", uaddressmode, "twrap", vaddressmode);
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(color in, output color out)
+{
+ float bias_in2_tmp = 0.055;
+ color bias_out = in + bias_in2_tmp;
+ float linSeg_in2_tmp = 12.92;
+ color linSeg_out = in / linSeg_in2_tmp;
+ float isAboveR_value2_tmp = 0.04045;
+ float isAboveR_in1_tmp = 1;
+ float isAboveR_in2_tmp = 0;
+ float isAboveR_out = mx_ternary(in[0] > isAboveR_value2_tmp, isAboveR_in1_tmp, isAboveR_in2_tmp);
+ float isAboveG_value2_tmp = 0.04045;
+ float isAboveG_in1_tmp = 1;
+ float isAboveG_in2_tmp = 0;
+ float isAboveG_out = mx_ternary(in[1] > isAboveG_value2_tmp, isAboveG_in1_tmp, isAboveG_in2_tmp);
+ float isAboveB_value2_tmp = 0.04045;
+ float isAboveB_in1_tmp = 1;
+ float isAboveB_in2_tmp = 0;
+ float isAboveB_out = mx_ternary(in[2] > isAboveB_value2_tmp, isAboveB_in1_tmp, isAboveB_in2_tmp);
+ float max_in2_tmp = 0;
+ color max_out = max(bias_out, max_in2_tmp);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ float scale_in2_tmp = 1.055;
+ color scale_out = max_out / scale_in2_tmp;
+ float powSeg_in2_tmp = 2.4;
+ color powSeg_out = pow(scale_out, powSeg_in2_tmp);
+ color mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out = mix_out;
+}
+
+void mx_normalmap(vector value, string map_space, float normal_scale, vector N, vector U, output vector result)
+{
+ // Tangent space
+ if (map_space == "tangent")
+ {
+ vector v = value * 2.0 - 1.0;
+ vector T = normalize(U - dot(U, N) * N);
+ vector B = normalize(cross(N, T));
+ result = normalize(T * v[0] * normal_scale + B * v[1] * normal_scale + N * v[2]);
+ }
+ // Object space
+ else
+ {
+ vector n = value * 2.0 - 1.0;
+ result = normalize(n);
+ }
+}
+
+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;
+ }
+}
+
+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));
+}
+
+// Rational curve fit approximation for the directional albedo of Imageworks sheen.
+float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
+{
+ float a = 5.25248 - 7.66024 * NdotV + 14.26377 * roughness;
+ float b = 1.0 + 30.66449 * NdotV + 32.53420 * roughness;
+ return a / b;
+}
+
+float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
+{
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
+ return clamp(dirAlbedo, 0.0, 1.0);
+}
+
+// TODO: Vanilla OSL doesn't have a proper sheen closure,
+// so use 'diffuse' scaled by sheen directional albedo for now.
+void mx_sheen_bsdf(float weight, color Ks, float roughness, vector N, output BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ bsdf.throughput = color(1.0);
+ return;
+ }
+
+ // TODO: Normalization should not be needed. My suspicion is that
+ // BSDF sampling of new outgoing direction in 'testrender' needs
+ // to be fixed.
+ vector V = normalize(-I);
+
+ float NdotV = fabs(dot(N,V));
+ float alpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float albedo = weight * mx_imageworks_sheen_dir_albedo(NdotV, alpha);
+ bsdf.response = albedo * Ks * diffuse(N);
+ bsdf.throughput = 1.0 - albedo;
+}
+
+void mx_luminance_color3(color in, color lumacoeffs, output color result)
+{
+ result = dot(in, lumacoeffs);
+}
+
+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);
+}
+
+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);
+}
+
+
+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;
+}
+
+
+// Compute the average of an anisotropic alpha pair.
+float mx_average_alpha(vector2 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);
+}
+
+// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
+color mx_ggx_dir_albedo(float NdotV, float alpha, color F0, color F90)
+{
+ float x = NdotV;
+ float y = alpha;
+ float x2 = mx_square(x);
+ float y2 = mx_square(y);
+ vector4 r = vector4(0.1003, 0.9345, 1.0, 1.0) +
+ vector4(-0.6303, -2.323, -1.765, 0.2281) * x +
+ vector4(9.748, 2.229, 8.263, 15.94) * y +
+ vector4(-2.038, -3.748, 11.53, -55.83) * x * y +
+ vector4(29.34, 1.424, 28.96, 13.08) * x2 +
+ vector4(-8.245, -0.7684, -7.507, 41.26) * y2 +
+ vector4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
+ vector4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
+ vector4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
+ vector2 AB = vector2(r.x, r.y) / vector2(r.z, r.w);
+ AB.x = clamp(AB.x, 0.0, 1.0);
+ AB.y = clamp(AB.y, 0.0, 1.0);
+ return F0 * AB.x + F90 * AB.y;
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(F0), color(F90));
+ return result[0];
+}
+
+float mx_ggx_dir_albedo(float NdotV, float alpha, float ior)
+{
+ color result = mx_ggx_dir_albedo(NdotV, alpha, color(mx_ior_to_f0(ior)), color(1.0));
+ return result[0];
+}
+
+// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
+// Equations 14 and 16
+color mx_ggx_energy_compensation(float NdotV, float alpha, color 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)
+{
+ color result = mx_ggx_energy_compensation(NdotV, alpha, color(Fss));
+ return result[0];
+}
+
+void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
+{
+ if (scatter_mode == "T")
+ {
+ bsdf.response = tint * weight * microfacet(distribution, N, U, roughness.x, roughness.y, ior, 1);
+ bsdf.throughput = tint * weight;
+ return;
+ }
+
+ float NdotV = clamp(dot(N,-I), M_FLOAT_EPS, 1.0);
+ float F0 = mx_ior_to_f0(ior);
+ float F = mx_fresnel_schlick(NdotV, F0);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ float comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Calculate throughput from directional albedo.
+ float dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, ior) * comp;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode == "R")
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 0);
+ }
+ else
+ {
+ bsdf.response = tint * weight * comp * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, ior, 2);
+ }
+}
+
+
+void mx_conductor_bsdf(float weight, color ior_n, color ior_k, vector2 roughness, normal N, vector U, string distribution, output BSDF bsdf)
+{
+ bsdf.throughput = color(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ bsdf.response = 0;
+ return;
+ }
+
+ // Calculate conductor fresnel
+ //
+ // Fresnel should be based on microfacet normal
+ // but we have no access to that from here, so just use
+ // view direction and surface normal instead
+ //
+ float NdotV = fabs(dot(N,-I));
+ color F = mx_fresnel_conductor(NdotV, ior_n, ior_k);
+
+ // Calculate compensation for multiple scattering.
+ // This should normally be done inside the closure
+ // but since vanilla OSL doesen't support this we
+ // add it here in shader code instead.
+ vector2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ color comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
+
+ // Set ior to 0.0 to disable the internal dielectric fresnel
+ bsdf.response = F * comp * weight * microfacet(distribution, N, U, safeAlpha.x, safeAlpha.y, 0.0, false);
+}
+
+void mx_translucent_bsdf(float weight, color _color, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * translucent(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_subsurface_bsdf(float weight, color _color, vector radius, float anisotropy, normal N, output BSDF bsdf)
+{
+ // TODO: Subsurface closure is not supported by vanilla OSL.
+ bsdf.response = _color * weight * diffuse(N);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_oren_nayar_diffuse_bsdf(float weight, color _color, float roughness, normal N, output BSDF bsdf)
+{
+ bsdf.response = _color * weight * oren_nayar(N, roughness);
+ bsdf.throughput = color(0.0);
+}
+
+void mx_surface(BSDF bsdf, EDF edf, float opacity, output surfaceshader result)
+{
+ result.bsdf = bsdf.response;
+ result.edf = edf;
+ result.opacity = clamp(opacity, 0.0, 1.0);
+}
+
+void NG_standard_surface_surfaceshader_100(float base, color base_color, float diffuse_roughness, float metalness, float specular, color specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, color transmission_color, float transmission_depth, color transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface1, color subsurface_color, color subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen1, color sheen_color, float sheen_roughness, float coat, color coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vector coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission1, color emission_color, color opacity, int thin_walled, vector normal1, vector tangent, output surfaceshader out)
+{
+ closure color null_closure = 0;
+ vector2 coat_roughness_vector_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ float coat_tangent_rotate_degree_in2_tmp = 360;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_in2_tmp = 360;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ float subsurface_color_nonnegative_in2_tmp = 0;
+ color subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ float coat_clamped_low_tmp = 0;
+ float coat_clamped_high_tmp = 1;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vector subsurface_radius_vector_out = vector(subsurface_radius[0], subsurface_radius[1], subsurface_radius[2]);
+ float subsurface_selector_out = float(thin_walled);
+ float base_color_nonnegative_in2_tmp = 0;
+ color base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ color coat_attenuation_bg_tmp = color(1, 1, 1);
+ color coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ float one_minus_coat_ior_in1_tmp = 1;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ float one_plus_coat_ior_in1_tmp = 1;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ color emission_weight_out = emission_color * emission1;
+ color opacity_luminance_out = color(0.0);
+ mx_luminance_color3(opacity, color(0.272229, 0.674082, 0.0536895), opacity_luminance_out);
+ vector coat_tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ color artistic_ior_ior = color(0.0);
+ color artistic_ior_extinction = color(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vector tangent_rotate_out = vector(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal1, tangent_rotate_out);
+ float transmission_roughness_clamped_low_tmp = 0;
+ float transmission_roughness_clamped_high_tmp = 1;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vector subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vector coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_fg_tmp = 1;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vector tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_fg_tmp = 1;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ float coat_gamma_in2_tmp = 1;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float coat_tangent_value2_tmp = 0;
+ vector coat_tangent_out = mx_ternary(coat_anisotropy > coat_tangent_value2_tmp, coat_tangent_rotate_normalize_out, tangent);
+ vector2 main_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ float main_tangent_value2_tmp = 0;
+ vector main_tangent_out = mx_ternary(specular_anisotropy > main_tangent_value2_tmp, tangent_rotate_normalize_out, tangent);
+ vector2 transmission_roughness_out = vector2(0.0, 0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ color coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, coat_gamma_out);
+ BSDF coat_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(coat, color(1, 1, 1), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, "ggx", "R", coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf(1, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, "ggx", metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf(specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, "ggx", "R", specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf(1, transmission_color, specular_IOR, transmission_roughness_out, normal1, main_tangent_out, "ggx", "T", transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_sheen_bsdf(sheen1, sheen_color, sheen_roughness, normal1, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_translucent_bsdf(1, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf(1, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf(base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface1);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface1);
+ BSDF sheen_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ color thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(null_closure, color(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ EDF emission_edf_out = emission_weight_out * emission();
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = null_closure;
+ mx_generalized_schlick_edf(color(1, 1, 1), color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ surfaceshader shader_constructor_out = surfaceshader(null_closure, null_closure, 1.0);
+ mx_surface(coat_layer_out, blended_coat_emission_edf_out, opacity_luminance_out[0], shader_constructor_out);
+ out = shader_constructor_out;
+}
+
+MATERIAL mx_surfacematerial(surfaceshader surface, displacementshader disp)
+{
+ float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
+ return (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
+}
+
+shader M_King_B
+[[
+ string mtlx_category = "surfacematerial",
+ string mtlx_name = "M_King_B"
+]]
+(
+ displacementshader displacementshader1 = vector(0.0),
+ string geomprop_Nworld_space = "world",
+ string geomprop_Tworld_space = "world",
+ int geomprop_Tworld_index = 0
+ [[
+ string widget = "number"
+ ]],
+ int geomprop_UV0_index = 0
+ [[
+ string widget = "number"
+ ]],
+ textureresource mtlximage1_file = {"chess_set/king_black_base_color.jpg", "srgb_texture"}
+ [[
+ string widget = "filename"
+ ]],
+ string mtlximage1_layer = "",
+ color mtlximage1_default = color(0, 0, 0),
+ string mtlximage1_uaddressmode = "periodic",
+ string mtlximage1_vaddressmode = "periodic",
+ string mtlximage1_filtertype = "linear",
+ string mtlximage1_framerange = "",
+ int mtlximage1_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage1_frameendaction = "constant",
+ textureresource mtlximage2_file = {"chess_set/king_shared_metallic.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string mtlximage2_layer = "",
+ float mtlximage2_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage2_uaddressmode = "periodic",
+ string mtlximage2_vaddressmode = "periodic",
+ string mtlximage2_filtertype = "linear",
+ string mtlximage2_framerange = "",
+ int mtlximage2_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage2_frameendaction = "constant",
+ textureresource mtlximage4_file = {"chess_set/king_black_roughness.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string mtlximage4_layer = "",
+ float mtlximage4_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage4_uaddressmode = "periodic",
+ string mtlximage4_vaddressmode = "periodic",
+ string mtlximage4_filtertype = "linear",
+ string mtlximage4_framerange = "",
+ int mtlximage4_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage4_frameendaction = "constant",
+ textureresource mtlximage3_file = {"chess_set/king_shared_scattering.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string mtlximage3_layer = "",
+ float mtlximage3_default = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage3_uaddressmode = "periodic",
+ string mtlximage3_vaddressmode = "periodic",
+ string mtlximage3_filtertype = "linear",
+ string mtlximage3_framerange = "",
+ int mtlximage3_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage3_frameendaction = "constant",
+ textureresource mtlximage6_file = {"chess_set/king_black_normal.jpg", "lin_rec709"}
+ [[
+ string widget = "filename"
+ ]],
+ string mtlximage6_layer = "",
+ vector mtlximage6_default = vector(0, 0, 0),
+ string mtlximage6_uaddressmode = "periodic",
+ string mtlximage6_vaddressmode = "periodic",
+ string mtlximage6_filtertype = "linear",
+ string mtlximage6_framerange = "",
+ int mtlximage6_frameoffset = 0
+ [[
+ string widget = "number"
+ ]],
+ string mtlximage6_frameendaction = "constant",
+ string mtlxnormalmap1_space = "tangent",
+ float mtlxnormalmap1_scale = 1
+ [[
+ string widget = "number"
+ ]],
+ float King_B_base = 1
+ [[
+ string widget = "number"
+ ]],
+ float King_B_diffuse_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_specular = 1
+ [[
+ string widget = "number"
+ ]],
+ color King_B_specular_color = color(1, 1, 1),
+ float King_B_specular_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float King_B_specular_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_specular_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_transmission = 0
+ [[
+ string widget = "number"
+ ]],
+ color King_B_transmission_color = color(1, 1, 1),
+ float King_B_transmission_depth = 0
+ [[
+ string widget = "number"
+ ]],
+ color King_B_transmission_scatter = color(0, 0, 0),
+ float King_B_transmission_scatter_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_transmission_dispersion = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_transmission_extra_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_subsurface_scale = 0.003
+ [[
+ string widget = "number"
+ ]],
+ float King_B_subsurface_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_sheen = 0
+ [[
+ string widget = "number"
+ ]],
+ color King_B_sheen_color = color(1, 1, 1),
+ float King_B_sheen_roughness = 0.3
+ [[
+ string widget = "number"
+ ]],
+ float King_B_coat = 0
+ [[
+ string widget = "number"
+ ]],
+ color King_B_coat_color = color(1, 1, 1),
+ float King_B_coat_roughness = 0.1
+ [[
+ string widget = "number"
+ ]],
+ float King_B_coat_anisotropy = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_coat_rotation = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_coat_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float King_B_coat_affect_color = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_coat_affect_roughness = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_thin_film_thickness = 0
+ [[
+ string widget = "number"
+ ]],
+ float King_B_thin_film_IOR = 1.5
+ [[
+ string widget = "number"
+ ]],
+ float King_B_emission = 0
+ [[
+ string widget = "number"
+ ]],
+ color King_B_emission_color = color(1, 1, 1),
+ color King_B_opacity = color(1, 1, 1),
+ int King_B_thin_walled = 0
+ [[
+ string widget = "checkBox"
+ ]],
+ output MATERIAL out = 0
+)
+{
+ closure color null_closure = 0;
+ vector geomprop_Nworld_out1 = transform(geomprop_Nworld_space, N);
+ vector geomprop_Tworld_out1 = transform(geomprop_Tworld_space, normalize(dPdu));
+ vector2 geomprop_UV0_out1 = vector2(u,v);
+ color mtlximage1_out = color(0.0);
+ mx_image_color3(mtlximage1_file, mtlximage1_layer, mtlximage1_default, geomprop_UV0_out1, mtlximage1_uaddressmode, mtlximage1_vaddressmode, mtlximage1_filtertype, mtlximage1_framerange, mtlximage1_frameoffset, mtlximage1_frameendaction, mtlximage1_out);
+ float mtlximage2_out = 0.0;
+ mx_image_float(mtlximage2_file, mtlximage2_layer, mtlximage2_default, geomprop_UV0_out1, mtlximage2_uaddressmode, mtlximage2_vaddressmode, mtlximage2_filtertype, mtlximage2_framerange, mtlximage2_frameoffset, mtlximage2_frameendaction, mtlximage2_out);
+ float mtlximage4_out = 0.0;
+ mx_image_float(mtlximage4_file, mtlximage4_layer, mtlximage4_default, geomprop_UV0_out1, mtlximage4_uaddressmode, mtlximage4_vaddressmode, mtlximage4_filtertype, mtlximage4_framerange, mtlximage4_frameoffset, mtlximage4_frameendaction, mtlximage4_out);
+ float mtlximage3_out = 0.0;
+ mx_image_float(mtlximage3_file, mtlximage3_layer, mtlximage3_default, geomprop_UV0_out1, mtlximage3_uaddressmode, mtlximage3_vaddressmode, mtlximage3_filtertype, mtlximage3_framerange, mtlximage3_frameoffset, mtlximage3_frameendaction, mtlximage3_out);
+ vector mtlximage6_out = vector(0.0);
+ mx_image_vector3(mtlximage6_file, mtlximage6_layer, mtlximage6_default, geomprop_UV0_out1, mtlximage6_uaddressmode, mtlximage6_vaddressmode, mtlximage6_filtertype, mtlximage6_framerange, mtlximage6_frameoffset, mtlximage6_frameendaction, mtlximage6_out);
+ color mtlximage1_out_cm_out = color(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(mtlximage1_out, mtlximage1_out_cm_out);
+ vector mtlxnormalmap1_out = vector(0.0);
+ mx_normalmap(mtlximage6_out, mtlxnormalmap1_space, mtlxnormalmap1_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap1_out);
+ surfaceshader King_B_out = surfaceshader(null_closure, null_closure, 1.0);
+ NG_standard_surface_surfaceshader_100(King_B_base, mtlximage1_out_cm_out, King_B_diffuse_roughness, mtlximage2_out, King_B_specular, King_B_specular_color, mtlximage4_out, King_B_specular_IOR, King_B_specular_anisotropy, King_B_specular_rotation, King_B_transmission, King_B_transmission_color, King_B_transmission_depth, King_B_transmission_scatter, King_B_transmission_scatter_anisotropy, King_B_transmission_dispersion, King_B_transmission_extra_roughness, mtlximage3_out, mtlximage1_out_cm_out, mtlximage1_out_cm_out, King_B_subsurface_scale, King_B_subsurface_anisotropy, King_B_sheen, King_B_sheen_color, King_B_sheen_roughness, King_B_coat, King_B_coat_color, King_B_coat_roughness, King_B_coat_anisotropy, King_B_coat_rotation, King_B_coat_IOR, geomprop_Nworld_out1, King_B_coat_affect_color, King_B_coat_affect_roughness, King_B_thin_film_thickness, King_B_thin_film_IOR, King_B_emission, King_B_emission_color, King_B_opacity, King_B_thin_walled, mtlxnormalmap1_out, geomprop_Tworld_out1, King_B_out);
+ MATERIAL M_King_B_out = mx_surfacematerial(King_B_out, displacementshader1);
+ out = M_King_B_out;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_King_W.glsl.frag b/Materials/Examples/StandardSurface/M_King_W.glsl.frag
new file mode 100644
index 0000000000..ccda9e6924
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_King_W.glsl.frag
@@ -0,0 +1,1866 @@
+#version 400
+
+struct BSDF { vec3 response; vec3 throughput; float thickness; float ior; };
+#define EDF vec3
+struct surfaceshader { vec3 color; vec3 transparency; };
+struct volumeshader { vec3 color; vec3 transparency; };
+struct displacementshader { vec3 offset; float scale; };
+struct lightshader { vec3 intensity; vec3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+uniform displacementshader displacementshader1;
+uniform sampler2D mtlximage7_file;
+uniform int mtlximage7_layer = 0;
+uniform vec3 mtlximage7_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int mtlximage7_uaddressmode = 2;
+uniform int mtlximage7_vaddressmode = 2;
+uniform int mtlximage7_filtertype = 1;
+uniform int mtlximage7_framerange = 0;
+uniform int mtlximage7_frameoffset = 0;
+uniform int mtlximage7_frameendaction = 0;
+uniform vec2 mtlximage7_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage7_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D mtlximage10_file;
+uniform int mtlximage10_layer = 0;
+uniform float mtlximage10_default = 0.000000;
+uniform int mtlximage10_uaddressmode = 2;
+uniform int mtlximage10_vaddressmode = 2;
+uniform int mtlximage10_filtertype = 1;
+uniform int mtlximage10_framerange = 0;
+uniform int mtlximage10_frameoffset = 0;
+uniform int mtlximage10_frameendaction = 0;
+uniform vec2 mtlximage10_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage10_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D mtlximage11_file;
+uniform int mtlximage11_layer = 0;
+uniform float mtlximage11_default = 0.000000;
+uniform int mtlximage11_uaddressmode = 2;
+uniform int mtlximage11_vaddressmode = 2;
+uniform int mtlximage11_filtertype = 1;
+uniform int mtlximage11_framerange = 0;
+uniform int mtlximage11_frameoffset = 0;
+uniform int mtlximage11_frameendaction = 0;
+uniform vec2 mtlximage11_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage11_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D mtlximage8_file;
+uniform int mtlximage8_layer = 0;
+uniform float mtlximage8_default = 0.000000;
+uniform int mtlximage8_uaddressmode = 2;
+uniform int mtlximage8_vaddressmode = 2;
+uniform int mtlximage8_filtertype = 1;
+uniform int mtlximage8_framerange = 0;
+uniform int mtlximage8_frameoffset = 0;
+uniform int mtlximage8_frameendaction = 0;
+uniform vec2 mtlximage8_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage8_uv_offset = vec2(0.000000, 0.000000);
+uniform sampler2D mtlximage9_file;
+uniform int mtlximage9_layer = 0;
+uniform vec3 mtlximage9_default = vec3(0.000000, 0.000000, 0.000000);
+uniform int mtlximage9_uaddressmode = 2;
+uniform int mtlximage9_vaddressmode = 2;
+uniform int mtlximage9_filtertype = 1;
+uniform int mtlximage9_framerange = 0;
+uniform int mtlximage9_frameoffset = 0;
+uniform int mtlximage9_frameendaction = 0;
+uniform vec2 mtlximage9_uv_scale = vec2(1.000000, 1.000000);
+uniform vec2 mtlximage9_uv_offset = vec2(0.000000, 0.000000);
+uniform int mtlxnormalmap11_space = 0;
+uniform float mtlxnormalmap11_scale = 1.000000;
+uniform float King_W_base = 1.000000;
+uniform float King_W_diffuse_roughness = 0.000000;
+uniform float King_W_specular = 1.000000;
+uniform vec3 King_W_specular_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float King_W_specular_IOR = 1.500000;
+uniform float King_W_specular_anisotropy = 0.000000;
+uniform float King_W_specular_rotation = 0.000000;
+uniform float King_W_transmission = 0.000000;
+uniform vec3 King_W_transmission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float King_W_transmission_depth = 0.000000;
+uniform vec3 King_W_transmission_scatter = vec3(0.000000, 0.000000, 0.000000);
+uniform float King_W_transmission_scatter_anisotropy = 0.000000;
+uniform float King_W_transmission_dispersion = 0.000000;
+uniform float King_W_transmission_extra_roughness = 0.000000;
+uniform float King_W_subsurface_scale = 0.003000;
+uniform float King_W_subsurface_anisotropy = 0.000000;
+uniform float King_W_sheen = 0.000000;
+uniform vec3 King_W_sheen_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float King_W_sheen_roughness = 0.300000;
+uniform float King_W_coat = 0.000000;
+uniform vec3 King_W_coat_color = vec3(1.000000, 1.000000, 1.000000);
+uniform float King_W_coat_roughness = 0.100000;
+uniform float King_W_coat_anisotropy = 0.000000;
+uniform float King_W_coat_rotation = 0.000000;
+uniform float King_W_coat_IOR = 1.500000;
+uniform float King_W_coat_affect_color = 0.000000;
+uniform float King_W_coat_affect_roughness = 0.000000;
+uniform float King_W_thin_film_thickness = 0.000000;
+uniform float King_W_thin_film_IOR = 1.500000;
+uniform float King_W_emission = 0.000000;
+uniform vec3 King_W_emission_color = vec3(1.000000, 1.000000, 1.000000);
+uniform vec3 King_W_opacity = vec3(1.000000, 1.000000, 1.000000);
+uniform bool King_W_thin_walled = false;
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_envMatrix = mat4(-1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000);
+uniform sampler2D u_envRadiance;
+uniform int u_envRadianceMips = 1;
+uniform int u_envRadianceSamples = 16;
+uniform sampler2D u_envIrradiance;
+uniform bool u_refractionTwoSided = false;
+uniform vec3 u_viewPosition = vec3(0.0);
+uniform int u_numActiveLightSources = 0;
+
+in VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+// Pixel shader outputs
+out vec4 out1;
+
+#define M_FLOAT_EPS 1e-8
+
+float mx_square(float x)
+{
+ return x*x;
+}
+
+vec2 mx_square(vec2 x)
+{
+ return x*x;
+}
+
+vec3 mx_square(vec3 x)
+{
+ return x*x;
+}
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+#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;
+}
+
+// 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 in 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(cos(phi) * sinTheta,
+ sin(phi) * sinTheta,
+ cosTheta);
+}
+
+// Fresnel model options.
+const int FRESNEL_MODEL_DIELECTRIC = 0;
+const int FRESNEL_MODEL_CONDUCTOR = 1;
+const int FRESNEL_MODEL_SCHLICK = 2;
+const int FRESNEL_MODEL_AIRY = 3;
+const int FRESNEL_MODEL_SCHLICK_AIRY = 4;
+
+// 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);
+
+// Parameters for Fresnel calculations.
+struct FresnelData
+{
+ int model;
+
+ // Physical Fresnel
+ vec3 ior;
+ vec3 extinction;
+
+ // Generalized Schlick Fresnel
+ vec3 F0;
+ vec3 F90;
+ float exponent;
+
+ // Thin film
+ float tf_thickness;
+ float tf_ior;
+
+ // Refraction
+ bool refraction;
+
+#ifdef __METAL__
+FresnelData(int _model = 0,
+ vec3 _ior = vec3(0.0f),
+ vec3 _extinction = vec3(0.0f),
+ vec3 _F0 = vec3(0.0f),
+ vec3 _F90 = vec3(0.0f),
+ float _exponent = 0.0f,
+ float _tf_thickness = 0.0f,
+ float _tf_ior = 0.0f,
+ bool _refraction = false) :
+ model(_model),
+ ior(_ior),
+ extinction(_extinction),
+ F0(_F0), F90(_F90), exponent(_exponent),
+ tf_thickness(_tf_thickness),
+ tf_ior(_tf_ior),
+ refraction(_refraction) {}
+#endif
+
+};
+
+// 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 * cos(phi);
+ float y = sinTheta * sin(phi);
+ vec3 c = vec3(x, y, z);
+
+ // Compute the microfacet normal.
+ vec3 H = c + V;
+
+ // Transform the microfacet normal back to the ellipsoid configuration.
+ H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
+
+ 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 / (lambdaL / NdotL + lambdaV / NdotV);
+}
+
+// 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(u_albedoTable, 0).x > 1)
+ {
+ vec2 AB = texture(u_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_colored(vec3 F0)
+{
+ vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
+ return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
+}
+
+// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
+float mx_fresnel_dielectric(float cosTheta, float ior)
+{
+ if (cosTheta < 0.0)
+ return 1.0;
+
+ float g = ior*ior + cosTheta*cosTheta - 1.0;
+ // Check for total internal reflection
+ if (g < 0.0)
+ return 1.0;
+
+ g = sqrt(g);
+ float gmc = g - cosTheta;
+ float gpc = g + cosTheta;
+ float x = gmc / gpc;
+ float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0);
+ return 0.5 * x * x * (1.0 + y * y);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float n, out float Rp, out float Rs)
+{
+ if (cosTheta < 0.0) {
+ Rp = 1.0;
+ Rs = 1.0;
+ return;
+ }
+
+ float cosTheta2 = cosTheta * cosTheta;
+ float sinTheta2 = 1.0 - cosTheta2;
+ float n2 = n * n;
+
+ float t0 = n2 - sinTheta2;
+ float a2plusb2 = sqrt(t0 * t0);
+ float t1 = a2plusb2 + cosTheta2;
+ float a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
+ float t2 = 2.0 * a * cosTheta;
+ Rs = (t1 - t2) / (t1 + t2);
+
+ float t3 = cosTheta2 * a2plusb2 + sinTheta2 * sinTheta2;
+ float t4 = t2 * sinTheta2;
+ Rp = Rs * (t3 - t4) / (t3 + t4);
+}
+
+void mx_fresnel_dielectric_polarized(float cosTheta, float eta1, float eta2, out float Rp, out float Rs)
+{
+ float n = eta2 / eta1;
+ mx_fresnel_dielectric_polarized(cosTheta, n, Rp, Rs);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
+{
+ cosTheta = clamp(cosTheta, 0.0, 1.0);
+ float cosTheta2 = cosTheta * cosTheta;
+ 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);
+}
+
+void mx_fresnel_conductor_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 Rp, out vec3 Rs)
+{
+ vec3 n = eta2 / eta1;
+ vec3 k = kappa2 / eta1;
+ mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
+}
+
+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);
+}
+
+// Phase shift due to a dielectric material
+void mx_fresnel_dielectric_phase_polarized(float cosTheta, float eta1, float eta2, out float phiP, out float phiS)
+{
+ float cosB = cos(atan(eta2 / eta1)); // Brewster's angle
+ if (eta2 > eta1) {
+ phiP = cosTheta < cosB ? M_PI : 0.0f;
+ phiS = 0.0f;
+ } else {
+ phiP = cosTheta < cosB ? 0.0f : M_PI;
+ phiS = M_PI;
+ }
+}
+
+// Phase shift due to a conducting material
+void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
+{
+ if (dot(kappa2, kappa2) == 0.0 && eta2.x == eta2.y && eta2.y == eta2.z) {
+ // Use dielectric formula to increase performance
+ float phiPx, phiSx;
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2.x, phiPx, phiSx);
+ phiP = vec3(phiPx, phiPx, phiPx);
+ phiS = vec3(phiSx, phiSx, phiSx);
+ return;
+ }
+ 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 = atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
+ phiP = 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));
+}
+
+// Evaluation XYZ sensitivity curves in Fourier space
+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) * cos(pos * phase + shift) * exp(- var * phase*phase);
+ xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * 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, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior,
+ vec3 f0, vec3 f90, float exponent, bool use_schlick)
+{
+ // Convert nm -> m
+ float d = tf_thickness * 1.0e-9;
+
+ // Assume vacuum on the outside
+ float eta1 = 1.0;
+ float eta2 = max(tf_ior, eta1);
+ vec3 eta3 = use_schlick ? mx_f0_to_ior_colored(f0) : ior;
+ vec3 kappa3 = use_schlick ? vec3(0.0) : extinction;
+
+ // Compute the Spectral versions of the Fresnel reflectance and
+ // transmitance for each interface.
+ float R12p, T121p, R12s, T121s;
+ vec3 R23p, R23s;
+
+ // Reflected and transmitted parts in the thin film
+ mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12p, R12s);
+
+ // Reflected part by the base
+ float scale = eta1 / eta2;
+ float cosThetaTSqr = 1.0 - (1.0-cosTheta*cosTheta) * scale*scale;
+ float cosTheta2 = sqrt(cosThetaTSqr);
+ if (use_schlick)
+ {
+ vec3 f = mx_fresnel_schlick(cosTheta2, f0, f90, exponent);
+ R23p = 0.5 * f;
+ R23s = 0.5 * f;
+ }
+ else
+ {
+ mx_fresnel_conductor_polarized(cosTheta2, eta2, eta3, kappa3, R23p, R23s);
+ }
+
+ // Check for total internal reflection
+ if (cosThetaTSqr <= 0.0f)
+ {
+ R12s = 1.0;
+ R12p = 1.0;
+ }
+
+ // Compute the transmission coefficients
+ T121p = 1.0 - R12p;
+ T121s = 1.0 - R12s;
+
+ // Optical path difference
+ float D = 2.0 * eta2 * d * cosTheta2;
+
+ float phi21p, phi21s;
+ vec3 phi23p, phi23s, r123s, r123p;
+
+ // Evaluate the phase shift
+ mx_fresnel_dielectric_phase_polarized(cosTheta, eta1, eta2, phi21p, phi21s);
+ if (use_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(cosTheta2, eta2, eta3, kappa3, phi23p, phi23s);
+ }
+
+ phi21p = M_PI - phi21p;
+ phi21s = M_PI - phi21s;
+
+ r123p = max(vec3(0.0), sqrt(R12p*R23p));
+ r123s = max(vec3(0.0), sqrt(R12s*R23s));
+
+ // Evaluate iridescence term
+ vec3 I = vec3(0.0);
+ vec3 C0, Cm, Sm;
+
+ // Iridescence term using spectral antialiasing for Parallel polarization
+
+ vec3 S0 = vec3(1.0);
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rs = (T121p*T121p*R23p) / (vec3(1.0) - R12p*R23p);
+ C0 = R12p + Rs;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rs - T121p;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123p;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23p+vec3(phi21p)));
+ I += Cm*Sm;
+ }
+
+ // Iridescence term using spectral antialiasing for Perpendicular polarization
+
+ // Reflectance term for m=0 (DC term amplitude)
+ vec3 Rp = (T121s*T121s*R23s) / (vec3(1.0) - R12s*R23s);
+ C0 = R12s + Rp;
+ I += C0 * S0;
+
+ // Reflectance term for m>0 (pairs of diracs)
+ Cm = Rp - T121s ;
+ for (int m=1; m<=2; ++m)
+ {
+ Cm *= r123s;
+ Sm = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*(phi23s+vec3(phi21s)));
+ I += Cm*Sm;
+ }
+
+ // Average parallel and perpendicular polarization
+ I *= 0.5;
+
+ // Convert back to RGB reflectance
+ I = clamp(XYZ_TO_RGB * I, vec3(0.0), vec3(1.0));
+
+ return I;
+}
+
+FresnelData mx_init_fresnel_data(int model)
+{
+ return FresnelData(model, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, false);
+}
+
+FresnelData mx_init_fresnel_dielectric(float ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_DIELECTRIC);
+ fd.ior = vec3(ior);
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_CONDUCTOR);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = vec3(1.0);
+ fd.exponent = 5.0f;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F90, float exponent, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
+ fd.F0 = F0;
+ fd.F90 = F90;
+ fd.exponent = exponent;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = vec3(ior);
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
+{
+ FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_AIRY);
+ fd.ior = ior;
+ fd.extinction = extinction;
+ fd.tf_thickness = tf_thickness;
+ fd.tf_ior = tf_ior;
+ return fd;
+}
+
+vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
+{
+ 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 if (fd.model == FRESNEL_MODEL_SCHLICK)
+ {
+ return mx_fresnel_schlick(cosTheta, fd.F0, fd.F90, fd.exponent);
+ }
+ else
+ {
+ return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
+ fd.F0, fd.F90, fd.exponent,
+ fd.model == FRESNEL_MODEL_SCHLICK_AIRY);
+ }
+}
+
+// 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 = -asin(dir.y) * M_PI_INV + 0.5;
+ float longitude = 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, sampler2D envSampler)
+{
+ vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
+ vec2 uv = mx_latlong_projection(envDir);
+ return textureLod(envSampler, uv, lod).rgb;
+}
+
+// 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);
+}
+
+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 = u_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(u_envRadianceMips - 1), envRadianceSamples);
+ vec3 sampleColor = mx_latlong_map_lookup(Lw, u_envMatrix, lod, u_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 is inverted for refraction.
+ vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : 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;
+}
+
+vec3 mx_environment_irradiance(vec3 N)
+{
+ return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance);
+}
+
+
+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 (u_refractionTwoSided)
+ {
+ tint = mx_square(tint);
+ }
+ return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
+}
+
+struct LightData
+{
+ int type;
+};
+
+uniform LightData u_lightData[MAX_LIGHT_SOURCES];
+
+int numActiveLightSources()
+{
+ return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ;
+}
+
+void sampleLightSource(LightData light, vec3 position, out lightshader result)
+{
+ result.intensity = vec3(0.0);
+ result.direction = vec3(0.0);
+}
+
+vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
+{
+ uv = uv * uv_scale + uv_offset;
+ return uv;
+}
+
+void mx_image_color3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_float(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).r;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+
+void mx_image_vector3(sampler2D tex_sampler, 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)
+{
+ if (textureSize(tex_sampler, 0).x > 1)
+ {
+ vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
+ result = texture(tex_sampler, uv).rgb;
+ }
+ else
+ {
+ result = defaultval;
+ }
+}
+
+void NG_srgb_texture_to_lin_rec709_color3(vec3 in1, out vec3 out1)
+{
+ const float bias_in2_tmp = 0.055000;
+ vec3 bias_out = in1 + bias_in2_tmp;
+ const float linSeg_in2_tmp = 12.920000;
+ vec3 linSeg_out = in1 / linSeg_in2_tmp;
+ const float isAboveR_value2_tmp = 0.040450;
+ const float isAboveR_in1_tmp = 1.000000;
+ const float isAboveR_in2_tmp = 0.000000;
+ float isAboveR_out = (in1.x > isAboveR_value2_tmp) ? isAboveR_in1_tmp : isAboveR_in2_tmp;
+ const float isAboveG_value2_tmp = 0.040450;
+ const float isAboveG_in1_tmp = 1.000000;
+ const float isAboveG_in2_tmp = 0.000000;
+ float isAboveG_out = (in1.y > isAboveG_value2_tmp) ? isAboveG_in1_tmp : isAboveG_in2_tmp;
+ const float isAboveB_value2_tmp = 0.040450;
+ const float isAboveB_in1_tmp = 1.000000;
+ const float isAboveB_in2_tmp = 0.000000;
+ float isAboveB_out = (in1.z > isAboveB_value2_tmp) ? isAboveB_in1_tmp : isAboveB_in2_tmp;
+ const float max_in2_tmp = 0.000000;
+ vec3 max_out = max(bias_out, max_in2_tmp);
+ vec3 isAbove_out = vec3(isAboveR_out, isAboveG_out, isAboveB_out);
+ const float scale_in2_tmp = 1.055000;
+ vec3 scale_out = max_out / scale_in2_tmp;
+ const float powSeg_in2_tmp = 2.400000;
+ vec3 powSeg_out = pow(scale_out, vec3(powSeg_in2_tmp));
+ vec3 mix_out = mix(linSeg_out, powSeg_out, isAbove_out);
+ out1 = mix_out;
+}
+
+void mx_normalmap(vec3 value, int map_space, float normal_scale, vec3 N, vec3 T, out vec3 result)
+{
+ // Decode the normal map.
+ value = (value == vec3(0.0f)) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
+
+ // Transform from tangent space if needed.
+ if (map_space == 0)
+ {
+ vec3 B = normalize(cross(N, T));
+ value.xy *= normal_scale;
+ value = T * value.x + B * value.y + N * value.z;
+ }
+
+ // Normalize the result.
+ result = normalize(value);
+}
+
+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;
+ }
+}
+
+
+// http://www.aconty.com/pdf/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(u_albedoTable, 0).x > 1)
+ {
+ return texture(u_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);
+}
+
+void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ vec3 H = normalize(L + V);
+
+ float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), 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);
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ // 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 * occlusion * weight;
+}
+
+void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ float dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
+ bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
+
+ vec3 Li = mx_environment_irradiance(N);
+ bsdf.response = Li * color * dirAlbedo * weight;
+}
+
+void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
+{
+ result = vec3(dot(_in, lumacoeffs));
+}
+
+mat4 mx_rotationMatrix(vec3 axis, float angle)
+{
+ axis = normalize(axis);
+ float s = sin(angle);
+ float c = 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 = radians(amount);
+ mat4 m = mx_rotationMatrix(axis, rotationRadians);
+ result = (m * vec4(_in, 1.0)).xyz;
+}
+
+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);
+}
+
+void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result)
+{
+ result = color;
+}
+
+
+void mx_generalized_schlick_edf(vec3 N, vec3 V, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
+{
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
+ result = base * f;
+}
+
+
+void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(VdotH, fd);
+ float D = mx_ggx_NDF(Ht, safeAlpha);
+ float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ // Note: NdotL is cancelled out
+ bsdf.response = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ if (scatter_mode != 0)
+ {
+ bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, tint) * weight;
+ }
+}
+
+void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
+{
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ {
+ fd = mx_init_fresnel_dielectric_airy(ior, bsdf.thickness, bsdf.ior);
+ }
+ else
+ {
+ fd = mx_init_fresnel_dielectric(ior);
+ }
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+
+ 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;
+ bsdf.throughput = 1.0 - dirAlbedo * weight;
+
+ vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
+ bsdf.response = Li * tint * comp * weight;
+}
+
+
+void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ 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 NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ 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 * occlusion * weight / (4.0 * NdotV);
+}
+
+void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ N = mx_forward_facing_normal(N, V);
+
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ FresnelData fd;
+ if (bsdf.thickness > 0.0)
+ fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, bsdf.thickness, bsdf.ior);
+ else
+ fd = mx_init_fresnel_conductor(ior_n, ior_k);
+
+ vec3 F = mx_compute_fresnel(NdotV, fd);
+
+ vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
+ float avgAlpha = mx_average_alpha(safeAlpha);
+ 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;
+}
+
+// We fake diffuse transmission by using diffuse reflection from the opposite side.
+// So this BTDF is really a BRDF.
+void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ // Invert normal since we're transmitting light from the other side
+ float NdotL = dot(L, -normal);
+ if (NdotL <= 0.0 || weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ bsdf.response = color * weight * NdotL * M_PI_INV;
+}
+
+void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ // Invert normal since we're transmitting light from the other side
+ vec3 Li = mx_environment_irradiance(-normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn
+// based on https://mimosa-pudica.net/improved-oren-nayar.html.
+float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+ float s = LdotV - NdotL * NdotV;
+ float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0;
+
+ float sigma2 = mx_square(roughness * M_PI);
+ float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
+ float B = 0.45 * sigma2 / (sigma2 + 0.09);
+
+ return A + B * stinv;
+}
+
+// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
+// Section 5.3
+float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness)
+{
+ vec3 H = normalize(L + V);
+ float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0);
+ float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
+
+ 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 = 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 * sin(x * 0.5));
+ vec3 R = mx_burley_diffusion_profile(dist, shape);
+ sumD += R * max(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);
+}
+
+void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius);
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+ float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
+ bsdf.response = sss * visibleOcclusion * weight;
+}
+
+void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ // For now, we render indirect subsurface as simple indirect diffuse.
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+
+void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0);
+
+ bsdf.response = color * occlusion * weight * NdotL * M_PI_INV;
+ if (roughness > 0.0)
+ {
+ bsdf.response *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness);
+ }
+}
+
+void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, inout BSDF bsdf)
+{
+ bsdf.throughput = vec3(0.0);
+
+ if (weight < M_FLOAT_EPS)
+ {
+ return;
+ }
+
+ normal = mx_forward_facing_normal(normal, V);
+
+ vec3 Li = mx_environment_irradiance(normal);
+ bsdf.response = Li * color * weight;
+}
+
+void NG_standard_surface_surfaceshader_100(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal, vec3 tangent, out surfaceshader out1)
+{
+ vec2 coat_roughness_vector_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out);
+ const float coat_tangent_rotate_degree_in2_tmp = 360.000000;
+ float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp;
+ vec3 metal_reflectivity_out = base_color * base;
+ vec3 metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ const float tangent_rotate_degree_in2_tmp = 360.000000;
+ float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ const float subsurface_color_nonnegative_in2_tmp = 0.000000;
+ vec3 subsurface_color_nonnegative_out = max(subsurface_color, subsurface_color_nonnegative_in2_tmp);
+ const float coat_clamped_low_tmp = 0.000000;
+ const float coat_clamped_high_tmp = 1.000000;
+ float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp);
+ vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z);
+ float subsurface_selector_out = float(thin_walled);
+ const float base_color_nonnegative_in2_tmp = 0.000000;
+ vec3 base_color_nonnegative_out = max(base_color, base_color_nonnegative_in2_tmp);
+ const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000);
+ vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat);
+ const float one_minus_coat_ior_in1_tmp = 1.000000;
+ float one_minus_coat_ior_out = one_minus_coat_ior_in1_tmp - coat_IOR;
+ const float one_plus_coat_ior_in1_tmp = 1.000000;
+ float one_plus_coat_ior_out = one_plus_coat_ior_in1_tmp + coat_IOR;
+ vec3 emission_weight_out = emission_color * emission;
+ vec3 opacity_luminance_out = vec3(0.0);
+ mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out);
+ vec3 coat_tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out);
+ vec3 artistic_ior_ior = vec3(0.0);
+ vec3 artistic_ior_extinction = vec3(0.0);
+ mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ vec3 tangent_rotate_out = vec3(0.0);
+ mx_rotate_vector3(tangent, tangent_rotate_degree_out, normal, tangent_rotate_out);
+ const float transmission_roughness_clamped_low_tmp = 0.000000;
+ const float transmission_roughness_clamped_high_tmp = 1.000000;
+ float transmission_roughness_clamped_out = clamp(transmission_roughness_add_out, transmission_roughness_clamped_low_tmp, transmission_roughness_clamped_high_tmp);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out);
+ const float coat_affected_roughness_fg_tmp = 1.000000;
+ float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out);
+ const float coat_affected_transmission_roughness_fg_tmp = 1.000000;
+ float coat_affected_transmission_roughness_out = mix(transmission_roughness_clamped_out, coat_affected_transmission_roughness_fg_tmp, coat_affect_roughness_multiply2_out);
+ const float coat_gamma_in2_tmp = 1.000000;
+ float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ const float coat_tangent_value2_tmp = 0.000000;
+ vec3 coat_tangent_out = (coat_anisotropy > coat_tangent_value2_tmp) ? coat_tangent_rotate_normalize_out : tangent;
+ vec2 main_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out);
+ const float main_tangent_value2_tmp = 0.000000;
+ vec3 main_tangent_out = (specular_anisotropy > main_tangent_value2_tmp) ? tangent_rotate_normalize_out : tangent;
+ vec2 transmission_roughness_out = vec2(0.0);
+ mx_roughness_anisotropy(coat_affected_transmission_roughness_out, specular_anisotropy, transmission_roughness_out);
+ vec3 coat_affected_subsurface_color_out = pow(subsurface_color_nonnegative_out, vec3(coat_gamma_out));
+ vec3 coat_affected_diffuse_color_out = pow(base_color_nonnegative_out, vec3(coat_gamma_out));
+ surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
+ {
+ vec3 N = normalize(vd.normalWorld);
+ vec3 V = normalize(u_viewPosition - vd.positionWorld);
+ vec3 P = vd.positionWorld;
+
+ float surfaceOpacity = opacity_luminance_out.x;
+
+ // Shadow occlusion
+ float occlusion = 1.0;
+
+ // Light loop
+ int numLights = numActiveLightSources();
+ lightshader lightShader;
+ for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)
+ {
+ sampleLightSource(u_lightData[activeLightIndex], vd.positionWorld, lightShader);
+ vec3 L = lightShader.direction;
+
+ // Calculate the BSDF response for this light source
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ // Accumulate the light's contribution
+ shader_constructor_out.color += lightShader.intensity * coat_layer_out.response;
+ }
+
+ // Ambient occlusion
+ occlusion = 1.0;
+
+ // Add environment contribution
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ metal_bsdf_out.ior = thin_film_IOR;
+ metal_bsdf_out.thickness = thin_film_thickness;
+ mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal, main_tangent_out, 0, metal_bsdf_out);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal, sheen_bsdf_out);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal, translucent_bsdf_out);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal, subsurface_bsdf_out);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal, diffuse_bsdf_out);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+
+ shader_constructor_out.color += occlusion * coat_layer_out.response;
+ }
+
+ // Add surface emission
+ {
+ EDF emission_edf_out = EDF(0.0);
+ mx_uniform_edf(N, V, emission_weight_out, emission_edf_out);
+ EDF coat_tinted_emission_edf_out = emission_edf_out * coat_color;
+ EDF coat_emission_edf_out = EDF(0.0);
+ mx_generalized_schlick_edf(N, V, vec3(1.000000, 1.000000, 1.000000), vec3(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), 5.000000, coat_tinted_emission_edf_out, coat_emission_edf_out);
+ // Omitted node 'emission_edf'. Function already called in this scope.
+ EDF blended_coat_emission_edf_out = mix(emission_edf_out, coat_emission_edf_out, coat);
+ shader_constructor_out.color += blended_coat_emission_edf_out;
+ }
+
+ // Calculate the BSDF transmission for viewing direction
+ {
+ BSDF coat_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
+ BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_bsdf_out.ior = thin_film_IOR;
+ specular_bsdf_out.thickness = thin_film_thickness;
+ mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, normal, main_tangent_out, 0, 0, specular_bsdf_out);
+ BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
+ BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ selected_subsurface_bsdf_out.response = mix(subsurface_bsdf_out.response, translucent_bsdf_out.response, subsurface_selector_out);
+ selected_subsurface_bsdf_out.throughput = mix(subsurface_bsdf_out.throughput, translucent_bsdf_out.throughput, subsurface_selector_out);
+ BSDF diffuse_bsdf_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ subsurface_mix_out.response = mix(diffuse_bsdf_out.response, selected_subsurface_bsdf_out.response, subsurface);
+ subsurface_mix_out.throughput = mix(diffuse_bsdf_out.throughput, selected_subsurface_bsdf_out.throughput, subsurface);
+ BSDF sheen_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ sheen_layer_out.response = sheen_bsdf_out.response + subsurface_mix_out.response * sheen_bsdf_out.throughput;
+ sheen_layer_out.throughput = sheen_bsdf_out.throughput * subsurface_mix_out.throughput;
+ BSDF transmission_mix_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ transmission_mix_out.response = mix(sheen_layer_out.response, transmission_bsdf_out.response, transmission);
+ transmission_mix_out.throughput = mix(sheen_layer_out.throughput, transmission_bsdf_out.throughput, transmission);
+ BSDF specular_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ specular_layer_out.response = specular_bsdf_out.response + transmission_mix_out.response * specular_bsdf_out.throughput;
+ specular_layer_out.throughput = specular_bsdf_out.throughput * transmission_mix_out.throughput;
+ BSDF thin_film_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
+ thin_film_layer_out.throughput = mix(specular_layer_out.throughput, metal_bsdf_out.throughput, metalness);
+ vec3 thin_film_layer_attenuated_out_in2_clamped = clamp(coat_attenuation_out, 0.0, 1.0);
+ BSDF thin_film_layer_attenuated_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ thin_film_layer_attenuated_out.response = thin_film_layer_out.response * thin_film_layer_attenuated_out_in2_clamped;
+ thin_film_layer_attenuated_out.throughput = thin_film_layer_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
+ BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.0), 0.0, 0.0);
+ coat_layer_out.response = coat_bsdf_out.response + thin_film_layer_attenuated_out.response * coat_bsdf_out.throughput;
+ coat_layer_out.throughput = coat_bsdf_out.throughput * thin_film_layer_attenuated_out.throughput;
+ shader_constructor_out.color += coat_layer_out.response;
+ }
+
+ // Compute and apply surface opacity
+ {
+ shader_constructor_out.color *= surfaceOpacity;
+ shader_constructor_out.transparency = mix(vec3(1.0), shader_constructor_out.transparency, surfaceOpacity);
+ }
+ }
+
+ out1 = shader_constructor_out;
+}
+
+void main()
+{
+ vec3 geomprop_Nworld_out1 = normalize(vd.normalWorld);
+ vec3 geomprop_Tworld_out1 = normalize(vd.tangentWorld);
+ vec2 geomprop_UV0_out1 = vd.texcoord_0;
+ vec3 mtlximage7_out = vec3(0.0);
+ mx_image_color3(mtlximage7_file, mtlximage7_layer, mtlximage7_default, geomprop_UV0_out1, mtlximage7_uaddressmode, mtlximage7_vaddressmode, mtlximage7_filtertype, mtlximage7_framerange, mtlximage7_frameoffset, mtlximage7_frameendaction, mtlximage7_uv_scale, mtlximage7_uv_offset, mtlximage7_out);
+ float mtlximage10_out = 0.0;
+ mx_image_float(mtlximage10_file, mtlximage10_layer, mtlximage10_default, geomprop_UV0_out1, mtlximage10_uaddressmode, mtlximage10_vaddressmode, mtlximage10_filtertype, mtlximage10_framerange, mtlximage10_frameoffset, mtlximage10_frameendaction, mtlximage10_uv_scale, mtlximage10_uv_offset, mtlximage10_out);
+ float mtlximage11_out = 0.0;
+ mx_image_float(mtlximage11_file, mtlximage11_layer, mtlximage11_default, geomprop_UV0_out1, mtlximage11_uaddressmode, mtlximage11_vaddressmode, mtlximage11_filtertype, mtlximage11_framerange, mtlximage11_frameoffset, mtlximage11_frameendaction, mtlximage11_uv_scale, mtlximage11_uv_offset, mtlximage11_out);
+ float mtlximage8_out = 0.0;
+ mx_image_float(mtlximage8_file, mtlximage8_layer, mtlximage8_default, geomprop_UV0_out1, mtlximage8_uaddressmode, mtlximage8_vaddressmode, mtlximage8_filtertype, mtlximage8_framerange, mtlximage8_frameoffset, mtlximage8_frameendaction, mtlximage8_uv_scale, mtlximage8_uv_offset, mtlximage8_out);
+ vec3 mtlximage9_out = vec3(0.0);
+ mx_image_vector3(mtlximage9_file, mtlximage9_layer, mtlximage9_default, geomprop_UV0_out1, mtlximage9_uaddressmode, mtlximage9_vaddressmode, mtlximage9_filtertype, mtlximage9_framerange, mtlximage9_frameoffset, mtlximage9_frameendaction, mtlximage9_uv_scale, mtlximage9_uv_offset, mtlximage9_out);
+ vec3 mtlximage7_out_cm_out = vec3(0.0);
+ NG_srgb_texture_to_lin_rec709_color3(mtlximage7_out, mtlximage7_out_cm_out);
+ vec3 mtlxnormalmap11_out = vec3(0.0);
+ mx_normalmap(mtlximage9_out, mtlxnormalmap11_space, mtlxnormalmap11_scale, geomprop_Nworld_out1, geomprop_Tworld_out1, mtlxnormalmap11_out);
+ surfaceshader King_W_out = surfaceshader(vec3(0.0),vec3(0.0));
+ NG_standard_surface_surfaceshader_100(King_W_base, mtlximage7_out_cm_out, King_W_diffuse_roughness, mtlximage10_out, King_W_specular, King_W_specular_color, mtlximage11_out, King_W_specular_IOR, King_W_specular_anisotropy, King_W_specular_rotation, King_W_transmission, King_W_transmission_color, King_W_transmission_depth, King_W_transmission_scatter, King_W_transmission_scatter_anisotropy, King_W_transmission_dispersion, King_W_transmission_extra_roughness, mtlximage8_out, mtlximage7_out_cm_out, mtlximage7_out_cm_out, King_W_subsurface_scale, King_W_subsurface_anisotropy, King_W_sheen, King_W_sheen_color, King_W_sheen_roughness, King_W_coat, King_W_coat_color, King_W_coat_roughness, King_W_coat_anisotropy, King_W_coat_rotation, King_W_coat_IOR, geomprop_Nworld_out1, King_W_coat_affect_color, King_W_coat_affect_roughness, King_W_thin_film_thickness, King_W_thin_film_IOR, King_W_emission, King_W_emission_color, King_W_opacity, King_W_thin_walled, mtlxnormalmap11_out, geomprop_Tworld_out1, King_W_out);
+ material M_King_W_out = King_W_out;
+ out1 = vec4(M_King_W_out.color, 1.0);
+}
+
diff --git a/Materials/Examples/StandardSurface/M_King_W.glsl.vert b/Materials/Examples/StandardSurface/M_King_W.glsl.vert
new file mode 100644
index 0000000000..60b47d493e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_King_W.glsl.vert
@@ -0,0 +1,31 @@
+#version 400
+
+// Uniform block: PrivateUniforms
+uniform mat4 u_worldMatrix = mat4(1.0);
+uniform mat4 u_viewProjectionMatrix = mat4(1.0);
+uniform mat4 u_worldInverseTransposeMatrix = mat4(1.0);
+
+// Inputs block: VertexInputs
+in vec3 i_position;
+in vec3 i_normal;
+in vec3 i_tangent;
+in vec2 i_texcoord_0;
+
+out VertexData
+{
+ vec3 normalWorld;
+ vec3 tangentWorld;
+ vec2 texcoord_0;
+ vec3 positionWorld;
+} vd;
+
+void main()
+{
+ vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
+ gl_Position = u_viewProjectionMatrix * hPositionWorld;
+ vd.normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
+ vd.tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
+ vd.texcoord_0 = i_texcoord_0;
+ vd.positionWorld = hPositionWorld.xyz;
+}
+
diff --git a/Materials/Examples/StandardSurface/M_King_W.mdl b/Materials/Examples/StandardSurface/M_King_W.mdl
new file mode 100644
index 0000000000..86f964d00e
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_King_W.mdl
@@ -0,0 +1,243 @@
+mdl 1.6;
+
+using mx = materialx;
+import ::df::*;
+import ::base::*;
+import ::math::*;
+import ::state::*;
+import ::anno::*;
+import ::tex::*;
+import ::mx::swizzle::*;
+using ::mx::core import *;
+using ::mx::stdlib import *;
+using ::mx::pbrlib import *;
+using ::mx::sampling import *;
+
+color NG_srgb_texture_to_lin_rec709_color3
+(
+ color in1 = color(0, 0, 0)
+)
+{
+ color bias_out = in1 + 0.055;
+ color linSeg_out = in1 / 12.92;
+ float isAboveR_out = mx::stdlib::mx_ifgreater_float(float3(in1).x, 0.04045, 1, 0);
+ float isAboveG_out = mx::stdlib::mx_ifgreater_float(float3(in1).y, 0.04045, 1, 0);
+ float isAboveB_out = mx::stdlib::mx_ifgreater_float(float3(in1).z, 0.04045, 1, 0);
+ color max_out = math::max(bias_out, 0);
+ color isAbove_out = color(isAboveR_out, isAboveG_out, isAboveB_out);
+ color scale_out = max_out / 1.055;
+ color powSeg_out = math::pow(scale_out, 2.4);
+ color mix_out = math::lerp(linSeg_out, powSeg_out, isAbove_out);
+ return mix_out;
+}
+
+material NG_standard_surface_surfaceshader_100
+(
+ float base = 0.8,
+ color base_color = color(1, 1, 1),
+ float diffuse_roughness = 0,
+ float metalness = 0,
+ float specular = 1,
+ color specular_color = color(1, 1, 1),
+ float specular_roughness = 0.2,
+ uniform float specular_IOR = 1.5,
+ float specular_anisotropy = 0,
+ float specular_rotation = 0,
+ float transmission = 0,
+ color transmission_color = color(1, 1, 1),
+ float transmission_depth = 0,
+ color transmission_scatter = color(0, 0, 0),
+ float transmission_scatter_anisotropy = 0,
+ float transmission_dispersion = 0,
+ float transmission_extra_roughness = 0,
+ float subsurface = 0,
+ color subsurface_color = color(1, 1, 1),
+ color subsurface_radius = color(1, 1, 1),
+ float subsurface_scale = 1,
+ float subsurface_anisotropy = 0,
+ float sheen = 0,
+ color sheen_color = color(1, 1, 1),
+ float sheen_roughness = 0.3,
+ float coat = 0,
+ color coat_color = color(1, 1, 1),
+ float coat_roughness = 0.1,
+ float coat_anisotropy = 0,
+ float coat_rotation = 0,
+ uniform float coat_IOR = 1.5,
+ float3 coat_normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float coat_affect_color = 0,
+ float coat_affect_roughness = 0,
+ float thin_film_thickness = 0,
+ float thin_film_IOR = 1.5,
+ float emission = 0,
+ color emission_color = color(1, 1, 1),
+ color opacity = color(1, 1, 1),
+ bool thin_walled = false,
+ float3 normal = state::transform_normal(state::coordinate_internal, state::coordinate_world, state::normal()),
+ float3 tangent = state::transform_vector(state::coordinate_internal, state::coordinate_world, state::texture_tangent_u(0))
+)
+ = let
+{
+ float2 coat_roughness_vector_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_roughness, mxp_anisotropy:coat_anisotropy);
+ float coat_tangent_rotate_degree_out = coat_rotation * 360;
+ color metal_reflectivity_out = base_color * base;
+ color metal_edgecolor_out = specular_color * specular;
+ float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat;
+ float tangent_rotate_degree_out = specular_rotation * 360;
+ float transmission_roughness_add_out = specular_roughness + transmission_extra_roughness;
+ color subsurface_color_nonnegative_out = math::max(subsurface_color, 0);
+ float coat_clamped_out = math::clamp(coat, 0, 1);
+ float3 subsurface_radius_vector_out = float3(float3(subsurface_radius).x, float3(subsurface_radius).y, float3(subsurface_radius).z);
+ float subsurface_selector_out = float(thin_walled);
+ color base_color_nonnegative_out = math::max(base_color, 0);
+ color coat_attenuation_out = math::lerp(color(1, 1, 1), coat_color, coat);
+ float one_minus_coat_ior_out = 1 - coat_IOR;
+ float one_plus_coat_ior_out = 1 + coat_IOR;
+ color emission_weight_out = emission_color * emission;
+ color opacity_luminance_out = mx::stdlib::mx_luminance_color3(opacity);
+ float3 coat_tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:coat_tangent_rotate_degree_out, mxp_axis:coat_normal);
+ mx::pbrlib::mx_artistic_ior__result artistic_ior_result = mx::pbrlib::mx_artistic_ior(mxp_reflectivity:metal_reflectivity_out, mxp_edge_color:metal_edgecolor_out);
+ float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness;
+ float3 tangent_rotate_out = mx::stdlib::mx_rotate3d_vector3(mxp_in:tangent, mxp_amount:tangent_rotate_degree_out, mxp_axis:normal);
+ float transmission_roughness_clamped_out = math::clamp(transmission_roughness_add_out, 0, 1);
+ float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color;
+ float3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale;
+ float coat_ior_to_F0_sqrt_out = one_minus_coat_ior_out / one_plus_coat_ior_out;
+ float3 coat_tangent_rotate_normalize_out = math::normalize(coat_tangent_rotate_out);
+ float coat_affected_roughness_out = math::lerp(specular_roughness, 1, coat_affect_roughness_multiply2_out);
+ float3 tangent_rotate_normalize_out = math::normalize(tangent_rotate_out);
+ float coat_affected_transmission_roughness_out = math::lerp(transmission_roughness_clamped_out, 1, coat_affect_roughness_multiply2_out);
+ float coat_gamma_out = coat_gamma_multiply_out + 1;
+ float coat_ior_to_F0_out = coat_ior_to_F0_sqrt_out * coat_ior_to_F0_sqrt_out;
+ float3 coat_tangent_out = mx::stdlib::mx_ifgreater_vector3(coat_anisotropy, 0, coat_tangent_rotate_normalize_out, tangent);
+ float2 main_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_roughness_out, mxp_anisotropy:specular_anisotropy);
+ float3 main_tangent_out = mx::stdlib::mx_ifgreater_vector3(specular_anisotropy, 0, tangent_rotate_normalize_out, tangent);
+ float2 transmission_roughness_out = mx::pbrlib::mx_roughness_anisotropy(mxp_roughness:coat_affected_transmission_roughness_out, mxp_anisotropy:specular_anisotropy);
+ color coat_affected_subsurface_color_out = math::pow(subsurface_color_nonnegative_out, coat_gamma_out);
+ color coat_affected_diffuse_color_out = math::pow(base_color_nonnegative_out, coat_gamma_out);
+ material metal_bsdf_out = mx::pbrlib::mx_conductor_bsdf(mxp_weight:1, mxp_ior:artistic_ior_result.mxp_ior, mxp_extinction:artistic_ior_result.mxp_extinction, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material transmission_bsdf_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:1, mxp_tint:transmission_color, mxp_ior:specular_IOR, mxp_roughness:transmission_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_T, mxp_base:material(), mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material translucent_bsdf_out = mx::pbrlib::mx_translucent_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_normal:normal);
+ material subsurface_bsdf_out = mx::pbrlib::mx_subsurface_bsdf(mxp_weight:1, mxp_color:coat_affected_subsurface_color_out, mxp_radius:subsurface_radius_scaled_out, mxp_anisotropy:subsurface_anisotropy, mxp_normal:normal);
+ material selected_subsurface_bsdf_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:translucent_bsdf_out, mxp_bg:subsurface_bsdf_out, mxp_mix:subsurface_selector_out);
+ material diffuse_bsdf_out = mx::pbrlib::mx_oren_nayar_diffuse_bsdf(mxp_weight:base, mxp_color:coat_affected_diffuse_color_out, mxp_roughness:diffuse_roughness, mxp_normal:normal);
+ material subsurface_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:selected_subsurface_bsdf_out, mxp_bg:diffuse_bsdf_out, mxp_mix:subsurface);
+ material sheen_layer_out = mx::pbrlib::mx_sheen_bsdf(mxp_weight:sheen, mxp_color:sheen_color, mxp_roughness:sheen_roughness, mxp_normal:normal, mxp_base:subsurface_mix_out);
+ material transmission_mix_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:transmission_bsdf_out, mxp_bg:sheen_layer_out, mxp_mix:transmission);
+ material specular_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:specular, mxp_tint:specular_color, mxp_ior:specular_IOR, mxp_roughness:main_roughness_out, mxp_normal:normal, mxp_tangent:main_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:transmission_mix_out, mxp_thinfilm_thickness:thin_film_thickness, mxp_thinfilm_ior:thin_film_IOR);
+ material thin_film_layer_out = mx::pbrlib::mx_mix_bsdf(mxp_fg:metal_bsdf_out, mxp_bg:specular_layer_out, mxp_mix:metalness);
+ material thin_film_layer_attenuated_out = mx::pbrlib::mx_multiply_bsdf_color3(mxp_in1:thin_film_layer_out, mxp_in2:coat_attenuation_out);
+ material coat_layer_out = mx::pbrlib::mx_dielectric_bsdf(mxp_weight:coat, mxp_tint:color(1, 1, 1), mxp_ior:coat_IOR, mxp_roughness:coat_roughness_vector_out, mxp_normal:coat_normal, mxp_tangent:coat_tangent_out, mxp_distribution:mx_distribution_type_ggx, mxp_scatter_mode:mx_scatter_mode_R, mxp_base:thin_film_layer_attenuated_out, mxp_thinfilm_thickness:0.0, mxp_thinfilm_ior:0.0);
+ material emission_edf_out = mx::pbrlib::mx_uniform_edf(mxp_color:emission_weight_out);
+ material coat_tinted_emission_edf_out = mx::pbrlib::mx_multiply_edf_color3(mxp_in1:emission_edf_out, mxp_in2:coat_color);
+ material coat_emission_edf_out = mx::pbrlib::mx_generalized_schlick_edf(mxp_color0:color(1, 1, 1), mxp_color90:color(coat_ior_to_F0_out, coat_ior_to_F0_out, coat_ior_to_F0_out), mxp_exponent:5, mxp_base:coat_tinted_emission_edf_out);
+ material blended_coat_emission_edf_out = mx::pbrlib::mx_mix_edf(mxp_fg:coat_emission_edf_out, mxp_bg:emission_edf_out, mxp_mix:coat);
+ material shader_constructor_out = mx::pbrlib::mx_surface(coat_layer_out, blended_coat_emission_edf_out, float3(opacity_luminance_out).x, specular_IOR);
+}
+in material(shader_constructor_out);
+
+export material M_King_W
+(
+ material displacementshader = material(),
+ uniform mx_coordinatespace_type geomprop_Nworld_space = mx_coordinatespace_type_world,
+ uniform mx_coordinatespace_type geomprop_Tworld_space = mx_coordinatespace_type_world,
+ uniform int geomprop_Tworld_index = 0,
+ uniform int geomprop_UV0_index = 0,
+ uniform texture_2d mtlximage7_file = texture_2d("/chess_set/king_white_base_color.jpg", tex::gamma_linear),
+ uniform string mtlximage7_layer = "",
+ color mtlximage7_default = color(0, 0, 0),
+ uniform mx_addressmode_type mtlximage7_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage7_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage7_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage7_framerange = "",
+ uniform int mtlximage7_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage7_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d mtlximage10_file = texture_2d("/chess_set/king_shared_metallic.jpg", tex::gamma_linear),
+ uniform string mtlximage10_layer = "",
+ float mtlximage10_default = 0,
+ uniform mx_addressmode_type mtlximage10_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage10_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage10_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage10_framerange = "",
+ uniform int mtlximage10_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage10_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d mtlximage11_file = texture_2d("/chess_set/king_white_roughness.jpg", tex::gamma_linear),
+ uniform string mtlximage11_layer = "",
+ float mtlximage11_default = 0,
+ uniform mx_addressmode_type mtlximage11_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage11_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage11_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage11_framerange = "",
+ uniform int mtlximage11_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage11_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d mtlximage8_file = texture_2d("/chess_set/king_shared_scattering.jpg", tex::gamma_linear),
+ uniform string mtlximage8_layer = "",
+ float mtlximage8_default = 0,
+ uniform mx_addressmode_type mtlximage8_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage8_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage8_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage8_framerange = "",
+ uniform int mtlximage8_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage8_frameendaction = mx_addressmode_type_constant,
+ uniform texture_2d mtlximage9_file = texture_2d("/chess_set/king_white_normal.jpg", tex::gamma_linear),
+ uniform string mtlximage9_layer = "",
+ float3 mtlximage9_default = float3(0, 0, 0),
+ uniform mx_addressmode_type mtlximage9_uaddressmode = mx_addressmode_type_periodic,
+ uniform mx_addressmode_type mtlximage9_vaddressmode = mx_addressmode_type_periodic,
+ uniform mx_filterlookup_type mtlximage9_filtertype = mx_filterlookup_type_linear,
+ uniform string mtlximage9_framerange = "",
+ uniform int mtlximage9_frameoffset = 0,
+ uniform mx_addressmode_type mtlximage9_frameendaction = mx_addressmode_type_constant,
+ uniform string mtlxnormalmap11_space = "tangent",
+ float mtlxnormalmap11_scale = 1,
+ float King_W_base = 1,
+ float King_W_diffuse_roughness = 0,
+ float King_W_specular = 1,
+ color King_W_specular_color = color(1, 1, 1),
+ uniform float King_W_specular_IOR = 1.5,
+ float King_W_specular_anisotropy = 0,
+ float King_W_specular_rotation = 0,
+ float King_W_transmission = 0,
+ color King_W_transmission_color = color(1, 1, 1),
+ float King_W_transmission_depth = 0,
+ color King_W_transmission_scatter = color(0, 0, 0),
+ float King_W_transmission_scatter_anisotropy = 0,
+ float King_W_transmission_dispersion = 0,
+ float King_W_transmission_extra_roughness = 0,
+ float King_W_subsurface_scale = 0.003,
+ float King_W_subsurface_anisotropy = 0,
+ float King_W_sheen = 0,
+ color King_W_sheen_color = color(1, 1, 1),
+ float King_W_sheen_roughness = 0.3,
+ float King_W_coat = 0,
+ color King_W_coat_color = color(1, 1, 1),
+ float King_W_coat_roughness = 0.1,
+ float King_W_coat_anisotropy = 0,
+ float King_W_coat_rotation = 0,
+ uniform float King_W_coat_IOR = 1.5,
+ float King_W_coat_affect_color = 0,
+ float King_W_coat_affect_roughness = 0,
+ float King_W_thin_film_thickness = 0,
+ float King_W_thin_film_IOR = 1.5,
+ float King_W_emission = 0,
+ color King_W_emission_color = color(1, 1, 1),
+ color King_W_opacity = color(1, 1, 1),
+ bool King_W_thin_walled = false
+)
+= let
+{
+ float3 geomprop_Nworld_out1 = mx::stdlib::mx_normal_vector3(mxp_space:geomprop_Nworld_space);
+ float3 geomprop_Tworld_out1 = mx::stdlib::mx_tangent_vector3(mxp_space:geomprop_Tworld_space, mxp_index:geomprop_Tworld_index);
+ float2 geomprop_UV0_out1 = mx::stdlib::mx_texcoord_vector2(mxp_index:geomprop_UV0_index);
+ color mtlximage7_out = mx::stdlib::mx_image_color3(mtlximage7_file, mtlximage7_layer, mtlximage7_default, geomprop_UV0_out1, mtlximage7_uaddressmode, mtlximage7_vaddressmode, mtlximage7_filtertype, mtlximage7_framerange, mtlximage7_frameoffset, mtlximage7_frameendaction);
+ float mtlximage10_out = mx::stdlib::mx_image_float(mtlximage10_file, mtlximage10_layer, mtlximage10_default, geomprop_UV0_out1, mtlximage10_uaddressmode, mtlximage10_vaddressmode, mtlximage10_filtertype, mtlximage10_framerange, mtlximage10_frameoffset, mtlximage10_frameendaction);
+ float mtlximage11_out = mx::stdlib::mx_image_float(mtlximage11_file, mtlximage11_layer, mtlximage11_default, geomprop_UV0_out1, mtlximage11_uaddressmode, mtlximage11_vaddressmode, mtlximage11_filtertype, mtlximage11_framerange, mtlximage11_frameoffset, mtlximage11_frameendaction);
+ float mtlximage8_out = mx::stdlib::mx_image_float(mtlximage8_file, mtlximage8_layer, mtlximage8_default, geomprop_UV0_out1, mtlximage8_uaddressmode, mtlximage8_vaddressmode, mtlximage8_filtertype, mtlximage8_framerange, mtlximage8_frameoffset, mtlximage8_frameendaction);
+ float3 mtlximage9_out = mx::stdlib::mx_image_vector3(mtlximage9_file, mtlximage9_layer, mtlximage9_default, geomprop_UV0_out1, mtlximage9_uaddressmode, mtlximage9_vaddressmode, mtlximage9_filtertype, mtlximage9_framerange, mtlximage9_frameoffset, mtlximage9_frameendaction);
+ color mtlximage7_out_cm_out = NG_srgb_texture_to_lin_rec709_color3(mtlximage7_out);
+ float3 mtlxnormalmap11_out = mx::stdlib::mx_normalmap(mxp_in:mtlximage9_out, mxp_space:mtlxnormalmap11_space, mxp_scale:mtlxnormalmap11_scale, mxp_normal:geomprop_Nworld_out1, mxp_tangent:geomprop_Tworld_out1);
+ material King_W_out = NG_standard_surface_surfaceshader_100(King_W_base, mtlximage7_out_cm_out, King_W_diffuse_roughness, mtlximage10_out, King_W_specular, King_W_specular_color, mtlximage11_out, King_W_specular_IOR, King_W_specular_anisotropy, King_W_specular_rotation, King_W_transmission, King_W_transmission_color, King_W_transmission_depth, King_W_transmission_scatter, King_W_transmission_scatter_anisotropy, King_W_transmission_dispersion, King_W_transmission_extra_roughness, mtlximage8_out, mtlximage7_out_cm_out, mtlximage7_out_cm_out, King_W_subsurface_scale, King_W_subsurface_anisotropy, King_W_sheen, King_W_sheen_color, King_W_sheen_roughness, King_W_coat, King_W_coat_color, King_W_coat_roughness, King_W_coat_anisotropy, King_W_coat_rotation, King_W_coat_IOR, geomprop_Nworld_out1, King_W_coat_affect_color, King_W_coat_affect_roughness, King_W_thin_film_thickness, King_W_thin_film_IOR, King_W_emission, King_W_emission_color, King_W_opacity, King_W_thin_walled, mtlxnormalmap11_out, geomprop_Tworld_out1);
+ material M_King_W_out = mx::stdlib::mx_surfacematerial(mxp_surfaceshader: King_W_out, mxp_displacementshader: displacementshader);
+ material finalOutput__ = M_King_W_out;
+}
+in material(finalOutput__);
diff --git a/Materials/Examples/StandardSurface/M_King_W.msl.frag b/Materials/Examples/StandardSurface/M_King_W.msl.frag
new file mode 100644
index 0000000000..82251a99b4
--- /dev/null
+++ b/Materials/Examples/StandardSurface/M_King_W.msl.frag
@@ -0,0 +1,2872 @@
+//Metal Shading Language version 2.3
+#define __METAL__
+#include
+#include
+using namespace metal;
+#define vec2 float2
+#define vec3 float3
+#define vec4 float4
+#define ivec2 int2
+#define ivec3 int3
+#define ivec4 int4
+#define uvec2 uint2
+#define uvec3 uint3
+#define uvec4 uint4
+#define bvec2 bool2
+#define bvec3 bool3
+#define bvec4 bool4
+#define mat3 float3x3
+#define mat4 float4x4
+
+
+struct MetalTexture
+{
+ texture2d tex;
+ sampler s;
+ 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(); }
+};
+
+int get_width(MetalTexture mtlTex) { return mtlTex.get_width(); }
+
+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));
+}
+
+int2 textureSize(MetalTexture mtlTex, int mipLevel)
+{
+ return int2(mtlTex.get_width(), mtlTex.get_height());
+}
+
+int texture_mips(MetalTexture mtlTex)
+{
+ return mtlTex.tex.get_num_mip_levels();
+}
+struct BSDF { float3 response; float3 throughput; float thickness; float ior; };
+#define EDF float3
+struct surfaceshader { float3 color; float3 transparency; };
+struct volumeshader { float3 color; float3 transparency; };
+struct displacementshader { float3 offset; float scale; };
+struct lightshader { float3 intensity; float3 direction; };
+#define material surfaceshader
+
+// Uniform block: PublicUniforms
+struct PublicUniforms
+{
+ displacementshader displacementshader1;
+ int mtlximage7_layer;
+ vec3 mtlximage7_default;
+ int mtlximage7_uaddressmode;
+ int mtlximage7_vaddressmode;
+ int mtlximage7_filtertype;
+ int mtlximage7_framerange;
+ int mtlximage7_frameoffset;
+ int mtlximage7_frameendaction;
+ vec2 mtlximage7_uv_scale;
+ vec2 mtlximage7_uv_offset;
+ int mtlximage10_layer;
+ float mtlximage10_default;
+ int mtlximage10_uaddressmode;
+ int mtlximage10_vaddressmode;
+ int mtlximage10_filtertype;
+ int mtlximage10_framerange;
+ int mtlximage10_frameoffset;
+ int mtlximage10_frameendaction;
+ vec2 mtlximage10_uv_scale;
+ vec2 mtlximage10_uv_offset;
+ int mtlximage11_layer;
+ float mtlximage11_default;
+ int mtlximage11_uaddressmode;
+ int mtlximage11_vaddressmode;
+ int mtlximage11_filtertype;
+ int mtlximage11_framerange;
+ int mtlximage11_frameoffset;
+ int mtlximage11_frameendaction;
+ vec2 mtlximage11_uv_scale;
+ vec2 mtlximage11_uv_offset;
+ int mtlximage8_layer;
+ float mtlximage8_default;
+ int mtlximage8_uaddressmode;
+ int mtlximage8_vaddressmode;
+ int mtlximage8_filtertype;
+ int mtlximage8_framerange;
+ int mtlximage8_frameoffset;
+ int mtlximage8_frameendaction;
+ vec2 mtlximage8_uv_scale;
+ vec2 mtlximage8_uv_offset;
+ int mtlximage9_layer;
+ vec3 mtlximage9_default;
+ int mtlximage9_uaddressmode;
+ int mtlximage9_vaddressmode;
+ int mtlximage9_filtertype;
+ int mtlximage9_framerange;
+ int mtlximage9_frameoffset;
+ int mtlximage9_frameendaction;
+ vec2 mtlximage9_uv_scale;
+ vec2 mtlximage9_uv_offset;
+ int mtlxnormalmap11_space;
+ float mtlxnormalmap11_scale;
+ float King_W_base;
+ float King_W_diffuse_roughness;
+ float King_W_specular;
+ vec3 King_W_specular_color;
+ float King_W_specular_IOR;
+ float King_W_specular_anisotropy;
+ float King_W_specular_rotation;
+ float King_W_transmission;
+ vec3 King_W_transmission_color;
+ float King_W_transmission_depth;
+ vec3 King_W_transmission_scatter;
+ float King_W_transmission_scatter_anisotropy;
+ float King_W_transmission_dispersion;
+ float King_W_transmission_extra_roughness;
+ float King_W_subsurface_scale;
+ float King_W_subsurface_anisotropy;
+ float King_W_sheen;
+ vec3 King_W_sheen_color;
+ float King_W_sheen_roughness;
+ float King_W_coat;
+ vec3 King_W_coat_color;
+ float King_W_coat_roughness;
+ float King_W_coat_anisotropy;
+ float King_W_coat_rotation;
+ float King_W_coat_IOR;
+ float King_W_coat_affect_color;
+ float King_W_coat_affect_roughness;
+ float King_W_thin_film_thickness;
+ float King_W_thin_film_IOR;
+ float King_W_emission;
+ vec3 King_W_emission_color;
+ vec3 King_W_opacity;
+ bool King_W_thin_walled;
+};
+
+// Uniform block: PrivateUniforms
+struct PrivateUniforms
+{
+ mat4 u_envMatrix;
+ int u_envRadianceMips;
+ int u_envRadianceSamples;
+ bool u_refractionTwoSided;
+ vec3 u_viewPosition;
+ int u_numActiveLightSources;
+};
+
+// Inputs block: VertexData
+struct VertexData
+{
+ float4 pos [[position]];
+ vec3 normalWorld ;
+ vec3 tangentWorld ;
+ vec2 texcoord_0 ;
+ vec3 positionWorld ;
+};
+// Pixel shader outputs
+struct PixelOutputs
+{
+ vec4 out1;
+};
+
+#define DIRECTIONAL_ALBEDO_METHOD 0
+
+#define MAX_LIGHT_SOURCES 3
+struct LightData
+{
+ int type;
+ float pad0;
+ float pad1;
+ float pad2;
+};
+
+struct LightData_pixel
+{
+ LightData u_lightData[MAX_LIGHT_SOURCES];
+};
+
+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;
+}
+struct GlobalContext
+{
+ GlobalContext(
+ VertexData vd
+, constant LightData u_lightData[MAX_LIGHT_SOURCES]
+ , displacementshader displacementshader1
+
+, MetalTexture mtlximage7_file , int mtlximage7_layer
+
+ , vec3 mtlximage7_default
+
+ , int mtlximage7_uaddressmode
+
+ , int mtlximage7_vaddressmode
+
+ , int mtlximage7_filtertype
+
+ , int mtlximage7_framerange
+
+ , int mtlximage7_frameoffset
+
+ , int mtlximage7_frameendaction
+
+ , vec2 mtlximage7_uv_scale
+
+ , vec2 mtlximage7_uv_offset
+
+, MetalTexture mtlximage10_file , int mtlximage10_layer
+
+ , float mtlximage10_default
+
+ , int mtlximage10_uaddressmode
+
+ , int mtlximage10_vaddressmode
+
+ , int mtlximage10_filtertype
+
+ , int mtlximage10_framerange
+
+ , int mtlximage10_frameoffset
+
+ , int mtlximage10_frameendaction
+
+ , vec2 mtlximage10_uv_scale
+
+ , vec2 mtlximage10_uv_offset
+
+, MetalTexture mtlximage11_file , int mtlximage11_layer
+
+ , float mtlximage11_default
+
+ , int mtlximage11_uaddressmode
+
+ , int mtlximage11_vaddressmode
+
+ , int mtlximage11_filtertype
+
+ , int mtlximage11_framerange
+
+ , int mtlximage11_frameoffset
+
+ , int mtlximage11_frameendaction
+
+ , vec2 mtlximage11_uv_scale
+
+ , vec2 mtlximage11_uv_offset
+
+, MetalTexture mtlximage8_file , int mtlximage8_layer
+
+ , float mtlximage8_default
+
+ , int mtlximage8_uaddressmode
+
+ , int mtlximage8_vaddressmode
+
+ , int mtlximage8_filtertype
+
+ , int mtlximage8_framerange
+
+ , int mtlximage8_frameoffset
+
+ , int mtlximage8_frameendaction
+
+ , vec2 mtlximage8_uv_scale
+
+ , vec2 mtlximage8_uv_offset
+
+, MetalTexture mtlximage9_file , int mtlximage9_layer
+
+ , vec3 mtlximage9_default
+
+ , int mtlximage9_uaddressmode
+
+ , int mtlximage9_vaddressmode
+
+ , int mtlximage9_filtertype
+
+ , int mtlximage9_framerange
+
+ , int mtlximage9_frameoffset
+
+ , int mtlximage9_frameendaction
+
+ , vec2 mtlximage9_uv_scale
+
+ , vec2 mtlximage9_uv_offset
+
+ , int mtlxnormalmap11_space
+
+ , float mtlxnormalmap11_scale
+
+ , float King_W_base
+
+ , float King_W_diffuse_roughness
+
+ , float King_W_specular
+
+ , vec3 King_W_specular_color
+
+ , float King_W_specular_IOR
+
+ , float King_W_specular_anisotropy
+
+ , float King_W_specular_rotation
+
+ , float King_W_transmission
+
+ , vec3 King_W_transmission_color
+
+ , float King_W_transmission_depth
+
+ , vec3 King_W_transmission_scatter
+
+ , float King_W_transmission_scatter_anisotropy
+
+ , float King_W_transmission_dispersion
+
+ , float King_W_transmission_extra_roughness
+
+ , float King_W_subsurface_scale
+
+ , float King_W_subsurface_anisotropy
+
+ , float King_W_sheen
+
+ , vec3 King_W_sheen_color
+
+ , float King_W_sheen_roughness
+
+ , float King_W_coat
+
+ , vec3 King_W_coat_color
+
+ , float King_W_coat_roughness
+
+ , float King_W_coat_anisotropy
+
+ , float King_W_coat_rotation
+
+ , float King_W_coat_IOR
+
+ , float King_W_coat_affect_color
+
+ , float King_W_coat_affect_roughness
+
+ , float King_W_thin_film_thickness
+
+ , float King_W_thin_film_IOR
+
+ , float King_W_emission
+
+ , vec3 King_W_emission_color
+
+ , vec3 King_W_opacity
+
+ , bool King_W_thin_walled
+
+ , mat4 u_envMatrix
+
+, MetalTexture u_envRadiance , int u_envRadianceMips
+
+ , int u_envRadianceSamples
+
+, MetalTexture u_envIrradiance , bool u_refractionTwoSided
+
+ , vec3 u_viewPosition
+
+ , int u_numActiveLightSources
+
+ ) :
+gl_FragCoord( vd.pos)
+, vd(vd)
+, u_lightData
+ {
+ u_lightData[0]
+, u_lightData[1]
+, u_lightData[2]
+ }
+ , displacementshader1(displacementshader1)
+
+, mtlximage7_file(mtlximage7_file)
+ , mtlximage7_layer(mtlximage7_layer)
+
+ , mtlximage7_default(mtlximage7_default)
+
+ , mtlximage7_uaddressmode(mtlximage7_uaddressmode)
+
+ , mtlximage7_vaddressmode(mtlximage7_vaddressmode)
+
+ , mtlximage7_filtertype(mtlximage7_filtertype)
+
+ , mtlximage7_framerange(mtlximage7_framerange)
+
+ , mtlximage7_frameoffset(mtlximage7_frameoffset)
+
+ , mtlximage7_frameendaction(mtlximage7_frameendaction)
+
+ , mtlximage7_uv_scale(mtlximage7_uv_scale)
+
+ , mtlximage7_uv_offset(mtlximage7_uv_offset)
+
+, mtlximage10_file(mtlximage10_file)
+ , mtlximage10_layer(mtlximage10_layer)
+
+ , mtlximage10_default(mtlximage10_default)
+
+ , mtlximage10_uaddressmode(mtlximage10_uaddressmode)
+
+ , mtlximage10_vaddressmode(mtlximage10_vaddressmode)
+
+ , mtlximage10_filtertype(mtlximage10_filtertype)
+
+ , mtlximage10_framerange(mtlximage10_framerange)
+
+ , mtlximage10_frameoffset(mtlximage10_frameoffset)
+
+ , mtlximage10_frameendaction(mtlximage10_frameendaction)
+
+ , mtlximage10_uv_scale(mtlximage10_uv_scale)
+
+ , mtlximage10_uv_offset(mtlximage10_uv_offset)
+
+, mtlximage11_file(mtlximage11_file)
+ , mtlximage11_layer(mtlximage11_layer)
+
+ , mtlximage11_default(mtlximage11_default)
+
+ , mtlximage11_uaddressmode(mtlximage11_uaddressmode)
+
+ , mtlximage11_vaddressmode(mtlximage11_vaddressmode)
+
+ , mtlximage11_filtertype(mtlximage11_filtertype)
+
+ , mtlximage11_framerange(mtlximage11_framerange)
+
+ , mtlximage11_frameoffset(mtlximage11_frameoffset)
+
+ , mtlximage11_frameendaction(mtlximage11_frameendaction)
+
+ , mtlximage11_uv_scale(mtlximage11_uv_scale)
+
+ , mtlximage11_uv_offset(mtlximage11_uv_offset)
+
+, mtlximage8_file(mtlximage8_file)
+ , mtlximage8_layer(mtlximage8_layer)
+
+ , mtlximage8_default(mtlximage8_default)
+
+ , mtlximage8_uaddressmode(mtlximage8_uaddressmode)
+
+ , mtlximage8_vaddressmode(mtlximage8_vaddressmode)
+
+ , mtlximage8_filtertype(mtlximage8_filtertype)
+
+ , mtlximage8_framerange(mtlximage8_framerange)
+
+ , mtlximage8_frameoffset(mtlximage8_frameoffset)
+
+ , mtlximage8_frameendaction(mtlximage8_frameendaction)
+
+ , mtlximage8_uv_scale(mtlximage8_uv_scale)
+
+ , mtlximage8_uv_offset(mtlximage8_uv_offset)
+
+, mtlximage9_file(mtlximage9_file)
+ , mtlximage9_layer(mtlximage9_layer)
+
+ , mtlximage9_default(mtlximage9_default)
+
+ , mtlximage9_uaddressmode(mtlximage9_uaddressmode)
+
+ , mtlximage9_vaddressmode(mtlximage9_vaddressmode)
+
+ , mtlximage9_filtertype(mtlximage9_filtertype)
+
+ , mtlximage9_framerange(mtlximage9_framerange)
+
+ , mtlximage9_frameoffset(mtlximage9_frameoffset)
+
+ , mtlximage9_frameendaction(mtlximage9_frameendaction)
+
+ , mtlximage9_uv_scale(mtlximage9_uv_scale)
+
+ , mtlximage9_uv_offset(mtlximage9_uv_offset)
+
+ , mtlxnormalmap11_space(mtlxnormalmap11_space)
+
+ , mtlxnormalmap11_scale(mtlxnormalmap11_scale)
+
+ , King_W_base(King_W_base)
+
+ , King_W_diffuse_roughness(King_W_diffuse_roughness)
+
+ , King_W_specular(King_W_specular)
+
+ , King_W_specular_color(King_W_specular_color)
+
+ , King_W_specular_IOR(King_W_specular_IOR)
+
+ , King_W_specular_anisotropy(King_W_specular_anisotropy)
+
+ , King_W_specular_rotation(King_W_specular_rotation)
+
+ , King_W_transmission(King_W_transmission)
+
+ , King_W_transmission_color(King_W_transmission_color)
+
+ , King_W_transmission_depth(King_W_transmission_depth)
+
+ , King_W_transmission_scatter(King_W_transmission_scatter)
+
+ , King_W_transmission_scatter_anisotropy(King_W_transmission_scatter_anisotropy)
+
+ , King_W_transmission_dispersion(King_W_transmission_dispersion)
+
+ , King_W_transmission_extra_roughness(King_W_transmission_extra_roughness)
+
+ , King_W_subsurface_scale(King_W_subsurface_scale)
+
+ , King_W_subsurface_anisotropy(King_W_subsurface_anisotropy)
+
+ , King_W_sheen(King_W_sheen)
+
+ , King_W_sheen_color(King_W_sheen_color)
+
+ , King_W_sheen_roughness(King_W_sheen_roughness)
+
+ , King_W_coat(King_W_coat)
+
+ , King_W_coat_color(King_W_coat_color)
+
+ , King_W_coat_roughness(King_W_coat_roughness)
+
+ , King_W_coat_anisotropy(King_W_coat_anisotropy)
+
+ , King_W_coat_rotation(King_W_coat_rotation)
+
+ , King_W_coat_IOR(King_W_coat_IOR)
+
+ , King_W_coat_affect_color(King_W_coat_affect_color)
+
+ , King_W_coat_affect_roughness(King_W_coat_affect_roughness)
+
+ , King_W_thin_film_thickness(King_W_thin_film_thickness)
+
+ , King_W_thin_film_IOR(King_W_thin_film_IOR)
+
+ , King_W_emission(King_W_emission)
+
+ , King_W_emission_color(King_W_emission_color)
+
+ , King_W_opacity(King_W_opacity)
+
+ , King_W_thin_walled(King_W_thin_walled)
+
+ , u_envMatrix(u_envMatrix)
+
+, u_envRadiance(u_envRadiance)
+ , u_envRadianceMips(u_envRadianceMips)
+
+ , u_envRadianceSamples(u_envRadianceSamples)
+
+, u_envIrradiance(u_envIrradiance)
+ , u_refractionTwoSided(u_refractionTwoSided)
+
+ , u_viewPosition(u_viewPosition)
+
+ , u_numActiveLightSources(u_numActiveLightSources)
+
+ {}
+ #define __DECL_GL_MATH_FUNCTIONS__
+ #define M_FLOAT_EPS 1e-8
+
+ float mx_square(float x)
+ {
+ return x*x;
+ }
+
+ vec2 mx_square(vec2 x)
+ {
+ return x*x;
+ }
+
+ vec3 mx_square(vec3 x)
+ {
+ return x*x;
+ }
+
+ #ifdef __DECL_GL_MATH_FUNCTIONS__
+
+ float radians(float degree) { return (degree * M_PI_F / 180.0f); }
+
+ float3x3 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 = 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 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 = 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;
+ }
+
+ template
+ T1 mod(T1 x, T2 y)
+ {
+ return x - y * floor(x/y);
+ }
+
+ template
+ T atan(T y_over_x) { return ::atan(y_over_x); }
+
+ template