This book will examine how to set up MaterialX for code generation. This book covers the generateshader.py script provided as part of the core distribution.
Topics covered:
Details behind code generation, generation options, introspection / reflection, and input binding is covered as part of rendering.
Background on code generation can be found here.
For the Python distribution, each code generator resides in a separate module in the MaterialX package.
The name of each module is of the form:
PyMaterialXGen<target>
where target is the code generation target written in camel-case.
All target names start with gen
and then the shading language name:
gen<target>
For example, the target for the OSL shading language is genosl
, with the module's postfix string being GenOsl
.
The variants for GLSL include: genglsl
and genessl
, which reside in a single module with postfix string GenGlsl
.
The C++ library equivalent to the module is named:
MaterialXGen<target>
This is basically the same as the Python module but without the Py
prefix.
The module PyMaterialxShader
contains the base support for all code generators. In the code below this module as well as modules for all targets are imported.
import sys, os, subprocess
import MaterialX as mx
import MaterialX.PyMaterialXGenShader as mx_gen_shader
import MaterialX.PyMaterialXGenGlsl as mx_gen_glsl
import MaterialX.PyMaterialXGenOsl as mx_gen_osl
import MaterialX.PyMaterialXGenMdl as mx_gen_mdl
# Version check
from mtlxutils.mxbase import *
supportsMSL = haveVersion(1, 38, 7)
if supportsMSL:
import MaterialX.PyMaterialXGenMsl as mx_gen_msl
print('Have required version for MSL')
The basic setup requires that a document is created, the standard libraries are loaded, and the document containing the elements to generate code for to be present.
For the purposes of showing formatted results we use the
IPython
package to be able to outputMarkdown
from Python code.
from IPython.display import display_markdown
Additional modules can be imported to support functionality such as code validation.
For GLSL
, ESSL
, and Vulkan
languages glslangValidator can be used for syntax and compilation validation. It is installed using vcpkg
and is run as part of the CI process. For OSL and MDL: olsc
and mdlc
compilers are used respectively.
The generateshader.py
script supports passing in a external program as an argument. The source code passed to this program for validation.
The utility function from that script has been extracted out and is included below as an example.
def validateCode(sourceCodeFile, codevalidator, codevalidatorArgs):
if codevalidator:
cmd = codevalidator + ' ' + sourceCodeFile
if codevalidatorArgs:
cmd += ' ' + codevalidatorArgs
print('----- Run Validator: '+ cmd)
try:
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
result = output.decode(encoding='utf-8')
except subprocess.CalledProcessError as out:
return (out.output.decode(encoding='utf-8'))
return ""
In the following code, a document is first created and then a sample file which defines a "marble" material is read in.
Note that MaterialX throws exceptions when encountering errors instead of keeping track of status code. There are some specific exceptions which provide additional information beyond the regular exception information.
It is always prudent to catch exceptions including checking of custom exceptions provided for file I/O, code generation and rendering.
In this example a "file missing" exception may be returned if the file cannot be read.
The possible Exception
types are defined in the API documentation. In Python, the exception name is the same as the C++ class name.
# Read in MaterialX file
#
inputFilename = 'data/standard_surface_marble_solid.mtlx'
doc = mx.createDocument()
try:
mx.readFromXmlFile(doc, inputFilename)
valid, msg = doc.validate()
if not valid:
raise mx.Exception('Document is invalid')
print('Read in valid file "'"%s"'" for code generation.' % inputFilename)
except mx.ExceptionMissing as err:
print('File %s could not be loaded: "' % inputFilename, err, '"')
except mx.Exception as err:
print('File %s fail to load properly: "' % inputFilename, err, '"')
Read in valid file "data/standard_surface_marble_solid.mtlx" for code generation.
The standard library includes both definitions as well as implementations for each shading language.
The stdlib
, stdlib
, and bxdf
folders contain definitions and any corresponding node graph and
source code implementations.
The sub-folders starting with gen
contain per-target source code implementations.
import pkg_resources
def getLibraryFoldersString(root='libraries', showMTLXFiles=True, showSourceFiles=False):
'''
Scan the MaterialX library folder and print out the folder structure
'''
folderString = ''
files = pkg_resources.resource_listdir('MaterialX', root)
for f in files:
if not mx.FilePath(f).getExtension():
folderString += '+--%s\n' % f
fpath = root+'/'+f
if pkg_resources.resource_isdir('MaterialX', fpath):
subfiles = pkg_resources.resource_listdir('MaterialX', fpath)
for sf in subfiles:
sfPath = mx.FilePath(fpath+'/'+sf)
extension = sfPath.getExtension()
if extension and showMTLXFiles:
folderString += '|\t|--%s\n' % sf
elif not extension:
if sf == 'genglsl':
folderString += '|\t+--%s <-- e.g. target genglsl implementations reside here\n' % sf
else:
folderString += '|\t+--%s\n' % sf
sfpath = fpath+'/'+sf
if pkg_resources.resource_isdir('MaterialX', sfpath):
subsubfiles = pkg_resources.resource_listdir('MaterialX', sfpath)
for ssf in subsubfiles:
extension = mx.FilePath(ssf).getExtension()
if extension and showSourceFiles:
print('show source : ssf')
folderString += '|\t \t+--%s\n' % ssf
elif not extension:
folderString += '|\t \t+--%s\n' % ssf
# If not last file, add a line break
#if f != files[-1]:
# folderString += '|\n'
#folderString += '|\n'
return folderString
folderString = getLibraryFoldersString('libraries', False, False)
folderString = '<p>Library Folders:</p><pre>\n' + folderString + "\n</pre>"
display_markdown(folderString, raw=True)
Library Folders:
+--bxdf | +--lama | +--translation +--cmlib +--lights | +--genglsl <-- e.g. target genglsl implementations reside here | +--genmsl +--nprlib | +--genglsl <-- e.g. target genglsl implementations reside here | +--genmdl | +--genmsl | +--genosl +--pbrlib | +--genglsl <-- e.g. target genglsl implementations reside here | +--lib | +--genmdl | +--genmsl | +--genosl | +--legacy | +--lib +--stdlib | +--genglsl <-- e.g. target genglsl implementations reside here | +--lib | +--genmdl | +--genmsl | +--lib | +--genosl | +--include | +--lib +--targets
As this code is required for code generation to occur, the standard libraries
folder must be read in.
The libraries
folder can be examined as part of the Python package as of 1.38.7. Below is a simple utility to traverse and print out the folders found. This starts where the site packages are located (as returned using site.getsitepackages()
). This works either whether called from a virtual environment or not.
import site
def printPaths(rootPath):
"Print a 'tree' of paths given a root path"
outputStrings = []
for dirpath, dirs, files in os.walk(rootPath):
testpath = dirpath.removeprefix(rootPath)
path = testpath.split(os.sep)
comment = ''
if len(path) > 1:
if path[len(path)-1].startswith('gen'):
comment = ' ( _Root of target specific implementations_ )'
indent = (len(path)-1)*' '
outputString = indent + '- ' + os.path.basename(dirpath) + comment + '\n'
outputStrings.append(outputString)
display_markdown(outputStrings, raw=True)
packages = site.getsitepackages()
for package in packages:
libraryPath = mx.FilePath(package + '/MaterialX/libraries')
if os.path.exists(libraryPath.asString()):
printPaths(libraryPath.asString())
break
Implementations are of the type Implementation
.
# Load in standard libraries, and include an definitions local to the input file
stdlib = mx.createDocument()
searchPath = mx.getDefaultDataSearchPath()
searchPath.append(os.path.dirname(inputFilename))
libraryFolders = mx.getDefaultDataLibraryFolders()
try:
libFiles = mx.loadLibraries(libraryFolders, searchPath, stdlib)
doc.importLibrary(stdlib)
print('Version: %s. Loaded %s standard library definitions' % (mx.getVersionString(), len(doc.getNodeDefs())))
except mx.Exception as err:
print('Failed to load standard library definitions: "', err, '"')
Version: 1.39.0. Loaded 750 standard library definitions
The getImplementations()
API is used to get a list of Implementation
references. Even though the total number
of implementations seem large, only the source code for a specific generator are used at any given time.
# Get list of all implementations
implmentations = doc.getImplementations()
if implmentations:
print('Read in %d implementations' % len(implmentations))
Read in 2169 implementations
Every non-nodegraph implementation must specify a target
that it supports.
A target
name is used to identify shading languages and their variants. The naming convention is:
gen<language name>
These are represented as a TargetDef
.
The target identifiers are loaded in as part of the standard library, and these can be queried by
looking for elements of category targetdef
. For convenience, a list of available targets can be retrieved from a
document using the getTargetDefs()
API.
However, at time of writing, this is missing from the Python API. Thus a simple utility function is provided here.
# The targetdef type and support API does not currently exist so cannot be used.
#doc.getTargetDefs()
#doc.getChildOfType(mx.TargetDef)
# Utility that basically does what doc.getTargetDefs() does.
# Matching the category can be used in lieu to testing for the class type.
def getTargetDefs(doc):
targets = []
for element in doc.getChildren():
if element.getCategory() == 'targetdef':
targets.append(element.getName())
return targets
foundTargets = getTargetDefs(doc)
for target in foundTargets:
implcount = 0
# Find out how many implementations we have
for impl in implmentations:
testtarget = target
if target == 'essl':
testtarget = 'genglsl'
if impl.getTarget() == testtarget:
implcount = implcount + 1
print('Found target identifier:', target, 'with', implcount, 'source implementations.')
Found target identifier: essl with 540 source implementations. Found target identifier: genglsl with 540 source implementations. Found target identifier: genmdl with 549 source implementations. Found target identifier: genmsl with 539 source implementations. Found target identifier: genosl with 537 source implementations.
Every code generator must have a target
identifier to indicate which shading langauge / variant it supports.
A language version can be used to distinguish a variant if appropriate (e.g. ESSL is distinguishable this way)
It is recommended that all new generators have a unique target name.
Currently there is no "registry" for generators by target so the user must know before hand which generators exist and
go through all generators to find one with the appropriate target
to use.
Targets themselves can be "inherited" which is reflected in the inheritance hierarchy for generators.
For example the essl
(ESSL) target inherits from the genglsl
(GLSL) target as does the corresponding generators.
Inheritance is generally used to specialize code generation to handle shading language variations.
For a list of generators and their derivations see documentation for the base class ShaderGenerator
Note that Vulkan has the same target as genglsl
, but has it's own generator. Also that the Metal generator will only show up
in the Mac build of documentation.
Integrations are free to create custom generators. Some notable existing generators include those used to support USD HDStorm, VEX, and Arnold OSL.
Any such generator can be instantiated and use the same generation process as described here.
For this example, we will show how all the the generators can be created, but will only produce OSL code via an OslShaderGenerator
generator. This can be found in the PyMaterialXGenOsl
Python submodule and corresponding MaterialXGenOsl
library in C++.
# Create all generators
generators = []
generators.append(mx_gen_osl.OslShaderGenerator.create())
generators.append(mx_gen_mdl.MdlShaderGenerator.create())
generators.append(mx_gen_glsl.EsslShaderGenerator.create())
generators.append(mx_gen_glsl.VkShaderGenerator.create())
if supportsMSL:
generators.append(mx_gen_msl.MslShaderGenerator.create())
# Create a dictionary based on target identifier
generatordict = {}
for gen in generators:
generatordict[gen.getTarget()] = gen
# Choose generator to use based on target identifier
language = 'glsl'
target = 'genglsl'
if language == 'osl':
target = 'genosl'
elif language == 'mdl':
target = 'genmdl'
elif language == 'essl':
target = 'essl'
elif language == 'msl':
target = 'genmsl'
elif language in ['glsl', 'vulkan']:
target = 'genglsl'
test_language = 'essl'
test_shadergen = generatordict[test_language]
print('Find code generator for target:', test_shadergen.getTarget(), ' for language:', test_language, ". Version:", test_shadergen.getVersion())
shadergen = generatordict[target]
print('Use code generator for target:', shadergen.getTarget(), ' for language: ', language)
Find code generator for target: essl for language: essl . Version: 300 es Use code generator for target: genglsl for language: glsl
A "generation context" is required to be created for each generator instance. This is represented by a
GenContext
structure.
This context provides a number of settings and options to be used for code generation.
For simplicity, we will only point out the minimal requirements. This includes providing a search path to where source code implementations can be found. Any number of paths can be added using the registerSourceCodeSearchPath()
function, on the context. The search order is first to last path added.
Adding the path to the libraries
folder is sufficient to find the source code for standard library definitions found in sub-folders. If the user has custom definitions in other locations, the root of those locations should be added.
# Create a context for a generator
context = mx_gen_shader.GenContext(shadergen)
# Register a path to where implmentations can be found.
context.registerSourceCodeSearchPath(searchPath)
Color management is used to ensure that input colors are interpreted properly via shader code.
A "color management system" cab be created and specified to be used by a shader generator. During code generation, additional logic is emitted into the shader source code via the system.
Usage of such as system during code generation is optional, as some renderers perform color management on input values and images before binding them to shading code.
Color management systems need to be derived from the base API interface ColorManagementSystem
). A "default" system is provided
as part of the MaterialX distribution.
It is necessary to indicate which target
shading language code when instantiating the color management system. Naturally specifying a non-matching target will inject incompatible code.
The setup steps are:
target
being specified at creation time.# Create default CMS
cms = mx_gen_shader.DefaultColorManagementSystem.create(shadergen.getTarget())
# Indicate to the CMS where definitions can be found
cms.loadLibrary(doc)
# Indicate to the code generator to use this CMS
shadergen.setColorManagementSystem(cms)
cms = shadergen.getColorManagementSystem()
if cms:
print('Set up CMS: %s for target: %s'
% (cms.getName(), shadergen.getTarget()))
Set up CMS: default_cms for target: genglsl
To handle real-world unit specifiers a "unit system" should be instantiated and associated with the generator. The API interface is a UnitSystem which definitions.
By default a unit system does not know how to perform any conversions. This is provided by a
UnitConverterRegistry
which contains a list of convertors.
Currently MaterialX supports convertors for converting linear units: distance, and angle.
The corresponding API interface is LinearUnitConverter
.
# Create unit registry
registry = mx.UnitConverterRegistry.create()
if registry:
# Get distance and angle unit type definitions and create a linear converter for each
distanceTypeDef = doc.getUnitTypeDef('distance')
if distanceTypeDef:
registry.addUnitConverter(distanceTypeDef, mx.LinearUnitConverter.create(distanceTypeDef))
angleTypeDef = doc.getUnitTypeDef('angle')
if angleTypeDef:
registry.addUnitConverter(angleTypeDef, mx.LinearUnitConverter.create(angleTypeDef))
print('Created unit converter registry')
Created unit converter registry
As with a color management system the location of implementations and the registry need to be set on a unit system. The unit system can then be set on the generator.
# Create unit system, set where definitions come from, and
# set up what registry to use
unitsystem = mx_gen_shader.UnitSystem.create(shadergen.getTarget())
unitsystem.loadLibrary(stdlib)
unitsystem.setUnitConverterRegistry(registry)
if unitsystem:
print('Set unit system on code generator')
shadergen.setUnitSystem(unitsystem)
Set unit system on code generator
This sets up how to perform unit conversions, but does not specify what unis the scene geometry is using. This can be specified as in an "options" structure found on the context.
The API interface is: GenOptions.
# Set the target scene unit to be `meter` on the context options
genoptions = context.getOptions()
genoptions.targetDistanceUnit = 'meter'
There are a few utilities which are included to find elements which are "renderable":
getMaterialNodes()
orSURFACE_SHADER_TYPE_STRING
in a document.For this example, the first "renderable" found is used.
# Look for renderable nodes
nodes = mx_gen_shader.findRenderableElements(doc, False)
print('Found: %d renderables' % len(nodes))
if not nodes:
nodes = doc.getMaterialNodes()
if not nodes:
nodes = doc.getNodesOfType(mx.SURFACE_SHADER_TYPE_STRING)
node = None
if nodes:
node = nodes[0]
print('Found node to render: ', node.getName())
Found: 1 renderables Found node to render: Marble_3D
After all of this setup, code can now be generated.
createValidName()
utility is called to ensure that the shader name produced is valid.generate()
to perform custom generation.Upon success a new Shader instance is created. Note that this is a special interface used to keep track of an entire shader. This instance can be inspected to extract information required for rendering.
shader = None
nodeName = node.getName() if node else ''
if nodeName:
shaderName = mx.createValidName(nodeName)
try:
genoptions = context.getOptions()
genoptions.shaderInterfaceType = mx_gen_shader.ShaderInterfaceType.SHADER_INTERFACE_COMPLETE
shader = shadergen.generate(shaderName, node, context)
except mx.Exception as err:
print('Shader generation errors:', err)
if shader:
print('Succeeded in generating code for shader "%s" code from node "%s"' % (shaderName, nodeName))
else:
print('Failed to generate code for shader "%s" code from node "%s"' % (shaderName, nodeName))
Succeeded in generating code for shader "Marble_3D" code from node "Marble_3D"
For hardware languages like GLSL, vertex, and pixel shader code is generated. OSL and MDL only produce pixel shader code. To complete this example the pixel shader code is queried from the Shader and shown below.
Code can be queried via the getSourceCode() interface with an argument indicating which code to return. The code returned can be directly compiled and used by a renderer.
It is at this point in the generateshader.py
script that validation is performed. (This will not be shown here.)
pixelSource = ''
vertexSource = ''
if shader:
errors = ''
# Use extension of .vert and .frag as it's type is
# recognized by glslangValidator
if language in ['glsl', 'essl', 'vulkan']:
vertexSource = shader.getSourceCode(mx_gen_shader.VERTEX_STAGE)
text = '<details><summary>Vertex Shader For: "' + nodeName + '"</summary>\n\n' + '```cpp\n' + vertexSource + '```\n' + '</details>\n'
display_markdown(text , raw=True)
pixelSource = shader.getSourceCode(mx_gen_shader.PIXEL_STAGE)
text = '<details><summary>Pixel Shader For: "' + nodeName + '"</summary>\n\n' + '```cpp\n' + pixelSource + '```\n' + '</details>\n'
display_markdown(text , raw=True)
#version 450
#pragma shader_stage(vertex)
// Uniform block: PrivateUniforms
layout (std140, binding=0) uniform PrivateUniforms_vertex
{
mat4 u_worldMatrix;
mat4 u_viewProjectionMatrix;
mat4 u_worldInverseTransposeMatrix;
};
// Inputs block: VertexInputs
layout (location = 0) in vec3 i_position;
layout (location = 1) in vec3 i_normal;
layout (location = 2) in vec3 i_tangent;
layout (location = 0) out vec3 normalWorld;
layout (location = 1) out vec3 tangentWorld;
layout (location = 2) out vec3 positionObject;
layout (location = 3) out vec3 positionWorld;
void main()
{
vec4 hPositionWorld = u_worldMatrix * vec4(i_position, 1.0);
gl_Position = u_viewProjectionMatrix * hPositionWorld;
normalWorld = normalize((u_worldInverseTransposeMatrix * vec4(i_normal, 0.0)).xyz);
tangentWorld = normalize((u_worldMatrix * vec4(i_tangent, 0.0)).xyz);
positionObject = i_position;
positionWorld = hPositionWorld.xyz;
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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: PrivateUniforms
layout (std140, binding=1) uniform PrivateUniforms_pixel
{
mat4 u_envMatrix;
int u_envRadianceMips;
int u_envRadianceSamples;
bool u_refractionTwoSided;
vec3 u_viewPosition;
int u_numActiveLightSources;
};
layout (binding=2) uniform sampler2D u_envRadiance;
layout (binding=3) uniform sampler2D u_envIrradiance;
// Uniform block: PublicUniforms
layout (std140, binding=4) uniform PublicUniforms_pixel
{
displacementshader displacementshader1;
vec3 add_xyz_in2;
float scale_pos_in2;
float scale_xyz_in2;
float noise_amplitude;
int noise_octaves;
float noise_lacunarity;
float noise_diminish;
float scale_noise_in2;
float scale_in2;
float bias_in2;
float power_in2;
vec3 color_mix_fg;
vec3 color_mix_bg;
float SR_marble1_base;
float SR_marble1_diffuse_roughness;
float SR_marble1_metalness;
float SR_marble1_specular;
vec3 SR_marble1_specular_color;
float SR_marble1_specular_roughness;
float SR_marble1_specular_IOR;
float SR_marble1_specular_anisotropy;
float SR_marble1_specular_rotation;
float SR_marble1_transmission;
vec3 SR_marble1_transmission_color;
float SR_marble1_transmission_depth;
vec3 SR_marble1_transmission_scatter;
float SR_marble1_transmission_scatter_anisotropy;
float SR_marble1_transmission_dispersion;
float SR_marble1_transmission_extra_roughness;
float SR_marble1_subsurface;
vec3 SR_marble1_subsurface_radius;
float SR_marble1_subsurface_scale;
float SR_marble1_subsurface_anisotropy;
float SR_marble1_sheen;
vec3 SR_marble1_sheen_color;
float SR_marble1_sheen_roughness;
float SR_marble1_coat;
vec3 SR_marble1_coat_color;
float SR_marble1_coat_roughness;
float SR_marble1_coat_anisotropy;
float SR_marble1_coat_rotation;
float SR_marble1_coat_IOR;
float SR_marble1_coat_affect_color;
float SR_marble1_coat_affect_roughness;
float SR_marble1_thin_film_thickness;
float SR_marble1_thin_film_IOR;
float SR_marble1_emission;
vec3 SR_marble1_emission_color;
vec3 SR_marble1_opacity;
bool SR_marble1_thin_walled;
};
// Inputs: VertexData
layout (location = 0) in vec3 normalWorld;
layout (location = 1) in vec3 tangentWorld;
layout (location = 2) in vec3 positionObject;
layout (location = 3) in vec3 positionWorld;
// Pixel shader outputs
layout (location = 0) 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;
}
float mx_pow6(float x)
{
float x2 = mx_square(x);
return mx_square(x2) * x2;
}
// Standard Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
// Generalized Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0, float F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
// Generalized Schlick Fresnel with a variable exponent
float mx_fresnel_schlick(float cosTheta, float F0, float F90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(F0, F90, pow(x, exponent));
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(F0, F90, pow(x, exponent));
}
// Enforce that the given normal is forward-facing from the specified view direction.
vec3 mx_forward_facing_normal(vec3 N, vec3 V)
{
return (dot(N, V) < 0.0) ? -N : N;
}
// https://www.graphics.rwth-aachen.de/publication/2/jgt.pdf
float mx_golden_ratio_sequence(int i)
{
const float GOLDEN_RATIO = 1.6180339887498948;
return fract((float(i) + 1.0) * GOLDEN_RATIO);
}
// https://people.irisa.fr/Ricardo.Marques/articles/2013/SF_CGF.pdf
vec2 mx_spherical_fibonacci(int i, int numSamples)
{
return vec2((float(i) + 0.5) / float(numSamples), mx_golden_ratio_sequence(i));
}
// Generate a uniform-weighted sample 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 F82;
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 _F82 = 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);
}
// https://renderwonk.com/publications/wp-generalization-adobe/gen-adobe.pdf
vec3 mx_fresnel_hoffman_schlick(float cosTheta, vec3 F0, vec3 F82, vec3 F90, float exponent)
{
const float COS_THETA_MAX = 1.0 / 7.0;
const float COS_THETA_FACTOR = 1.0 / (COS_THETA_MAX * pow(1.0 - COS_THETA_MAX, 6.0));
float x = clamp(cosTheta, 0.0, 1.0);
vec3 a = mix(F0, F90, pow(1.0 - COS_THETA_MAX, exponent)) * (vec3(1.0) - F82) * COS_THETA_FACTOR;
return mix(F0, F90, pow(1.0 - x, exponent)) - a * x * mx_pow6(1.0 - x);
}
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 F82, 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_hoffman_schlick(cosTheta2, F0, F82, 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), 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 F82, vec3 F90, float exponent)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
fd.F0 = F0;
fd.F82 = F82;
fd.F90 = F90;
fd.exponent = exponent;
return fd;
}
FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F82, vec3 F90, float exponent, float tf_thickness, float tf_ior)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
fd.F0 = F0;
fd.F82 = F82;
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_hoffman_schlick(cosTheta, fd.F0, fd.F82, fd.F90, fd.exponent);
}
else
{
return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
fd.F0, fd.F82, 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;
}
// Return the mip level with the appropriate coverage for a filtered importance sample.
// https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
// Section 20.4 Equation 13
float mx_latlong_compute_lod(vec3 dir, float pdf, float maxMipLevel, int envSamples)
{
const float MIP_LEVEL_OFFSET = 1.5;
float effectiveMaxMipLevel = maxMipLevel - MIP_LEVEL_OFFSET;
float distortion = sqrt(1.0 - mx_square(dir.y));
return max(effectiveMaxMipLevel - 0.5 * log2(float(envSamples) * pdf * distortion), 0.0);
}
// Return the mip level associated with the given alpha in a prefiltered environment.
float mx_latlong_alpha_to_lod(float alpha)
{
float lodBias = (alpha < 0.25) ? sqrt(alpha) : 0.5 * alpha + 0.375;
return lodBias * float(u_envRadianceMips - 1);
}
// Return the alpha associated with the given mip level in a prefiltered environment.
float mx_latlong_lod_to_alpha(float lod)
{
float lodBias = lod / float(u_envRadianceMips - 1);
return (lodBias < 0.5) ? mx_square(lodBias) : 2.0 * (lodBias - 0.375);
}
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;
float pad0;
float pad1;
float pad2;
};
layout (std140, binding=5) uniform LightData_pixel
{
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);
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
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_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
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;
vec3 safeTint = max(tint, 0.0);
if (thinfilm_thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, thinfilm_thickness, thinfilm_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 * safeTint * occlusion * weight / (4.0 * NdotV);
}
void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
vec3 safeTint = max(tint, 0.0);
if (thinfilm_thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, thinfilm_thickness, thinfilm_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, safeTint) * weight;
}
}
void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
vec3 safeTint = max(tint, 0.0);
if (thinfilm_thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, thinfilm_thickness, thinfilm_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 * safeTint * 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, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
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 (thinfilm_thickness > 0.0)
fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, thinfilm_thickness, thinfilm_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, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
if (thinfilm_thickness > 0.0)
fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, thinfilm_thickness, thinfilm_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 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 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));
const float one_minus_coat_ior_to_F0_in1_tmp = 1.000000;
float one_minus_coat_ior_to_F0_out = one_minus_coat_ior_to_F0_in1_tmp - coat_ior_to_F0_out;
surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
{
vec3 N = normalize(normalWorld);
vec3 V = normalize(u_viewPosition - positionWorld);
vec3 P = 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], 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));
mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, 0.000000, 1.500000, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, metal_bsdf_out);
BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 0, specular_bsdf_out);
BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.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));
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));
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));
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));
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));
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));
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));
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));
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 metalness_mix_out = BSDF(vec3(0.0),vec3(1.0));
metalness_mix_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
metalness_mix_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));
thin_film_layer_attenuated_out.response = metalness_mix_out.response * thin_film_layer_attenuated_out_in2_clamped;
thin_film_layer_attenuated_out.throughput = metalness_mix_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.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));
mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, 0.000000, 1.500000, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, metal_bsdf_out);
BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 0, specular_bsdf_out);
BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.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));
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));
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));
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));
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));
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));
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));
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));
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 metalness_mix_out = BSDF(vec3(0.0),vec3(1.0));
metalness_mix_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
metalness_mix_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));
thin_film_layer_attenuated_out.response = metalness_mix_out.response * thin_film_layer_attenuated_out_in2_clamped;
thin_film_layer_attenuated_out.throughput = metalness_mix_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.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(one_minus_coat_ior_to_F0_out, one_minus_coat_ior_to_F0_out, one_minus_coat_ior_to_F0_out), vec3(0.000000, 0.000000, 0.000000), 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));
mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, 0.000000, 1.500000, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 0, specular_bsdf_out);
BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.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));
BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.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));
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));
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));
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 metalness_mix_out = BSDF(vec3(0.0),vec3(1.0));
metalness_mix_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
metalness_mix_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));
thin_film_layer_attenuated_out.response = metalness_mix_out.response * thin_film_layer_attenuated_out_in2_clamped;
thin_film_layer_attenuated_out.throughput = metalness_mix_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.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(normalWorld);
vec3 geomprop_Tworld_out1 = normalize(tangentWorld);
vec3 obj_pos_out = positionObject;
float add_xyz_out = dot(obj_pos_out, add_xyz_in2);
vec3 scale_pos_out = obj_pos_out * scale_pos_in2;
float scale_xyz_out = add_xyz_out * scale_xyz_in2;
float noise_out = 0.0;
mx_fractal3d_float(noise_amplitude, noise_octaves, noise_lacunarity, noise_diminish, scale_pos_out, noise_out);
float scale_noise_out = noise_out * scale_noise_in2;
float sum_out = scale_xyz_out + scale_noise_out;
float sin_out = sin(sum_out);
float scale_out = sin_out * scale_in2;
float bias_out = scale_out + bias_in2;
float power_out = pow(bias_out, power_in2);
vec3 color_mix_out = mix(color_mix_bg, color_mix_fg, power_out);
surfaceshader SR_marble1_out = surfaceshader(vec3(0.0),vec3(0.0));
NG_standard_surface_surfaceshader_100(SR_marble1_base, color_mix_out, SR_marble1_diffuse_roughness, SR_marble1_metalness, SR_marble1_specular, SR_marble1_specular_color, SR_marble1_specular_roughness, SR_marble1_specular_IOR, SR_marble1_specular_anisotropy, SR_marble1_specular_rotation, SR_marble1_transmission, SR_marble1_transmission_color, SR_marble1_transmission_depth, SR_marble1_transmission_scatter, SR_marble1_transmission_scatter_anisotropy, SR_marble1_transmission_dispersion, SR_marble1_transmission_extra_roughness, SR_marble1_subsurface, color_mix_out, SR_marble1_subsurface_radius, SR_marble1_subsurface_scale, SR_marble1_subsurface_anisotropy, SR_marble1_sheen, SR_marble1_sheen_color, SR_marble1_sheen_roughness, SR_marble1_coat, SR_marble1_coat_color, SR_marble1_coat_roughness, SR_marble1_coat_anisotropy, SR_marble1_coat_rotation, SR_marble1_coat_IOR, geomprop_Nworld_out1, SR_marble1_coat_affect_color, SR_marble1_coat_affect_roughness, SR_marble1_thin_film_thickness, SR_marble1_thin_film_IOR, SR_marble1_emission, SR_marble1_emission_color, SR_marble1_opacity, SR_marble1_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_marble1_out);
material Marble_3D_out = SR_marble1_out;
out1 = vec4(Marble_3D_out.color, 1.0);
}
It is often required to be able to get information about the shader uniforms / arguments. These uniforms are organized into "blocks" such that each block's uniforms has a corresponding ShaderPort
which can be inspected. The general term for provide additional information is shader refection.
The following utility function is used to extract information about the shader uniforms. Integrations can customize to create their own reflection structures.
def getPortPath(inputPath, doc):
'''
Find any upstream interface input which maps to a given path.
Note: This is only required pre version 1.38.9 where interface inputs traversed when
reflecting the MaterialX path on the shader port.
'''
if not inputPath:
return inputPath, None
input = doc.getDescendant(inputPath)
if input:
# Redirect to interface input if it exists.
interfaceInput = input.getInterfaceInput()
if interfaceInput:
input = interfaceInput
return input.getNamePath(), interfaceInput
return inputPath, None
def reflectStage(shader, doc, filterStage='pixel', filterBlock='Public'):
'''
Scan through each stage of a shader and get the uniform blocks for each stage.
For each block, extract out some desired information
'''
reflectionStages = []
if not shader:
return
for i in range(0, shader.numStages()):
stage = shader.getStage(i)
if stage:
if filterStage and filterStage not in stage.getName():
continue
stageName = stage.getName()
if len(stageName) == 0:
continue
reflectionStage = dict()
for blockName in stage.getUniformBlocks():
block = stage.getUniformBlock(blockName)
if filterBlock and filterBlock not in block.getName():
#print('--------- skip block: ', block.getName())
continue
if not block.getName() in reflectionStage:
reflectionStage[block.getName()] = []
reflectionBlock = reflectionStage[block.getName()]
for shaderPort in block:
variable = shaderPort.getVariable()
value = shaderPort.getValue().getValueString() if shaderPort.getValue() else '<NONE>'
origPath = shaderPort.getPath()
path, interfaceInput = getPortPath(shaderPort.getPath(), doc)
if not path:
path = '<NONE>'
else:
if path != origPath:
path = origPath + ' --> ' + path
type = shaderPort.getType().getName()
unit = shaderPort.getUnit()
colorspace = ''
if interfaceInput:
colorspace = interfaceInput.getColorSpace()
else:
colorspace = shaderPort.getColorSpace()
#print('add uniform: ', variable, value, type, path, unit, colorspace)
portEntry = [ variable, value, type, path, unit, colorspace ]
#print('add port to block: ', portEntry)
reflectionBlock.append(portEntry)
if len(reflectionBlock) > 0:
reflectionStage[block.getName()] = reflectionBlock
if len(reflectionStage) > 0:
reflectionStages.append((stageName, reflectionStage))
return reflectionStages
if shader:
# Examine public uniforms first
stages = reflectStage(shader, doc, 'pixel', 'Public')
if stages:
for stage in stages:
for block in stage[1]:
log = '<h4>Stage "%s". Block: "%s"</h4>\n\n' % (stage[0], block)
log += '| Variable | Value | Type | Path | Unit | Colorspace |\n'
log += '| --- | --- | --- | --- | --- | --- |\n'
for entry in stage[1][block]:
log += '| %s | %s | %s | %s | %s | %s |\n' % (entry[0], entry[1], entry[2], entry[3], entry[4], entry[5])
log += '\n'
display_markdown(log, raw=True)
Variable | Value | Type | Path | Unit | Colorspace |
---|---|---|---|---|---|
displacementshader1 | displacementshader | ||||
add_xyz_in2 | 1, 1, 1 | vector3 | NG_marble1/add_xyz/in2 | ||
scale_pos_in2 | 4 | float | NG_marble1/noise_scale_2 | ||
scale_xyz_in2 | 6 | float | NG_marble1/noise_scale_1 | ||
noise_amplitude | 1 | float | NG_marble1/noise/amplitude | ||
noise_octaves | 3 | integer | NG_marble1/noise_octaves | ||
noise_lacunarity | 2 | float | NG_marble1/noise/lacunarity | ||
noise_diminish | 0.5 | float | NG_marble1/noise/diminish | ||
scale_noise_in2 | 3 | float | NG_marble1/scale_noise/in2 | ||
scale_in2 | 0.5 | float | NG_marble1/scale/in2 | ||
bias_in2 | 0.5 | float | NG_marble1/bias/in2 | ||
power_in2 | 3 | float | NG_marble1/noise_power | ||
color_mix_fg | 0.1, 0.1, 0.3 | color3 | NG_marble1/base_color_2 | ||
color_mix_bg | 0.8, 0.8, 0.8 | color3 | NG_marble1/base_color_1 | ||
SR_marble1_base | 1 | float | SR_marble1/base | ||
SR_marble1_diffuse_roughness | 0 | float | SR_marble1/diffuse_roughness | ||
SR_marble1_metalness | 0 | float | SR_marble1/metalness | ||
SR_marble1_specular | 1 | float | SR_marble1/specular | ||
SR_marble1_specular_color | 1, 1, 1 | color3 | SR_marble1/specular_color | ||
SR_marble1_specular_roughness | 0.1 | float | SR_marble1/specular_roughness | ||
SR_marble1_specular_IOR | 1.5 | float | SR_marble1/specular_IOR | ||
SR_marble1_specular_anisotropy | 0 | float | SR_marble1/specular_anisotropy | ||
SR_marble1_specular_rotation | 0 | float | SR_marble1/specular_rotation | ||
SR_marble1_transmission | 0 | float | SR_marble1/transmission | ||
SR_marble1_transmission_color | 1, 1, 1 | color3 | SR_marble1/transmission_color | ||
SR_marble1_transmission_depth | 0 | float | SR_marble1/transmission_depth | ||
SR_marble1_transmission_scatter | 0, 0, 0 | color3 | SR_marble1/transmission_scatter | ||
SR_marble1_transmission_scatter_anisotropy | 0 | float | SR_marble1/transmission_scatter_anisotropy | ||
SR_marble1_transmission_dispersion | 0 | float | SR_marble1/transmission_dispersion | ||
SR_marble1_transmission_extra_roughness | 0 | float | SR_marble1/transmission_extra_roughness | ||
SR_marble1_subsurface | 0.4 | float | SR_marble1/subsurface | ||
SR_marble1_subsurface_radius | 1, 1, 1 | color3 | SR_marble1/subsurface_radius | ||
SR_marble1_subsurface_scale | 1 | float | SR_marble1/subsurface_scale | ||
SR_marble1_subsurface_anisotropy | 0 | float | SR_marble1/subsurface_anisotropy | ||
SR_marble1_sheen | 0 | float | SR_marble1/sheen | ||
SR_marble1_sheen_color | 1, 1, 1 | color3 | SR_marble1/sheen_color | ||
SR_marble1_sheen_roughness | 0.3 | float | SR_marble1/sheen_roughness | ||
SR_marble1_coat | 0 | float | SR_marble1/coat | ||
SR_marble1_coat_color | 1, 1, 1 | color3 | SR_marble1/coat_color | ||
SR_marble1_coat_roughness | 0.1 | float | SR_marble1/coat_roughness | ||
SR_marble1_coat_anisotropy | 0 | float | SR_marble1/coat_anisotropy | ||
SR_marble1_coat_rotation | 0 | float | SR_marble1/coat_rotation | ||
SR_marble1_coat_IOR | 1.5 | float | SR_marble1/coat_IOR | ||
SR_marble1_coat_affect_color | 0 | float | SR_marble1/coat_affect_color | ||
SR_marble1_coat_affect_roughness | 0 | float | SR_marble1/coat_affect_roughness | ||
SR_marble1_thin_film_thickness | 0 | float | SR_marble1/thin_film_thickness | ||
SR_marble1_thin_film_IOR | 1.5 | float | SR_marble1/thin_film_IOR | ||
SR_marble1_emission | 0 | float | SR_marble1/emission | ||
SR_marble1_emission_color | 1, 1, 1 | color3 | SR_marble1/emission_color | ||
SR_marble1_opacity | 1, 1, 1 | color3 | SR_marble1/opacity | ||
SR_marble1_thin_walled | false | boolean | SR_marble1/thin_walled |
getDownstreamPorts()
can be used to traverse downstream from a given Node
. With the current release this does not work with NodeGraphs
so custom logic is used instead which calls into getMatchingPorts()
on a document.
def getDownstreamPorts(nodeName):
downstreamPorts = []
for port in doc.getMatchingPorts(nodeName):
#print('- check port:', port)
#print('- Compare: ', port.getConnectedNode().getName(), ' vs ', nodeName)
#if port.getConnectedNode().getName() == nodeName:
downstreamPorts.append(port)
return downstreamPorts
getMatchingPorts()
should return all ports in the document which reference a given node. Again there is an issue with the current release that this will not find any matches when a nodegraph
is referenced by a port. For this example a custom build was used which addresses this issue.
A wrapper utility called getDownStreamNodes()
is written to perform downstream traversal starting from a node. It returns ports and corresponding nodes as well as what is considered to be "renderable". This is akin logic found in findRenderableElements()
but instead will only look at nodes connected downstream from a node.
"Renderable" is considered to be
output
portssurfaceshader
and volumeshader
nodes) andmaterial
node)from collections import OrderedDict
def getDownstreamNodes(node, foundPorts, foundNodes, renderableElements,
renderableTypes = ['material', 'surfaceshader', 'volumeshader']):
"""
For a given "node", traverse downstream connections until there are none to be found.
Along the way collect a list of ports and corresponding nodes visited (in order), and
a list of "renderable" elements.
"""
testPaths = set()
testPaths.add(node.getNamePath())
while testPaths:
nextPaths = set()
for path in testPaths:
testNode = doc.getDescendant(path)
#print('test node:', testNode.getName())
ports = []
if testNode.isA(mx.Node):
ports = testNode.getDownstreamPorts()
else:
ports = getDownstreamPorts(testNode.getName())
for port in ports:
downNode = port.getParent()
downNodePath = downNode.getNamePath()
if downNode and downNodePath not in nextPaths: #and downNode.isA(mx.Node):
foundPorts.append(port.getNamePath())
if port.isA(mx.Output):
renderableElements.append(port.getNamePath())
nodedef = downNode.getNodeDef()
if nodedef:
nodetype = nodedef.getType()
if nodetype in renderableTypes:
renderableElements.append(port.getNamePath())
foundNodes.append(downNode.getNamePath())
nextPaths.add(downNodePath)
testPaths = nextPaths
def examineNodes(nodes):
"""
Traverse downstream for a set of nodes to find information
Returns the set of common ports, nodes, and renderables found
"""
commonPorts = []
commonNodes = []
commonRenderables = []
for node in nodes:
foundPorts = []
foundNodes = []
renderableElements = []
getDownstreamNodes(node, foundPorts, foundNodes, renderableElements)
foundPorts = list(OrderedDict.fromkeys(foundPorts))
foundNodes = list(OrderedDict.fromkeys(foundNodes))
renderableElements = list(OrderedDict.fromkeys(renderableElements))
#print('Traverse downstream from node: ', node.getNamePath())
#print('- Downstream ports:', ', '.join(foundPorts))
#print('- Downstream nodes:', ', '.join(foundNodes))
#print('- Renderable elements:', ', '.join(renderableElements))
commonPorts.extend(foundPorts)
commonNodes.extend(foundNodes)
commonRenderables.extend(renderableElements)
commonPorts = list(OrderedDict.fromkeys(commonPorts))
commonNodes = list(OrderedDict.fromkeys(commonNodes))
commonRenderables = list(OrderedDict.fromkeys(commonRenderables))
return commonPorts, commonNodes, commonRenderables
nodegraph = doc.getChild('NG_marble1')
nodes = [nodegraph.getChild('obj_pos'), nodegraph.getChild('scale_pos')]
commonPorts, commonNodes, commonRenderables = examineNodes(nodes)
display_markdown('Common downstream:', raw=True)
display_markdown(' - Common downstream ports: [ ' + ', '.join(commonPorts) + ' ]', raw=True)
display_markdown(' - Common downstream nodes: [ ' + ', '.join(commonNodes) + ' ]', raw=True)
display_markdown(' - Common renderable elements: [ ' + ', '.join(commonRenderables) + ' ]', raw=True)
Common downstream:
The Marble graph is shown below for reference:
Given the ability to traverse downstream from a node, it is possible to produce code just a given node or the upstream subgraph rooted at a given node. If a non-surface shader or material node is used to generate from then only that nodes could will be considered.
To generate code for the entire upstream graph the node needs to have a downstream root which is either a shader or a material.
As of version 1.38.7, the easiest way to do this is to create a temporary convert
node to route the output type to a downstream surface shader.
For example a convert
from color to surfaceshader
can be used
import mtlxutils.mxnodegraph as mxg
for nodePath in commonNodes:
node = doc.getDescendant(nodePath)
if not node:
continue
if node.isA(mx.NodeGraph):
outputs = node.getOutputs()
for output in outputs:
outputPath = output.getNamePath()
if outputPath in commonRenderables:
node = output
nodePath = outputPath
else:
convertNode = None
#parent = node.getParent()
#convertNode = mxg.MtlxNodeGraph.addNode(parent, 'ND_convert_' + node.getType() + '_surfaceshader', 'convert_' + node.getName())
#if convertNode:
# mxg.MtlxNodeGraph.connectNodeToNode(convertNode, 'in', node, '')
shaderName = mx.createValidName(nodePath)
try:
shader = shadergen.generate(shaderName, node, context)
except mx.Exception as err:
print('Shader generation errors:', err)
if shader:
pixelSource = shader.getSourceCode(mx_gen_shader.PIXEL_STAGE)
text = '<details><summary>Code For: "' + nodePath + '"</summary>\n\n' + '```cpp\n' + pixelSource + '```\n' + '</details>\n'
display_markdown(text , raw=True)
else:
print('Failed to generate code for shader "%s" code from node "%s"' % (shaderName, nodeName))
if convertNode:
nodePath = convertNode.getNamePath()
shaderName = mx.createValidName(nodePath)
try:
shader = shadergen.generate(shaderName, convertNode, context)
except mx.Exception as err:
print('Shader generation errors:', err)
if shader:
pixelSource = shader.getSourceCode(mx_gen_shader.PIXEL_STAGE)
text = '<details><summary>Convert Code For: "' + nodePath + '"</summary>\n\n' + '```cpp\n' + pixelSource + '```\n' + '</details>\n'
display_markdown(text , raw=True)
else:
print('Failed to generate code for shader "%s" code from node "%s"' % (shaderName, nodeName))
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
vec3 in2;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
void main()
{
vec3 obj_pos_out = positionObject;
float add_xyz_out = dot(obj_pos_out, in2);
out1 = vec4(add_xyz_out, add_xyz_out, add_xyz_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
float in2;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
void main()
{
vec3 obj_pos_out = positionObject;
vec3 scale_pos_out = obj_pos_out * in2;
out1 = vec4(scale_pos_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
float amplitude;
int octaves;
float lacunarity;
float diminish;
float scale_pos_in2;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
void main()
{
vec3 obj_pos_out = positionObject;
vec3 scale_pos_out = obj_pos_out * scale_pos_in2;
float noise_out = 0.0;
mx_fractal3d_float(amplitude, octaves, lacunarity, diminish, scale_pos_out, noise_out);
out1 = vec4(noise_out, noise_out, noise_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
float in2;
vec3 add_xyz_in2;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
void main()
{
vec3 obj_pos_out = positionObject;
float add_xyz_out = dot(obj_pos_out, add_xyz_in2);
float scale_xyz_out = add_xyz_out * in2;
out1 = vec4(scale_xyz_out, scale_xyz_out, scale_xyz_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
float in2;
float scale_pos_in2;
float noise_amplitude;
int noise_octaves;
float noise_lacunarity;
float noise_diminish;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
void main()
{
vec3 obj_pos_out = positionObject;
vec3 scale_pos_out = obj_pos_out * scale_pos_in2;
float noise_out = 0.0;
mx_fractal3d_float(noise_amplitude, noise_octaves, noise_lacunarity, noise_diminish, scale_pos_out, noise_out);
float scale_noise_out = noise_out * in2;
out1 = vec4(scale_noise_out, scale_noise_out, scale_noise_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
vec3 add_xyz_in2;
float scale_pos_in2;
float scale_xyz_in2;
float noise_amplitude;
int noise_octaves;
float noise_lacunarity;
float noise_diminish;
float scale_noise_in2;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
void main()
{
vec3 obj_pos_out = positionObject;
float add_xyz_out = dot(obj_pos_out, add_xyz_in2);
vec3 scale_pos_out = obj_pos_out * scale_pos_in2;
float scale_xyz_out = add_xyz_out * scale_xyz_in2;
float noise_out = 0.0;
mx_fractal3d_float(noise_amplitude, noise_octaves, noise_lacunarity, noise_diminish, scale_pos_out, noise_out);
float scale_noise_out = noise_out * scale_noise_in2;
float sum_out = scale_xyz_out + scale_noise_out;
out1 = vec4(sum_out, sum_out, sum_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
vec3 add_xyz_in2;
float scale_pos_in2;
float scale_xyz_in2;
float noise_amplitude;
int noise_octaves;
float noise_lacunarity;
float noise_diminish;
float scale_noise_in2;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
void main()
{
vec3 obj_pos_out = positionObject;
float add_xyz_out = dot(obj_pos_out, add_xyz_in2);
vec3 scale_pos_out = obj_pos_out * scale_pos_in2;
float scale_xyz_out = add_xyz_out * scale_xyz_in2;
float noise_out = 0.0;
mx_fractal3d_float(noise_amplitude, noise_octaves, noise_lacunarity, noise_diminish, scale_pos_out, noise_out);
float scale_noise_out = noise_out * scale_noise_in2;
float sum_out = scale_xyz_out + scale_noise_out;
float sin_out = sin(sum_out);
out1 = vec4(sin_out, sin_out, sin_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
float in2;
vec3 add_xyz_in2;
float scale_pos_in2;
float scale_xyz_in2;
float noise_amplitude;
int noise_octaves;
float noise_lacunarity;
float noise_diminish;
float scale_noise_in2;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
void main()
{
vec3 obj_pos_out = positionObject;
float add_xyz_out = dot(obj_pos_out, add_xyz_in2);
vec3 scale_pos_out = obj_pos_out * scale_pos_in2;
float scale_xyz_out = add_xyz_out * scale_xyz_in2;
float noise_out = 0.0;
mx_fractal3d_float(noise_amplitude, noise_octaves, noise_lacunarity, noise_diminish, scale_pos_out, noise_out);
float scale_noise_out = noise_out * scale_noise_in2;
float sum_out = scale_xyz_out + scale_noise_out;
float sin_out = sin(sum_out);
float scale_out = sin_out * in2;
out1 = vec4(scale_out, scale_out, scale_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
float in2;
vec3 add_xyz_in2;
float scale_pos_in2;
float scale_xyz_in2;
float noise_amplitude;
int noise_octaves;
float noise_lacunarity;
float noise_diminish;
float scale_noise_in2;
float scale_in2;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
void main()
{
vec3 obj_pos_out = positionObject;
float add_xyz_out = dot(obj_pos_out, add_xyz_in2);
vec3 scale_pos_out = obj_pos_out * scale_pos_in2;
float scale_xyz_out = add_xyz_out * scale_xyz_in2;
float noise_out = 0.0;
mx_fractal3d_float(noise_amplitude, noise_octaves, noise_lacunarity, noise_diminish, scale_pos_out, noise_out);
float scale_noise_out = noise_out * scale_noise_in2;
float sum_out = scale_xyz_out + scale_noise_out;
float sin_out = sin(sum_out);
float scale_out = sin_out * scale_in2;
float bias_out = scale_out + in2;
out1 = vec4(bias_out, bias_out, bias_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
float in2;
vec3 add_xyz_in2;
float scale_pos_in2;
float scale_xyz_in2;
float noise_amplitude;
int noise_octaves;
float noise_lacunarity;
float noise_diminish;
float scale_noise_in2;
float scale_in2;
float bias_in2;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
void main()
{
vec3 obj_pos_out = positionObject;
float add_xyz_out = dot(obj_pos_out, add_xyz_in2);
vec3 scale_pos_out = obj_pos_out * scale_pos_in2;
float scale_xyz_out = add_xyz_out * scale_xyz_in2;
float noise_out = 0.0;
mx_fractal3d_float(noise_amplitude, noise_octaves, noise_lacunarity, noise_diminish, scale_pos_out, noise_out);
float scale_noise_out = noise_out * scale_noise_in2;
float sum_out = scale_xyz_out + scale_noise_out;
float sin_out = sin(sum_out);
float scale_out = sin_out * scale_in2;
float bias_out = scale_out + bias_in2;
float power_out = pow(bias_out, in2);
out1 = vec4(power_out, power_out, power_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
vec3 fg;
vec3 bg;
vec3 add_xyz_in2;
float scale_pos_in2;
float scale_xyz_in2;
float noise_amplitude;
int noise_octaves;
float noise_lacunarity;
float noise_diminish;
float scale_noise_in2;
float scale_in2;
float bias_in2;
float power_in2;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
void main()
{
vec3 obj_pos_out = positionObject;
float add_xyz_out = dot(obj_pos_out, add_xyz_in2);
vec3 scale_pos_out = obj_pos_out * scale_pos_in2;
float scale_xyz_out = add_xyz_out * scale_xyz_in2;
float noise_out = 0.0;
mx_fractal3d_float(noise_amplitude, noise_octaves, noise_lacunarity, noise_diminish, scale_pos_out, noise_out);
float scale_noise_out = noise_out * scale_noise_in2;
float sum_out = scale_xyz_out + scale_noise_out;
float sin_out = sin(sum_out);
float scale_out = sin_out * scale_in2;
float bias_out = scale_out + bias_in2;
float power_out = pow(bias_out, power_in2);
vec3 color_mix_out = mix(bg, fg, power_out);
out1 = vec4(color_mix_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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
layout (std140, binding=1) uniform PublicUniforms_pixel
{
vec3 base_color_1;
vec3 base_color_2;
float noise_scale_1;
float noise_scale_2;
float noise_power;
int noise_octaves;
vec3 add_xyz_in2;
float noise_amplitude;
float noise_lacunarity;
float noise_diminish;
float scale_noise_in2;
float scale_in2;
float bias_in2;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
// Pixel shader outputs
layout (location = 0) 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;
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
void main()
{
vec3 obj_pos_out = positionObject;
float add_xyz_out = dot(obj_pos_out, add_xyz_in2);
vec3 scale_pos_out = obj_pos_out * noise_scale_2;
float scale_xyz_out = add_xyz_out * noise_scale_1;
float noise_out = 0.0;
mx_fractal3d_float(noise_amplitude, noise_octaves, noise_lacunarity, noise_diminish, scale_pos_out, noise_out);
float scale_noise_out = noise_out * scale_noise_in2;
float sum_out = scale_xyz_out + scale_noise_out;
float sin_out = sin(sum_out);
float scale_out = sin_out * scale_in2;
float bias_out = scale_out + bias_in2;
float power_out = pow(bias_out, noise_power);
vec3 color_mix_out = mix(base_color_1, base_color_2, power_out);
out1 = vec4(color_mix_out, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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: PrivateUniforms
layout (std140, binding=1) uniform PrivateUniforms_pixel
{
mat4 u_envMatrix;
int u_envRadianceMips;
int u_envRadianceSamples;
bool u_refractionTwoSided;
vec3 u_viewPosition;
int u_numActiveLightSources;
};
layout (binding=2) uniform sampler2D u_envRadiance;
layout (binding=3) uniform sampler2D u_envIrradiance;
// Uniform block: PublicUniforms
layout (std140, binding=4) uniform PublicUniforms_pixel
{
float base;
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_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;
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 add_xyz_in2;
float scale_pos_in2;
float scale_xyz_in2;
float noise_amplitude;
int noise_octaves;
float noise_lacunarity;
float noise_diminish;
float scale_noise_in2;
float scale_in2;
float bias_in2;
float power_in2;
vec3 color_mix_fg;
vec3 color_mix_bg;
};
// Inputs: VertexData
layout (location = 0) in vec3 positionObject;
layout (location = 1) in vec3 normalWorld;
layout (location = 2) in vec3 tangentWorld;
layout (location = 3) in vec3 positionWorld;
// Pixel shader outputs
layout (location = 0) 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;
}
float mx_pow6(float x)
{
float x2 = mx_square(x);
return mx_square(x2) * x2;
}
// Standard Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
// Generalized Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0, float F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
// Generalized Schlick Fresnel with a variable exponent
float mx_fresnel_schlick(float cosTheta, float F0, float F90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(F0, F90, pow(x, exponent));
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(F0, F90, pow(x, exponent));
}
// Enforce that the given normal is forward-facing from the specified view direction.
vec3 mx_forward_facing_normal(vec3 N, vec3 V)
{
return (dot(N, V) < 0.0) ? -N : N;
}
// https://www.graphics.rwth-aachen.de/publication/2/jgt.pdf
float mx_golden_ratio_sequence(int i)
{
const float GOLDEN_RATIO = 1.6180339887498948;
return fract((float(i) + 1.0) * GOLDEN_RATIO);
}
// https://people.irisa.fr/Ricardo.Marques/articles/2013/SF_CGF.pdf
vec2 mx_spherical_fibonacci(int i, int numSamples)
{
return vec2((float(i) + 0.5) / float(numSamples), mx_golden_ratio_sequence(i));
}
// Generate a uniform-weighted sample 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 F82;
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 _F82 = 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);
}
// https://renderwonk.com/publications/wp-generalization-adobe/gen-adobe.pdf
vec3 mx_fresnel_hoffman_schlick(float cosTheta, vec3 F0, vec3 F82, vec3 F90, float exponent)
{
const float COS_THETA_MAX = 1.0 / 7.0;
const float COS_THETA_FACTOR = 1.0 / (COS_THETA_MAX * pow(1.0 - COS_THETA_MAX, 6.0));
float x = clamp(cosTheta, 0.0, 1.0);
vec3 a = mix(F0, F90, pow(1.0 - COS_THETA_MAX, exponent)) * (vec3(1.0) - F82) * COS_THETA_FACTOR;
return mix(F0, F90, pow(1.0 - x, exponent)) - a * x * mx_pow6(1.0 - x);
}
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 F82, 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_hoffman_schlick(cosTheta2, F0, F82, 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), 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 F82, vec3 F90, float exponent)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
fd.F0 = F0;
fd.F82 = F82;
fd.F90 = F90;
fd.exponent = exponent;
return fd;
}
FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F82, vec3 F90, float exponent, float tf_thickness, float tf_ior)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
fd.F0 = F0;
fd.F82 = F82;
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_hoffman_schlick(cosTheta, fd.F0, fd.F82, fd.F90, fd.exponent);
}
else
{
return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
fd.F0, fd.F82, 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;
}
// Return the mip level with the appropriate coverage for a filtered importance sample.
// https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
// Section 20.4 Equation 13
float mx_latlong_compute_lod(vec3 dir, float pdf, float maxMipLevel, int envSamples)
{
const float MIP_LEVEL_OFFSET = 1.5;
float effectiveMaxMipLevel = maxMipLevel - MIP_LEVEL_OFFSET;
float distortion = sqrt(1.0 - mx_square(dir.y));
return max(effectiveMaxMipLevel - 0.5 * log2(float(envSamples) * pdf * distortion), 0.0);
}
// Return the mip level associated with the given alpha in a prefiltered environment.
float mx_latlong_alpha_to_lod(float alpha)
{
float lodBias = (alpha < 0.25) ? sqrt(alpha) : 0.5 * alpha + 0.375;
return lodBias * float(u_envRadianceMips - 1);
}
// Return the alpha associated with the given mip level in a prefiltered environment.
float mx_latlong_lod_to_alpha(float lod)
{
float lodBias = lod / float(u_envRadianceMips - 1);
return (lodBias < 0.5) ? mx_square(lodBias) : 2.0 * (lodBias - 0.375);
}
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;
float pad0;
float pad1;
float pad2;
};
layout (std140, binding=5) uniform LightData_pixel
{
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);
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
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_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
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;
vec3 safeTint = max(tint, 0.0);
if (thinfilm_thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, thinfilm_thickness, thinfilm_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 * safeTint * occlusion * weight / (4.0 * NdotV);
}
void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
vec3 safeTint = max(tint, 0.0);
if (thinfilm_thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, thinfilm_thickness, thinfilm_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, safeTint) * weight;
}
}
void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
vec3 safeTint = max(tint, 0.0);
if (thinfilm_thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, thinfilm_thickness, thinfilm_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 * safeTint * 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, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
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 (thinfilm_thickness > 0.0)
fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, thinfilm_thickness, thinfilm_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, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
if (thinfilm_thickness > 0.0)
fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, thinfilm_thickness, thinfilm_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 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 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));
const float one_minus_coat_ior_to_F0_in1_tmp = 1.000000;
float one_minus_coat_ior_to_F0_out = one_minus_coat_ior_to_F0_in1_tmp - coat_ior_to_F0_out;
surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
{
vec3 N = normalize(normalWorld);
vec3 V = normalize(u_viewPosition - positionWorld);
vec3 P = 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], 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));
mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, 0.000000, 1.500000, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, metal_bsdf_out);
BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 0, specular_bsdf_out);
BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.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));
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));
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));
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));
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));
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));
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));
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));
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 metalness_mix_out = BSDF(vec3(0.0),vec3(1.0));
metalness_mix_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
metalness_mix_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));
thin_film_layer_attenuated_out.response = metalness_mix_out.response * thin_film_layer_attenuated_out_in2_clamped;
thin_film_layer_attenuated_out.throughput = metalness_mix_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.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));
mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, 0.000000, 1.500000, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, metal_bsdf_out);
BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 0, specular_bsdf_out);
BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.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));
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));
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));
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));
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));
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));
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));
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));
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 metalness_mix_out = BSDF(vec3(0.0),vec3(1.0));
metalness_mix_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
metalness_mix_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));
thin_film_layer_attenuated_out.response = metalness_mix_out.response * thin_film_layer_attenuated_out_in2_clamped;
thin_film_layer_attenuated_out.throughput = metalness_mix_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.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(one_minus_coat_ior_to_F0_out, one_minus_coat_ior_to_F0_out, one_minus_coat_ior_to_F0_out), vec3(0.000000, 0.000000, 0.000000), 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));
mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, 0.000000, 1.500000, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 0, specular_bsdf_out);
BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.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));
BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.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));
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));
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));
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 metalness_mix_out = BSDF(vec3(0.0),vec3(1.0));
metalness_mix_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
metalness_mix_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));
thin_film_layer_attenuated_out.response = metalness_mix_out.response * thin_film_layer_attenuated_out_in2_clamped;
thin_film_layer_attenuated_out.throughput = metalness_mix_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.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 obj_pos_out = positionObject;
vec3 geomprop_Nworld_out = normalize(normalWorld);
vec3 geomprop_Tworld_out = normalize(tangentWorld);
float add_xyz_out = dot(obj_pos_out, add_xyz_in2);
vec3 scale_pos_out = obj_pos_out * scale_pos_in2;
float scale_xyz_out = add_xyz_out * scale_xyz_in2;
float noise_out = 0.0;
mx_fractal3d_float(noise_amplitude, noise_octaves, noise_lacunarity, noise_diminish, scale_pos_out, noise_out);
float scale_noise_out = noise_out * scale_noise_in2;
float sum_out = scale_xyz_out + scale_noise_out;
float sin_out = sin(sum_out);
float scale_out = sin_out * scale_in2;
float bias_out = scale_out + bias_in2;
float power_out = pow(bias_out, power_in2);
vec3 color_mix_out = mix(color_mix_bg, color_mix_fg, power_out);
surfaceshader SR_marble1_out = surfaceshader(vec3(0.0),vec3(0.0));
NG_standard_surface_surfaceshader_100(base, color_mix_out, diffuse_roughness, metalness, specular, specular_color, specular_roughness, specular_IOR, specular_anisotropy, specular_rotation, transmission, transmission_color, transmission_depth, transmission_scatter, transmission_scatter_anisotropy, transmission_dispersion, transmission_extra_roughness, subsurface, color_mix_out, subsurface_radius, subsurface_scale, subsurface_anisotropy, sheen, sheen_color, sheen_roughness, coat, coat_color, coat_roughness, coat_anisotropy, coat_rotation, coat_IOR, geomprop_Nworld_out, coat_affect_color, coat_affect_roughness, thin_film_thickness, thin_film_IOR, emission, emission_color, opacity, thin_walled, geomprop_Nworld_out, geomprop_Tworld_out, SR_marble1_out);
out1 = vec4(SR_marble1_out.color, 1.0);
}
#version 450
#pragma shader_stage(fragment)
struct BSDF { vec3 response; vec3 throughput; };
#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: PrivateUniforms
layout (std140, binding=1) uniform PrivateUniforms_pixel
{
mat4 u_envMatrix;
int u_envRadianceMips;
int u_envRadianceSamples;
bool u_refractionTwoSided;
vec3 u_viewPosition;
int u_numActiveLightSources;
};
layout (binding=2) uniform sampler2D u_envRadiance;
layout (binding=3) uniform sampler2D u_envIrradiance;
// Uniform block: PublicUniforms
layout (std140, binding=4) uniform PublicUniforms_pixel
{
displacementshader displacementshader1;
vec3 add_xyz_in2;
float scale_pos_in2;
float scale_xyz_in2;
float noise_amplitude;
int noise_octaves;
float noise_lacunarity;
float noise_diminish;
float scale_noise_in2;
float scale_in2;
float bias_in2;
float power_in2;
vec3 color_mix_fg;
vec3 color_mix_bg;
float SR_marble1_base;
float SR_marble1_diffuse_roughness;
float SR_marble1_metalness;
float SR_marble1_specular;
vec3 SR_marble1_specular_color;
float SR_marble1_specular_roughness;
float SR_marble1_specular_IOR;
float SR_marble1_specular_anisotropy;
float SR_marble1_specular_rotation;
float SR_marble1_transmission;
vec3 SR_marble1_transmission_color;
float SR_marble1_transmission_depth;
vec3 SR_marble1_transmission_scatter;
float SR_marble1_transmission_scatter_anisotropy;
float SR_marble1_transmission_dispersion;
float SR_marble1_transmission_extra_roughness;
float SR_marble1_subsurface;
vec3 SR_marble1_subsurface_radius;
float SR_marble1_subsurface_scale;
float SR_marble1_subsurface_anisotropy;
float SR_marble1_sheen;
vec3 SR_marble1_sheen_color;
float SR_marble1_sheen_roughness;
float SR_marble1_coat;
vec3 SR_marble1_coat_color;
float SR_marble1_coat_roughness;
float SR_marble1_coat_anisotropy;
float SR_marble1_coat_rotation;
float SR_marble1_coat_IOR;
float SR_marble1_coat_affect_color;
float SR_marble1_coat_affect_roughness;
float SR_marble1_thin_film_thickness;
float SR_marble1_thin_film_IOR;
float SR_marble1_emission;
vec3 SR_marble1_emission_color;
vec3 SR_marble1_opacity;
bool SR_marble1_thin_walled;
};
// Inputs: VertexData
layout (location = 0) in vec3 normalWorld;
layout (location = 1) in vec3 tangentWorld;
layout (location = 2) in vec3 positionObject;
layout (location = 3) in vec3 positionWorld;
// Pixel shader outputs
layout (location = 0) 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;
}
float mx_pow6(float x)
{
float x2 = mx_square(x);
return mx_square(x2) * x2;
}
// Standard Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
// Generalized Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0, float F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
// Generalized Schlick Fresnel with a variable exponent
float mx_fresnel_schlick(float cosTheta, float F0, float F90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(F0, F90, pow(x, exponent));
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(F0, F90, pow(x, exponent));
}
// Enforce that the given normal is forward-facing from the specified view direction.
vec3 mx_forward_facing_normal(vec3 N, vec3 V)
{
return (dot(N, V) < 0.0) ? -N : N;
}
// https://www.graphics.rwth-aachen.de/publication/2/jgt.pdf
float mx_golden_ratio_sequence(int i)
{
const float GOLDEN_RATIO = 1.6180339887498948;
return fract((float(i) + 1.0) * GOLDEN_RATIO);
}
// https://people.irisa.fr/Ricardo.Marques/articles/2013/SF_CGF.pdf
vec2 mx_spherical_fibonacci(int i, int numSamples)
{
return vec2((float(i) + 0.5) / float(numSamples), mx_golden_ratio_sequence(i));
}
// Generate a uniform-weighted sample 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 F82;
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 _F82 = 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);
}
// https://renderwonk.com/publications/wp-generalization-adobe/gen-adobe.pdf
vec3 mx_fresnel_hoffman_schlick(float cosTheta, vec3 F0, vec3 F82, vec3 F90, float exponent)
{
const float COS_THETA_MAX = 1.0 / 7.0;
const float COS_THETA_FACTOR = 1.0 / (COS_THETA_MAX * pow(1.0 - COS_THETA_MAX, 6.0));
float x = clamp(cosTheta, 0.0, 1.0);
vec3 a = mix(F0, F90, pow(1.0 - COS_THETA_MAX, exponent)) * (vec3(1.0) - F82) * COS_THETA_FACTOR;
return mix(F0, F90, pow(1.0 - x, exponent)) - a * x * mx_pow6(1.0 - x);
}
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 F82, 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_hoffman_schlick(cosTheta2, F0, F82, 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), 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 F82, vec3 F90, float exponent)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK);
fd.F0 = F0;
fd.F82 = F82;
fd.F90 = F90;
fd.exponent = exponent;
return fd;
}
FresnelData mx_init_fresnel_schlick_airy(vec3 F0, vec3 F82, vec3 F90, float exponent, float tf_thickness, float tf_ior)
{
FresnelData fd = mx_init_fresnel_data(FRESNEL_MODEL_SCHLICK_AIRY);
fd.F0 = F0;
fd.F82 = F82;
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_hoffman_schlick(cosTheta, fd.F0, fd.F82, fd.F90, fd.exponent);
}
else
{
return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior,
fd.F0, fd.F82, 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;
}
// Return the mip level with the appropriate coverage for a filtered importance sample.
// https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
// Section 20.4 Equation 13
float mx_latlong_compute_lod(vec3 dir, float pdf, float maxMipLevel, int envSamples)
{
const float MIP_LEVEL_OFFSET = 1.5;
float effectiveMaxMipLevel = maxMipLevel - MIP_LEVEL_OFFSET;
float distortion = sqrt(1.0 - mx_square(dir.y));
return max(effectiveMaxMipLevel - 0.5 * log2(float(envSamples) * pdf * distortion), 0.0);
}
// Return the mip level associated with the given alpha in a prefiltered environment.
float mx_latlong_alpha_to_lod(float alpha)
{
float lodBias = (alpha < 0.25) ? sqrt(alpha) : 0.5 * alpha + 0.375;
return lodBias * float(u_envRadianceMips - 1);
}
// Return the alpha associated with the given mip level in a prefiltered environment.
float mx_latlong_lod_to_alpha(float lod)
{
float lodBias = lod / float(u_envRadianceMips - 1);
return (lodBias < 0.5) ? mx_square(lodBias) : 2.0 * (lodBias - 0.375);
}
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;
float pad0;
float pad1;
float pad2;
};
layout (std140, binding=5) uniform LightData_pixel
{
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);
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overal "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<<k) | (x>>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_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;
}
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;
}
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_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
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;
vec3 safeTint = max(tint, 0.0);
if (thinfilm_thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, thinfilm_thickness, thinfilm_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 * safeTint * occlusion * weight / (4.0 * NdotV);
}
void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
vec3 safeTint = max(tint, 0.0);
if (thinfilm_thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, thinfilm_thickness, thinfilm_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, safeTint) * weight;
}
}
void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
vec3 safeTint = max(tint, 0.0);
if (thinfilm_thickness > 0.0)
{
fd = mx_init_fresnel_dielectric_airy(ior, thinfilm_thickness, thinfilm_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 * safeTint * 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, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
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 (thinfilm_thickness > 0.0)
fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, thinfilm_thickness, thinfilm_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, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd;
if (thinfilm_thickness > 0.0)
fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, thinfilm_thickness, thinfilm_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 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 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));
const float one_minus_coat_ior_to_F0_in1_tmp = 1.000000;
float one_minus_coat_ior_to_F0_out = one_minus_coat_ior_to_F0_in1_tmp - coat_ior_to_F0_out;
surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0));
{
vec3 N = normalize(normalWorld);
vec3 V = normalize(u_viewPosition - positionWorld);
vec3 P = 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], 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));
mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, 0.000000, 1.500000, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, metal_bsdf_out);
BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 0, specular_bsdf_out);
BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.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));
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));
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));
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));
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));
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));
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));
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));
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 metalness_mix_out = BSDF(vec3(0.0),vec3(1.0));
metalness_mix_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
metalness_mix_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));
thin_film_layer_attenuated_out.response = metalness_mix_out.response * thin_film_layer_attenuated_out_in2_clamped;
thin_film_layer_attenuated_out.throughput = metalness_mix_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.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));
mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, 0.000000, 1.500000, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, metal_bsdf_out);
BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 0, specular_bsdf_out);
BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.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));
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));
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));
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));
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));
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));
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));
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));
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 metalness_mix_out = BSDF(vec3(0.0),vec3(1.0));
metalness_mix_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
metalness_mix_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));
thin_film_layer_attenuated_out.response = metalness_mix_out.response * thin_film_layer_attenuated_out_in2_clamped;
thin_film_layer_attenuated_out.throughput = metalness_mix_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.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(one_minus_coat_ior_to_F0_out, one_minus_coat_ior_to_F0_out, one_minus_coat_ior_to_F0_out), vec3(0.000000, 0.000000, 0.000000), 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));
mx_dielectric_bsdf_transmission(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, 0.000000, 1.500000, coat_normal, coat_tangent_out, 0, 0, coat_bsdf_out);
BSDF metal_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF specular_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_transmission(V, specular, specular_color, specular_IOR, main_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 0, specular_bsdf_out);
BSDF transmission_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
mx_dielectric_bsdf_transmission(V, 1.000000, transmission_color, specular_IOR, transmission_roughness_out, thin_film_thickness, thin_film_IOR, normal, main_tangent_out, 0, 1, transmission_bsdf_out);
BSDF sheen_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF translucent_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.0));
BSDF selected_subsurface_bsdf_out = BSDF(vec3(0.0),vec3(1.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));
BSDF subsurface_mix_out = BSDF(vec3(0.0),vec3(1.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));
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));
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));
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 metalness_mix_out = BSDF(vec3(0.0),vec3(1.0));
metalness_mix_out.response = mix(specular_layer_out.response, metal_bsdf_out.response, metalness);
metalness_mix_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));
thin_film_layer_attenuated_out.response = metalness_mix_out.response * thin_film_layer_attenuated_out_in2_clamped;
thin_film_layer_attenuated_out.throughput = metalness_mix_out.throughput * thin_film_layer_attenuated_out_in2_clamped;
BSDF coat_layer_out = BSDF(vec3(0.0),vec3(1.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(normalWorld);
vec3 geomprop_Tworld_out1 = normalize(tangentWorld);
vec3 obj_pos_out = positionObject;
float add_xyz_out = dot(obj_pos_out, add_xyz_in2);
vec3 scale_pos_out = obj_pos_out * scale_pos_in2;
float scale_xyz_out = add_xyz_out * scale_xyz_in2;
float noise_out = 0.0;
mx_fractal3d_float(noise_amplitude, noise_octaves, noise_lacunarity, noise_diminish, scale_pos_out, noise_out);
float scale_noise_out = noise_out * scale_noise_in2;
float sum_out = scale_xyz_out + scale_noise_out;
float sin_out = sin(sum_out);
float scale_out = sin_out * scale_in2;
float bias_out = scale_out + bias_in2;
float power_out = pow(bias_out, power_in2);
vec3 color_mix_out = mix(color_mix_bg, color_mix_fg, power_out);
surfaceshader SR_marble1_out = surfaceshader(vec3(0.0),vec3(0.0));
NG_standard_surface_surfaceshader_100(SR_marble1_base, color_mix_out, SR_marble1_diffuse_roughness, SR_marble1_metalness, SR_marble1_specular, SR_marble1_specular_color, SR_marble1_specular_roughness, SR_marble1_specular_IOR, SR_marble1_specular_anisotropy, SR_marble1_specular_rotation, SR_marble1_transmission, SR_marble1_transmission_color, SR_marble1_transmission_depth, SR_marble1_transmission_scatter, SR_marble1_transmission_scatter_anisotropy, SR_marble1_transmission_dispersion, SR_marble1_transmission_extra_roughness, SR_marble1_subsurface, color_mix_out, SR_marble1_subsurface_radius, SR_marble1_subsurface_scale, SR_marble1_subsurface_anisotropy, SR_marble1_sheen, SR_marble1_sheen_color, SR_marble1_sheen_roughness, SR_marble1_coat, SR_marble1_coat_color, SR_marble1_coat_roughness, SR_marble1_coat_anisotropy, SR_marble1_coat_rotation, SR_marble1_coat_IOR, geomprop_Nworld_out1, SR_marble1_coat_affect_color, SR_marble1_coat_affect_roughness, SR_marble1_thin_film_thickness, SR_marble1_thin_film_IOR, SR_marble1_emission, SR_marble1_emission_color, SR_marble1_opacity, SR_marble1_thin_walled, geomprop_Nworld_out1, geomprop_Tworld_out1, SR_marble1_out);
material Marble_3D_out = SR_marble1_out;
out1 = vec4(Marble_3D_out.color, 1.0);
}
port element in the standard data library !
# getAncestorOfType not in Python API.
def elementInDefinition(elem):
parent = elem.getParent()
while parent:
if parent.isA(mx.NodeGraph):
if parent.getNodeDef():
#print('Skip elem: ', elem.getNamePath())
return True
return False
else:
parent = parent.getParent()
return False
def getParentGraph(elem):
parent = elem.getParent()
while parent:
if parent.isA(mx.NodeGraph):
return parent
else:
parent = parent.getParent()
return None
portElementMap = dict()
for elem in doc.traverseTree():
if not elem.isA(mx.PortElement):
continue
graph = getParentGraph(elem)
graphName = ''
if graph:
if graph.getNodeDef():
continue
graphName = graph.getNamePath()
nd = elem.getAttribute('nodename')
if nd:
qn = graphName + '/' + elem.getQualifiedName(nd)
if qn not in portElementMap:
portElementMap[qn] = [ elem ]
else:
portElementMap[qn].append( elem )
ng = elem.getAttribute("nodegraph")
if ng:
qng = graphName + '/' + elem.getQualifiedName(ng)
if qng not in portElementMap:
portElementMap[qng] = [ elem ]
else:
portElementMap[qng].append( elem )
for k in portElementMap:
print('Node:', k)
for p in portElementMap[k]:
print(' used by:', p.getNamePath(), 'out:' + p.getAttribute('output') if p.getAttribute('output') else '')
Node: NG_marble1/obj_pos used by: NG_marble1/add_xyz/in1 used by: NG_marble1/scale_pos/in1 Node: NG_marble1/add_xyz used by: NG_marble1/scale_xyz/in1 Node: NG_marble1/scale_pos used by: NG_marble1/noise/position Node: NG_marble1/noise used by: NG_marble1/scale_noise/in1 Node: NG_marble1/scale_xyz used by: NG_marble1/sum/in1 Node: NG_marble1/scale_noise used by: NG_marble1/sum/in2 Node: NG_marble1/sum used by: NG_marble1/sin/in Node: NG_marble1/sin used by: NG_marble1/scale/in1 Node: NG_marble1/scale used by: NG_marble1/bias/in1 Node: NG_marble1/bias used by: NG_marble1/power/in1 Node: NG_marble1/power used by: NG_marble1/color_mix/mix Node: NG_marble1/color_mix used by: NG_marble1/out Node: /NG_marble1 used by: SR_marble1/base_color out:out used by: SR_marble1/subsurface_color out:out Node: /SR_marble1 used by: Marble_3D/surfaceshader