if(MATERIALX_BUILD_DATA_LIBRARY)
# Build generated products from the MaterialX data library.
# Initially, this step is a simple copy across folders, but our intent
# is for it to include meaningful work in the future.
set(DATA_LIBRARY_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/DataLibraryBuild)
file(GLOB_RECURSE MATERIALX_DATA_LIBRARY_SOURCE_FILES
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
LIST_DIRECTORIES false
*.mtlx
*.md
*.glsl
*.osl
*.h
*.metal)
foreach(SOURCE_FILE IN LISTS MATERIALX_DATA_LIBRARY_SOURCE_FILES)
set(SOURCE_FILEPATH ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE_FILE})
set(DEST_FILEPATH ${DATA_LIBRARY_BUILD_DIR}/${SOURCE_FILE})
add_custom_command(
OUTPUT ${DEST_FILEPATH}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${SOURCE_FILEPATH} ${DEST_FILEPATH}
DEPENDS ${SOURCE_FILEPATH})
list(APPEND MATERIALX_DATA_LIBRARY_BUILD_FILES ${DEST_FILEPATH})
endforeach()
add_custom_target(MaterialXBuildData ALL
DEPENDS ${MATERIALX_DATA_LIBRARY_BUILD_FILES})
set(DATA_LIBRARY_DIR ${DATA_LIBRARY_BUILD_DIR})
else()
set(DATA_LIBRARY_DIR ${CMAKE_CURRENT_SOURCE_DIR})
endif()
if(NOT SKBUILD)
install(DIRECTORY ${DATA_LIBRARY_DIR}/
DESTINATION "${MATERIALX_INSTALL_STDLIB_PATH}"
PATTERN "CMakeLists.txt" EXCLUDE)
endif()
if(MATERIALX_BUILD_PYTHON)
set(MATERIALX_PYTHON_LIBRARIES_PATH "${MATERIALX_PYTHON_FOLDER_NAME}/${MATERIALX_INSTALL_STDLIB_PATH}")
if(SKBUILD)
set(MATERIALX_PYTHON_LIBRARIES_PATH "${SKBUILD_PLATLIB_DIR}/MaterialX/libraries")
endif()
install(DIRECTORY ${DATA_LIBRARY_DIR}/
DESTINATION "${MATERIALX_PYTHON_LIBRARIES_PATH}"
PATTERN "CMakeLists.txt" EXCLUDE)
endif()
set(MATERIALX_DATA_LIBRARY_DIR ${DATA_LIBRARY_DIR} PARENT_SCOPE)
# MaterialX Data Libraries
This folder contains the standard data libraries for MaterialX, providing declarations and graph definitions for the MaterialX nodes, and source code for all supported shader generators.
## Standard Pattern Library
- [stdlib](stdlib)
- [stdlib_defs.mtlx](stdlib/stdlib_defs.mtlx) : Nodedef declarations.
- [stdlib_ng.mtlx](stdlib/stdlib_ng.mtlx) : Nodegraph definitions.
- [genglsl](stdlib/genglsl): GLSL language support.
- [lib](stdlib/genglsl/lib) : Shader utility files.
- [stdlib_genglsl_impl.mtlx](stdlib/genglsl/stdlib_genglsl_impl.mtlx) : Mapping from declarations to implementations.
- [genosl](stdlib/genosl): OSL language support.
- [lib](stdlib/genosl/lib) : Shader utility files.
- [stdlib_genosl_impl.mtlx](stdlib/genosl/stdlib_genosl_impl.mtlx) : Mapping from declarations to implementations.
- [genmdl](stdlib/genmdl): MDL language support.
- [stdlib_genmdl_impl.mtlx](stdlib/genmdl/stdlib_genmdl_impl.mtlx) : Mapping from declarations to implementations.
- Additional MaterialX support libraries for MDL are located in the [source/MaterialXGenMdl/mdl/materialx](../source/MaterialXGenMdl/mdl/materialx) package folder
- [genmsl](stdlib/genmsl): MSL language support.
- [lib](stdlib/genmsl/lib) : Shader utility files.
- [stdlib_genmsl_impl.mtlx](stdlib/genmsl/stdlib_genmsl_impl.mtlx) : Mapping from declarations to implementations.
## Physically Based Shading Library
- [pbrlib](pbrlib)
- [pbrlib_defs.mtlx](pbrlib/pbrlib_defs.mtlx) : Nodedef declarations.
- [pbrlib_ng.mtlx](pbrlib/pbrlib_ng.mtlx) : Nodegraph definitions.
- [genglsl](pbrlib/genglsl) : GLSL language support
- [lib](pbrlib/genglsl/lib) : Shader utility files.
- [pbrlib_genglsl_impl.mtlx](pbrlib/genglsl/pbrlib_genglsl_impl.mtlx) : Mapping from declarations to implementations.
- [genosl](pbrlib/genosl) : OSL language support
- [lib](pbrlib/genosl/lib) : Shader utility files.
- [pbrlib_genosl_impl.mtlx](pbrlib/genosl/pbrlib_genosl_impl.mtlx) : Mapping from declarations to implementations.
- [genmdl](pbrlib/genmdl) : MDL language support
- [pbrlib_genmdl_impl.mtlx](pbrlib/genmdl/pbrlib_genmdl_impl.mtlx) : Mapping from declarations to implementations.
- [genmsl](pbrlib/genmsl) : MSL language support
- [pbrlib_genmsl_impl.mtlx](pbrlib/genmsl/pbrlib_genmsl_impl.mtlx) : Mapping from declarations to implementations.
## BxDF Graph Library
- [bxdf](bxdf)
- [standard_surface.mtlx](bxdf/standard_surface.mtlx) : Graph definition of the [Autodesk Standard Surface](https://autodesk.github.io/standard-surface/) shading model.
- [gltf_pbr.mtlx](bxdf/gltf_pbr.mtlx) : Graph definition of the [glTF PBR](https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#appendix-b-brdf-implementation) shading model.
- [usd_preview_surface.mtlx](bxdf/usd_preview_surface.mtlx) : Graph definition of the [UsdPreviewSurface](https://openusd.org/release/spec_usdpreviewsurface.html) shading model.
- [lama](bxdf/lama) : Graph definitions of the [MaterialX Lama](https://rmanwiki.pixar.com/display/REN24/MaterialX+Lama) node set.
## Color Management Library
- MaterialX shader generation natively supports a small set of common spaces for input colors, with all color transforms implemented as language-independent MaterialX graphs.The canonical definitions of these color transforms may be found in the OpenColorIO configuration for [ACES 1.2](https://github.com/colour-science/OpenColorIO-Configs/tree/feature/aces-1.2-config/aces_1.2).
- lin_rec709
- g18_rec709
- g22_rec709
- rec709_display
- acescg (lin_ap1)
- g22_ap1
- srgb_texture
- lin_adobergb
- adobergb
- srgb_displayp3
- lin_displayp3
- [cmlib](cmlib)
- [cmlib_defs.mtlx](cmlib/cmlib_defs.mtlx) : Nodedef declarations.
- [cmlib_ng.mtlx](cmlib/cmlib_ng.mtlx) : Nodegraph definitions.
## Target Definitions
- Each target implementation requires a target definition for declaration / implementation correspondence to work.
- The [targets](targets) folder contains definition files for the following core targets:
- GLSL : `genglsl`
- OSL : `genosl`
- MDL : `genmdl`
- MSL : `genmsl`
- Any additional target files should be added under this folder and loaded in as required.
### Target Support
- GLSL target support is for version 4.0 or higher.
- OSL target support is for version 1.12.6 or higher.
- MDL target support is for version 1.6 or higher.
- Basic GLSL and MSL `lightshader` node definitions and implementations are provided for the following light types:
- point, directional, spot
- Shader generation does not currently support:
- `displacementshader` and `volumeshader` nodes for hardware shading targets (GLSL, MSL).
- `hextiledimage` and `hextilednormalmap` for OSL and MDL.
- `blur` the implementation passes through `in` unmodified in all shading languages.
/>
void mx_directional_light(LightData light, vec3 position, out lightshader result)
{
result.direction = -light.direction;
result.intensity = light.color * light.intensity;
}
void mx_point_light(LightData light, vec3 position, out lightshader result)
{
result.direction = light.position - position;
float distance = length(result.direction) + M_FLOAT_EPS;
float attenuation = pow(distance + 1.0, light.decay_rate + M_FLOAT_EPS);
result.intensity = light.color * light.intensity / attenuation;
result.direction /= distance;
}
void mx_spot_light(LightData light, vec3 position, out lightshader result)
{
result.direction = light.position - position;
float distance = length(result.direction) + M_FLOAT_EPS;
float attenuation = pow(distance + 1.0, light.decay_rate + M_FLOAT_EPS);
result.intensity = light.color * light.intensity / attenuation;
result.direction /= distance;
float low = min(light.inner_angle, light.outer_angle);
float high = light.inner_angle;
float cosDir = dot(result.direction, -light.direction);
float spotAttenuation = smoothstep(low, high, cosDir);
result.intensity *= spotAttenuation;
}
void mx_directional_light(LightData light, float3 position, thread lightshader& result)
{
result.direction = -light.direction;
result.intensity = light.color * light.intensity;
}
void mx_point_light(LightData light, float3 position, thread lightshader& result)
{
result.direction = light.position - position;
float distance = length(result.direction) + M_FLOAT_EPS;
float attenuation = pow(distance + 1.0, light.decay_rate + M_FLOAT_EPS);
result.intensity = light.color * light.intensity / attenuation;
result.direction /= distance;
}
void mx_spot_light(LightData light, float3 position, thread lightshader& result)
{
result.direction = light.position - position;
float distance = length(result.direction) + M_FLOAT_EPS;
float attenuation = pow(distance + 1.0, light.decay_rate + M_FLOAT_EPS);
result.intensity = light.color * light.intensity / attenuation;
result.direction /= distance;
float low = min(light.inner_angle, light.outer_angle);
float high = light.inner_angle;
float cosDir = dot(result.direction, -light.direction);
float spotAttenuation = smoothstep(low, high, cosDir);
result.intensity *= spotAttenuation;
}
// These are defined based on the HwShaderGenerator::ClosureContextType enum
// if that changes - these need to be updated accordingly.
#define CLOSURE_TYPE_DEFAULT 0
#define CLOSURE_TYPE_REFLECTION 1
#define CLOSURE_TYPE_TRANSMISSION 2
#define CLOSURE_TYPE_INDIRECT 3
#define CLOSURE_TYPE_EMISSION 4
struct ClosureData {
int closureType;
vec3 L;
vec3 V;
vec3 N;
vec3 P;
float occlusion;
};
#include "mx_microfacet_specular.glsl"
vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd)
{
// Generate tangent frame.
X = normalize(X - dot(X, N) * N);
vec3 Y = cross(N, X);
mat3 tangentToWorld = mat3(X, Y, N);
// Transform the view vector to tangent space.
V = vec3(dot(V, X), dot(V, Y), dot(V, N));
// Compute derived properties.
float NdotV = clamp(V.z, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(alpha);
float G1V = mx_ggx_smith_G1(NdotV, avgAlpha);
// Integrate outgoing radiance using filtered importance sampling.
// http://cgg.mff.cuni.cz/~jaroslav/papers/2008-egsr-fis/2008-egsr-fis-final-embedded.pdf
vec3 radiance = vec3(0.0);
int envRadianceSamples = $envRadianceSamples;
for (int i = 0; i < envRadianceSamples; i++)
{
vec2 Xi = mx_spherical_fibonacci(i, envRadianceSamples);
// Compute the half vector and incoming light direction.
vec3 H = mx_ggx_importance_sample_VNDF(Xi, V, alpha);
vec3 L = fd.refraction ? mx_refraction_solid_sphere(-V, H, fd.ior.x) : -reflect(V, H);
// Compute dot products for this sample.
float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
// Sample the environment light from the given direction.
vec3 Lw = tangentToWorld * L;
float pdf = mx_ggx_NDF(H, alpha) * G1V / (4.0 * NdotV);
float lod = mx_latlong_compute_lod(Lw, pdf, float($envRadianceMips - 1), envRadianceSamples);
vec3 sampleColor = mx_latlong_map_lookup(Lw, $envMatrix, lod, $envRadiance);
// Compute the Fresnel term.
vec3 F = mx_compute_fresnel(VdotH, fd);
// Compute the geometric term.
float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
// Compute the combined FG term, which simplifies to inverted Fresnel for refraction.
vec3 FG = fd.refraction ? vec3(1.0) - F : F * G;
// Add the radiance contribution of this sample.
// From https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
// incidentLight = sampleColor * NdotL
// microfacetSpecular = D * F * G / (4 * NdotL * NdotV)
// pdf = D * G1V / (4 * NdotV);
// radiance = incidentLight * microfacetSpecular / pdf
radiance += sampleColor * FG;
}
// Apply the global component of the geometric term and normalize.
radiance /= G1V * float(envRadianceSamples);
// Return the final radiance.
return radiance * $envLightIntensity;
}
vec3 mx_environment_irradiance(vec3 N)
{
vec3 Li = mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance);
return Li * $envLightIntensity;
}
#include "mx_microfacet_specular.glsl"
vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 roughness, int distribution, FresnelData fd)
{
return vec3(0.0);
}
vec3 mx_environment_irradiance(vec3 N)
{
return vec3(0.0);
}
#include "mx_microfacet_specular.glsl"
// Return the mip level associated with the given alpha in a prefiltered environment.
float mx_latlong_alpha_to_lod(float alpha)
{
float lodBias = (alpha < 0.25) ? sqrt(alpha) : 0.5 * alpha + 0.375;
return lodBias * float($envRadianceMips - 1);
}
vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd)
{
N = mx_forward_facing_normal(N, V);
vec3 L = fd.refraction ? mx_refraction_solid_sphere(-V, N, fd.ior.x) : -reflect(V, N);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(alpha);
vec3 F = mx_compute_fresnel(NdotV, fd);
float G = mx_ggx_smith_G2(NdotV, NdotV, avgAlpha);
vec3 FG = fd.refraction ? vec3(1.0) - (F * G) : F * G;
vec3 Li = mx_latlong_map_lookup(L, $envMatrix, mx_latlong_alpha_to_lod(avgAlpha), $envRadiance);
return Li * FG * $envLightIntensity;
}
vec3 mx_environment_irradiance(vec3 N)
{
vec3 Li = mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance);
return Li * $envLightIntensity;
}
#include "mx_microfacet_sheen.glsl"
#include "mx_microfacet_specular.glsl"
vec3 mx_generate_dir_albedo_table()
{
vec2 uv = gl_FragCoord.xy / $albedoTableSize;
vec2 ggxDirAlbedo = mx_ggx_dir_albedo(uv.x, uv.y, vec3(1, 0, 0), vec3(0, 1, 0)).xy;
float sheenDirAlbedo = mx_imageworks_sheen_dir_albedo(uv.x, uv.y);
return vec3(ggxDirAlbedo, sheenDirAlbedo);
}
#include "mx_microfacet_specular.glsl"
// Return the alpha associated with the given mip level in a prefiltered environment.
float mx_latlong_lod_to_alpha(float lod)
{
float lodBias = lod / float($envRadianceMips - 1);
return (lodBias < 0.5) ? mx_square(lodBias) : 2.0 * (lodBias - 0.375);
}
// The inverse of mx_latlong_projection.
vec3 mx_latlong_map_projection_inverse(vec2 uv)
{
float latitude = (uv.y - 0.5) * M_PI;
float longitude = (uv.x - 0.5) * M_PI * 2.0;
float x = -mx_cos(latitude) * mx_sin(longitude);
float y = -mx_sin(latitude);
float z = mx_cos(latitude) * mx_cos(longitude);
return vec3(x, y, z);
}
vec3 mx_generate_prefilter_env()
{
// The tangent view vector is aligned with the normal.
vec3 V = vec3(0.0, 0.0, 1.0);
float NdotV = 1.0;
// Compute derived properties.
vec2 uv = gl_FragCoord.xy * pow(2.0, $envPrefilterMip) / vec2(textureSize($envRadianceSampler2D, 0));
vec3 worldN = mx_latlong_map_projection_inverse(uv);
mat3 tangentToWorld = mx_orthonormal_basis(worldN);
float alpha = mx_latlong_lod_to_alpha(float($envPrefilterMip));
float G1V = mx_ggx_smith_G1(NdotV, alpha);
// Integrate the LD term for the given environment and alpha.
vec3 radiance = vec3(0.0, 0.0, 0.0);
float weight = 0.0;
int envRadianceSamples = 1024;
for (int i = 0; i < envRadianceSamples; i++)
{
vec2 Xi = mx_spherical_fibonacci(i, envRadianceSamples);
// Compute the half vector and incoming light direction.
vec3 H = mx_ggx_importance_sample_VNDF(Xi, V, vec2(alpha));
vec3 L = -V + 2.0 * H.z * H;
// Compute dot products for this sample.
float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
// Compute the geometric term.
float G = mx_ggx_smith_G2(NdotL, NdotV, alpha);
// Sample the environment light from the given direction.
vec3 Lw = tangentToWorld * L;
float pdf = mx_ggx_NDF(H, vec2(alpha)) * G1V / (4.0 * NdotV);
float lod = mx_latlong_compute_lod(Lw, pdf, float($envRadianceMips - 1), envRadianceSamples);
vec3 sampleColor = mx_latlong_map_lookup(Lw, $envMatrix, lod, $envRadiance);
// Add the radiance contribution of this sample.
radiance += G * sampleColor;
weight += G;
}
return radiance / weight;
}
#define M_PI 3.1415926535897932
#define M_PI_INV (1.0 / M_PI)
float mx_pow5(float x)
{
return mx_square(mx_square(x)) * x;
}
float mx_pow6(float x)
{
float x2 = mx_square(x);
return mx_square(x2) * x2;
}
// Standard Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
// Generalized Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0, float F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
// Generalized Schlick Fresnel with a variable exponent
float mx_fresnel_schlick(float cosTheta, float F0, float F90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(F0, F90, pow(x, exponent));
}
vec3 mx_fresnel_schlick(float cosTheta, vec3 F0, vec3 F90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(F0, F90, pow(x, exponent));
}
// Enforce that the given normal is forward-facing from the specified view direction.
vec3 mx_forward_facing_normal(vec3 N, vec3 V)
{
return (dot(N, V) < 0.0) ? -N : N;
}
// https://www.graphics.rwth-aachen.de/publication/2/jgt.pdf
float mx_golden_ratio_sequence(int i)
{
const float GOLDEN_RATIO = 1.6180339887498948;
return fract((float(i) + 1.0) * GOLDEN_RATIO);
}
// https://people.irisa.fr/Ricardo.Marques/articles/2013/SF_CGF.pdf
vec2 mx_spherical_fibonacci(int i, int numSamples)
{
return vec2((float(i) + 0.5) / float(numSamples), mx_golden_ratio_sequence(i));
}
// Generate a uniform-weighted sample on the unit hemisphere.
vec3 mx_uniform_sample_hemisphere(vec2 Xi)
{
float phi = 2.0 * M_PI * Xi.x;
float cosTheta = 1.0 - Xi.y;
float sinTheta = sqrt(1.0 - mx_square(cosTheta));
return vec3(mx_cos(phi) * sinTheta,
mx_sin(phi) * sinTheta,
cosTheta);
}
// Generate a cosine-weighted sample on the unit hemisphere.
vec3 mx_cosine_sample_hemisphere(vec2 Xi)
{
float phi = 2.0 * M_PI * Xi.x;
float cosTheta = sqrt(Xi.y);
float sinTheta = sqrt(1.0 - Xi.y);
return vec3(mx_cos(phi) * sinTheta,
mx_sin(phi) * sinTheta,
cosTheta);
}
// Construct an orthonormal basis from a unit vector.
// https://graphics.pixar.com/library/OrthonormalB/paper.pdf
mat3 mx_orthonormal_basis(vec3 N)
{
float sign = (N.z < 0.0) ? -1.0 : 1.0;
float a = -1.0 / (sign + N.z);
float b = N.x * N.y * a;
vec3 X = vec3(1.0 + sign * N.x * N.x * a, sign * b, -sign * N.x);
vec3 Y = vec3(b, sign + N.y * N.y * a, -N.y);
return mat3(X, Y, N);
}
#include "mx_microfacet.glsl"
const float FUJII_CONSTANT_1 = 0.5 - 2.0 / (3.0 * M_PI);
const float FUJII_CONSTANT_2 = 2.0 / 3.0 - 28.0 / (15.0 * M_PI);
// Qualitative Oren-Nayar diffuse with simplified math:
// https://www1.cs.columbia.edu/CAVE/publications/pdfs/Oren_SIGGRAPH94.pdf
float mx_oren_nayar_diffuse(float NdotV, float NdotL, float LdotV, float roughness)
{
float s = LdotV - NdotL * NdotV;
float stinv = (s > 0.0) ? s / max(NdotL, NdotV) : 0.0;
float sigma2 = mx_square(roughness);
float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33));
float B = 0.45 * sigma2 / (sigma2 + 0.09);
return A + B * stinv;
}
// Rational quadratic fit to Monte Carlo data for Oren-Nayar directional albedo.
float mx_oren_nayar_diffuse_dir_albedo_analytic(float NdotV, float roughness)
{
vec2 r = vec2(1.0, 1.0) +
vec2(-0.4297, -0.6076) * roughness +
vec2(-0.7632, -0.4993) * NdotV * roughness +
vec2(1.4385, 2.0315) * mx_square(roughness);
return r.x / r.y;
}
float mx_oren_nayar_diffuse_dir_albedo_table_lookup(float NdotV, float roughness)
{
#if DIRECTIONAL_ALBEDO_METHOD == 1
if (textureSize($albedoTable, 0).x > 1)
{
return texture($albedoTable, vec2(NdotV, roughness)).b;
}
#endif
return 0.0;
}
float mx_oren_nayar_diffuse_dir_albedo_monte_carlo(float NdotV, float roughness)
{
NdotV = clamp(NdotV, M_FLOAT_EPS, 1.0);
vec3 V = vec3(sqrt(1.0 - mx_square(NdotV)), 0, NdotV);
float radiance = 0.0;
const int SAMPLE_COUNT = 64;
for (int i = 0; i < SAMPLE_COUNT; i++)
{
vec2 Xi = mx_spherical_fibonacci(i, SAMPLE_COUNT);
// Compute the incoming light direction.
vec3 L = mx_uniform_sample_hemisphere(Xi);
// Compute dot products for this sample.
float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
// Compute diffuse reflectance.
float reflectance = mx_oren_nayar_diffuse(NdotV, NdotL, LdotV, roughness);
// Add the radiance contribution of this sample.
// uniform_pdf = 1 / (2 * PI)
// radiance = (reflectance * NdotL) / (uniform_pdf * PI);
radiance += reflectance * NdotL;
}
// Apply global components and normalize.
radiance *= 2.0 / float(SAMPLE_COUNT);
// Return the final directional albedo.
return radiance;
}
float mx_oren_nayar_diffuse_dir_albedo(float NdotV, float roughness)
{
#if DIRECTIONAL_ALBEDO_METHOD == 2
float dirAlbedo = mx_oren_nayar_diffuse_dir_albedo_monte_carlo(NdotV, roughness);
#else
float dirAlbedo = mx_oren_nayar_diffuse_dir_albedo_analytic(NdotV, roughness);
#endif
return clamp(dirAlbedo, 0.0, 1.0);
}
// Improved Oren-Nayar diffuse from Fujii:
// https://mimosa-pudica.net/improved-oren-nayar.html
float mx_oren_nayar_fujii_diffuse_dir_albedo(float cosTheta, float roughness)
{
float A = 1.0 / (1.0 + FUJII_CONSTANT_1 * roughness);
float B = roughness * A;
float Si = sqrt(max(0.0, 1.0 - mx_square(cosTheta)));
float G = Si * (mx_acos(clamp(cosTheta, -1.0, 1.0)) - Si * cosTheta) +
2.0 * ((Si / cosTheta) * (1.0 - Si * Si * Si) - Si) / 3.0;
return A + (B * G * M_PI_INV);
}
float mx_oren_nayar_fujii_diffuse_avg_albedo(float roughness)
{
float A = 1.0 / (1.0 + FUJII_CONSTANT_1 * roughness);
return A * (1.0 + FUJII_CONSTANT_2 * roughness);
}
// Energy-compensated Oren-Nayar diffuse from OpenPBR Surface:
// https://academysoftwarefoundation.github.io/OpenPBR/
vec3 mx_oren_nayar_compensated_diffuse(float NdotV, float NdotL, float LdotV, float roughness, vec3 color)
{
float s = LdotV - NdotL * NdotV;
float stinv = (s > 0.0) ? s / max(NdotL, NdotV) : s;
// Compute the single-scatter lobe.
float A = 1.0 / (1.0 + FUJII_CONSTANT_1 * roughness);
vec3 lobeSingleScatter = color * A * (1.0 + roughness * stinv);
// Compute the multi-scatter lobe.
float dirAlbedoV = mx_oren_nayar_fujii_diffuse_dir_albedo(NdotV, roughness);
float dirAlbedoL = mx_oren_nayar_fujii_diffuse_dir_albedo(NdotL, roughness);
float avgAlbedo = mx_oren_nayar_fujii_diffuse_avg_albedo(roughness);
vec3 colorMultiScatter = mx_square(color) * avgAlbedo /
(vec3(1.0) - color * max(0.0, 1.0 - avgAlbedo));
vec3 lobeMultiScatter = colorMultiScatter *
max(M_FLOAT_EPS, 1.0 - dirAlbedoV) *
max(M_FLOAT_EPS, 1.0 - dirAlbedoL) /
max(M_FLOAT_EPS, 1.0 - avgAlbedo);
// Return the sum.
return lobeSingleScatter + lobeMultiScatter;
}
vec3 mx_oren_nayar_compensated_diffuse_dir_albedo(float cosTheta, float roughness, vec3 color)
{
float dirAlbedo = mx_oren_nayar_fujii_diffuse_dir_albedo(cosTheta, roughness);
float avgAlbedo = mx_oren_nayar_fujii_diffuse_avg_albedo(roughness);
vec3 colorMultiScatter = mx_square(color) * avgAlbedo /
(vec3(1.0) - color * max(0.0, 1.0 - avgAlbedo));
return mix(colorMultiScatter, color, dirAlbedo);
}
// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
// Section 5.3
float mx_burley_diffuse(float NdotV, float NdotL, float LdotH, float roughness)
{
float F90 = 0.5 + (2.0 * roughness * mx_square(LdotH));
float refL = mx_fresnel_schlick(NdotL, 1.0, F90);
float refV = mx_fresnel_schlick(NdotV, 1.0, F90);
return refL * refV;
}
// Compute the directional albedo component of Burley diffuse for the given
// view angle and roughness. Curve fit provided by Stephen Hill.
float mx_burley_diffuse_dir_albedo(float NdotV, float roughness)
{
float x = NdotV;
float fit0 = 0.97619 - 0.488095 * mx_pow5(1.0 - x);
float fit1 = 1.55754 + (-2.02221 + (2.56283 - 1.06244 * x) * x) * x;
return mix(fit0, fit1, roughness);
}
// Evaluate the Burley diffusion profile for the given distance and diffusion shape.
// Based on https://graphics.pixar.com/library/ApproxBSSRDF/
vec3 mx_burley_diffusion_profile(float dist, vec3 shape)
{
vec3 num1 = exp(-shape * dist);
vec3 num2 = exp(-shape * dist / 3.0);
float denom = max(dist, M_FLOAT_EPS);
return (num1 + num2) / denom;
}
// Integrate the Burley diffusion profile over a sphere of the given radius.
// Inspired by Eric Penner's presentation in http://advances.realtimerendering.com/s2011/
vec3 mx_integrate_burley_diffusion(vec3 N, vec3 L, float radius, vec3 mfp)
{
float theta = mx_acos(dot(N, L));
// Estimate the Burley diffusion shape from mean free path.
vec3 shape = vec3(1.0) / max(mfp, 0.1);
// Integrate the profile over the sphere.
vec3 sumD = vec3(0.0);
vec3 sumR = vec3(0.0);
const int SAMPLE_COUNT = 32;
const float SAMPLE_WIDTH = (2.0 * M_PI) / float(SAMPLE_COUNT);
for (int i = 0; i < SAMPLE_COUNT; i++)
{
float x = -M_PI + (float(i) + 0.5) * SAMPLE_WIDTH;
float dist = radius * abs(2.0 * mx_sin(x * 0.5));
vec3 R = mx_burley_diffusion_profile(dist, shape);
sumD += R * max(mx_cos(theta + x), 0.0);
sumR += R;
}
return sumD / sumR;
}
vec3 mx_subsurface_scattering_approx(vec3 N, vec3 L, vec3 P, vec3 albedo, vec3 mfp)
{
float curvature = length(fwidth(N)) / length(fwidth(P));
float radius = 1.0 / max(curvature, 0.01);
return albedo * mx_integrate_burley_diffusion(N, L, radius, mfp) / vec3(M_PI);
}
#include "mx_microfacet.glsl"
// https://fpsunflower.github.io/ckulla/data/s2017_pbs_imageworks_sheen.pdf
// Equation 2
float mx_imageworks_sheen_NDF(float NdotH, float roughness)
{
float invRoughness = 1.0 / max(roughness, 0.005);
float cos2 = NdotH * NdotH;
float sin2 = 1.0 - cos2;
return (2.0 + invRoughness) * pow(sin2, invRoughness * 0.5) / (2.0 * M_PI);
}
float mx_imageworks_sheen_brdf(float NdotL, float NdotV, float NdotH, float roughness)
{
// Microfacet distribution.
float D = mx_imageworks_sheen_NDF(NdotH, roughness);
// Fresnel and geometry terms are ignored.
float F = 1.0;
float G = 1.0;
// We use a smoother denominator, as in:
// https://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf
return D * F * G / (4.0 * (NdotL + NdotV - NdotL*NdotV));
}
// Rational quadratic fit to Monte Carlo data for Imageworks sheen directional albedo.
float mx_imageworks_sheen_dir_albedo_analytic(float NdotV, float roughness)
{
vec2 r = vec2(13.67300, 1.0) +
vec2(-68.78018, 61.57746) * NdotV +
vec2(799.08825, 442.78211) * roughness +
vec2(-905.00061, 2597.49308) * NdotV * roughness +
vec2(60.28956, 121.81241) * mx_square(NdotV) +
vec2(1086.96473, 3045.55075) * mx_square(roughness);
return r.x / r.y;
}
float mx_imageworks_sheen_dir_albedo_table_lookup(float NdotV, float roughness)
{
#if DIRECTIONAL_ALBEDO_METHOD == 1
if (textureSize($albedoTable, 0).x > 1)
{
return texture($albedoTable, vec2(NdotV, roughness)).b;
}
#endif
return 0.0;
}
float mx_imageworks_sheen_dir_albedo_monte_carlo(float NdotV, float roughness)
{
NdotV = clamp(NdotV, M_FLOAT_EPS, 1.0);
vec3 V = vec3(sqrt(1.0f - mx_square(NdotV)), 0, NdotV);
float radiance = 0.0;
const int SAMPLE_COUNT = 64;
for (int i = 0; i < SAMPLE_COUNT; i++)
{
vec2 Xi = mx_spherical_fibonacci(i, SAMPLE_COUNT);
// Compute the incoming light direction and half vector.
vec3 L = mx_uniform_sample_hemisphere(Xi);
vec3 H = normalize(L + V);
// Compute dot products for this sample.
float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
float NdotH = clamp(H.z, M_FLOAT_EPS, 1.0);
// Compute sheen reflectance.
float reflectance = mx_imageworks_sheen_brdf(NdotL, NdotV, NdotH, roughness);
// Add the radiance contribution of this sample.
// uniform_pdf = 1 / (2 * PI)
// radiance = reflectance * NdotL / uniform_pdf;
radiance += reflectance * NdotL * 2.0 * M_PI;
}
// Return the final directional albedo.
return radiance / float(SAMPLE_COUNT);
}
float mx_imageworks_sheen_dir_albedo(float NdotV, float roughness)
{
#if DIRECTIONAL_ALBEDO_METHOD == 0
float dirAlbedo = mx_imageworks_sheen_dir_albedo_analytic(NdotV, roughness);
#elif DIRECTIONAL_ALBEDO_METHOD == 1
float dirAlbedo = mx_imageworks_sheen_dir_albedo_table_lookup(NdotV, roughness);
#else
float dirAlbedo = mx_imageworks_sheen_dir_albedo_monte_carlo(NdotV, roughness);
#endif
return clamp(dirAlbedo, 0.0, 1.0);
}
// The following functions are adapted from https://github.com/tizian/ltc-sheen.
// "Practical Multiple-Scattering Sheen Using Linearly Transformed Cosines", Zeltner et al.
// Gaussian fit to directional albedo table.
float mx_zeltner_sheen_dir_albedo(float x, float y)
{
float s = y*(0.0206607 + 1.58491*y)/(0.0379424 + y*(1.32227 + y));
float m = y*(-0.193854 + y*(-1.14885 + y*(1.7932 - 0.95943*y*y)))/(0.046391 + y);
float o = y*(0.000654023 + (-0.0207818 + 0.119681*y)*y)/(1.26264 + y*(-1.92021 + y));
return exp(-0.5*mx_square((x - m)/s))/(s*sqrt(2.0*M_PI)) + o;
}
// Rational fits to LTC matrix coefficients.
float mx_zeltner_sheen_ltc_aInv(float x, float y)
{
return (2.58126*x + 0.813703*y)*y/(1.0 + 0.310327*x*x + 2.60994*x*y);
}
float mx_zeltner_sheen_ltc_bInv(float x, float y)
{
return sqrt(1.0 - x)*(y - 1.0)*y*y*y/(0.0000254053 + 1.71228*x - 1.71506*x*y + 1.34174*y*y);
}
// V and N are assumed to be unit vectors.
mat3 mx_orthonormal_basis_ltc(vec3 V, vec3 N, float NdotV)
{
// Generate a tangent vector in the plane of V and N.
// This required to correctly orient the LTC lobe.
vec3 X = V - N*NdotV;
float lenSqr = dot(X, X);
if (lenSqr > 0.0)
{
X *= mx_inversesqrt(lenSqr);
vec3 Y = cross(N, X);
return mat3(X, Y, N);
}
// If lenSqr == 0, then V == N, so any orthonormal basis will do.
return mx_orthonormal_basis(N);
}
// Multiplication by directional albedo is handled by the calling function.
float mx_zeltner_sheen_brdf(vec3 L, vec3 V, vec3 N, float NdotV, float roughness)
{
mat3 toLTC = transpose(mx_orthonormal_basis_ltc(V, N, NdotV));
vec3 w = toLTC * L;
float aInv = mx_zeltner_sheen_ltc_aInv(NdotV, roughness);
float bInv = mx_zeltner_sheen_ltc_bInv(NdotV, roughness);
// Transform w to original configuration (clamped cosine).
// |aInv 0 bInv|
// wo = M^-1 . w = | 0 aInv 0| . w
// | 0 0 1|
vec3 wo = vec3(aInv*w.x + bInv*w.z, aInv * w.y, w.z);
float lenSqr = dot(wo, wo);
// D(w) = Do(M^-1.w / ||M^-1.w||) . |M^-1| / ||M^-1.w||^3
// = Do(M^-1.w) . |M^-1| / ||M^-1.w||^4
// = Do(wo) . |M^-1| / dot(wo, wo)^2
// = Do(wo) . aInv^2 / dot(wo, wo)^2
// = Do(wo) . (aInv / dot(wo, wo))^2
return max(wo.z, 0.0) * M_PI_INV * mx_square(aInv / lenSqr);
}
vec3 mx_zeltner_sheen_importance_sample(vec2 Xi, vec3 V, vec3 N, float roughness, out float pdf)
{
float NdotV = clamp(dot(N, V), 0.0, 1.0);
roughness = clamp(roughness, 0.01, 1.0); // Clamp to range of original impl.
vec3 wo = mx_cosine_sample_hemisphere(Xi);
float aInv = mx_zeltner_sheen_ltc_aInv(NdotV, roughness);
float bInv = mx_zeltner_sheen_ltc_bInv(NdotV, roughness);
// Transform wo from original configuration (clamped cosine).
// |1/aInv 0 -bInv/aInv|
// w = M . wo = | 0 1/aInv 0| . wo
// | 0 0 1|
vec3 w = vec3(wo.x/aInv - wo.z*bInv/aInv, wo.y / aInv, wo.z);
float lenSqr = dot(w, w);
w *= mx_inversesqrt(lenSqr);
// D(w) = Do(wo) . ||M.wo||^3 / |M|
// = Do(wo / ||M.wo||) . ||M.wo||^4 / |M|
// = Do(w) . ||M.wo||^4 / |M| (possible because M doesn't change z component)
// = Do(w) . dot(w, w)^2 * aInv^2
// = Do(w) . (aInv * dot(w, w))^2
pdf = max(w.z, 0.0) * M_PI_INV * mx_square(aInv * lenSqr);
mat3 fromLTC = mx_orthonormal_basis_ltc(V, N, NdotV);
w = fromLTC * w;
return w;
}
#include "mx_microfacet.glsl"
const int FRESNEL_MODEL_DIELECTRIC = 0;
const int FRESNEL_MODEL_CONDUCTOR = 1;
const int FRESNEL_MODEL_SCHLICK = 2;
// Parameters for Fresnel calculations
struct FresnelData
{
// Fresnel model
int model;
bool airy;
// Physical Fresnel
vec3 ior;
vec3 extinction;
// Generalized Schlick Fresnel
vec3 F0;
vec3 F82;
vec3 F90;
float exponent;
// Thin film
float tf_thickness;
float tf_ior;
// Refraction
bool refraction;
};
// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf
// Appendix B.2 Equation 13
float mx_ggx_NDF(vec3 H, vec2 alpha)
{
vec2 He = H.xy / alpha;
float denom = dot(He, He) + mx_square(H.z);
return 1.0 / (M_PI * alpha.x * alpha.y * mx_square(denom));
}
// https://ggx-research.github.io/publication/2023/06/09/publication-ggx.html
vec3 mx_ggx_importance_sample_VNDF(vec2 Xi, vec3 V, vec2 alpha)
{
// Transform the view direction to the hemisphere configuration.
V = normalize(vec3(V.xy * alpha, V.z));
// Sample a spherical cap in (-V.z, 1].
float phi = 2.0 * M_PI * Xi.x;
float z = (1.0 - Xi.y) * (1.0 + V.z) - V.z;
float sinTheta = sqrt(clamp(1.0 - z * z, 0.0, 1.0));
float x = sinTheta * mx_cos(phi);
float y = sinTheta * mx_sin(phi);
vec3 c = vec3(x, y, z);
// Compute the microfacet normal.
vec3 H = c + V;
// Transform the microfacet normal back to the ellipsoid configuration.
H = normalize(vec3(H.xy * alpha, max(H.z, 0.0)));
return H;
}
// https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
// Equation 34
float mx_ggx_smith_G1(float cosTheta, float alpha)
{
float cosTheta2 = mx_square(cosTheta);
float tanTheta2 = (1.0 - cosTheta2) / cosTheta2;
return 2.0 / (1.0 + sqrt(1.0 + mx_square(alpha) * tanTheta2));
}
// Height-correlated Smith masking-shadowing
// http://jcgt.org/published/0003/02/03/paper.pdf
// Equations 72 and 99
float mx_ggx_smith_G2(float NdotL, float NdotV, float alpha)
{
float alpha2 = mx_square(alpha);
float lambdaL = sqrt(alpha2 + (1.0 - alpha2) * mx_square(NdotL));
float lambdaV = sqrt(alpha2 + (1.0 - alpha2) * mx_square(NdotV));
return 2.0 * NdotL * NdotV / (lambdaL * NdotV + lambdaV * NdotL);
}
// Rational quadratic fit to Monte Carlo data for GGX directional albedo.
vec3 mx_ggx_dir_albedo_analytic(float NdotV, float alpha, vec3 F0, vec3 F90)
{
float x = NdotV;
float y = alpha;
float x2 = mx_square(x);
float y2 = mx_square(y);
vec4 r = vec4(0.1003, 0.9345, 1.0, 1.0) +
vec4(-0.6303, -2.323, -1.765, 0.2281) * x +
vec4(9.748, 2.229, 8.263, 15.94) * y +
vec4(-2.038, -3.748, 11.53, -55.83) * x * y +
vec4(29.34, 1.424, 28.96, 13.08) * x2 +
vec4(-8.245, -0.7684, -7.507, 41.26) * y2 +
vec4(-26.44, 1.436, -36.11, 54.9) * x2 * y +
vec4(19.99, 0.2913, 15.86, 300.2) * x * y2 +
vec4(-5.448, 0.6286, 33.37, -285.1) * x2 * y2;
vec2 AB = clamp(r.xy / r.zw, 0.0, 1.0);
return F0 * AB.x + F90 * AB.y;
}
vec3 mx_ggx_dir_albedo_table_lookup(float NdotV, float alpha, vec3 F0, vec3 F90)
{
#if DIRECTIONAL_ALBEDO_METHOD == 1
if (textureSize($albedoTable, 0).x > 1)
{
vec2 AB = texture($albedoTable, vec2(NdotV, alpha)).rg;
return F0 * AB.x + F90 * AB.y;
}
#endif
return vec3(0.0);
}
// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
vec3 mx_ggx_dir_albedo_monte_carlo(float NdotV, float alpha, vec3 F0, vec3 F90)
{
NdotV = clamp(NdotV, M_FLOAT_EPS, 1.0);
vec3 V = vec3(sqrt(1.0 - mx_square(NdotV)), 0, NdotV);
vec2 AB = vec2(0.0);
const int SAMPLE_COUNT = 64;
for (int i = 0; i < SAMPLE_COUNT; i++)
{
vec2 Xi = mx_spherical_fibonacci(i, SAMPLE_COUNT);
// Compute the half vector and incoming light direction.
vec3 H = mx_ggx_importance_sample_VNDF(Xi, V, vec2(alpha));
vec3 L = -reflect(V, H);
// Compute dot products for this sample.
float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0);
float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
// Compute the Fresnel term.
float Fc = mx_fresnel_schlick(VdotH, 0.0, 1.0);
// Compute the per-sample geometric term.
// https://hal.inria.fr/hal-00996995v2/document, Algorithm 2
float G2 = mx_ggx_smith_G2(NdotL, NdotV, alpha);
// Add the contribution of this sample.
AB += vec2(G2 * (1.0 - Fc), G2 * Fc);
}
// Apply the global component of the geometric term and normalize.
AB /= mx_ggx_smith_G1(NdotV, alpha) * float(SAMPLE_COUNT);
// Return the final directional albedo.
return F0 * AB.x + F90 * AB.y;
}
vec3 mx_ggx_dir_albedo(float NdotV, float alpha, vec3 F0, vec3 F90)
{
#if DIRECTIONAL_ALBEDO_METHOD == 0
return mx_ggx_dir_albedo_analytic(NdotV, alpha, F0, F90);
#elif DIRECTIONAL_ALBEDO_METHOD == 1
return mx_ggx_dir_albedo_table_lookup(NdotV, alpha, F0, F90);
#else
return mx_ggx_dir_albedo_monte_carlo(NdotV, alpha, F0, F90);
#endif
}
float mx_ggx_dir_albedo(float NdotV, float alpha, float F0, float F90)
{
return mx_ggx_dir_albedo(NdotV, alpha, vec3(F0), vec3(F90)).x;
}
// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
// Equations 14 and 16
vec3 mx_ggx_energy_compensation(float NdotV, float alpha, vec3 Fss)
{
float Ess = mx_ggx_dir_albedo(NdotV, alpha, 1.0, 1.0);
return 1.0 + Fss * (1.0 - Ess) / Ess;
}
float mx_ggx_energy_compensation(float NdotV, float alpha, float Fss)
{
return mx_ggx_energy_compensation(NdotV, alpha, vec3(Fss)).x;
}
// Compute the average of an anisotropic alpha pair.
float mx_average_alpha(vec2 alpha)
{
return sqrt(alpha.x * alpha.y);
}
// Convert a real-valued index of refraction to normal-incidence reflectivity.
float mx_ior_to_f0(float ior)
{
return mx_square((ior - 1.0) / (ior + 1.0));
}
// Convert normal-incidence reflectivity to real-valued index of refraction.
float mx_f0_to_ior(float F0)
{
float sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
return (1.0 + sqrtF0) / (1.0 - sqrtF0);
}
vec3 mx_f0_to_ior(vec3 F0)
{
vec3 sqrtF0 = sqrt(clamp(F0, 0.01, 0.99));
return (vec3(1.0) + sqrtF0) / (vec3(1.0) - sqrtF0);
}
// https://renderwonk.com/publications/wp-generalization-adobe/gen-adobe.pdf
vec3 mx_fresnel_hoffman_schlick(float cosTheta, FresnelData fd)
{
const float COS_THETA_MAX = 1.0 / 7.0;
const float COS_THETA_FACTOR = 1.0 / (COS_THETA_MAX * pow(1.0 - COS_THETA_MAX, 6.0));
float x = clamp(cosTheta, 0.0, 1.0);
vec3 a = mix(fd.F0, fd.F90, pow(1.0 - COS_THETA_MAX, fd.exponent)) * (vec3(1.0) - fd.F82) * COS_THETA_FACTOR;
return mix(fd.F0, fd.F90, pow(1.0 - x, fd.exponent)) - a * x * mx_pow6(1.0 - x);
}
// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
float mx_fresnel_dielectric(float cosTheta, float ior)
{
float c = cosTheta;
float g2 = ior*ior + c*c - 1.0;
if (g2 < 0.0)
{
// Total internal reflection
return 1.0;
}
float g = sqrt(g2);
return 0.5 * mx_square((g - c) / (g + c)) *
(1.0 + mx_square(((g + c) * c - 1.0) / ((g - c) * c + 1.0)));
}
// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
vec2 mx_fresnel_dielectric_polarized(float cosTheta, float ior)
{
float cosTheta2 = mx_square(clamp(cosTheta, 0.0, 1.0));
float sinTheta2 = 1.0 - cosTheta2;
float t0 = max(ior * ior - sinTheta2, 0.0);
float t1 = t0 + cosTheta2;
float t2 = 2.0 * sqrt(t0) * cosTheta;
float Rs = (t1 - t2) / (t1 + t2);
float t3 = cosTheta2 * t0 + sinTheta2 * sinTheta2;
float t4 = t2 * sinTheta2;
float Rp = Rs * (t3 - t4) / (t3 + t4);
return vec2(Rp, Rs);
}
// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
void mx_fresnel_conductor_polarized(float cosTheta, vec3 n, vec3 k, out vec3 Rp, out vec3 Rs)
{
float cosTheta2 = mx_square(clamp(cosTheta, 0.0, 1.0));
float sinTheta2 = 1.0 - cosTheta2;
vec3 n2 = n * n;
vec3 k2 = k * k;
vec3 t0 = n2 - k2 - vec3(sinTheta2);
vec3 a2plusb2 = sqrt(t0 * t0 + 4.0 * n2 * k2);
vec3 t1 = a2plusb2 + vec3(cosTheta2);
vec3 a = sqrt(max(0.5 * (a2plusb2 + t0), 0.0));
vec3 t2 = 2.0 * a * cosTheta;
Rs = (t1 - t2) / (t1 + t2);
vec3 t3 = cosTheta2 * a2plusb2 + vec3(sinTheta2 * sinTheta2);
vec3 t4 = t2 * sinTheta2;
Rp = Rs * (t3 - t4) / (t3 + t4);
}
vec3 mx_fresnel_conductor(float cosTheta, vec3 n, vec3 k)
{
vec3 Rp, Rs;
mx_fresnel_conductor_polarized(cosTheta, n, k, Rp, Rs);
return 0.5 * (Rp + Rs);
}
// https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html
void mx_fresnel_conductor_phase_polarized(float cosTheta, float eta1, vec3 eta2, vec3 kappa2, out vec3 phiP, out vec3 phiS)
{
vec3 k2 = kappa2 / eta2;
vec3 sinThetaSqr = vec3(1.0) - cosTheta * cosTheta;
vec3 A = eta2*eta2*(vec3(1.0)-k2*k2) - eta1*eta1*sinThetaSqr;
vec3 B = sqrt(A*A + mx_square(2.0*eta2*eta2*k2));
vec3 U = sqrt((A+B)/2.0);
vec3 V = max(vec3(0.0), sqrt((B-A)/2.0));
phiS = mx_atan(2.0*eta1*V*cosTheta, U*U + V*V - mx_square(eta1*cosTheta));
phiP = mx_atan(2.0*eta1*eta2*eta2*cosTheta * (2.0*k2*U - (vec3(1.0)-k2*k2) * V),
mx_square(eta2*eta2*(vec3(1.0)+k2*k2)*cosTheta) - eta1*eta1*(U*U+V*V));
}
// https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html
vec3 mx_eval_sensitivity(float opd, vec3 shift)
{
// Use Gaussian fits, given by 3 parameters: val, pos and var
float phase = 2.0*M_PI * opd;
vec3 val = vec3(5.4856e-13, 4.4201e-13, 5.2481e-13);
vec3 pos = vec3(1.6810e+06, 1.7953e+06, 2.2084e+06);
vec3 var = vec3(4.3278e+09, 9.3046e+09, 6.6121e+09);
vec3 xyz = val * sqrt(2.0*M_PI * var) * mx_cos(pos * phase + shift) * exp(- var * phase*phase);
xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * mx_cos(2.2399e+06 * phase + shift[0]) * exp(- 4.5282e+09 * phase*phase);
return xyz / 1.0685e-7;
}
// A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence
// https://belcour.github.io/blog/research/publication/2017/05/01/brdf-thin-film.html
vec3 mx_fresnel_airy(float cosTheta, FresnelData fd)
{
// XYZ to CIE 1931 RGB color space (using neutral E illuminant)
const mat3 XYZ_TO_RGB = mat3(2.3706743, -0.5138850, 0.0052982, -0.9000405, 1.4253036, -0.0146949, -0.4706338, 0.0885814, 1.0093968);
// Assume vacuum on the outside
float eta1 = 1.0;
float eta2 = max(fd.tf_ior, eta1);
vec3 eta3 = (fd.model == FRESNEL_MODEL_SCHLICK) ? mx_f0_to_ior(fd.F0) : fd.ior;
vec3 kappa3 = (fd.model == FRESNEL_MODEL_SCHLICK) ? vec3(0.0) : fd.extinction;
float cosThetaT = sqrt(1.0 - (1.0 - mx_square(cosTheta)) * mx_square(eta1 / eta2));
// First interface
vec2 R12 = mx_fresnel_dielectric_polarized(cosTheta, eta2 / eta1);
if (cosThetaT <= 0.0)
{
// Total internal reflection
R12 = vec2(1.0);
}
vec2 T121 = vec2(1.0) - R12;
// Second interface
vec3 R23p, R23s;
if (fd.model == FRESNEL_MODEL_SCHLICK)
{
vec3 f = mx_fresnel_hoffman_schlick(cosThetaT, fd);
R23p = 0.5 * f;
R23s = 0.5 * f;
}
else
{
mx_fresnel_conductor_polarized(cosThetaT, eta3 / eta2, kappa3 / eta2, R23p, R23s);
}
// Phase shift
float cosB = mx_cos(mx_atan(eta2 / eta1));
vec2 phi21 = vec2(cosTheta < cosB ? 0.0 : M_PI, M_PI);
vec3 phi23p, phi23s;
if (fd.model == FRESNEL_MODEL_SCHLICK)
{
phi23p = vec3((eta3[0] < eta2) ? M_PI : 0.0,
(eta3[1] < eta2) ? M_PI : 0.0,
(eta3[2] < eta2) ? M_PI : 0.0);
phi23s = phi23p;
}
else
{
mx_fresnel_conductor_phase_polarized(cosThetaT, eta2, eta3, kappa3, phi23p, phi23s);
}
vec3 r123p = max(sqrt(R12.x*R23p), 0.0);
vec3 r123s = max(sqrt(R12.y*R23s), 0.0);
// Iridescence term
vec3 I = vec3(0.0);
vec3 Cm, Sm;
// Optical path difference
float distMeters = fd.tf_thickness * 1.0e-9;
float opd = 2.0 * eta2 * cosThetaT * distMeters;
// Iridescence term using spectral antialiasing for Parallel polarization
// Reflectance term for m=0 (DC term amplitude)
vec3 Rs = (mx_square(T121.x) * R23p) / (vec3(1.0) - R12.x*R23p);
I += R12.x + Rs;
// Reflectance term for m>0 (pairs of diracs)
Cm = Rs - T121.x;
for (int m=1; m<=2; m++)
{
Cm *= r123p;
Sm = 2.0 * mx_eval_sensitivity(float(m) * opd, float(m)*(phi23p+vec3(phi21.x)));
I += Cm*Sm;
}
// Iridescence term using spectral antialiasing for Perpendicular polarization
// Reflectance term for m=0 (DC term amplitude)
vec3 Rp = (mx_square(T121.y) * R23s) / (vec3(1.0) - R12.y*R23s);
I += R12.y + Rp;
// Reflectance term for m>0 (pairs of diracs)
Cm = Rp - T121.y;
for (int m=1; m<=2; m++)
{
Cm *= r123s;
Sm = 2.0 * mx_eval_sensitivity(float(m) * opd, float(m)*(phi23s+vec3(phi21.y)));
I += Cm*Sm;
}
// Average parallel and perpendicular polarization
I *= 0.5;
// Convert back to RGB reflectance
I = clamp(XYZ_TO_RGB * I, 0.0, 1.0);
return I;
}
FresnelData mx_init_fresnel_dielectric(float ior, float tf_thickness, float tf_ior)
{
FresnelData fd;
fd.model = FRESNEL_MODEL_DIELECTRIC;
fd.airy = tf_thickness > 0.0;
fd.ior = vec3(ior);
fd.extinction = vec3(0.0);
fd.F0 = vec3(0.0);
fd.F82 = vec3(0.0);
fd.F90 = vec3(0.0);
fd.exponent = 0.0;
fd.tf_thickness = tf_thickness;
fd.tf_ior = tf_ior;
fd.refraction = false;
return fd;
}
FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior)
{
FresnelData fd;
fd.model = FRESNEL_MODEL_CONDUCTOR;
fd.airy = tf_thickness > 0.0;
fd.ior = ior;
fd.extinction = extinction;
fd.F0 = vec3(0.0);
fd.F82 = vec3(0.0);
fd.F90 = vec3(0.0);
fd.exponent = 0.0;
fd.tf_thickness = tf_thickness;
fd.tf_ior = tf_ior;
fd.refraction = false;
return fd;
}
FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F82, vec3 F90, float exponent, float tf_thickness, float tf_ior)
{
FresnelData fd;
fd.model = FRESNEL_MODEL_SCHLICK;
fd.airy = tf_thickness > 0.0;
fd.ior = vec3(0.0);
fd.extinction = vec3(0.0);
fd.F0 = F0;
fd.F82 = F82;
fd.F90 = F90;
fd.exponent = exponent;
fd.tf_thickness = tf_thickness;
fd.tf_ior = tf_ior;
fd.refraction = false;
return fd;
}
vec3 mx_compute_fresnel(float cosTheta, FresnelData fd)
{
if (fd.airy)
{
return mx_fresnel_airy(cosTheta, fd);
}
else if (fd.model == FRESNEL_MODEL_DIELECTRIC)
{
return vec3(mx_fresnel_dielectric(cosTheta, fd.ior.x));
}
else if (fd.model == FRESNEL_MODEL_CONDUCTOR)
{
return mx_fresnel_conductor(cosTheta, fd.ior, fd.extinction);
}
else
{
return mx_fresnel_hoffman_schlick(cosTheta, fd);
}
}
// Compute the refraction of a ray through a solid sphere.
vec3 mx_refraction_solid_sphere(vec3 R, vec3 N, float ior)
{
R = refract(R, N, 1.0 / ior);
vec3 N1 = normalize(R * dot(R, N) - N * 0.5);
return refract(R, N1, ior);
}
vec2 mx_latlong_projection(vec3 dir)
{
float latitude = -mx_asin(dir.y) * M_PI_INV + 0.5;
float longitude = mx_atan(dir.x, -dir.z) * M_PI_INV * 0.5 + 0.5;
return vec2(longitude, latitude);
}
vec3 mx_latlong_map_lookup(vec3 dir, mat4 transform, float lod, $texSamplerSignature)
{
vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz);
vec2 uv = mx_latlong_projection(envDir);
return textureLod($texSamplerSampler2D, uv, lod).rgb;
}
// Return the mip level with the appropriate coverage for a filtered importance sample.
// https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html
// Section 20.4 Equation 13
float mx_latlong_compute_lod(vec3 dir, float pdf, float maxMipLevel, int envSamples)
{
const float MIP_LEVEL_OFFSET = 1.5;
float effectiveMaxMipLevel = maxMipLevel - MIP_LEVEL_OFFSET;
float distortion = sqrt(1.0 - mx_square(dir.y));
return max(effectiveMaxMipLevel - 0.5 * log2(float(envSamples) * pdf * distortion), 0.0);
}
// https://developer.nvidia.com/gpugems/gpugems3/part-ii-light-and-shadows/chapter-8-summed-area-variance-shadow-maps
float mx_variance_shadow_occlusion(vec2 moments, float fragmentDepth)
{
const float MIN_VARIANCE = 0.00001;
// One-tailed inequality valid if fragmentDepth > moments.x.
float p = (fragmentDepth <= moments.x) ? 1.0 : 0.0;
// Compute variance.
float variance = moments.y - mx_square(moments.x);
variance = max(variance, MIN_VARIANCE);
// Compute probabilistic upper bound.
float d = fragmentDepth - moments.x;
float pMax = variance / (variance + mx_square(d));
return max(p, pMax);
}
vec2 mx_compute_depth_moments()
{
float depth = gl_FragCoord.z;
return vec2(depth, mx_square(depth));
}
#include "mx_microfacet_specular.glsl"
vec3 mx_surface_transmission(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd, vec3 tint)
{
return tint;
}
#include "mx_microfacet_specular.glsl"
vec3 mx_surface_transmission(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd, vec3 tint)
{
// Approximate the appearance of surface transmission as glossy
// environment map refraction, ignoring any scene geometry that might
// be visible through the surface.
fd.refraction = true;
if ($refractionTwoSided)
{
tint = mx_square(tint);
}
return mx_environment_radiance(N, V, X, alpha, distribution, fd) * tint;
}
#include "lib/mx_closure_type.glsl"
void mx_add_bsdf(ClosureData closureData, BSDF in1, BSDF in2, out BSDF result)
{
result.response = in1.response + in2.response;
// We derive the throughput for closure addition as follows:
// throughput_1 = 1 - dir_albedo_1
// throughput_2 = 1 - dir_albedo_2
// throughput_sum = 1 - (dir_albedo_1 + dir_albedo_2)
// = 1 - ((1 - throughput_1) + (1 - throughput_2))
// = throughput_1 + throughput_2 - 1
result.throughput = max(in1.throughput + in2.throughput - 1.0, 0.0);
}
#include "lib/mx_closure_type.glsl"
void mx_add_edf(ClosureData closureData, EDF in1, EDF in2, out EDF result)
{
result = in1 + in2;
}
#include "lib/mx_closure_type.glsl"
void mx_anisotropic_vdf(ClosureData closureData, vec3 absorption, vec3 scattering, float anisotropy, inout BSDF bsdf)
{
// TODO: Add some approximation for volumetric light absorption.
}
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);
}
/// XYZ to Rec.709 RGB colorspace conversion
const mat3 XYZ_to_RGB = mat3( 3.2406, -0.9689, 0.0557,
-1.5372, 1.8758, -0.2040,
-0.4986, 0.0415, 1.0570);
void mx_blackbody(float temperatureKelvin, out vec3 colorValue)
{
float xc, yc;
float t, t2, t3, xc2, xc3;
// if value outside valid range of approximation clamp to accepted temperature range
temperatureKelvin = clamp(temperatureKelvin, 1667.0, 25000.0);
t = 1000.0 / temperatureKelvin;
t2 = t * t;
t3 = t * t * t;
// Cubic spline approximation for Kelvin temperature to sRGB conversion
// (https://en.wikipedia.org/wiki/Planckian_locus#Approximation)
if (temperatureKelvin < 4000.0) { // 1667K <= temperatureKelvin < 4000K
xc = -0.2661239 * t3 - 0.2343580 * t2 + 0.8776956 * t + 0.179910;
}
else { // 4000K <= temperatureKelvin <= 25000K
xc = -3.0258469 * t3 + 2.1070379 * t2 + 0.2226347 * t + 0.240390;
}
xc2 = xc * xc;
xc3 = xc * xc * xc;
if (temperatureKelvin < 2222.0) { // 1667K <= temperatureKelvin < 2222K
yc = -1.1063814 * xc3 - 1.34811020 * xc2 + 2.18555832 * xc - 0.20219683;
}
else if (temperatureKelvin < 4000.0) { // 2222K <= temperatureKelvin < 4000K
yc = -0.9549476 * xc3 - 1.37418593 * xc2 + 2.09137015 * xc - 0.16748867;
}
else { // 4000K <= temperatureKelvin <= 25000K
yc = 3.0817580 * xc3 - 5.87338670 * xc2 + 3.75112997 * xc - 0.37001483;
}
if (yc <= 0.0) { // avoid division by zero
colorValue = vec3(1.0);
return;
}
vec3 XYZ = vec3(xc / yc, 1.0, (1.0 - xc - yc) / yc);
colorValue = XYZ_to_RGB * XYZ;
colorValue = max(colorValue, vec3(0.0));
}
#include "lib/mx_closure_type.glsl"
#include "lib/mx_microfacet_diffuse.glsl"
void mx_burley_diffuse_bsdf(ClosureData closureData, float weight, vec3 color, float roughness, vec3 N, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
vec3 V = closureData.V;
vec3 L = closureData.L;
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
{
float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
float LdotH = clamp(dot(L, normalize(L + V)), M_FLOAT_EPS, 1.0);
bsdf.response = color * closureData.occlusion * weight * NdotL * M_PI_INV;
bsdf.response *= mx_burley_diffuse(NdotV, NdotL, LdotH, roughness);
}
else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
{
vec3 Li = mx_environment_irradiance(N) *
mx_burley_diffuse_dir_albedo(NdotV, roughness);
bsdf.response = Li * color * weight;
}
}
#include "lib/mx_closure_type.glsl"
#include "lib/mx_microfacet_specular.glsl"
// https://eugenedeon.com/pdfs/egsrhair.pdf
void mx_deon_hair_absorption_from_melanin(
float melanin_concentration,
float melanin_redness,
// constants converted to color via exp(-c). the defaults are lin_rec709 colors, they may be
// transformed to scene-linear rendering color space.
vec3 eumelanin_color, // default: (0.657704, 0.498077, 0.254106) == exp(-(0.419, 0.697, 1.37))
vec3 pheomelanin_color, // default: (0.829443, 0.670320, 0.349937) == exp(-(0.187, 0.4, 1.05))
out vec3 absorption)
{
float melanin = -log(max(1.0 - melanin_concentration, 0.0001));
float eumelanin = melanin * (1.0 - melanin_redness);
float pheomelanin = melanin * melanin_redness;
absorption = max(
eumelanin * -log(eumelanin_color) + pheomelanin * -log(pheomelanin_color),
vec3(0.0)
);
}
// https://media.disneyanimation.com/uploads/production/publication_asset/152/asset/eurographics2016Fur_Smaller.pdf
void mx_chiang_hair_absorption_from_color(vec3 color, float betaN, out vec3 absorption)
{
float b2 = betaN* betaN;
float b4 = b2 * b2;
float b_fac =
5.969 -
(0.215 * betaN) +
(2.532 * b2) -
(10.73 * b2 * betaN) +
(5.574 * b4) +
(0.245 * b4 * betaN);
vec3 sigma = log(min(max(color, 0.001), vec3(1.0))) / b_fac;
absorption = sigma * sigma;
}
void mx_chiang_hair_roughness(
float longitudinal,
float azimuthal,
float scale_TT, // empirical roughness scale from Marschner et al. (2003).
float scale_TRT, // default: scale_TT = 0.5, scale_TRT = 2.0
out vec2 roughness_R,
out vec2 roughness_TT,
out vec2 roughness_TRT
)
{
float lr = clamp(longitudinal, 0.001, 1.0);
float ar = clamp(azimuthal, 0.001, 1.0);
// longitudinal variance
float v = 0.726 * lr + 0.812 * lr * lr + 3.7 * pow(lr, 20.0);
v = v * v;
float s = 0.265 * ar + 1.194 * ar * ar + 5.372 * pow(ar, 22.0);
roughness_R = vec2(v, s);
roughness_TT = vec2(v * scale_TT * scale_TT, s);
roughness_TRT = vec2(v * scale_TRT * scale_TRT, s);
}
float mx_hair_transform_sin_cos(float x)
{
return sqrt(max(1.0 - x * x, 0.0));
}
float mx_hair_I0(float x)
{
float v = 1.0;
float n = 1.0;
float d = 1.0;
float f = 1.0;
float x2 = x * x;
for (int i = 0; i < 9 ; ++i)
{
d *= 4.0 * (f * f);
n *= x2;
v += n / d;
f += 1.0;
}
return v;
}
float mx_hair_log_I0(float x)
{
if (x > 12.0)
return x + 0.5 * (-log(2.0 * M_PI) + log(1.0 / x) + 1.0 / (8.0 * x));
else
return log(mx_hair_I0(x));
}
float mx_hair_logistic(float x, float s)
{
if (x > 0.0)
x = -x;
float f = exp(x / s);
return f / (s * (1.0 + f) * (1.0 + f));
}
float mx_hair_logistic_cdf(float x, float s)
{
return 1.0 / (1.0 + exp(-x / s));
}
float mx_hair_trimmed_logistic(float x, float s, float a, float b)
{
// the constant can be found in Chiang et al. (2016) Appendix A, eq. (12)
s *= 0.626657; // sqrt(M_PI/8)
return mx_hair_logistic(x, s) / (mx_hair_logistic_cdf(b, s) - mx_hair_logistic_cdf(a, s));
}
float mx_hair_phi(int p, float gammaO, float gammaT)
{
float fP = float(p);
return 2.0 * fP * gammaT - 2.0 * gammaO + fP * M_PI;
}
float mx_hair_longitudinal_scattering( // Mp
float sinThetaI,
float cosThetaI,
float sinThetaO,
float cosThetaO,
float v
)
{
float inv_v = 1.0 / v;
float a = cosThetaO * cosThetaI * inv_v;
float b = sinThetaO * sinThetaI * inv_v;
if (v < 0.1)
return exp(mx_hair_log_I0(a) - b - inv_v + 0.6931 + log(0.5 * inv_v));
else
return ((exp(-b) * mx_hair_I0(a)) / (2.0 * v * sinh(inv_v)));
}
float mx_hair_azimuthal_scattering( // Np
float phi,
int p,
float s,
float gammaO,
float gammaT
)
{
if (p >= 3)
return float(0.5 / M_PI);
float dphi = phi - mx_hair_phi(p, gammaO, gammaT);
if (isinf(dphi))
return float(0.5 / M_PI);
while (dphi > M_PI) dphi -= (2.0 * M_PI);
while (dphi < (-M_PI)) dphi += (2.0 * M_PI);
return mx_hair_trimmed_logistic(dphi, s, -M_PI, M_PI);
}
void mx_hair_alpha_angles(
float alpha,
float sinThetaI,
float cosThetaI,
out vec2 angles[4]
)
{
// 0:R, 1:TT, 2:TRT, 3:TRRT+
for (int i = 0; i <= 3; ++i)
{
if (alpha == 0.0 || i == 3)
angles[i] = vec2(sinThetaI, cosThetaI);
else
{
float m = 2.0 - float(i) * 3.0;
float sa = sin(m * alpha);
float ca = cos(m * alpha);
angles[i].x = sinThetaI * ca + cosThetaI * sa;
angles[i].y = cosThetaI * ca - sinThetaI * sa;
}
}
}
void mx_hair_attenuation(float f, vec3 T, out vec3 Ap[4]) // Ap
{
// 0:R, 1:TT, 2:TRT, 3:TRRT+
Ap[0] = vec3(f);
Ap[1] = (1.0 - f) * (1.0 - f) * T;
Ap[2] = Ap[1] * T * f;
Ap[3] = Ap[2] * T * f / (vec3(1.0) - T * f);
}
void mx_chiang_hair_bsdf(ClosureData closureData, vec3 tint_R, vec3 tint_TT, vec3 tint_TRT, float ior,
vec2 roughness_R, vec2 roughness_TT, vec2 roughness_TRT, float cuticle_angle,
vec3 absorption_coefficient, vec3 N, vec3 X, inout BSDF bsdf)
{
vec3 V = closureData.V;
vec3 L = closureData.L;
N = mx_forward_facing_normal(N, V);
bsdf.throughput = vec3(0.0);
if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
{
X = normalize(X - dot(X, N) * N);
vec3 Y = cross(N, X);
float sinThetaO = dot(V, X);
float sinThetaI = dot(L, X);
float cosThetaO = mx_hair_transform_sin_cos(sinThetaO);
float cosThetaI = mx_hair_transform_sin_cos(sinThetaI);
float y1 = dot(L, N);
float x1 = dot(L, Y);
float y2 = dot(V, N);
float x2 = dot(V, Y);
float phi = mx_atan(y1 * x2 - y2 * x1, x1 * x2 + y1 * y2);
vec3 k1_p = normalize(V - X * dot(V, X));
float cosGammaO = dot(N, k1_p);
float sinGammaO = mx_hair_transform_sin_cos(cosGammaO);
if (dot(k1_p, Y) > 0.0)
sinGammaO = -sinGammaO;
float gammaO = asin(sinGammaO);
float sinThetaT = sinThetaO / ior;
float cosThetaT = mx_hair_transform_sin_cos(sinThetaT);
float etaP = sqrt(max(ior * ior - sinThetaO * sinThetaO, 0.0)) / max(cosThetaO, M_FLOAT_EPS);
float sinGammaT = max(min(sinGammaO / etaP, 1.0), -1.0);
float cosGammaT = sqrt(1.0 - sinGammaT * sinGammaT);
float gammaT = asin(sinGammaT);
// attenuation
vec3 Ap[4];
float fresnel = mx_fresnel_dielectric(cosThetaO * cosGammaO, ior);
vec3 T = exp(-absorption_coefficient * (2.0 * cosGammaT / cosThetaT));
mx_hair_attenuation(fresnel, T, Ap);
// parameters for each lobe
vec2 angles[4];
float alpha = cuticle_angle * M_PI - (M_PI / 2.0); // remap [0, 1] to [-PI/2, PI/2]
mx_hair_alpha_angles(alpha, sinThetaI, cosThetaI, angles);
vec3 tint[4];
tint[0] = tint_R;
tint[1] = tint_TT;
tint[2] = tint_TRT;
tint[3] = tint_TRT;
roughness_R = clamp(roughness_R, 0.001, 1.0);
roughness_TT = clamp(roughness_TT, 0.001, 1.0);
roughness_TRT = clamp(roughness_TRT, 0.001, 1.0);
vec2 vs[4];
vs[0] = roughness_R;
vs[1] = roughness_TT;
vs[2] = roughness_TRT;
vs[3] = roughness_TRT;
// R, TT, TRT, TRRT+
vec3 F = vec3(0.0);
for (int i = 0; i <= 3; ++i)
{
tint[i] = max(tint[i], vec3(0.0));
float Mp = mx_hair_longitudinal_scattering(angles[i].x, angles[i].y, sinThetaO, cosThetaO, vs[i].x);
float Np = (i == 3) ? (1.0 / 2.0 * M_PI) : mx_hair_azimuthal_scattering(phi, i, vs[i].y, gammaO, gammaT);
F += Mp * Np * tint[i] * Ap[i];
}
bsdf.response = F * closureData.occlusion * M_PI_INV;
}
else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
{
// This indirect term is a *very* rough approximation.
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd = mx_init_fresnel_dielectric(ior, 0.0, 1.0);
vec3 F = mx_compute_fresnel(NdotV, fd);
vec2 roughness = (roughness_R + roughness_TT + roughness_TRT) / vec2(3.0); // ?
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
// Use GGX to match the behavior of mx_environment_radiance.
float F0 = mx_ior_to_f0(ior);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, 0, fd);
vec3 tint = (tint_R + tint_TT + tint_TRT) / vec3(3.0); // ?
bsdf.response = Li * comp * tint;
}
}
#include "lib/mx_closure_type.glsl"
#include "lib/mx_microfacet_specular.glsl"
void mx_conductor_bsdf(ClosureData closureData, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
vec3 V = closureData.V;
vec3 L = closureData.L;
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd = mx_init_fresnel_conductor(ior_n, ior_k, thinfilm_thickness, thinfilm_ior);
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
{
X = normalize(X - dot(X, N) * N);
vec3 Y = cross(N, X);
vec3 H = normalize(L + V);
float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
vec3 F = mx_compute_fresnel(VdotH, fd);
float D = mx_ggx_NDF(Ht, safeAlpha);
float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
// Note: NdotL is cancelled out
bsdf.response = D * F * G * comp * closureData.occlusion * weight / (4.0 * NdotV);
}
else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
{
vec3 F = mx_compute_fresnel(NdotV, fd);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
bsdf.response = Li * comp * weight;
}
}
#include "lib/mx_closure_type.glsl"
#include "lib/mx_microfacet_specular.glsl"
void mx_dielectric_bsdf(ClosureData closureData, float weight, vec3 tint, float ior, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
if (closureData.closureType != CLOSURE_TYPE_TRANSMISSION && scatter_mode == 1)
{
return;
}
vec3 V = closureData.V;
vec3 L = closureData.L;
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
FresnelData fd = mx_init_fresnel_dielectric(ior, thinfilm_thickness, thinfilm_ior);
float F0 = mx_ior_to_f0(ior);
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
vec3 safeTint = max(tint, 0.0);
if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
{
X = normalize(X - dot(X, N) * N);
vec3 Y = cross(N, X);
vec3 H = normalize(L + V);
float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
vec3 F = mx_compute_fresnel(VdotH, fd);
float D = mx_ggx_NDF(Ht, safeAlpha);
float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
bsdf.throughput = 1.0 - dirAlbedo * weight;
bsdf.response = D * F * G * comp * safeTint * closureData.occlusion * weight / (4.0 * NdotV);
}
else if (closureData.closureType == CLOSURE_TYPE_TRANSMISSION)
{
vec3 F = mx_compute_fresnel(NdotV, fd);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
bsdf.throughput = 1.0 - dirAlbedo * weight;
if (scatter_mode != 0)
{
bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, safeTint) * weight;
}
}
else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
{
vec3 F = mx_compute_fresnel(NdotV, fd);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, F0, 1.0) * comp;
bsdf.throughput = 1.0 - dirAlbedo * weight;
vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
bsdf.response = Li * safeTint * comp * weight;
}
}
void mx_displacement_float(float disp, float scale, out displacementshader result)
{
result.offset = vec3(disp);
result.scale = scale;
}
void mx_displacement_vector3(vec3 disp, float scale, out displacementshader result)
{
result.offset = disp;
result.scale = scale;
}
#include "lib/mx_closure_type.glsl"
#include "lib/mx_microfacet_specular.glsl"
void mx_generalized_schlick_bsdf(ClosureData closureData, float weight, vec3 color0, vec3 color82, vec3 color90, float exponent, vec2 roughness, float thinfilm_thickness, float thinfilm_ior, vec3 N, vec3 X, int distribution, int scatter_mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
if (closureData.closureType != CLOSURE_TYPE_TRANSMISSION && scatter_mode == 1)
{
return;
}
vec3 V = closureData.V;
vec3 L = closureData.L;
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
vec3 safeColor0 = max(color0, 0.0);
vec3 safeColor82 = max(color82, 0.0);
vec3 safeColor90 = max(color90, 0.0);
FresnelData fd = mx_init_fresnel_schlick(safeColor0, safeColor82, safeColor90, exponent, thinfilm_thickness, thinfilm_ior);
vec2 safeAlpha = clamp(roughness, M_FLOAT_EPS, 1.0);
float avgAlpha = mx_average_alpha(safeAlpha);
if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
{
X = normalize(X - dot(X, N) * N);
vec3 Y = cross(N, X);
vec3 H = normalize(L + V);
float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0);
vec3 Ht = vec3(dot(H, X), dot(H, Y), dot(H, N));
vec3 F = mx_compute_fresnel(VdotH, fd);
float D = mx_ggx_NDF(Ht, safeAlpha);
float G = mx_ggx_smith_G2(NdotL, NdotV, avgAlpha);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, safeColor0, safeColor90) * comp;
float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0));
bsdf.throughput = vec3(1.0 - avgDirAlbedo * weight);
// Note: NdotL is cancelled out
bsdf.response = D * F * G * comp * closureData.occlusion * weight / (4.0 * NdotV);
}
else if (closureData.closureType == CLOSURE_TYPE_TRANSMISSION)
{
vec3 F = mx_compute_fresnel(NdotV, fd);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, safeColor0, safeColor90) * comp;
float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0));
bsdf.throughput = vec3(1.0 - avgDirAlbedo * weight);
if (scatter_mode != 0)
{
float avgF0 = dot(safeColor0, vec3(1.0 / 3.0));
fd.ior = vec3(mx_f0_to_ior(avgF0));
bsdf.response = mx_surface_transmission(N, V, X, safeAlpha, distribution, fd, vec3(1.0)) * weight;
}
}
else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
{
vec3 F = mx_compute_fresnel(NdotV, fd);
vec3 comp = mx_ggx_energy_compensation(NdotV, avgAlpha, F);
vec3 dirAlbedo = mx_ggx_dir_albedo(NdotV, avgAlpha, safeColor0, safeColor90) * comp;
float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0));
bsdf.throughput = vec3(1.0 - avgDirAlbedo * weight);
vec3 Li = mx_environment_radiance(N, V, X, safeAlpha, distribution, fd);
bsdf.response = Li * comp * weight;
}
}
#include "lib/mx_closure_type.glsl"
#include "lib/mx_microfacet.glsl"
void mx_generalized_schlick_edf(ClosureData closureData, vec3 color0, vec3 color90, float exponent, EDF base, out EDF result)
{
if (closureData.closureType == CLOSURE_TYPE_EMISSION)
{
vec3 N = mx_forward_facing_normal(closureData.N, closureData.V);
float NdotV = clamp(dot(N, closureData.V), M_FLOAT_EPS, 1.0);
vec3 f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
result = base * f;
}
}
#include "lib/mx_closure_type.glsl"
void mx_layer_bsdf(ClosureData closureData, BSDF top, BSDF base, out BSDF result)
{
result.response = top.response + base.response * top.throughput;
result.throughput = top.throughput * base.throughput;
}
#include "lib/mx_closure_type.glsl"
void mx_layer_vdf(ClosureData closureData, BSDF top, BSDF base, out BSDF result)
{
result.response = top.response + base.response;
result.throughput = top.throughput + base.throughput;
}
#include "lib/mx_closure_type.glsl"
void mx_mix_bsdf(ClosureData closureData, BSDF fg, BSDF bg, float mixValue, out BSDF result)
{
result.response = mix(bg.response, fg.response, mixValue);
result.throughput = mix(bg.throughput, fg.throughput, mixValue);
}
#include "lib/mx_closure_type.glsl"
void mx_mix_edf(ClosureData closureData, EDF fg, EDF bg, float mixValue, out EDF result)
{
result = mix(bg, fg, mixValue);
}
#include "lib/mx_closure_type.glsl"
void mx_multiply_bsdf_color3(ClosureData closureData, BSDF in1, vec3 in2, out BSDF result)
{
vec3 tint = clamp(in2, 0.0, 1.0);
result.response = in1.response * tint;
result.throughput = in1.throughput;
}
#include "lib/mx_closure_type.glsl"
void mx_multiply_bsdf_float(ClosureData closureData, BSDF in1, float in2, out BSDF result)
{
float weight = clamp(in2, 0.0, 1.0);
result.response = in1.response * weight;
result.throughput = in1.throughput;
}
#include "lib/mx_closure_type.glsl"
void mx_multiply_edf_color3(ClosureData closureData, EDF in1, vec3 in2, out EDF result)
{
result = in1 * in2;
}
#include "lib/mx_closure_type.glsl"
void mx_multiply_edf_float(ClosureData closureData, EDF in1, float in2, out EDF result)
{
result = in1 * in2;
}
#include "lib/mx_closure_type.glsl"
#include "lib/mx_microfacet_diffuse.glsl"
void mx_oren_nayar_diffuse_bsdf(ClosureData closureData, float weight, vec3 color, float roughness, vec3 N, bool energy_compensation, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
vec3 V = closureData.V;
vec3 L = closureData.L;
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
{
float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0);
vec3 diffuse = energy_compensation ?
mx_oren_nayar_compensated_diffuse(NdotV, NdotL, LdotV, roughness, color) :
mx_oren_nayar_diffuse(NdotV, NdotL, LdotV, roughness) * color;
bsdf.response = diffuse * closureData.occlusion * weight * NdotL * M_PI_INV;
}
else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
{
vec3 diffuse = energy_compensation ?
mx_oren_nayar_compensated_diffuse_dir_albedo(NdotV, roughness, color) :
mx_oren_nayar_diffuse_dir_albedo(NdotV, roughness) * color;
vec3 Li = mx_environment_irradiance(N);
bsdf.response = Li * diffuse * weight;
}
}
void mx_roughness_anisotropy(float roughness, float anisotropy, out vec2 result)
{
float roughness_sqr = clamp(roughness*roughness, M_FLOAT_EPS, 1.0);
if (anisotropy > 0.0)
{
float aspect = sqrt(1.0 - clamp(anisotropy, 0.0, 0.98));
result.x = min(roughness_sqr / aspect, 1.0);
result.y = roughness_sqr * aspect;
}
else
{
result.x = roughness_sqr;
result.y = roughness_sqr;
}
}
void mx_roughness_dual(vec2 roughness, out vec2 result)
{
if (roughness.y < 0.0)
{
roughness.y = roughness.x;
}
result.x = clamp(roughness.x * roughness.x, M_FLOAT_EPS, 1.0);
result.y = clamp(roughness.y * roughness.y, M_FLOAT_EPS, 1.0);
}
#include "lib/mx_closure_type.glsl"
#include "lib/mx_microfacet_sheen.glsl"
void mx_sheen_bsdf(ClosureData closureData, float weight, vec3 color, float roughness, vec3 N, int mode, inout BSDF bsdf)
{
if (weight < M_FLOAT_EPS)
{
return;
}
vec3 V = closureData.V;
vec3 L = closureData.L;
N = mx_forward_facing_normal(N, V);
float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0);
if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
{
float dirAlbedo;
if (mode == 0)
{
vec3 H = normalize(L + V);
float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
float NdotH = clamp(dot(N, H), M_FLOAT_EPS, 1.0);
vec3 fr = color * mx_imageworks_sheen_brdf(NdotL, NdotV, NdotH, roughness);
dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
// We need to include NdotL from the light integral here
// as in this case it's not cancelled out by the BRDF denominator.
bsdf.response = fr * NdotL * closureData.occlusion * weight;
}
else
{
roughness = clamp(roughness, 0.01, 1.0); // Clamp to range of original impl.
vec3 fr = color * mx_zeltner_sheen_brdf(L, V, N, NdotV, roughness);
dirAlbedo = mx_zeltner_sheen_dir_albedo(NdotV, roughness);
bsdf.response = dirAlbedo * fr * closureData.occlusion * weight;
}
bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
}
else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
{
float dirAlbedo;
if (mode == 0)
{
dirAlbedo = mx_imageworks_sheen_dir_albedo(NdotV, roughness);
}
else
{
roughness = clamp(roughness, 0.01, 1.0); // Clamp to range of original impl.
dirAlbedo = mx_zeltner_sheen_dir_albedo(NdotV, roughness);
}
vec3 Li = mx_environment_irradiance(N);
bsdf.response = Li * color * dirAlbedo * weight;
bsdf.throughput = vec3(1.0 - dirAlbedo * weight);
}
}
#include "lib/mx_closure_type.glsl"
#include "lib/mx_microfacet_diffuse.glsl"
void mx_subsurface_bsdf(ClosureData closureData, float weight, vec3 color, vec3 radius, float anisotropy, vec3 N, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
vec3 V = closureData.V;
vec3 L = closureData.L;
vec3 P = closureData.P;
float occlusion = closureData.occlusion;
N = mx_forward_facing_normal(N, V);
if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
{
vec3 sss = mx_subsurface_scattering_approx(N, L, P, color, radius);
float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0);
float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion);
bsdf.response = sss * visibleOcclusion * weight;
}
else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
{
// For now, we render indirect subsurface as simple indirect diffuse.
vec3 Li = mx_environment_irradiance(N);
bsdf.response = Li * color * weight;
}
}
#include "lib/mx_closure_type.glsl"
void mx_translucent_bsdf(ClosureData closureData, float weight, vec3 color, vec3 N, inout BSDF bsdf)
{
bsdf.throughput = vec3(0.0);
if (weight < M_FLOAT_EPS)
{
return;
}
vec3 V = closureData.V;
vec3 L = closureData.L;
// Invert normal since we're transmitting light from the other side
N = -N;
if (closureData.closureType == CLOSURE_TYPE_REFLECTION)
{
float NdotL = clamp(dot(N, L), 0.0, 1.0);
bsdf.response = color * weight * NdotL * M_PI_INV;
}
else if (closureData.closureType == CLOSURE_TYPE_INDIRECT)
{
vec3 Li = mx_environment_irradiance(N);
bsdf.response = Li * color * weight;
}
}
#include "lib/mx_closure_type.glsl"
void mx_uniform_edf(ClosureData closureData, vec3 color, out EDF result)
{
if (closureData.closureType == CLOSURE_TYPE_EMISSION)
{
result = color;
}
}
float mx_square(float x)
{
return x*x;
}
vector2 mx_square(vector2 x)
{
return x*x;
}
vector mx_square(vector x)
{
return x*x;
}
vector4 mx_square(vector4 x)
{
return x*x;
}
float mx_pow5(float x)
{
return mx_square(mx_square(x)) * x;
}
color mx_fresnel_conductor(float cosTheta, vector n, vector k)
{
float c2 = cosTheta*cosTheta;
vector n2_k2 = n*n + k*k;
vector nc2 = 2.0 * n * cosTheta;
vector rs_a = n2_k2 + c2;
vector rp_a = n2_k2 * c2 + 1.0;
vector rs = (rs_a - nc2) / (rs_a + nc2);
vector rp = (rp_a - nc2) / (rp_a + nc2);
return 0.5 * (rs + rp);
}
// Standard Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
color mx_fresnel_schlick(float cosTheta, color F0)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return F0 + (1.0 - F0) * x5;
}
// Generalized Schlick Fresnel
float mx_fresnel_schlick(float cosTheta, float F0, float F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
color mx_fresnel_schlick(float cosTheta, color F0, color F90)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
float x5 = mx_pow5(x);
return mix(F0, F90, x5);
}
// Generalized Schlick Fresnel with a variable exponent
color mx_fresnel_schlick(float cosTheta, float f0, float f90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(f0, f90, pow(x, exponent));
}
color mx_fresnel_schlick(float cosTheta, color f0, color f90, float exponent)
{
float x = clamp(1.0 - cosTheta, 0.0, 1.0);
return mix(f0, f90, pow(x, exponent));
}
void mx_anisotropic_vdf(color absorption, color scattering, float anisotropy, output VDF vdf)
{
// Convert from absorption and scattering coefficients to
// extinction coefficient and single-scattering albedo.
color extinction = absorption + scattering;
color albedo = scattering / extinction;
vdf = anisotropic_vdf(albedo, extinction, anisotropy);
}
void mx_artistic_ior(color reflectivity, color edge_color, output vector ior, output vector extinction)
{
// "Artist Friendly Metallic Fresnel", Ole Gulbrandsen, 2014
// http://jcgt.org/published/0003/04/03/paper.pdf
color r = clamp(reflectivity, 0.0, 0.99);
color r_sqrt = sqrt(r);
color n_min = (1.0 - r) / (1.0 + r);
color n_max = (1.0 + r_sqrt) / (1.0 - r_sqrt);
ior = mix(n_max, n_min, edge_color);
color np1 = ior + 1.0;
color nm1 = ior - 1.0;
color k2 = (np1*np1 * r - nm1*nm1) / (1.0 - r);
k2 = max(k2, 0.0);
extinction = sqrt(k2);
}
void mx_blackbody(float temp, output color color_value)
{
float xc, yc;
float t, t2, t3, xc2, xc3;
// if value outside valid range of approximation clamp to accepted temperature range
float temperature = clamp(temp, 1667.0, 25000.0);
t = 1000.0 / temperature;
t2 = t * t;
t3 = t * t * t;
// Cubic spline approximation for Kelvin temperature to sRGB conversion
// (https://en.wikipedia.org/wiki/Planckian_locus#Approximation)
if (temperature < 4000.0) { // 1667K <= temperature < 4000K
xc = -0.2661239 * t3 - 0.2343580 * t2 + 0.8776956 * t + 0.179910;
}
else { // 4000K <= temperature <= 25000K
xc = -3.0258469 * t3 + 2.1070379 * t2 + 0.2226347 * t + 0.240390;
}
xc2 = xc * xc;
xc3 = xc * xc * xc;
if (temperature < 2222.0) { // 1667K <= temperature < 2222K
yc = -1.1063814 * xc3 - 1.34811020 * xc2 + 2.18555832 * xc - 0.20219683;
}
else if (temperature < 4000.0) { // 2222K <= temperature < 4000K
yc = -0.9549476 * xc3 - 1.37418593 * xc2 + 2.09137015 * xc - 0.16748867;
}
else { // 4000K <= temperature <= 25000K
yc = 3.0817580 * xc3 - 5.87338670 * xc2 + 3.75112997 * xc - 0.37001483;
}
if (yc <= 0.0) { // avoid division by zero
color_value = color(1.0);
return;
}
vector XYZ = vector(xc / yc, 1.0, (1 - xc - yc) / yc);
/// XYZ to Rec.709 RGB colorspace conversion
matrix XYZ_to_RGB = matrix( 3.2406, -0.9689, 0.0557, 0.0,
-1.5372, 1.8758, -0.2040, 0.0,
-0.4986, 0.0415, 1.0570, 0.0,
0.0, 0.0, 0.0, 1.0);
color_value = transform(XYZ_to_RGB, XYZ);
color_value = max(color_value, vector(0.0));
}
void mx_chiang_hair_bsdf(color tint_R, color tint_TT, color tint_TRT, float ior,
vector2 roughness_R, vector2 roughness_TT, vector2 roughness_TRT,
float cuticle_angle, vector absorption_coefficient, normal N, vector U, output BSDF bsdf)
{
#if OSL_VERSION_MAJOR >= 1 && OSL_VERSION_MINOR >= 14
bsdf = chiang_hair_bsdf(N, U, tint_R, tint_TT, tint_TRT, ior,
roughness_R.x, roughness_TT.x, roughness_TRT.x, roughness_R.y, roughness_TT.y, roughness_TRT.y,
cuticle_angle, absorption_coefficient);
#else
bsdf = dielectric_bsdf(N, U, color(1), color(0), 0.1, 0.1, ior, "ggx");
#endif
}
void mx_chiang_hair_roughness(float longitudinal, float azimuthal, float scale_TT, float scale_TRT, output vector2 roughness_R, output vector2 roughness_TT, output vector2 roughness_TRT)
{
// TODO: Write OSL implementation of this node.
roughness_R = vector2(0.0, 0.0);
roughness_TT = vector2(0.0, 0.0);
roughness_TRT = vector2(0.0, 0.0);
}
void mx_dielectric_bsdf(float weight, color tint, float ior, vector2 roughness, float thinfilm_thickness, float thinfilm_ior, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
{
color reflection_tint = (scatter_mode == "T") ? color(0.0) : tint;
color transmission_tint = (scatter_mode == "R") ? color(0.0) : tint;
bsdf = weight * dielectric_bsdf(N, U, reflection_tint, transmission_tint, roughness.x, roughness.y, ior, distribution, "thinfilm_thickness", thinfilm_thickness, "thinfilm_ior", thinfilm_ior);
}
void mx_generalized_schlick_bsdf(float weight, color color0, color color82, color color90, float exponent, vector2 roughness, float thinfilm_thickness, float thinfilm_ior, normal N, vector U, string distribution, string scatter_mode, output BSDF bsdf)
{
color reflection_tint = (scatter_mode == "T") ? color(0.0) : color(1.0);
color transmission_tint = (scatter_mode == "R") ? color(0.0) : color(1.0);
bsdf = weight * generalized_schlick_bsdf(N, U, reflection_tint, transmission_tint, roughness.x, roughness.y, color0, color90, exponent, distribution, "thinfilm_thickness", thinfilm_thickness, "thinfilm_ior", thinfilm_ior);
}
#include "lib/mx_microfacet.osl"
void mx_generalized_schlick_edf(color color0, color color90, float exponent, EDF base, output EDF result)
{
float NdotV = fabs(dot(N,-I));
color f = mx_fresnel_schlick(NdotV, color0, color90, exponent);
result = base * f;
}
void mx_roughness_anisotropy(float roughness, float anisotropy, output vector2 result)
{
float roughness_sqr = clamp(roughness*roughness, M_FLOAT_EPS, 1.0);
if (anisotropy > 0.0)
{
float aspect = sqrt(1.0 - clamp(anisotropy, 0.0, 0.98));
result.x = min(roughness_sqr / aspect, 1.0);
result.y = roughness_sqr * aspect;
}
else
{
result.x = roughness_sqr;
result.y = roughness_sqr;
}
}
void mx_roughness_dual(vector2 roughness, output vector2 result)
{
result.x = clamp(roughness.x * roughness.x, M_FLOAT_EPS, 1.0);
if (roughness.y < 0.0)
{
result.y = result.x;
}
else
{
result.y = clamp(roughness.y * roughness.y, M_FLOAT_EPS, 1.0);
}
}
void mx_subsurface_bsdf(float weight, color albedo, color radius, float anisotropy, normal N, output BSDF bsdf)
{
#if OSL_VERSION_MAJOR >= 1 && OSL_VERSION_MINOR >= 14
bsdf = weight * subsurface_bssrdf(N, albedo, radius, anisotropy);
#else
bsdf = weight * subsurface_bssrdf(N, albedo, 1.0, radius, anisotropy);
#endif
}
void mx_surface(BSDF bsdf, EDF edf, float opacity, int thin_walled, output surfaceshader result)
{
result.bsdf = bsdf;
result.edf = edf;
result.opacity = clamp(opacity, 0.0, 1.0);
}
// Blend 3 normals by blending the gradients
// Morten S. Mikkelsen, Surface Gradient–Based Bump Mapping Framework, Journal of
// Computer Graphics Techniques (JCGT), vol. 9, no. 3, 60–90, 2020
// http://jcgt.org/published/0009/03/04/
vec3 mx_normals_to_gradient(vec3 N, vec3 Np)
{
float d = dot(N, Np);
vec3 g = (d * N - Np) / max(M_FLOAT_EPS, abs(d));
return g;
}
vec3 mx_gradient_blend_3_normals(vec3 N, vec3 N1, float N1_weight, vec3 N2, float N2_weight, vec3 N3, float N3_weight)
{
float w1 = clamp(N1_weight, 0.0, 1.0);
float w2 = clamp(N2_weight, 0.0, 1.0);
float w3 = clamp(N3_weight, 0.0, 1.0);
vec3 g1 = mx_normals_to_gradient(N, N1);
vec3 g2 = mx_normals_to_gradient(N, N2);
vec3 g3 = mx_normals_to_gradient(N, N3);
// blend
vec3 gg = w1 * g1 + w2 * g2 + w3 * g3;
// gradient to normal
return normalize(N - gg);
}
// This function should be categorized in mx_math.glsl but it causes build errors in MSL
// so adding here for a workaround
mat3 mx_axis_rotation_matrix(vec3 a, float r)
{
float s = sin(r);
float c = cos(r);
float omc = 1.0 - c;
return mat3(
a.x*a.x*omc + c, a.x*a.y*omc - a.z*s, a.x*a.z*omc + a.y*s,
a.y*a.x*omc + a.z*s, a.y*a.y*omc + c, a.y*a.z*omc - a.x*s,
a.z*a.x*omc - a.y*s, a.z*a.y*omc + a.x*s, a.z*a.z*omc + c
);
}
// https://www.shadertoy.com/view/4djSRW
vec2 mx_hextile_hash(vec2 p)
{
vec3 p3 = fract(vec3(p.x, p.y, p.x) * vec3(0.1031, 0.1030, 0.0973));
p3 += dot(p3, vec3(p3.y, p3.z, p3.x) + 33.33);
return fract((vec2(p3.x, p3.x) + vec2(p3.y, p3.z)) * vec2(p3.z, p3.y));
}
// Christophe Schlick. “Fast Alternatives to Perlin’s Bias and Gain Functions”.
// In Graphics Gems IV, Morgan Kaufmann, 1994, pages 401–403.
// https://dept-info.labri.fr/~schlick/DOC/gem2.html
float mx_schlick_gain(float x, float r)
{
float rr = clamp(r, 0.001, 0.999); // to avoid glitch
float a = (1.0 / rr - 2.0) * (1.0 - 2.0 * x);
return (x < 0.5) ? x / (a + 1.0) : (a - x) / (a - 1.0);
}
struct HextileData
{
vec2 coord1;
vec2 coord2;
vec2 coord3;
vec3 weights;
float rot_radian1;
float rot_radian2;
float rot_radian3;
vec2 ddx1;
vec2 ddx2;
vec2 ddx3;
vec2 ddy1;
vec2 ddy2;
vec2 ddy3;
};
// Morten S. Mikkelsen, Practical Real-Time Hex-Tiling, Journal of Computer Graphics
// Techniques (JCGT), vol. 11, no. 2, 77-94, 2022
// http://jcgt.org/published/0011/03/05/
HextileData mx_hextile_coord(
vec2 coord,
float rotation,
vec2 rotation_range,
float scale,
vec2 scale_range,
float offset,
vec2 offset_range)
{
float sqrt3_2 = sqrt(3.0) * 2.0;
// scale coord to maintain the original fit
vec2 st = coord * sqrt3_2;
// skew input space into simplex triangle grid
// (1, 0, -tan(30), 2*tan(30))
mat2 to_skewed = mat2(1.0, 0.0, -0.57735027, 1.15470054);
vec2 st_skewed = to_skewed * st;
// barycentric weights
vec2 st_frac = fract(st_skewed);
vec3 temp = vec3(st_frac.x, st_frac.y, 0.0);
temp.z = 1.0 - temp.x - temp.y;
float s = step(0.0, -temp.z);
float s2 = 2.0 * s - 1.0;
float w1 = -temp.z * s2;
float w2 = s - temp.y * s2;
float w3 = s - temp.x * s2;
// vertex IDs
ivec2 base_id = ivec2(floor(st_skewed));
int si = int(s);
ivec2 id1 = base_id + ivec2(si, si);
ivec2 id2 = base_id + ivec2(si, 1 - si);
ivec2 id3 = base_id + ivec2(1 - si, si);
// tile center
mat2 inv_skewed = mat2(1.0, 0.0, 0.5, 1.0 / 1.15470054);
vec2 ctr1 = inv_skewed * vec2(id1) / vec2(sqrt3_2);
vec2 ctr2 = inv_skewed * vec2(id2) / vec2(sqrt3_2);
vec2 ctr3 = inv_skewed * vec2(id3) / vec2(sqrt3_2);
// reuse hash for performance
vec2 seed_offset = vec2(0.12345); // to avoid some zeros
vec2 rand1 = mx_hextile_hash(vec2(id1) + seed_offset);
vec2 rand2 = mx_hextile_hash(vec2(id2) + seed_offset);
vec2 rand3 = mx_hextile_hash(vec2(id3) + seed_offset);
// randomized rotation matrix
vec2 rr = mx_radians(rotation_range);
float rv1 = mix(rr.x, rr.y, rand1.x * rotation);
float rv2 = mix(rr.x, rr.y, rand2.x * rotation);
float rv3 = mix(rr.x, rr.y, rand3.x * rotation);
float sin_r1 = sin(rv1);
float sin_r2 = sin(rv2);
float sin_r3 = sin(rv3);
float cos_r1 = cos(rv1);
float cos_r2 = cos(rv2);
float cos_r3 = cos(rv3);
mat2 rm1 = mat2(cos_r1, -sin_r1, sin_r1, cos_r1);
mat2 rm2 = mat2(cos_r2, -sin_r2, sin_r2, cos_r2);
mat2 rm3 = mat2(cos_r3, -sin_r3, sin_r3, cos_r3);
// randomized scale
vec2 sr = scale_range;
vec2 scale1 = vec2(mix(1.0, mix(sr.x, sr.y, rand1.y), scale));
vec2 scale2 = vec2(mix(1.0, mix(sr.x, sr.y, rand2.y), scale));
vec2 scale3 = vec2(mix(1.0, mix(sr.x, sr.y, rand3.y), scale));
// randomized offset
vec2 offset1 = mix(vec2(offset_range.x), vec2(offset_range.y), rand1 * offset);
vec2 offset2 = mix(vec2(offset_range.x), vec2(offset_range.y), rand2 * offset);
vec2 offset3 = mix(vec2(offset_range.x), vec2(offset_range.y), rand3 * offset);
HextileData tile_data;
tile_data.weights = vec3(w1, w2, w3);
tile_data.rot_radian1 = rv1;
tile_data.rot_radian2 = rv2;
tile_data.rot_radian3 = rv3;
// get coord
tile_data.coord1 = ((coord - ctr1) * rm1 / scale1) + ctr1 + offset1;
tile_data.coord2 = ((coord - ctr2) * rm2 / scale2) + ctr2 + offset2;
tile_data.coord3 = ((coord - ctr3) * rm3 / scale3) + ctr3 + offset3;
// derivatives
vec2 ddx = dFdx(coord);
vec2 ddy = dFdy(coord);
tile_data.ddx1 = ddx * rm1 / scale1;
tile_data.ddx2 = ddx * rm2 / scale2;
tile_data.ddx3 = ddx * rm3 / scale3;
tile_data.ddy1 = ddy * rm1 / scale1;
tile_data.ddy2 = ddy * rm2 / scale2;
tile_data.ddy3 = ddy * rm3 / scale3;
return tile_data;
}
/*
Color transform functions.
These functions are modified versions of the color operators found in Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/liboslexec/opcolor.cpp
It contains the subset of color operators needed to implement the MaterialX
standard library. The modifications are for conversions from C++ to GLSL.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
vec3 mx_hsvtorgb(vec3 hsv)
{
// Reference for this technique: Foley & van Dam
float h = hsv.x; float s = hsv.y; float v = hsv.z;
if (s < 0.0001f) {
return vec3 (v, v, v);
} else {
h = 6.0f * (h - floor(h)); // expand to [0..6)
int hi = int(trunc(h));
float f = h - float(hi);
float p = v * (1.0f-s);
float q = v * (1.0f-s*f);
float t = v * (1.0f-s*(1.0f-f));
if (hi == 0)
return vec3 (v, t, p);
else if (hi == 1)
return vec3 (q, v, p);
else if (hi == 2)
return vec3 (p, v, t);
else if (hi == 3)
return vec3 (p, q, v);
else if (hi == 4)
return vec3 (t, p, v);
return vec3 (v, p, q);
}
}
vec3 mx_rgbtohsv(vec3 c)
{
// See Foley & van Dam
float r = c.x; float g = c.y; float b = c.z;
float mincomp = min (r, min(g, b));
float maxcomp = max (r, max(g, b));
float delta = maxcomp - mincomp; // chroma
float h, s, v;
v = maxcomp;
if (maxcomp > 0.0f)
s = delta / maxcomp;
else s = 0.0f;
if (s <= 0.0f)
h = 0.0f;
else {
if (r >= maxcomp) h = (g-b) / delta;
else if (g >= maxcomp) h = 2.0f + (b-r) / delta;
else h = 4.0f + (r-g) / delta;
h *= (1.0f/6.0f);
if (h < 0.0f)
h += 1.0f;
}
return vec3(h, s, v);
}
#define M_FLOAT_EPS 1e-8
#define mx_mod mod
#define mx_inverse inverse
#define mx_inversesqrt inversesqrt
#define mx_sin sin
#define mx_cos cos
#define mx_tan tan
#define mx_asin asin
#define mx_acos acos
#define mx_atan atan
#define mx_radians radians
float mx_square(float x)
{
return x*x;
}
vec2 mx_square(vec2 x)
{
return x*x;
}
vec3 mx_square(vec3 x)
{
return x*x;
}
vec3 mx_srgb_encode(vec3 color)
{
bvec3 isAbove = greaterThan(color, vec3(0.0031308));
vec3 linSeg = color * 12.92;
vec3 powSeg = 1.055 * pow(max(color, vec3(0.0)), vec3(1.0 / 2.4)) - 0.055;
return mix(linSeg, powSeg, isAbove);
}
/*
Noise Library.
This library is a modified version of the noise library found in
Open Shading Language:
github.com/imageworks/OpenShadingLanguage/blob/master/src/include/OSL/oslnoise.h
It contains the subset of noise types needed to implement the MaterialX
standard library. The modifications are mainly conversions from C++ to GLSL.
Produced results should be identical to the OSL noise functions.
Original copyright notice:
------------------------------------------------------------------------
Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Sony Pictures Imageworks nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
------------------------------------------------------------------------
*/
float mx_select(bool b, float t, float f)
{
return b ? t : f;
}
float mx_negate_if(float val, bool b)
{
return b ? -val : val;
}
int mx_floor(float x)
{
return int(floor(x));
}
// return mx_floor as well as the fractional remainder
float mx_floorfrac(float x, out int i)
{
i = mx_floor(x);
return x - float(i);
}
float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
vec3 mx_bilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, float s, float t)
{
float s1 = 1.0 - s;
return (1.0 - t) * (v0*s1 + v1*s) + t * (v2*s1 + v3*s);
}
float mx_trilerp(float v0, float v1, float v2, float v3, float v4, float v5, float v6, float v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
vec3 mx_trilerp(vec3 v0, vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec3 v5, vec3 v6, vec3 v7, float s, float t, float r)
{
float s1 = 1.0 - s;
float t1 = 1.0 - t;
float r1 = 1.0 - r;
return (r1*(t1*(v0*s1 + v1*s) + t*(v2*s1 + v3*s)) +
r*(t1*(v4*s1 + v5*s) + t*(v6*s1 + v7*s)));
}
// 2 and 3 dimensional gradient functions - perform a dot product against a
// randomly chosen vector. Note that the gradient vector is not normalized, but
// this only affects the overall "scale" of the result, so we simply account for
// the scale by multiplying in the corresponding "perlin" function.
float mx_gradient_float(uint hash, float x, float y)
{
// 8 possible directions (+-1,+-2) and (+-2,+-1)
uint h = hash & 7u;
float u = mx_select(h<4u, x, y);
float v = 2.0 * mx_select(h<4u, y, x);
// compute the dot product with (x,y).
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
float mx_gradient_float(uint hash, float x, float y, float z)
{
// use vectors pointing to the edges of the cube
uint h = hash & 15u;
float u = mx_select(h<8u, x, y);
float v = mx_select(h<4u, y, mx_select((h==12u)||(h==14u), x, z));
return mx_negate_if(u, bool(h&1u)) + mx_negate_if(v, bool(h&2u));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y)
{
return vec3(mx_gradient_float(hash.x, x, y), mx_gradient_float(hash.y, x, y), mx_gradient_float(hash.z, x, y));
}
vec3 mx_gradient_vec3(uvec3 hash, float x, float y, float z)
{
return vec3(mx_gradient_float(hash.x, x, y, z), mx_gradient_float(hash.y, x, y, z), mx_gradient_float(hash.z, x, y, z));
}
// Scaling factors to normalize the result of gradients above.
// These factors were experimentally calculated to be:
// 2D: 0.6616
// 3D: 0.9820
float mx_gradient_scale2d(float v) { return 0.6616 * v; }
float mx_gradient_scale3d(float v) { return 0.9820 * v; }
vec3 mx_gradient_scale2d(vec3 v) { return 0.6616 * v; }
vec3 mx_gradient_scale3d(vec3 v) { return 0.9820 * v; }
/// Bitwise circular rotation left by k bits (for 32 bit unsigned integers)
uint mx_rotl32(uint x, int k)
{
return (x<>(32-k));
}
void mx_bjmix(inout uint a, inout uint b, inout uint c)
{
a -= c; a ^= mx_rotl32(c, 4); c += b;
b -= a; b ^= mx_rotl32(a, 6); a += c;
c -= b; c ^= mx_rotl32(b, 8); b += a;
a -= c; a ^= mx_rotl32(c,16); c += b;
b -= a; b ^= mx_rotl32(a,19); a += c;
c -= b; c ^= mx_rotl32(b, 4); b += a;
}
// Mix up and combine the bits of a, b, and c (doesn't change them, but
// returns a hash of those three original values).
uint mx_bjfinal(uint a, uint b, uint c)
{
c ^= b; c -= mx_rotl32(b,14);
a ^= c; a -= mx_rotl32(c,11);
b ^= a; b -= mx_rotl32(a,25);
c ^= b; c -= mx_rotl32(b,16);
a ^= c; a -= mx_rotl32(c,4);
b ^= a; b -= mx_rotl32(a,14);
c ^= b; c -= mx_rotl32(b,24);
return c;
}
// Convert a 32 bit integer into a floating point number in [0,1]
float mx_bits_to_01(uint bits)
{
return float(bits) / float(uint(0xffffffff));
}
float mx_fade(float t)
{
return t * t * t * (t * (t * 6.0 - 15.0) + 10.0);
}
uint mx_hash_int(int x)
{
uint len = 1u;
uint seed = uint(0xdeadbeef) + (len << 2u) + 13u;
return mx_bjfinal(seed+uint(x), seed, seed);
}
uint mx_hash_int(int x, int y)
{
uint len = 2u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z)
{
uint len = 3u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx)
{
uint len = 4u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
return mx_bjfinal(a, b, c);
}
uint mx_hash_int(int x, int y, int z, int xx, int yy)
{
uint len = 5u;
uint a, b, c;
a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u;
a += uint(x);
b += uint(y);
c += uint(z);
mx_bjmix(a, b, c);
a += uint(xx);
b += uint(yy);
return mx_bjfinal(a, b, c);
}
uvec3 mx_hash_vec3(int x, int y)
{
uint h = mx_hash_int(x, y);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
uvec3 mx_hash_vec3(int x, int y, int z)
{
uint h = mx_hash_int(x, y, z);
// we only need the low-order bits to be random, so split out
// the 32 bit result into 3 parts for each channel
uvec3 result;
result.x = (h ) & 0xFFu;
result.y = (h >> 8 ) & 0xFFu;
result.z = (h >> 16) & 0xFFu;
return result;
}
float mx_perlin_noise_float(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
float result = mx_bilerp(
mx_gradient_float(mx_hash_int(X , Y ), fx , fy ),
mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ),
mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
float mx_perlin_noise_float(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
float result = mx_trilerp(
mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ),
mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
vec3 mx_perlin_noise_vec3(vec2 p)
{
int X, Y;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float u = mx_fade(fx);
float v = mx_fade(fy);
vec3 result = mx_bilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0),
u, v);
return mx_gradient_scale2d(result);
}
vec3 mx_perlin_noise_vec3(vec3 p)
{
int X, Y, Z;
float fx = mx_floorfrac(p.x, X);
float fy = mx_floorfrac(p.y, Y);
float fz = mx_floorfrac(p.z, Z);
float u = mx_fade(fx);
float v = mx_fade(fy);
float w = mx_fade(fz);
vec3 result = mx_trilerp(
mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ),
mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0),
mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0),
u, v, w);
return mx_gradient_scale3d(result);
}
float mx_cell_noise_float(float p)
{
int ix = mx_floor(p);
return mx_bits_to_01(mx_hash_int(ix));
}
float mx_cell_noise_float(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return mx_bits_to_01(mx_hash_int(ix, iy));
}
float mx_cell_noise_float(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return mx_bits_to_01(mx_hash_int(ix, iy, iz));
}
float mx_cell_noise_float(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw));
}
vec3 mx_cell_noise_vec3(float p)
{
int ix = mx_floor(p);
return vec3(
mx_bits_to_01(mx_hash_int(ix, 0)),
mx_bits_to_01(mx_hash_int(ix, 1)),
mx_bits_to_01(mx_hash_int(ix, 2))
);
}
vec3 mx_cell_noise_vec3(vec2 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, 2))
);
}
vec3 mx_cell_noise_vec3(vec3 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, 2))
);
}
vec3 mx_cell_noise_vec3(vec4 p)
{
int ix = mx_floor(p.x);
int iy = mx_floor(p.y);
int iz = mx_floor(p.z);
int iw = mx_floor(p.w);
return vec3(
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)),
mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2))
);
}
float mx_fractal2d_noise_float(vec2 p, int octaves, float lacunarity, float diminish)
{
float result = 0.0;
float amplitude = 1.0;
for (int i = 0; i < octaves; ++i)
{
result += amplitude * mx_perlin_noise_float(p);
amplitude *= diminish;
p *= lacunarity;
}
return result;
}
vec3 mx_fractal2d_noise_vec3(vec2 p, int octaves, float lacunarity, float diminish)
{
vec3 result = vec3(0.0);
float amplitude = 1.0;
for (int i = 0; i < octaves; ++i)
{
result += amplitude * mx_perlin_noise_vec3(p);
amplitude *= diminish;
p *= lacunarity;
}
return result;
}
vec2 mx_fractal2d_noise_vec2(vec2 p, int octaves, float lacunarity, float diminish)
{
return vec2(mx_fractal2d_noise_float(p, octaves, lacunarity, diminish),
mx_fractal2d_noise_float(p+vec2(19, 193), octaves, lacunarity, diminish));
}
vec4 mx_fractal2d_noise_vec4(vec2 p, int octaves, float lacunarity, float diminish)
{
vec3 c = mx_fractal2d_noise_vec3(p, octaves, lacunarity, diminish);
float f = mx_fractal2d_noise_float(p+vec2(19, 193), octaves, lacunarity, diminish);
return vec4(c, f);
}
float mx_fractal3d_noise_float(vec3 p, int octaves, float lacunarity, float diminish)
{
float result = 0.0;
float amplitude = 1.0;
for (int i = 0; i < octaves; ++i)
{
result += amplitude * mx_perlin_noise_float(p);
amplitude *= diminish;
p *= lacunarity;
}
return result;
}
vec3 mx_fractal3d_noise_vec3(vec3 p, int octaves, float lacunarity, float diminish)
{
vec3 result = vec3(0.0);
float amplitude = 1.0;
for (int i = 0; i < octaves; ++i)
{
result += amplitude * mx_perlin_noise_vec3(p);
amplitude *= diminish;
p *= lacunarity;
}
return result;
}
vec2 mx_fractal3d_noise_vec2(vec3 p, int octaves, float lacunarity, float diminish)
{
return vec2(mx_fractal3d_noise_float(p, octaves, lacunarity, diminish),
mx_fractal3d_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish));
}
vec4 mx_fractal3d_noise_vec4(vec3 p, int octaves, float lacunarity, float diminish)
{
vec3 c = mx_fractal3d_noise_vec3(p, octaves, lacunarity, diminish);
float f = mx_fractal3d_noise_float(p+vec3(19, 193, 17), octaves, lacunarity, diminish);
return vec4(c, f);
}
vec2 mx_worley_cell_position(int x, int y, int xoff, int yoff, float jitter)
{
vec3 tmp = mx_cell_noise_vec3(vec2(x+xoff, y+yoff));
vec2 off = vec2(tmp.x, tmp.y);
off -= 0.5f;
off *= jitter;
off += 0.5f;
return vec2(float(x), float(y)) + off;
}
vec3 mx_worley_cell_position(int x, int y, int z, int xoff, int yoff, int zoff, float jitter)
{
vec3 off = mx_cell_noise_vec3(vec3(x+xoff, y+yoff, z+zoff));
off -= 0.5f;
off *= jitter;
off += 0.5f;
return vec3(float(x), float(y), float(z)) + off;
}
float mx_worley_distance(vec2 p, int x, int y, int xoff, int yoff, float jitter, int metric)
{
vec2 cellpos = mx_worley_cell_position(x, y, xoff, yoff, jitter);
vec2 diff = cellpos - p;
if (metric == 2)
return abs(diff.x) + abs(diff.y); // Manhattan distance
if (metric == 3)
return max(abs(diff.x), abs(diff.y)); // Chebyshev distance
// Either Euclidean or Distance^2
return dot(diff, diff);
}
float mx_worley_distance(vec3 p, int x, int y, int z, int xoff, int yoff, int zoff, float jitter, int metric)
{
vec3 cellpos = mx_worley_cell_position(x, y, z, xoff, yoff, zoff, jitter);
vec3 diff = cellpos - p;
if (metric == 2)
return abs(diff.x) + abs(diff.y) + abs(diff.z); // Manhattan distance
if (metric == 3)
return max(max(abs(diff.x), abs(diff.y)), abs(diff.z)); // Chebyshev distance
// Either Euclidean or Distance^2
return dot(diff, diff);
}
float mx_worley_noise_float(vec2 p, float jitter, int style, int metric)
{
int X, Y;
float dist;
vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
float sqdist = 1e6f; // Some big number for jitter > 1 (not all GPUs may be IEEE)
vec2 minpos = vec2(0,0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
vec2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
if(dist < sqdist)
{
sqdist = dist;
minpos = cellpos;
}
}
}
if (style == 1)
return mx_cell_noise_float(minpos + p);
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
vec2 mx_worley_noise_vec2(vec2 p, float jitter, int style, int metric)
{
int X, Y;
vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
vec2 sqdist = vec2(1e6f, 1e6f);
vec2 minpos = vec2(0,0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
vec2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
if (dist < sqdist.x)
{
sqdist.y = sqdist.x;
sqdist.x = dist;
minpos = cellpos;
}
else if (dist < sqdist.y)
{
sqdist.y = dist;
}
}
}
if (style == 1)
{
vec3 tmp = mx_cell_noise_vec3(minpos + p);
return vec2(tmp.x,tmp.y);
}
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
vec3 mx_worley_noise_vec3(vec2 p, float jitter, int style, int metric)
{
int X, Y;
vec2 localpos = vec2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
vec3 sqdist = vec3(1e6f, 1e6f, 1e6f);
vec2 minpos = vec2(0,0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
vec2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
if (dist < sqdist.x)
{
sqdist.z = sqdist.y;
sqdist.y = sqdist.x;
sqdist.x = dist;
minpos = cellpos;
}
else if (dist < sqdist.y)
{
sqdist.z = sqdist.y;
sqdist.y = dist;
}
else if (dist < sqdist.z)
{
sqdist.z = dist;
}
}
}
if (style == 1)
return mx_cell_noise_vec3(minpos + p);
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
float mx_worley_noise_float(vec3 p, float jitter, int style, int metric)
{
int X, Y, Z;
vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
float sqdist = 1e6f;
vec3 minpos = vec3(0,0,0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
for (int z = -1; z <= 1; ++z)
{
float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
vec3 cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
if(dist < sqdist)
{
sqdist = dist;
minpos = cellpos;
}
}
}
}
if (style == 1)
return mx_cell_noise_float(minpos + p);
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
vec2 mx_worley_noise_vec2(vec3 p, float jitter, int style, int metric)
{
int X, Y, Z;
vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
vec2 sqdist = vec2(1e6f, 1e6f);
vec3 minpos = vec3(0,0,0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
for (int z = -1; z <= 1; ++z)
{
float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
vec3 cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
if (dist < sqdist.x)
{
sqdist.y = sqdist.x;
sqdist.x = dist;
minpos = cellpos;
}
else if (dist < sqdist.y)
{
sqdist.y = dist;
}
}
}
}
if (style == 1)
{
vec3 tmp = mx_cell_noise_vec3(minpos + p);
return vec2(tmp.x,tmp.y);
}
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
vec3 mx_worley_noise_vec3(vec3 p, float jitter, int style, int metric)
{
int X, Y, Z;
vec3 localpos = vec3(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
vec3 sqdist = vec3(1e6f, 1e6f, 1e6f);
vec3 minpos = vec3(0,0,0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
for (int z = -1; z <= 1; ++z)
{
float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
vec3 cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
if (dist < sqdist.x)
{
sqdist.z = sqdist.y;
sqdist.y = sqdist.x;
sqdist.x = dist;
minpos = cellpos;
}
else if (dist < sqdist.y)
{
sqdist.z = sqdist.y;
sqdist.y = dist;
}
else if (dist < sqdist.z)
{
sqdist.z = dist;
}
}
}
}
if (style == 1)
return mx_cell_noise_vec3(minpos + p);
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
{
uv = uv * uv_scale + uv_offset;
return uv;
}
vec2 mx_transform_uv(vec2 uv, vec2 uv_scale, vec2 uv_offset)
{
uv = uv * uv_scale + uv_offset;
return vec2(uv.x, 1.0 - uv.y);
}
float mx_aastep(float threshold, float value)
{
float afwidth = length(vec2(dFdx(value), dFdy(value))) * 0.70710678118654757;
return smoothstep(threshold-afwidth, threshold+afwidth, value);
}
#include "mx_burn_float.glsl"
void mx_burn_color3(vec3 fg, vec3 bg, float mixval, out vec3 result)
{
float f;
mx_burn_float(fg.x, bg.x, mixval, f); result.x = f;
mx_burn_float(fg.y, bg.y, mixval, f); result.y = f;
mx_burn_float(fg.z, bg.z, mixval, f); result.z = f;
}
#include "mx_burn_float.glsl"
void mx_burn_color4(vec4 fg, vec4 bg, float mixval, out vec4 result)
{
float f;
mx_burn_float(fg.x, bg.x, mixval, f); result.x = f;
mx_burn_float(fg.y, bg.y, mixval, f); result.y = f;
mx_burn_float(fg.z, bg.z, mixval, f); result.z = f;
mx_burn_float(fg.w, bg.w, mixval, f); result.w = f;
}
void mx_burn_float(float fg, float bg, float mixval, out float result)
{
if (abs(fg) < M_FLOAT_EPS)
{
result = 0.0;
return;
}
result = mixval*(1.0 - ((1.0 - bg) / fg)) + ((1.0-mixval)*bg);
}
#include "lib/mx_noise.glsl"
void mx_cellnoise2d_float(vec2 texcoord, out float result)
{
result = mx_cell_noise_float(texcoord);
}
#include "lib/mx_noise.glsl"
void mx_cellnoise3d_float(vec3 position, out float result)
{
result = mx_cell_noise_float(position);
}
void mx_creatematrix_vector3_matrix33(vec3 in1, vec3 in2, vec3 in3, out mat3 result)
{
result = mat3(in1.x, in1.y, in1.z,
in2.x, in2.y, in2.z,
in3.x, in3.y, in3.z);
}
void mx_creatematrix_vector3_matrix44(vec3 in1, vec3 in2, vec3 in3, vec3 in4, out mat4 result)
{
result = mat4(in1.x, in1.y, in1.z, 0.0,
in2.x, in2.y, in2.z, 0.0,
in3.x, in3.y, in3.z, 0.0,
in4.x, in4.y, in4.z, 1.0);
}
void mx_creatematrix_vector4_matrix44(vec4 in1, vec4 in2, vec4 in3, vec4 in4, out mat4 result)
{
result = mat4(in1.x, in1.y, in1.z, in1.w,
in2.x, in2.y, in2.z, in2.w,
in3.x, in3.y, in3.z, in3.w,
in4.x, in4.y, in4.z, in4.w);
}
void mx_disjointover_color4(vec4 fg, vec4 bg, float mixval, out vec4 result)
{
float summedAlpha = fg.w + bg.w;
if (summedAlpha <= 1.0)
{
result.xyz = fg.xyz + bg.xyz;
}
else
{
if (abs(bg.w) < M_FLOAT_EPS)
{
result.xyz = vec3(0.0);
}
else
{
float x = (1.0 - fg.w) / bg.w;
result.xyz = fg.xyz + bg.xyz * x;
}
}
result.w = min(summedAlpha, 1.0);
result.xyz = result.xyz * mixval + (1.0 - mixval) * bg.xyz;
result.w = result.w * mixval + (1.0 - mixval) * bg.w;
}
#include "mx_dodge_float.glsl"
void mx_dodge_color3(vec3 fg, vec3 bg, float mixval, out vec3 result)
{
float f;
mx_dodge_float(fg.x, bg.x, mixval, f); result.x = f;
mx_dodge_float(fg.y, bg.y, mixval, f); result.y = f;
mx_dodge_float(fg.z, bg.z, mixval, f); result.z = f;
}
#include "mx_dodge_float.glsl"
void mx_dodge_color4(vec4 fg , vec4 bg , float mixval, out vec4 result)
{
float f;
mx_dodge_float(fg.x, bg.x, mixval, f); result.x = f;
mx_dodge_float(fg.y, bg.y, mixval, f); result.y = f;
mx_dodge_float(fg.z, bg.z, mixval, f); result.z = f;
mx_dodge_float(fg.w, bg.w, mixval, f); result.w = f;
}
void mx_dodge_float(float fg, float bg, float mixval, out float result)
{
if (abs(1.0 - fg) < M_FLOAT_EPS)
{
result = 0.0;
return;
}
result = mixval*(bg / (1.0 - fg)) + ((1.0-mixval)*bg);
}
#include "lib/mx_noise.glsl"
void mx_fractal2d_float(float amplitude, int octaves, float lacunarity, float diminish, vec2 texcoord, out float result)
{
float value = mx_fractal2d_noise_float(texcoord, octaves, lacunarity, diminish);
result = value * amplitude;
}
#include "lib/mx_noise.glsl"
void mx_fractal2d_vector2(vec2 amplitude, int octaves, float lacunarity, float diminish, vec2 texcoord, out vec2 result)
{
vec2 value = mx_fractal2d_noise_vec2(texcoord, octaves, lacunarity, diminish);
result = value * amplitude;
}
#include "lib/mx_noise.glsl"
void mx_fractal2d_vector3(vec3 amplitude, int octaves, float lacunarity, float diminish, vec2 texcoord, out vec3 result)
{
vec3 value = mx_fractal2d_noise_vec3(texcoord, octaves, lacunarity, diminish);
result = value * amplitude;
}
#include "lib/mx_noise.glsl"
void mx_fractal2d_vector4(vec4 amplitude, int octaves, float lacunarity, float diminish, vec2 texcoord, out vec4 result)
{
vec4 value = mx_fractal2d_noise_vec4(texcoord, octaves, lacunarity, diminish);
result = value * amplitude;
}
#include "lib/mx_noise.glsl"
void mx_fractal3d_float(float amplitude, int octaves, float lacunarity, float diminish, vec3 position, out float result)
{
float value = mx_fractal3d_noise_float(position, octaves, lacunarity, diminish);
result = value * amplitude;
}
#include "lib/mx_noise.glsl"
void mx_fractal3d_vector2(vec2 amplitude, int octaves, float lacunarity, float diminish, vec3 position, out vec2 result)
{
vec2 value = mx_fractal3d_noise_vec2(position, octaves, lacunarity, diminish);
result = value * amplitude;
}
#include "lib/mx_noise.glsl"
void mx_fractal3d_vector3(vec3 amplitude, int octaves, float lacunarity, float diminish, vec3 position, out vec3 result)
{
vec3 value = mx_fractal3d_noise_vec3(position, octaves, lacunarity, diminish);
result = value * amplitude;
}
#include "lib/mx_noise.glsl"
void mx_fractal3d_vector4(vec4 amplitude, int octaves, float lacunarity, float diminish, vec3 position, out vec4 result)
{
vec4 value = mx_fractal3d_noise_vec4(position, octaves, lacunarity, diminish);
result = value * amplitude;
}
void mx_heighttonormal_vector3(float height, float scale, vec2 texcoord, out vec3 result)
{
// Scale factor for parity with traditional Sobel filtering.
const float SOBEL_SCALE_FACTOR = 1.0 / 16.0;
// Compute screen-space gradients of the heightfield and texture coordinates.
vec2 dHdS = vec2(dFdx(height), dFdy(height)) * scale * SOBEL_SCALE_FACTOR;
vec2 dUdS = vec2(dFdx(texcoord.x), dFdy(texcoord.x));
vec2 dVdS = vec2(dFdx(texcoord.y), dFdy(texcoord.y));
// Construct a screen-space tangent frame.
vec3 tangent = vec3(dUdS.x, dVdS.x, dHdS.x);
vec3 bitangent = vec3(dUdS.y, dVdS.y, dHdS.y);
vec3 n = cross(tangent, bitangent);
// Handle invalid and mirrored texture coordinates.
if (dot(n, n) < M_FLOAT_EPS * M_FLOAT_EPS)
{
n = vec3(0, 0, 1);
}
else if (n.z < 0.0)
{
n *= -1.0;
}
// Normalize and encode the results.
result = normalize(n) * 0.5 + 0.5;
}
#include "lib/$fileTransformUv"
#include "lib/mx_hextile.glsl"
// Morten S. Mikkelsen, Practical Real-Time Hex-Tiling, Journal of Computer Graphics
// Techniques (JCGT), vol. 11, no. 2, 77-94, 2022
// http://jcgt.org/published/0011/03/05/
void mx_hextiledimage_color3(
$texSamplerSignature,
vec3 default_value,
vec2 tex_coord,
vec2 tiling,
float rotation,
vec2 rotation_range,
float scale,
vec2 scale_range,
float offset,
vec2 offset_range,
float falloff,
float falloff_contrast,
vec3 lumacoeffs,
out vec3 result
)
{
vec2 coord = mx_transform_uv(tex_coord, tiling, vec2(0.0));
HextileData tile_data = mx_hextile_coord(coord, rotation, rotation_range, scale, scale_range, offset, offset_range);
vec3 c1 = textureGrad($texSamplerSampler2D, tile_data.coord1, tile_data.ddx1, tile_data.ddy1).rgb;
vec3 c2 = textureGrad($texSamplerSampler2D, tile_data.coord2, tile_data.ddx2, tile_data.ddy2).rgb;
vec3 c3 = textureGrad($texSamplerSampler2D, tile_data.coord3, tile_data.ddx3, tile_data.ddy3).rgb;
// luminance as weights
vec3 cw = vec3(dot(c1, lumacoeffs), dot(c2, lumacoeffs), dot(c3, lumacoeffs));
cw = mix(vec3(1.0), cw, vec3(falloff_contrast));
// blend weights
vec3 w = cw * pow(tile_data.weights, vec3(7.0));
w /= (w.x + w.y + w.z);
// apply s-curve gain
if (falloff != 0.5)
{
w.x = mx_schlick_gain(w.x, falloff);
w.y = mx_schlick_gain(w.y, falloff);
w.z = mx_schlick_gain(w.z, falloff);
w /= (w.x + w.y + w.z);
}
// blend
result = vec3(w.x * c1 + w.y * c2 + w.z * c3);
}
void mx_hextiledimage_color4(
$texSamplerSignature,
vec4 default_value,
vec2 tex_coord,
vec2 tiling,
float rotation,
vec2 rotation_range,
float scale,
vec2 scale_range,
float offset,
vec2 offset_range,
float falloff,
float falloff_contrast,
vec3 lumacoeffs,
out vec4 result
)
{
vec2 coord = mx_transform_uv(tex_coord, tiling, vec2(0.0));
HextileData tile_data = mx_hextile_coord(coord, rotation, rotation_range, scale, scale_range, offset, offset_range);
vec4 c1 = textureGrad($texSamplerSampler2D, tile_data.coord1, tile_data.ddx1, tile_data.ddy1);
vec4 c2 = textureGrad($texSamplerSampler2D, tile_data.coord2, tile_data.ddx2, tile_data.ddy2);
vec4 c3 = textureGrad($texSamplerSampler2D, tile_data.coord3, tile_data.ddx3, tile_data.ddy3);
// luminance as weights
vec3 cw = vec3(dot(c1.rgb, lumacoeffs), dot(c2.rgb, lumacoeffs), dot(c3.rgb, lumacoeffs));
cw = mix(vec3(1.0), cw, vec3(falloff_contrast));
// blend weights
vec3 w = cw * pow(tile_data.weights, vec3(7.0));
w /= (w.x + w.y + w.z);
// alpha
float a = (c1.a + c2.a + c3.a) / 3.0;
// apply s-curve gain
if (falloff != 0.5)
{
w.x = mx_schlick_gain(w.x, falloff);
w.y = mx_schlick_gain(w.y, falloff);
w.z = mx_schlick_gain(w.z, falloff);
w /= (w.x + w.y + w.z);
a = mx_schlick_gain(a, falloff);
}
// blend
result.rgb = vec3(w.x * c1 + w.y * c2 + w.z * c3);
result.a = a;
}
#include "lib/$fileTransformUv"
#include "lib/mx_hextile.glsl"
#include "lib/mx_geometry.glsl"
// Morten S. Mikkelsen, Practical Real-Time Hex-Tiling, Journal of Computer Graphics
// Techniques (JCGT), vol. 11, no. 2, 77-94, 2022
// http://jcgt.org/published/0011/03/05/
void mx_hextilednormalmap_vector3(
$texSamplerSignature,
vec3 default_value,
vec2 tex_coord,
vec2 tiling,
float rotation,
vec2 rotation_range,
float scale,
vec2 scale_range,
float offset,
vec2 offset_range,
float falloff,
float strength,
bool flip_g,
vec3 N,
vec3 T,
vec3 B,
out vec3 result
)
{
vec2 coord = mx_transform_uv(tex_coord, tiling, vec2(0.0));
HextileData tile_data = mx_hextile_coord(coord, rotation, rotation_range, scale, scale_range, offset, offset_range);
vec3 nm1 = textureGrad($texSamplerSampler2D, tile_data.coord1, tile_data.ddx1, tile_data.ddy1).xyz;
vec3 nm2 = textureGrad($texSamplerSampler2D, tile_data.coord2, tile_data.ddx2, tile_data.ddy2).xyz;
vec3 nm3 = textureGrad($texSamplerSampler2D, tile_data.coord3, tile_data.ddx3, tile_data.ddy3).xyz;
nm1.y = flip_g ? 1.0 - nm1.y : nm1.y;
nm2.y = flip_g ? 1.0 - nm2.y : nm2.y;
nm3.y = flip_g ? 1.0 - nm3.y : nm3.y;
// normalmap to shading normal
nm1 = 2.0 * nm1 - 1.0;
nm2 = 2.0 * nm2 - 1.0;
nm3 = 2.0 * nm3 - 1.0;
mat3 tangent_rot_mat1 = mx_axis_rotation_matrix(N, -tile_data.rot_radian1);
mat3 tangent_rot_mat2 = mx_axis_rotation_matrix(N, -tile_data.rot_radian2);
mat3 tangent_rot_mat3 = mx_axis_rotation_matrix(N, -tile_data.rot_radian3);
vec3 T1 = tangent_rot_mat1 * T * strength;
vec3 T2 = tangent_rot_mat2 * T * strength;
vec3 T3 = tangent_rot_mat3 * T * strength;
vec3 B1 = tangent_rot_mat1 * B * strength;
vec3 B2 = tangent_rot_mat2 * B * strength;
vec3 B3 = tangent_rot_mat3 * B * strength;
vec3 N1 = normalize(T1 * nm1.x + B1 * nm1.y + N * nm1.z);
vec3 N2 = normalize(T2 * nm2.x + B2 * nm2.y + N * nm2.z);
vec3 N3 = normalize(T3 * nm3.x + B3 * nm3.y + N * nm3.z);
// blend weights
vec3 w = pow(tile_data.weights, vec3(7.0));
w /= (w.x + w.y + w.z);
// apply s-curve gain
if (falloff != 0.5)
{
w.x = mx_schlick_gain(w.x, falloff);
w.y = mx_schlick_gain(w.y, falloff);
w.z = mx_schlick_gain(w.z, falloff);
w /= (w.x + w.y + w.z);
}
// blend
result = mx_gradient_blend_3_normals(N, N1, w.x, N2, w.y, N3, w.z);
}
#include "lib/mx_hsv.glsl"
void mx_hsvtorgb_color3(vec3 _in, out vec3 result)
{
result = mx_hsvtorgb(_in);
}
#include "lib/mx_hsv.glsl"
void mx_hsvtorgb_color4(vec4 _in, out vec4 result)
{
result = vec4(mx_hsvtorgb(_in.rgb), 1.0);
}
#include "lib/$fileTransformUv"
void mx_image_color3($texSamplerSignature, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result)
{
vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
result = texture($texSamplerSampler2D, uv).rgb;
}
#include "lib/$fileTransformUv"
void mx_image_color4($texSamplerSignature, int layer, vec4 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec4 result)
{
vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
result = texture($texSamplerSampler2D, uv);
}
#include "lib/$fileTransformUv"
void mx_image_float($texSamplerSignature, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out float result)
{
vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
result = texture($texSamplerSampler2D, uv).r;
}
#include "lib/$fileTransformUv"
void mx_image_vector2($texSamplerSignature, int layer, vec2 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec2 result)
{
vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
result = texture($texSamplerSampler2D, uv).rg;
}
#include "lib/$fileTransformUv"
void mx_image_vector3($texSamplerSignature, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result)
{
vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
result = texture($texSamplerSampler2D, uv).rgb;
}
#include "lib/$fileTransformUv"
void mx_image_vector4($texSamplerSignature, int layer, vec4 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec4 result)
{
vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset);
result = texture($texSamplerSampler2D, uv);
}
void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result)
{
result = vec3(dot(_in, lumacoeffs));
}
void mx_luminance_color4(vec4 _in, vec3 lumacoeffs, out vec4 result)
{
result = vec4(vec3(dot(_in.rgb, lumacoeffs)), _in.a);
}
void mx_mix_surfaceshader(surfaceshader fg, surfaceshader bg, float w, out surfaceshader returnshader)
{
returnshader.color = mix(bg.color, fg.color, w);
returnshader.transparency = mix(bg.transparency, fg.transparency, w);
}
#include "lib/mx_noise.glsl"
void mx_noise2d_float(float amplitude, float pivot, vec2 texcoord, out float result)
{
float value = mx_perlin_noise_float(texcoord);
result = value * amplitude + pivot;
}
#include "lib/mx_noise.glsl"
void mx_noise2d_vector2(vec2 amplitude, float pivot, vec2 texcoord, out vec2 result)
{
vec3 value = mx_perlin_noise_vec3(texcoord);
result = value.xy * amplitude + pivot;
}
#include "lib/mx_noise.glsl"
void mx_noise2d_vector3(vec3 amplitude, float pivot, vec2 texcoord, out vec3 result)
{
vec3 value = mx_perlin_noise_vec3(texcoord);
result = value * amplitude + pivot;
}
#include "lib/mx_noise.glsl"
void mx_noise2d_vector4(vec4 amplitude, float pivot, vec2 texcoord, out vec4 result)
{
vec3 xyz = mx_perlin_noise_vec3(texcoord);
float w = mx_perlin_noise_float(texcoord + vec2(19, 73));
result = vec4(xyz, w) * amplitude + pivot;
}
#include "lib/mx_noise.glsl"
void mx_noise3d_float(float amplitude, float pivot, vec3 position, out float result)
{
float value = mx_perlin_noise_float(position);
result = value * amplitude + pivot;
}
#include "lib/mx_noise.glsl"
void mx_noise3d_vector2(vec2 amplitude, float pivot, vec3 position, out vec2 result)
{
vec3 value = mx_perlin_noise_vec3(position);
result = value.xy * amplitude + pivot;
}
#include "lib/mx_noise.glsl"
void mx_noise3d_vector3(vec3 amplitude, float pivot, vec3 position, out vec3 result)
{
vec3 value = mx_perlin_noise_vec3(position);
result = value * amplitude + pivot;
}
#include "lib/mx_noise.glsl"
void mx_noise3d_vector4(vec4 amplitude, float pivot, vec3 position, out vec4 result)
{
vec3 xyz = mx_perlin_noise_vec3(position);
float w = mx_perlin_noise_float(position + vec3(19, 73, 29));
result = vec4(xyz, w) * amplitude + pivot;
}
void mx_normalmap_vector2(vec3 value, vec2 normal_scale, vec3 N, vec3 T, vec3 B, out vec3 result)
{
value = (dot(value, value) == 0.0) ? vec3(0.0, 0.0, 1.0) : value * 2.0 - 1.0;
value = T * value.x * normal_scale.x +
B * value.y * normal_scale.y +
N * value.z;
result = normalize(value);
}
void mx_normalmap_float(vec3 value, float normal_scale, vec3 N, vec3 T, vec3 B, out vec3 result)
{
mx_normalmap_vector2(value, vec2(normal_scale), N, T, B, result);
}
void mx_premult_color4(vec4 _in, out vec4 result)
{
result = vec4(_in.rgb * _in.a, _in.a);
}
void mx_ramplr_float(float valuel, float valuer, vec2 texcoord, out float result)
{
result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
}
void mx_ramplr_vector2(vec2 valuel, vec2 valuer, vec2 texcoord, out vec2 result)
{
result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
}
void mx_ramplr_vector3(vec3 valuel, vec3 valuer, vec2 texcoord, out vec3 result)
{
result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
}
void mx_ramplr_vector4(vec4 valuel, vec4 valuer, vec2 texcoord, out vec4 result)
{
result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) );
}
void mx_ramptb_float(float valuet, float valueb, vec2 texcoord, out float result)
{
result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) );
}
void mx_ramptb_vector2(vec2 valuet, vec2 valueb, vec2 texcoord, out vec2 result)
{
result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) );
}
void mx_ramptb_vector3(vec3 valuet, vec3 valueb, vec2 texcoord, out vec3 result)
{
result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) );
}
void mx_ramptb_vector4(vec4 valuet, vec4 valueb, vec2 texcoord, out vec4 result)
{
result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) );
}
#include "lib/mx_hsv.glsl"
void mx_rgbtohsv_color3(vec3 _in, out vec3 result)
{
result = mx_rgbtohsv(_in);
}
#include "lib/mx_hsv.glsl"
void mx_rgbtohsv_color4(vec4 _in, out vec4 result)
{
result = vec4(mx_rgbtohsv(_in.rgb), 1.0);
}
void mx_rotate_vector2(vec2 _in, float amount, out vec2 result)
{
float rotationRadians = mx_radians(amount);
float sa = mx_sin(rotationRadians);
float ca = mx_cos(rotationRadians);
result = vec2(ca*_in.x + sa*_in.y, -sa*_in.x + ca*_in.y);
}
mat4 mx_rotationMatrix(vec3 axis, float angle)
{
axis = normalize(axis);
float s = mx_sin(angle);
float c = mx_cos(angle);
float oc = 1.0 - c;
return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,
oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,
oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,
0.0, 0.0, 0.0, 1.0);
}
void mx_rotate_vector3(vec3 _in, float amount, vec3 axis, out vec3 result)
{
float rotationRadians = mx_radians(amount);
mat4 m = mx_rotationMatrix(axis, rotationRadians);
result = (m * vec4(_in, 1.0)).xyz;
}
void mx_smoothstep_float(float val, float low, float high, out float result)
{
if (val >= high)
result = 1.0;
else if (val <= low)
result = 0.0;
else
result = smoothstep(low, high, val);
}
#include "mx_aastep.glsl"
void mx_splitlr_float(float valuel, float valuer, float center, vec2 texcoord, out float result)
{
result = mix(valuel, valuer, mx_aastep(center, texcoord.x));
}
#include "mx_aastep.glsl"
void mx_splitlr_vector2(vec2 valuel, vec2 valuer, float center, vec2 texcoord, out vec2 result)
{
result = mix(valuel, valuer, mx_aastep(center, texcoord.x));
}
#include "mx_aastep.glsl"
void mx_splitlr_vector3(vec3 valuel, vec3 valuer, float center, vec2 texcoord, out vec3 result)
{
result = mix(valuel, valuer, mx_aastep(center, texcoord.x));
}
#include "mx_aastep.glsl"
void mx_splitlr_vector4(vec4 valuel, vec4 valuer, float center, vec2 texcoord, out vec4 result)
{
result = mix(valuel, valuer, mx_aastep(center, texcoord.x));
}
#include "mx_aastep.glsl"
void mx_splittb_float(float valuet, float valueb, float center, vec2 texcoord, out float result)
{
result = mix(valuet, valueb, mx_aastep(center, texcoord.y));
}
#include "mx_aastep.glsl"
void mx_splittb_vector2(vec2 valuet, vec2 valueb, float center, vec2 texcoord, out vec2 result)
{
result = mix(valuet, valueb, mx_aastep(center, texcoord.y));
}
#include "mx_aastep.glsl"
void mx_splittb_vector3(vec3 valuet, vec3 valueb, float center, vec2 texcoord, out vec3 result)
{
result = mix(valuet, valueb, mx_aastep(center, texcoord.y));
}
#include "mx_aastep.glsl"
void mx_splittb_vector4(vec4 valuet, vec4 valueb, float center, vec2 texcoord, out vec4 result)
{
result = mix(valuet, valueb, mx_aastep(center, texcoord.y));
}
void mx_surface_unlit(float emission, vec3 emission_color, float transmission, vec3 transmission_color, float opacity, out surfaceshader result)
{
result.color = emission * emission_color * opacity;
result.transparency = mix(vec3(1.0), transmission * transmission_color, opacity);
}
void mx_transformmatrix_vector2M3(vec2 val, mat3 transform, out vec2 result)
{
vec3 res = transform * vec3(val, 1.0);
result = res.xy;
}
void mx_transformmatrix_vector3M4(vec3 val, mat4 transform, out vec3 result)
{
vec4 res = transform * vec4(val, 1.0);
result = res.xyz;
}
void mx_unpremult_color4(vec4 _in, out vec4 result)
{
result = vec4(_in.rgb / _in.a, _in.a);
}
#include "lib/mx_noise.glsl"
void mx_worleynoise2d_float(vec2 texcoord, float jitter, int style, out float result)
{
result = mx_worley_noise_float(texcoord, jitter, style, 0);
}
#include "lib/mx_noise.glsl"
void mx_worleynoise2d_vector2(vec2 texcoord, float jitter, int style, out vec2 result)
{
result = mx_worley_noise_vec2(texcoord, jitter, style, 0);
}
#include "lib/mx_noise.glsl"
void mx_worleynoise2d_vector3(vec2 texcoord, float jitter, int style, out vec3 result)
{
result = mx_worley_noise_vec3(texcoord, jitter, style, 0);
}
#include "lib/mx_noise.glsl"
void mx_worleynoise3d_float(vec3 position, float jitter, int style, out float result)
{
result = mx_worley_noise_float(position, jitter, style, 0);
}
#include "lib/mx_noise.glsl"
void mx_worleynoise3d_vector2(vec3 position, float jitter, int style, out vec2 result)
{
result = mx_worley_noise_vec2(position, jitter, style, 0);
}
#include "lib/mx_noise.glsl"
void mx_worleynoise3d_vector3(vec3 position, float jitter, int style, out vec3 result)
{
result = mx_worley_noise_vec3(position, jitter, style, 0);
}
#define M_FLOAT_EPS 1e-8
#define mx_sin metal::sin
#define mx_cos metal::cos
#define mx_tan metal::tan
#define mx_asin metal::asin
#define mx_acos metal::acos
float mx_square(float x)
{
return x*x;
}
vec2 mx_square(vec2 x)
{
return x*x;
}
vec3 mx_square(vec3 x)
{
return x*x;
}
float mx_inversesqrt(float x)
{
return metal::rsqrt(x);
}
template
T1 mx_mod(T1 x, T2 y)
{
return x - y * floor(x/y);
}
float3x3 mx_inverse(float3x3 m)
{
float n11 = m[0][0], n12 = m[1][0], n13 = m[2][0];
float n21 = m[0][1], n22 = m[1][1], n23 = m[2][1];
float n31 = m[0][2], n32 = m[1][2], n33 = m[2][2];
float det = metal::determinant(m);
float idet = 1.0f / det;
float3x3 ret;
ret[0][0] = idet * (n22 * n33 - n32 * n23);
ret[1][0] = idet * (n32 * n13 - n12 * n33);
ret[2][0] = idet * (n12 * n23 - n22 * n13);
ret[0][1] = idet * (n31 * n23 - n21 * n33);
ret[1][1] = idet * (n11 * n33 - n31 * n13);
ret[2][1] = idet * (n21 * n13 - n11 * n23);
ret[0][2] = idet * (n21 * n32 - n31 * n22);
ret[1][2] = idet * (n31 * n12 - n11 * n32);
ret[2][2] = idet * (n11 * n22 - n21 * n12);
return ret;
}
float4x4 mx_inverse(float4x4 m)
{
float n11 = m[0][0], n12 = m[1][0], n13 = m[2][0], n14 = m[3][0];
float n21 = m[0][1], n22 = m[1][1], n23 = m[2][1], n24 = m[3][1];
float n31 = m[0][2], n32 = m[1][2], n33 = m[2][2], n34 = m[3][2];
float n41 = m[0][3], n42 = m[1][3], n43 = m[2][3], n44 = m[3][3];
float t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44;
float t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44;
float t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44;
float t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;
float det = metal::determinant(m);
float idet = 1.0f / det;
float4x4 ret;
ret[0][0] = t11 * idet;
ret[0][1] = (n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44) * idet;
ret[0][2] = (n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44) * idet;
ret[0][3] = (n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43) * idet;
ret[1][0] = t12 * idet;
ret[1][1] = (n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44) * idet;
ret[1][2] = (n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44) * idet;
ret[1][3] = (n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43) * idet;
ret[2][0] = t13 * idet;
ret[2][1] = (n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44) * idet;
ret[2][2] = (n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44) * idet;
ret[2][3] = (n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43) * idet;
ret[3][0] = t14 * idet;
ret[3][1] = (n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34) * idet;
ret[3][2] = (n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34) * idet;
ret[3][3] = (n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33) * idet;
return ret;
}
float mx_atan(float y_over_x)
{
return metal::atan(y_over_x);
}
float mx_atan(float y, float x)
{
return metal::atan2(y, x);
}
vec2 mx_atan(vec2 y, vec2 x)
{
return metal::atan2(y, x);
}
vec3 mx_atan(vec3 y, vec3 x)
{
return metal::atan2(y, x);
}
vec4 mx_atan(vec4 y, vec4 x)
{
return metal::atan2(y, x);
}
float mx_radians(float degree)
{
return (degree * M_PI_F / 180.0f);
}
vec2 mx_radians(vec2 degree)
{
return (degree * M_PI_F / 180.0f);
}
float3x3 operator+(float3x3 a, float b)
{
return a + float3x3(b,b,b,b,b,b,b,b,b);
}
float4x4 operator+(float4x4 a, float b)
{
return a + float4x4(b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b);
}
float3x3 operator-(float3x3 a, float b)
{
return a - float3x3(b,b,b,b,b,b,b,b,b);
}
float4x4 operator-(float4x4 a, float b)
{
return a - float4x4(b,b,b,b,b,b,b,b,b,b,b,b,b,b,b,b);
}
float3x3 operator/(float3x3 a, float3x3 b)
{
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 3; ++j)
a[i][j] /= b[i][j];
return a;
}
float4x4 operator/(float4x4 a, float4x4 b)
{
for(int i = 0; i < 4; ++i)
for(int j = 0; j < 4; ++j)
a[i][j] /= b[i][j];
return a;
}
float3x3 operator/(float3x3 a, float b)
{
for(int i = 0; i < 3; ++i)
for(int j = 0; j < 3; ++j)
a[i][j] /= b;
return a;
}
float4x4 operator/(float4x4 a, float b)
{
for(int i = 0; i < 4; ++i)
for(int j = 0; j < 4; ++j)
a[i][j] /= b;
return a;
}
struct MetalTexture
{
texture2d tex;
sampler s;
// needed for Storm
int get_width() { return tex.get_width(); }
int get_height() { return tex.get_height(); }
int get_num_mip_levels() { return tex.get_num_mip_levels(); }
};
float4 texture(MetalTexture mtlTex, float2 uv)
{
return mtlTex.tex.sample(mtlTex.s, uv);
}
float4 textureLod(MetalTexture mtlTex, float2 uv, float lod)
{
return mtlTex.tex.sample(mtlTex.s, uv, level(lod));
}
float4 textureGrad(MetalTexture mtlTex, float2 uv, float2 dx, float2 dy)
{
return mtlTex.tex.sample(mtlTex.s, uv, gradient2d(dx, dy));
}
int2 textureSize(MetalTexture mtlTex, int mipLevel)
{
return int2(mtlTex.tex.get_width(), mtlTex.tex.get_height());
}
// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
#pragma once
#define COLOR4_H
// color4 is a color + alpha
struct color4
{
color rgb;
float a;
};
//
// For color4, define math operators to match color
//
color4 __operator__neg__(color4 a)
{
return color4(-a.rgb, -a.a);
}
color4 __operator__add__(color4 a, color4 b)
{
return color4(a.rgb + b.rgb, a.a + b.a);
}
color4 __operator__add__(color4 a, int b)
{
return a + color4(color(b), b);
}
color4 __operator__add__(color4 a, float b)
{
return a + color4(color(b), b);
}
color4 __operator__add__(int a, color4 b)
{
return color4(color(a), a) + b;
}
color4 __operator__add__(float a, color4 b)
{
return color4(color(a), a) + b;
}
color4 __operator__sub__(color4 a, color4 b)
{
return color4(a.rgb - b.rgb, a.a - b.a);
}
color4 __operator__sub__(color4 a, int b)
{
return a - color4(color(b), b);
}
color4 __operator__sub__(color4 a, float b)
{
return a - color4(color(b), b);
}
color4 __operator__sub__(int a, color4 b)
{
return color4(color(a), a) - b;
}
color4 __operator__sub__(float a, color4 b)
{
return color4(color(a), a) - b;
}
color4 __operator__mul__(color4 a, color4 b)
{
return color4(a.rgb * b.rgb, a.a * b.a);
}
color4 __operator__mul__(color4 a, int b)
{
return a * color4(color(b), b);
}
color4 __operator__mul__(color4 a, float b)
{
return a * color4(color(b), b);
}
color4 __operator__mul__(int a, color4 b)
{
return color4(color(a), a) * b;
}
color4 __operator__mul__(float a, color4 b)
{
return color4(color(a), a) * b;
}
color4 __operator__div__(color4 a, color4 b)
{
return color4(a.rgb / b.rgb, a.a / b.a);
}
color4 __operator__div__(color4 a, int b)
{
float b_inv = 1.0 / float(b);
return a * color4(color(b_inv), b_inv);
}
color4 __operator__div__(color4 a, float b)
{
float b_inv = 1.0 / b;
return a * color4(color(b_inv), b_inv);
}
color4 __operator_div__(int a, color4 b)
{
return color4(color(a), a) / b;
}
color4 __operator__div__(float a, color4 b)
{
return color4(color(a), a) / b;
}
int __operator__eq__(color4 a, color4 b)
{
return (a.rgb == b.rgb) && (a.a == b.a);
}
int __operator__neq__(color4 a, color4 b)
{
return (a.rgb != b.rgb) || (a.a != b.a);
}
//
// For color4, define most of the stdosl functions to match color
//
color4 abs(color4 a)
{
return color4(abs(a.rgb), abs(a.a));
}
color4 ceil(color4 a)
{
return color4(ceil(a.rgb), ceil(a.a));
}
color4 round(color4 a)
{
return color4(round(a.rgb), round(a.a));
}
color4 floor(color4 a)
{
return color4(floor(a.rgb), floor(a.a));
}
color4 sqrt(color4 a)
{
return color4(sqrt(a.rgb), sqrt(a.a));
}
color4 exp(color4 a)
{
return color4(exp(a.rgb), exp(a.a));
}
color4 log(color4 a)
{
return color4(log(a.rgb), log(a.a));
}
color4 log2(color4 a)
{
return color4(log2(a.rgb), log2(a.a));
}
color4 mix(color4 a, color4 b, float x )
{
return color4(mix(a.rgb, b.rgb, x),
mix(a.a, b.a, x));
}
color4 mix(color4 a, color4 b, color4 x )
{
return color4(mix(a.rgb, b.rgb, x.rgb),
mix(a.a, b.a, x.a));
}
color4 smoothstep(color4 edge0, color4 edge1, color4 c)
{
return color4(smoothstep(edge0.rgb, edge1.rgb, c.rgb),
smoothstep(edge0.a, edge1.a, c.a));
}
color4 smoothstep(float edge0, float edge1, color4 c)
{
return smoothstep(color4(color(edge0), edge0), color4(color(edge1), edge1), c);
}
color4 clamp(color4 c, color4 minval, color4 maxval)
{
return color4(clamp(c.rgb, minval.rgb, maxval.rgb),
clamp(c.a, minval.a, maxval.a));
}
color4 clamp(color4 c, float minval, float maxval)
{
return clamp(c, color4(color(minval), minval), color4(color(maxval), maxval));
}
color4 max(color4 a, color4 b)
{
return color4(max(a.rgb, b.rgb),
max(a.a, b.a));
}
color4 max(color4 a, float b)
{
return color4(max(a.rgb, b),
max(a.a, b));
}
color4 min(color4 a, color4 b)
{
return color4(min(a.rgb, b.rgb),
min(a.a, b.a));
}
color4 min(color4 a, float b)
{
return color4(min(a.rgb, b),
min(a.a, b));
}
color4 mod(color4 a, color4 b)
{
return color4(mod(a.rgb, b.rgb),
mod(a.a, b.a));
}
color4 mod(color4 a, float b)
{
return mod(a, color4(color(b), b));
}
color4 fmod(color4 a, color4 b)
{
return color4(fmod(a.rgb, b.rgb),
fmod(a.a, b.a));
}
color4 fmod(color4 a, int b)
{
return fmod(a, color4(color(b), b));
}
color4 fmod(color4 a, float b)
{
return fmod(a, color4(color(b), b));
}
color4 pow(color4 base, color4 power)
{
return color4(pow(base.rgb, power.rgb), pow(base.a, power.a));
}
color4 pow(color4 base, float power)
{
return pow(base, color4(color(power), power));
}
color4 sign(color4 a)
{
return color4(sign(a.rgb),
sign(a.a));
}
color4 sin(color4 a)
{
return color4(sin(a.rgb),
sin(a.a));
}
color4 cos(color4 a)
{
return color4(cos(a.rgb),
cos(a.a));
}
color4 tan(color4 a)
{
return color4(tan(a.rgb),
tan(a.a));
}
color4 asin(color4 a)
{
return color4(asin(a.rgb),
asin(a.a));
}
color4 acos(color4 a)
{
return color4(acos(a.rgb),
acos(a.a));
}
color4 atan2(color4 a, float f)
{
return color4(atan2(a.rgb, f),
atan2(a.a, f));
}
color4 atan2(color4 a, color4 b)
{
return color4(atan2(a.rgb, b.rgb),
atan2(a.a, b.a));
}
color4 transformc (string fromspace, string tospace, color4 C)
{
return color4 (transformc (fromspace, tospace, C.rgb), C.a);
}
// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
#pragma once
#define MATRIX33_H
struct matrix33
{
matrix m;
};
int isValidAs33(matrix m44)
{
return m44[0][3] == 0 &&
m44[1][3] == 0 &&
m44[2][3] == 0 &&
m44[3][0] == 0 &&
m44[3][1] == 0 &&
m44[3][2] == 0 &&
m44[3][3] == 1;
}
matrix matrix33To44 (matrix33 m33)
{
return m33.m;
}
// Convert an arbitrary m44 to m33 by removing the translation
//QUESTION: should we check if it's valid to represent the 4x4 as a 3x3?
matrix33 matrix44To33 (matrix m44)
{
matrix33 m33;
m33.m = m44;
m33.m[0][3] = 0;
m33.m[1][3] = 0;
m33.m[2][3] = 0;
m33.m[3][0] = 0;
m33.m[3][1] = 0;
m33.m[3][2] = 0;
m33.m[3][3] = 1;
return m33;
}
matrix33 __operator__neg__(matrix33 a)
{
matrix33 m33;
m33.m = -a.m;
return m33;
}
matrix33 __operator__mul__(int a, matrix33 b)
{
matrix33 m33;
m33.m = a * b.m;
return m33;
}
matrix33 __operator__mul__(float a, matrix33 b)
{
matrix33 m33;
m33.m = a * b.m;
return m33;
}
matrix33 __operator__mul__(matrix33 a, int b)
{
matrix33 m33;
m33.m = a.m * b;
return m33;
}
matrix33 __operator__mul__(matrix33 a, float b)
{
matrix33 m33;
m33.m = a.m * b;
return m33;
}
matrix33 __operator__mul__(matrix33 a, matrix33 b)
{
matrix33 m33;
m33.m = a.m * b.m;
return m33;
}
matrix33 __operator__div__(int a, matrix33 b)
{
matrix33 m33;
m33.m = a / b.m;
return m33;
}
matrix33 __operator__div__(float a, matrix33 b)
{
matrix33 m33;
m33.m = a / b.m;
return m33;
}
matrix33 __operator__div__(matrix33 a, int b)
{
matrix33 m33;
m33.m = a.m / b;
return m33;
}
matrix33 __operator__div__(matrix33 a, float b)
{
matrix33 m33;
m33.m = a.m / b;
return m33;
}
matrix33 __operator__div__(matrix33 a, matrix33 b)
{
matrix33 m33;
m33.m = a.m / b.m;
return m33;
}
int __operator__eq__(matrix33 a, matrix33 b)
{
return a.m == b.m;
}
int __operator__ne__(matrix33 a, matrix33 b)
{
return a.m != b.m;
}
float determinant (matrix33 a)
{
return determinant(a.m);
}
matrix33 transpose(matrix33 a)
{
matrix33 m33;
m33.m = transpose(a.m);
return m33;
}
point transform(matrix33 a, point b)
{
return transform(a.m, b);
}
vector transform(matrix33 a, vector b)
{
return transform(a.m, b);
}
normal transform(matrix33 a, normal b)
{
return transform(a.m, b);
}
// Open Shading Language : Copyright (c) 2009-2017 Sony Pictures Imageworks Inc., et al.
// https://github.com/imageworks/OpenShadingLanguage/blob/master/LICENSE
//
// MaterialX specification (c) 2017 Lucasfilm Ltd.
// http://www.materialx.org/
#pragma once
#include "color4.h"
#include "vector2.h"
#include "vector4.h"
#include "matrix33.h"
//
// Support functions for OSL implementations of the MaterialX nodes.
//
float mx_ternary(int expr, float v1, float v2) { if (expr) return v1; else return v2; }
int mx_ternary(int expr, int v1, int v2) { if (expr) return v1; else return v2; }
color mx_ternary(int expr, color v1, color v2) { if (expr) return v1; else return v2; }
color4 mx_ternary(int expr, color4 v1, color4 v2) { if (expr) return v1; else return v2; }
vector mx_ternary(int expr, vector v1, vector v2) { if (expr) return v1; else return v2; }
vector2 mx_ternary(int expr, vector2 v1, vector2 v2) { if (expr) return v1; else return v2; }
vector4 mx_ternary(int expr, vector4 v1, vector4 v2) { if (expr) return v1; else return v2; }
matrix mx_ternary(int expr, matrix v1, matrix v2) { if (expr) return v1; else return v2; }
matrix33 mx_ternary(int expr, matrix33 v1, matrix33 v2) { if (expr) return v1; else return v2; }
matrix33 mx_add(matrix33 a, matrix33 b)
{
return matrix33(matrix(
a.m[0][0]+b.m[0][0], a.m[0][1]+b.m[0][1], a.m[0][2]+b.m[0][2], 0.0,
a.m[1][0]+b.m[1][0], a.m[1][1]+b.m[1][1], a.m[1][2]+b.m[1][2], 0.0,
a.m[2][0]+b.m[2][0], a.m[2][1]+b.m[2][1], a.m[2][2]+b.m[2][2], 0.0,
0.0, 0.0, 0.0, 1.0));
}
matrix33 mx_add(matrix33 a, float b)
{
return matrix33(matrix(
a.m[0][0]+b, a.m[0][1]+b, a.m[0][2]+b, 0.0,
a.m[1][0]+b, a.m[1][1]+b, a.m[1][2]+b, 0.0,
a.m[2][0]+b, a.m[2][1]+b, a.m[2][2]+b, 0.0,
0.0, 0.0, 0.0, 1.0));
}
matrix mx_add(matrix a, matrix b)
{
return matrix(
a[0][0]+b[0][0], a[0][1]+b[0][1], a[0][2]+b[0][2], a[0][3]+b[0][3],
a[1][0]+b[1][0], a[1][1]+b[1][1], a[1][2]+b[1][2], a[1][3]+b[1][3],
a[2][0]+b[2][0], a[2][1]+b[2][1], a[2][2]+b[2][2], a[2][3]+b[2][3],
a[3][0]+b[3][0], a[3][1]+b[3][1], a[3][2]+b[3][2], a[3][3]+b[3][3]);
}
matrix mx_add(matrix a, float b)
{
return matrix(
a[0][0]+b, a[0][1]+b, a[0][2]+b, a[0][3]+b,
a[1][0]+b, a[1][1]+b, a[1][2]+b, a[1][3]+b,
a[2][0]+b, a[2][1]+b, a[2][2]+b, a[2][3]+b,
a[3][0]+b, a[3][1]+b, a[3][2]+b, a[3][3]+b);
}
matrix33 mx_subtract(matrix33 a, matrix33 b)
{
return matrix33(matrix(
a.m[0][0]-b.m[0][0], a.m[0][1]-b.m[0][1], a.m[0][2]-b.m[0][2], 0.0,
a.m[1][0]-b.m[1][0], a.m[1][1]-b.m[1][1], a.m[1][2]-b.m[1][2], 0.0,
a.m[2][0]-b.m[2][0], a.m[2][1]-b.m[2][1], a.m[2][2]-b.m[2][2], 0.0,
0.0, 0.0, 0.0, 1.0));
}
matrix33 mx_subtract(matrix33 a, float b)
{
return matrix33(matrix(
a.m[0][0]-b, a.m[0][1]-b, a.m[0][2]-b, 0.0,
a.m[1][0]-b, a.m[1][1]-b, a.m[1][2]-b, 0.0,
a.m[2][0]-b, a.m[2][1]-b, a.m[2][2]-b, 0.0,
0.0, 0.0, 0.0, 1.0));
}
matrix mx_subtract(matrix a, matrix b)
{
return matrix(
a[0][0]-b[0][0], a[0][1]-b[0][1], a[0][2]-b[0][2], a[0][3]-b[0][3],
a[1][0]-b[1][0], a[1][1]-b[1][1], a[1][2]-b[1][2], a[1][3]-b[1][3],
a[2][0]-b[2][0], a[2][1]-b[2][1], a[2][2]-b[2][2], a[2][3]-b[2][3],
a[3][0]-b[3][0], a[3][1]-b[3][1], a[3][2]-b[3][2], a[3][3]-b[3][3]);
}
matrix mx_subtract(matrix a, float b)
{
return matrix(
a[0][0]-b, a[0][1]-b, a[0][2]-b, a[0][3]-b,
a[1][0]-b, a[1][1]-b, a[1][2]-b, a[1][3]-b,
a[2][0]-b, a[2][1]-b, a[2][2]-b, a[2][3]-b,
a[3][0]-b, a[3][1]-b, a[3][2]-b, a[3][3]-b);
}
float mx_extract(color in, int index)
{
return in[index];
}
float mx_extract(color4 in, int index)
{
if (index == 0) return in.rgb.r;
else if (index == 1) return in.rgb.g;
else if (index == 2) return in.rgb.b;
else return in.a;
}
float mx_extract(vector2 in, int index)
{
if (index == 0) return in.x;
else return in.y;
}
float mx_extract(vector in, int index)
{
return in[index];
}
float mx_extract(vector4 in, int index)
{
if (index == 0) return in.x;
else if (index == 1) return in.y;
else if (index == 2) return in.z;
else return in.w;
}
float mx_remap(float in, float inLow, float inHigh, float outLow, float outHigh, int doClamp)
{
float x = (in - inLow)/(inHigh-inLow);
if (doClamp == 1) {
x = clamp(x, 0, 1);
}
return outLow + (outHigh - outLow) * x;
}
color mx_remap(color in, color inLow, color inHigh, color outLow, color outHigh, int doClamp)
{
color x = (in - inLow) / (inHigh - inLow);
if (doClamp == 1) {
x = clamp(x, 0, 1);
}
return outLow + (outHigh - outLow) * x;
}
color mx_remap(color in, float inLow, float inHigh, float outLow, float outHigh, int doClamp)
{
color x = (in - inLow) / (inHigh - inLow);
if (doClamp == 1) {
x = clamp(x, 0, 1);
}
return outLow + (outHigh - outLow) * x;
}
color4 mx_remap(color4 c, color4 inLow, color4 inHigh, color4 outLow, color4 outHigh, int doClamp)
{
return color4(mx_remap(c.rgb, inLow.rgb, inHigh.rgb, outLow.rgb, outHigh.rgb, doClamp),
mx_remap(c.a, inLow.a, inHigh.a, outLow.a, outHigh.a, doClamp));
}
color4 mx_remap(color4 c, float inLow, float inHigh, float outLow, float outHigh, int doClamp)
{
color4 c4_inLow = color4(color(inLow), inLow);
color4 c4_inHigh = color4(color(inHigh), inHigh);
color4 c4_outLow = color4(color(outLow), outLow);
color4 c4_outHigh = color4(color(outHigh), outHigh);
return mx_remap(c, c4_inLow, c4_inHigh, c4_outLow, c4_outHigh, doClamp);
}
vector2 mx_remap(vector2 in, vector2 inLow, vector2 inHigh, vector2 outLow, vector2 outHigh, int doClamp)
{
return vector2(mx_remap(in.x, inLow.x, inHigh.x, outLow.x, outHigh.x, doClamp),
mx_remap(in.y, inLow.y, inHigh.y, outLow.y, outHigh.y, doClamp));
}
vector2 mx_remap(vector2 in, float inLow, float inHigh, float outLow, float outHigh, int doClamp)
{
return vector2(mx_remap(in.x, inLow, inHigh, outLow, outHigh, doClamp),
mx_remap(in.y, inLow, inHigh, outLow, outHigh, doClamp));
}
vector4 mx_remap(vector4 in, vector4 inLow, vector4 inHigh, vector4 outLow, vector4 outHigh, int doClamp)
{
return vector4(mx_remap(in.x, inLow.x, inHigh.x, outLow.x, outHigh.x, doClamp),
mx_remap(in.y, inLow.y, inHigh.y, outLow.y, outHigh.y, doClamp),
mx_remap(in.z, inLow.z, inHigh.z, outLow.z, outHigh.z, doClamp),
mx_remap(in.w, inLow.w, inHigh.w, outLow.w, outHigh.w, doClamp));
}
vector4 mx_remap(vector4 in, float inLow, float inHigh, float outLow, float outHigh, int doClamp)
{
return vector4(mx_remap(in.x, inLow, inHigh, outLow, outHigh, doClamp),
mx_remap(in.y, inLow, inHigh, outLow, outHigh, doClamp),
mx_remap(in.z, inLow, inHigh, outLow, outHigh, doClamp),
mx_remap(in.w, inLow, inHigh, outLow, outHigh, doClamp));
}
float mx_contrast(float in, float amount, float pivot)
{
float out = in - pivot;
out *= amount;
out += pivot;
return out;
}
color mx_contrast(color in, color amount, color pivot)
{
color out = in - pivot;
out *= amount;
out += pivot;
return out;
}
color mx_contrast(color in, float amount, float pivot)
{
color out = in - pivot;
out *= amount;
out += pivot;
return out;
}
color4 mx_contrast(color4 c, color4 amount, color4 pivot)
{
return color4(mx_contrast(c.rgb, amount.rgb, pivot.rgb),
mx_contrast(c.a, amount.a, pivot.a));
}
color4 mx_contrast(color4 c, float amount, float pivot)
{
return mx_contrast(c, color4(color(amount), amount), color4(color(pivot), pivot));
}
vector2 mx_contrast(vector2 in, vector2 amount, vector2 pivot)
{
return vector2 (mx_contrast(in.x, amount.x, pivot.x),
mx_contrast(in.y, amount.y, pivot.y));
}
vector2 mx_contrast(vector2 in, float amount, float pivot)
{
return mx_contrast(in, vector2(amount, amount), vector2(pivot, pivot));
}
vector4 mx_contrast(vector4 in, vector4 amount, vector4 pivot)
{
return vector4(mx_contrast(in.x, amount.x, pivot.x),
mx_contrast(in.y, amount.y, pivot.y),
mx_contrast(in.z, amount.z, pivot.z),
mx_contrast(in.w, amount.w, pivot.w));
}
vector4 mx_contrast(vector4 in, float amount, float pivot)
{
return vector4(mx_contrast(in.x, amount, pivot),
mx_contrast(in.y, amount, pivot),
mx_contrast(in.z, amount, pivot),
mx_contrast(in.w, amount, pivot));
}
vector2 mx_noise(string noisetype, float x, float y)
{
color cnoise = (color) noise(noisetype, x, y);
return vector2 (cnoise[0], cnoise[1]);
}
color4 mx_noise(string noisetype, float x, float y)
{
color cnoise = (color) noise(noisetype, x, y);
float fnoise = (float) noise(noisetype, x + 19, y + 73);
return color4 (cnoise, fnoise);
}
vector4 mx_noise(string noisetype, float x, float y)
{
color cnoise = (color) noise(noisetype, x, y);
float fnoise = (float) noise(noisetype, x + 19, y + 73);
return vector4 (cnoise[0], cnoise[1], cnoise[2], fnoise);
}
vector2 mx_noise(string noisetype, point position)
{
color cnoise = (color) noise(noisetype, position);
return vector2 (cnoise[0], cnoise[1]);
}
color4 mx_noise(string noisetype, point position)
{
color cnoise = (color) noise(noisetype, position);
float fnoise = (float) noise(noisetype, position+vector(19,73,29));
return color4 (cnoise, fnoise);
}
vector4 mx_noise(string noisetype, point position)
{
color cnoise = (color) noise(noisetype, position);
float fnoise = (float) noise(noisetype, position+vector(19,73,29));
return vector4 (cnoise[0], cnoise[1], cnoise[2], fnoise);
}
float mx_fbm(float x, float y, int octaves, float lacunarity, float diminish, string noisetype)
{
float out = 0;
float amp = 1.0;
float xx = x;
float yy = y;
for (int i = 0; i < octaves; i += 1) {
out += amp * noise(noisetype, xx, yy);
amp *= diminish;
xx *= lacunarity;
yy *= lacunarity;
}
return out;
}
color mx_fbm(float x, float y, int octaves, float lacunarity, float diminish, string noisetype)
{
color out = 0;
float amp = 1.0;
float xx = x;
float yy = y;
for (int i = 0; i < octaves; i += 1) {
out += amp * (color)noise(noisetype, xx, yy);
amp *= diminish;
xx *= lacunarity;
yy *= lacunarity;
}
return out;
}
vector2 mx_fbm(float x, float y, int octaves, float lacunarity, float diminish, string noisetype)
{
return vector2((float) mx_fbm(x, y, octaves, lacunarity, diminish, noisetype),
(float) mx_fbm(x+19, y+193, octaves, lacunarity, diminish, noisetype));
}
color4 mx_fbm(float x, float y, int octaves, float lacunarity, float diminish, string noisetype)
{
color c = (color) mx_fbm(x, y, octaves, lacunarity, diminish, noisetype);
float f = (float) mx_fbm(x+19, y+193, octaves, lacunarity, diminish, noisetype);
return color4 (c, f);
}
vector4 mx_fbm(float x, float y, int octaves, float lacunarity, float diminish, string noisetype)
{
color c = (color) mx_fbm(x, y, octaves, lacunarity, diminish, noisetype);
float f = (float) mx_fbm(x+19, y+193, octaves, lacunarity, diminish, noisetype);
return vector4 (c[0], c[1], c[2], f);
}
float mx_fbm(point position, int octaves, float lacunarity, float diminish, string noisetype)
{
float out = 0;
float amp = 1.0;
point p = position;
for (int i = 0; i < octaves; i += 1) {
out += amp * noise(noisetype, p);
amp *= diminish;
p *= lacunarity;
}
return out;
}
color mx_fbm(point position, int octaves, float lacunarity, float diminish, string noisetype)
{
color out = 0;
float amp = 1.0;
point p = position;
for (int i = 0; i < octaves; i += 1) {
out += amp * (color)noise(noisetype, p);
amp *= diminish;
p *= lacunarity;
}
return out;
}
vector2 mx_fbm(point position, int octaves, float lacunarity, float diminish, string noisetype)
{
return vector2((float) mx_fbm(position, octaves, lacunarity, diminish, noisetype),
(float) mx_fbm(position+point(19, 193, 17), octaves, lacunarity, diminish, noisetype));
}
color4 mx_fbm(point position, int octaves, float lacunarity, float diminish, string noisetype)
{
color c = (color) mx_fbm(position, octaves, lacunarity, diminish, noisetype);
float f = (float) mx_fbm(position+point(19, 193, 17), octaves, lacunarity, diminish, noisetype);
return color4 (c, f);
}
vector4 mx_fbm(point position, int octaves, float lacunarity, float diminish, string noisetype)
{
color c = (color) mx_fbm(position, octaves, lacunarity, diminish, noisetype);
float f = (float) mx_fbm(position+point(19, 193, 17), octaves, lacunarity, diminish, noisetype);
return vector4 (c[0], c[1], c[2], f);
}
vector2 mx_worley_cell_position(int x, int y, int xoff, int yoff, float jitter)
{
vector tmp = cellnoise(x+xoff, y+yoff);
vector2 off = vector2(tmp.x, tmp.y);
off -= 0.5;
off *= jitter;
off += 0.5;
return vector2(x, y) + off;
}
vector mx_worley_cell_position(int x, int y, int z, int xoff, int yoff, int zoff, float jitter)
{
vector off = cellnoise(vector(x+xoff, y+yoff, z+zoff));
off -= 0.5;
off *= jitter;
off += 0.5;
return vector(x,y,z) + off;
}
float mx_worley_distance(vector2 p, int x, int y, int X, int Y, float jitter, int metric)
{
vector2 cellpos = mx_worley_cell_position(x,y,X,Y,jitter);
vector2 diff = cellpos - p;
if (metric == 2)
return abs(diff.x) + abs(diff.y); // Manhattan distance
if (metric == 3)
return max(abs(diff.x), abs(diff.y)); // Chebyshev distance
return diff.x*diff.x + diff.y*diff.y; // Euclidean or distance^2
}
float mx_worley_distance(vector p, int x, int y, int z, int X, int Y, int Z, float jitter, int metric)
{
vector cellpos = mx_worley_cell_position(x,y,z,X,Y,Z,jitter);
vector diff = cellpos - p;
if (metric == 2)
return abs(diff[0]) + abs(diff[1]); // Manhattan distance
if (metric == 3)
return max(abs(diff[0]), abs(diff[1])); // Chebyshev distance
return dot(diff, diff); // Eucldean or distance^2
}
void mx_sort_distance(float dist, output vector2 result)
{
if (dist < result.x)
{
result.y = result.x;
result.x = dist;
}
else if (dist < result.y)
{
result.y = dist;
}
}
void mx_sort_distance(float dist, output vector result)
{
if (dist < result[0])
{
result[2] = result[1];
result[1] = result[0];
result[0] = dist;
}
else if (dist < result[1])
{
result[2] = result[1];
result[1] = dist;
}
else if (dist < result[2])
{
result[2] = dist;
}
}
// return floor as well as the fractional remainder
float mx_floorfrac(float x, output int i)
{
i = (int)floor(x);
return x - float(i);
}
float mx_worley_noise_float(vector2 p, float jitter, int style, int metric)
{
int X, Y;
float sqdist = 1e6;
vector2 localpos = vector2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
vector2 minpos = vector2(0.0, 0.0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
vector2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
if (dist < sqdist)
{
sqdist = dist;
minpos = cellpos;
}
}
}
if (style == 1)
{
vector2 tmpP = minpos + p;
return cellnoise(tmpP.x, tmpP.y);
}
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
vector2 mx_worley_noise_vector2(vector2 p, float jitter, int style, int metric)
{
int X, Y;
vector2 sqdist = vector2(1e6, 1e6);
vector2 localpos = vector2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
vector2 minpos = vector2(0.0, 0.0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
vector2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
if (dist < sqdist.x)
{
sqdist.y = sqdist.x;
sqdist.x = dist;
minpos = cellpos;
}
else if (dist < sqdist.y)
{
sqdist.y = dist;
}
}
}
if (style == 1)
{
vector2 tmpP = minpos + p;
vector tmp = cellnoise(tmpP.x, tmpP.y);
return vector2(tmp.x, tmp.y);
}
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
vector mx_worley_noise_vector3(vector2 p, float jitter, int style, int metric)
{
int X, Y;
vector sqdist = vector(1e6, 1e6, 1e6);
vector2 localpos = vector2(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y));
vector2 minpos = vector2(0.0, 0.0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
float dist = mx_worley_distance(localpos, x, y, X, Y, jitter, metric);
vector2 cellpos = mx_worley_cell_position(x, y, X, Y, jitter) - localpos;
if (dist < sqdist.x)
{
sqdist.z = sqdist.y;
sqdist.y = sqdist.x;
sqdist.x = dist;
minpos = cellpos;
}
else if (dist < sqdist.y)
{
sqdist.z = sqdist.y;
sqdist.y = dist;
}
else if (dist < sqdist.z)
{
sqdist.z = dist;
}
}
}
if (style == 1)
{
vector2 tmpP = minpos + p;
return cellnoise(tmpP.x, tmpP.y);
}
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
float mx_worley_noise_float(vector p, float jitter, int style, int metric)
{
int X, Y, Z;
vector seed = p;
float sqdist = 1e6;
vector localpos = vector(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
vector minpos = vector(0.0, 0.0, 0.0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
for (int z = -1; z <= 1; ++z)
{
float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
vector cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
if(dist < sqdist)
{
sqdist = dist;
minpos = cellpos;
}
}
}
}
if (style == 1)
return cellnoise(minpos + p);
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
vector2 mx_worley_noise_vector2(vector p, float jitter, int style, int metric)
{
int X, Y, Z;
vector2 sqdist = vector2(1e6, 1e6);
vector localpos = vector(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
vector minpos = vector(0.0, 0.0, 0.0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
for (int z = -1; z <= 1; ++z)
{
float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
vector cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
if (dist < sqdist.x)
{
sqdist.y = sqdist.x;
sqdist.x = dist;
minpos = cellpos;
}
else if (dist < sqdist.y)
{
sqdist.y = dist;
}
}
}
}
if (style == 1)
{
vector tmp = cellnoise(minpos + p);
return vector2(tmp.x,tmp.y);
}
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
vector mx_worley_noise_vector3(vector p, float jitter, int style, int metric)
{
int X, Y, Z;
vector sqdist = 1e6;
vector localpos = vector(mx_floorfrac(p.x, X), mx_floorfrac(p.y, Y), mx_floorfrac(p.z, Z));
vector minpos = vector(0.0, 0.0, 0.0);
for (int x = -1; x <= 1; ++x)
{
for (int y = -1; y <= 1; ++y)
{
for (int z = -1; z <= 1; ++z)
{
float dist = mx_worley_distance(localpos, x, y, z, X, Y, Z, jitter, metric);
vector cellpos = mx_worley_cell_position(x, y, z, X, Y, Z, jitter) - localpos;
if (dist < sqdist.x)
{
sqdist.z = sqdist.y;
sqdist.y = sqdist.x;
sqdist.x = dist;
minpos = cellpos;
}
else if (dist < sqdist.y)
{
sqdist.z = sqdist.y;
sqdist.y = dist;
}
else if (dist < sqdist.z)
{
sqdist.z = dist;
}
}
}
}
if (style == 1)
return cellnoise(minpos + p);
else
{
if (metric == 0)
sqdist = sqrt(sqdist);
return sqdist;
}
}
// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
#pragma once
#define VECTOR2_H
// vector2 is a 2D vector
struct vector2
{
float x;
float y;
};
//
// For vector2, define math operators to match vector
//
vector2 __operator__neg__(vector2 a)
{
return vector2(-a.x, -a.y);
}
vector2 __operator__add__(vector2 a, vector2 b)
{
return vector2(a.x + b.x, a.y + b.y);
}
vector2 __operator__add__(vector2 a, int b)
{
return a + vector2(b, b);
}
vector2 __operator__add__(vector2 a, float b)
{
return a + vector2(b, b);
}
vector2 __operator__add__(int a, vector2 b)
{
return vector2(a, a) + b;
}
vector2 __operator__add__(float a, vector2 b)
{
return vector2(a, a) + b;
}
vector2 __operator__sub__(vector2 a, vector2 b)
{
return vector2(a.x - b.x, a.y - b.y);
}
vector2 __operator__sub__(vector2 a, int b)
{
return a - vector2(b, b);
}
vector2 __operator__sub__(vector2 a, float b)
{
return a - vector2(b, b);
}
vector2 __operator__sub__(int a, vector2 b)
{
return vector2(a, a) - b;
}
vector2 __operator__sub__(float a, vector2 b)
{
return vector2(a, a) - b;
}
vector2 __operator__mul__(vector2 a, vector2 b)
{
return vector2(a.x * b.x, a.y * b.y);
}
vector2 __operator__mul__(vector2 a, int b)
{
return a * vector2(b, b);
}
vector2 __operator__mul__(vector2 a, float b)
{
return a * vector2(b, b);
}
vector2 __operator__mul__(int a, vector2 b)
{
return b * vector2(a, a);
}
vector2 __operator__mul__(float a, vector2 b)
{
return b * vector2(a, a);
}
vector2 __operator__div__(vector2 a, vector2 b)
{
return vector2(a.x / b.x, a.y / b.y);
}
vector2 __operator__div__(vector2 a, int b)
{
float b_inv = 1.0 / float(b);
return a * vector2(b_inv, b_inv);
}
vector2 __operator__div__(vector2 a, float b)
{
float b_inv = 1.0 / b;
return a * vector2(b_inv, b_inv);
}
vector2 __operator__div__(int a, vector2 b)
{
return vector2(a, a) / b;
}
vector2 __operator__div__(float a, vector2 b)
{
return vector2(a, a) / b;
}
int __operator__eq__(vector2 a, vector2 b)
{
return (a.x == b.x) && (a.y == b.y);
}
int __operator__neq__(vector2 a, vector2 b)
{
return (a.x != b.x) || (a.y != b.y);
}
//
// For vector2, define most of the stdosl functions to match vector
//
vector2 abs(vector2 a)
{
return vector2 (abs(a.x), abs(a.y));
}
vector2 ceil(vector2 a)
{
return vector2 (ceil(a.x), ceil(a.y));
}
vector2 round(vector2 a)
{
return vector2 (round(a.x), round(a.y));
}
vector2 floor(vector2 a)
{
return vector2 (floor(a.x), floor(a.y));
}
vector2 sqrt(vector2 a)
{
return vector2 (sqrt(a.x), sqrt(a.y));
}
vector2 exp(vector2 a)
{
return vector2 (exp(a.x), exp(a.y));
}
vector2 log(vector2 a)
{
return vector2 (log(a.x), log(a.y));
}
vector2 log2(vector2 a)
{
return vector2 (log2(a.x), log2(a.y));
}
vector2 mix(vector2 a, vector2 b, float x )
{
return vector2 (mix(a.x, b.x, x), mix(a.y, b.y, x));
}
vector2 mix(vector2 a, vector2 b, vector2 x )
{
return vector2 (mix(a.x, b.x, x.x), mix(a.y, b.y, x.y));
}
float dot(vector2 a, vector2 b)
{
return (a.x * b.x + a.y * b.y);
}
float length (vector2 a)
{
return hypot (a.x, a.y);
}
vector2 smoothstep(vector2 low, vector2 high, vector2 in)
{
return vector2 (smoothstep(low.x, high.x, in.x),
smoothstep(low.y, high.y, in.y));
}
vector2 smoothstep(float low, float high, vector2 in)
{
return vector2 (smoothstep(low, high, in.x),
smoothstep(low, high, in.y));
}
vector2 clamp(vector2 in, vector2 low, vector2 high)
{
return vector2 (clamp(in.x, low.x, high.x),
clamp(in.y, low.y, high.y));
}
vector2 clamp(vector2 in, float low, float high)
{
return clamp(in, vector2(low, low), vector2(high, high));
}
vector2 max(vector2 a, vector2 b)
{
return vector2 (max(a.x, b.x),
max(a.y, b.y));
}
vector2 min(vector2 a, vector2 b)
{
return vector2 (min(a.x, b.x),
min(a.y, b.y));
}
vector2 min(vector2 a, float b)
{
return min(a, vector2(b, b));
}
vector2 max(vector2 a, float b)
{
return max(a, vector2(b, b));
}
vector2 normalize(vector2 a)
{
return a / length(a);
}
vector2 mod(vector2 a, vector2 b)
{
return vector2(mod(a.x, b.x),
mod(a.y, b.y));
}
vector2 mod(vector2 a, float b)
{
return mod(a, vector2(b, b));
}
vector2 fmod(vector2 a, vector2 b)
{
return vector2 (fmod(a.x, b.x),
fmod(a.y, b.y));
}
vector2 fmod(vector2 a, float b)
{
return fmod(a, vector2(b, b));
}
vector2 pow(vector2 in, vector2 amount)
{
return vector2(pow(in.x, amount.x), pow(in.y, amount.y));
}
vector2 pow(vector2 in, float amount)
{
return pow(in, vector2(amount, amount));
}
vector2 sign(vector2 a)
{
return vector2(sign(a.x),
sign(a.y));
}
vector2 sin(vector2 a)
{
return vector2(sin(a.x),
sin(a.y));
}
vector2 cos(vector2 a)
{
return vector2(cos(a.x),
cos(a.y));
}
vector2 tan(vector2 a)
{
return vector2(tan(a.x),
tan(a.y));
}
vector2 asin(vector2 a)
{
return vector2(asin(a.x),
asin(a.y));
}
vector2 acos(vector2 a)
{
return vector2(acos(a.x),
acos(a.y));
}
vector2 atan2(vector2 a, float f)
{
return vector2(atan2(a.x, f),
atan2(a.y, f));
}
vector2 atan2(vector2 a, vector2 b)
{
return vector2(atan2(a.x, b.x),
atan2(a.y, b.y));
}
// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
#pragma once
#define VECTOR4_H
// vector4 is a 4D vector
struct vector4
{
float x;
float y;
float z;
float w;
};
//
// For vector4, define math operators to match vector
//
vector4 __operator__neg__(vector4 a)
{
return vector4(-a.x, -a.y, -a.z, -a.w);
}
vector4 __operator__add__(vector4 a, vector4 b)
{
return vector4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w);
}
vector4 __operator__add__(vector4 a, int b)
{
return a + vector4(b, b, b, b);
}
vector4 __operator__add__(vector4 a, float b)
{
return a + vector4(b, b, b, b);
}
vector4 __operator__add__(int a, vector4 b)
{
return vector4(a, a, a, a) + b;
}
vector4 __operator__add__(float a, vector4 b)
{
return vector4(a, a, a, a) + b;
}
vector4 __operator__sub__(vector4 a, vector4 b)
{
return vector4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w);
}
vector4 __operator__sub__(vector4 a, int b)
{
return a - vector4(b, b, b, b);
}
vector4 __operator__sub__(vector4 a, float b)
{
return a - vector4(b, b, b, b);
}
vector4 __operator__sub__(int a, vector4 b)
{
return vector4(a, a, a, a) - b;
}
vector4 __operator__sub__(float a, vector4 b)
{
return vector4(a, a, a, a) - b;
}
vector4 __operator__mul__(vector4 a, vector4 b)
{
return vector4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w);
}
vector4 __operator__mul__(vector4 a, int b)
{
return a * vector4(b, b, b, b);
}
vector4 __operator__mul__(vector4 a, float b)
{
return a * vector4(b, b, b, b);
}
vector4 __operator__mul__(int a, vector4 b)
{
return vector4(a, a, a, a) * b;
}
vector4 __operator__mul__(float a, vector4 b)
{
return vector4(a, a, a, a) * b;
}
vector4 __operator__div__(vector4 a, vector4 b)
{
return vector4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w);
}
vector4 __operator__div__(vector4 a, int b)
{
float b_inv = 1.0 / float(b);
return a * vector4(b_inv, b_inv, b_inv, b_inv);
}
vector4 __operator__div__(vector4 a, float b)
{
float b_inv = 1.0 / b;
return a * vector4(b_inv, b_inv, b_inv, b_inv);
}
vector4 __operator__div__(int a, vector4 b)
{
return vector4(a, a, a, a) / b;
}
vector4 __operator__div__(float a, vector4 b)
{
return vector4(a, a, a, a) / b;
}
int __operator__eq__(vector4 a, vector4 b)
{
return (a.x == b.x) && (a.y == b.y) && (a.z == b.z) && (a.w == b.w);
}
int __operator__neq__(vector4 a, vector4 b)
{
return (a.x != b.x) || (a.y != b.y) || (a.z != b.z) || (a.w != b.w);
}
//
// For vector4, define most of the stdosl functions to match vector
//
vector4 abs(vector4 in)
{
return vector4 (abs(in.x),
abs(in.y),
abs(in.z),
abs(in.w));
}
vector4 ceil(vector4 in)
{
return vector4 (ceil(in.x),
ceil(in.y),
ceil(in.z),
ceil(in.w));
}
vector4 round(vector4 in)
{
return vector4 (round(in.x),
round(in.y),
round(in.z),
round(in.w));
}
vector4 floor(vector4 in)
{
return vector4 (floor(in.x),
floor(in.y),
floor(in.z),
floor(in.w));
}
vector4 sqrt(vector4 in)
{
return vector4 (sqrt(in.x),
sqrt(in.y),
sqrt(in.z),
sqrt(in.w));
}
vector4 exp(vector4 in)
{
return vector4 (exp(in.x),
exp(in.y),
exp(in.z),
exp(in.w));
}
vector4 log(vector4 in)
{
return vector4 (log(in.x),
log(in.y),
log(in.z),
log(in.w));
}
vector4 log2(vector4 in)
{
return vector4 (log2(in.x),
log2(in.y),
log2(in.z),
log2(in.w));
}
vector4 mix(vector4 value1, vector4 value2, float x )
{
return vector4 (mix( value1.x, value2.x, x),
mix( value1.y, value2.y, x),
mix( value1.z, value2.z, x),
mix( value1.w, value2.w, x));
}
vector4 mix(vector4 value1, vector4 value2, vector4 x )
{
return vector4 (mix( value1.x, value2.x, x.x),
mix( value1.y, value2.y, x.y),
mix( value1.z, value2.z, x.z),
mix( value1.w, value2.w, x.w));
}
vector vec4ToVec3(vector4 v)
{
return vector(v.x, v.y, v.z) / v.w;
}
float dot(vector4 a, vector4 b)
{
return ((a.x * b.x) + (a.y * b.y) + (a.z * b.z) + (a.w * b.w));
}
float length (vector4 a)
{
return sqrt (a.x*a.x + a.y*a.y + a.z*a.z + a.w*a.w);
}
vector4 smoothstep(vector4 low, vector4 high, vector4 in)
{
return vector4 (smoothstep(low.x, high.x, in.x),
smoothstep(low.y, high.y, in.y),
smoothstep(low.z, high.z, in.z),
smoothstep(low.w, high.w, in.w));
}
vector4 smoothstep(float low, float high, vector4 in)
{
return vector4 (smoothstep(low, high, in.x),
smoothstep(low, high, in.y),
smoothstep(low, high, in.z),
smoothstep(low, high, in.w));
}
vector4 clamp(vector4 in, vector4 low, vector4 high)
{
return vector4 (clamp(in.x, low.x, high.x),
clamp(in.y, low.y, high.y),
clamp(in.z, low.z, high.z),
clamp(in.w, low.w, high.w));
}
vector4 clamp(vector4 in, float low, float high)
{
return vector4 (clamp(in.x, low, high),
clamp(in.y, low, high),
clamp(in.z, low, high),
clamp(in.w, low, high));
}
vector4 max(vector4 a, vector4 b)
{
return vector4 (max(a.x, b.x),
max(a.y, b.y),
max(a.z, b.z),
max(a.w, b.w));
}
vector4 max(vector4 a, float b)
{
return max(a, vector4(b, b, b, b));
}
vector4 min(vector4 a, vector4 b)
{
return vector4 (min(a.x, b.x),
min(a.y, b.y),
min(a.z, b.z),
min(a.w, b.w));
}
vector4 min(vector4 a, float b)
{
return min(a, vector4(b, b, b, b));
}
vector4 normalize(vector4 a)
{
return a / length(a);
}
vector4 mod(vector4 a, vector4 b)
{
return vector4(mod(a.x, b.x),
mod(a.y, b.y),
mod(a.z, b.z),
mod(a.w, b.w));
}
vector4 mod(vector4 a, float b)
{
return mod(a, vector4(b, b, b, b));
}
vector4 fmod(vector4 a, vector4 b)
{
return vector4 (fmod(a.x, b.x),
fmod(a.y, b.y),
fmod(a.z, b.z),
fmod(a.w, b.w));
}
vector4 fmod(vector4 a, float b)
{
return fmod(a, vector4(b, b, b, b));
}
vector4 pow(vector4 in, vector4 amount)
{
return vector4 (pow(in.x, amount.x),
pow(in.y, amount.y),
pow(in.z, amount.z),
pow(in.w, amount.w));
}
vector4 pow(vector4 in, float amount)
{
return vector4 (pow(in.x, amount),
pow(in.y, amount),
pow(in.z, amount),
pow(in.w, amount));
}
vector4 sign(vector4 a)
{
return vector4(sign(a.x),
sign(a.y),
sign(a.z),
sign(a.w));
}
vector4 sin(vector4 a)
{
return vector4(sin(a.x),
sin(a.y),
sin(a.z),
sin(a.w));
}
vector4 cos(vector4 a)
{
return vector4(cos(a.x),
cos(a.y),
cos(a.z),
cos(a.w));
}
vector4 tan(vector4 a)
{
return vector4(tan(a.x),
tan(a.y),
tan(a.z),
tan(a.w));
}
vector4 asin(vector4 a)
{
return vector4(asin(a.x),
asin(a.y),
asin(a.z),
asin(a.w));
}
vector4 acos(vector4 a)
{
return vector4(acos(a.x),
acos(a.y),
acos(a.z),
acos(a.w));
}
vector4 atan2(vector4 a, float f)
{
return vector4(atan2(a.x, f),
atan2(a.y, f),
atan2(a.z, f),
atan2(a.w, f));
}
vector4 atan2(vector4 a, vector4 b)
{
return vector4(atan2(a.x, b.x),
atan2(a.y, b.y),
atan2(a.z, b.z),
atan2(a.w, b.w));
}
vector4 transform (matrix M, vector4 p)
{
return vector4 (M[0][0]*p.x + M[1][0]*p.y + M[2][0]*p.z + M[3][0]*p.w,
M[0][1]*p.x + M[1][1]*p.y + M[2][1]*p.z + M[3][1]*p.w,
M[0][2]*p.x + M[1][2]*p.y + M[2][2]*p.z + M[3][2]*p.w,
M[0][3]*p.x + M[1][3]*p.y + M[2][3]*p.z + M[3][3]*p.w);
}
vector4 transform (string fromspace, string tospace, vector4 p)
{
return transform (matrix(fromspace,tospace), p);
}
vector2 mx_transform_uv(vector2 texcoord)
{
return texcoord;
}
vector2 mx_transform_uv(vector2 texcoord)
{
return vector2(texcoord.x, 1.0 - texcoord.y);
}
// Adds some syntactic sugar allowing mixing vector4 and color4 as
// arguments of some binary operators used by OCIO transform code.
vector4 __operator__mul__(matrix m, vector4 v)
{
return transform(m, v);
}
vector4 __operator__mul__(color4 c, vector4 v)
{
return vector4(c.rgb.r, c.rgb.g, c.rgb.b, c.a) * v;
}
vector4 __operator__mul__(vector4 v, color4 c)
{
return c * v;
}
vector4 __operator__sub__(color4 c, vector4 v)
{
return vector4(c.rgb.r, c.rgb.g, c.rgb.b, c.a) - v;
}
vector4 __operator__add__(vector4 v, color4 c)
{
return v + vector4(c.rgb.r, c.rgb.g, c.rgb.b, c.a);
}
vector4 __operator__add__(color4 c, vector4 v)
{
return v + c;
}
vector4 pow(color4 c, vector4 v)
{
return pow(vector4(c.rgb.r, c.rgb.g, c.rgb.b, c.a), v);
}
vector4 max(vector4 v, color4 c)
{
return max(v, vector4(c.rgb.r, c.rgb.g, c.rgb.b, c.a));
}
#include "mx_burn_float.osl"
void mx_burn_color3(color fg, color bg, float mix, output color result)
{
mx_burn_float(fg[0], bg[0], mix, result[0]);
mx_burn_float(fg[1], bg[1], mix, result[1]);
mx_burn_float(fg[2], bg[2], mix, result[2]);
}
#include "mx_burn_float.osl"
void mx_burn_color4(color4 fg, color4 bg, float mix, output color4 result)
{
mx_burn_float(fg.rgb[0], bg.rgb[0], mix, result.rgb[0]);
mx_burn_float(fg.rgb[1], bg.rgb[1], mix, result.rgb[1]);
mx_burn_float(fg.rgb[2], bg.rgb[2], mix, result.rgb[2]);
mx_burn_float(fg.a, bg.a, mix, result.a);
}
void mx_burn_float(float fg, float bg, float mix, output float result)
{
if (abs(fg) < M_FLOAT_EPS)
{
result = 0.0;
return;
}
result = mix*(1.0 - ((1.0 - bg) / fg)) + ((1.0-mix)*bg);
}
void mx_cellnoise2d_float(vector2 texcoord, output float result)
{
result = cellnoise(texcoord.x, texcoord.y);
}
void mx_cellnoise3d_float(vector position, output float result)
{
result = cellnoise(position);
}
void mx_creatematrix_vector3_matrix33(vector in1, vector in2, vector in3, output matrix result)
{
result = matrix(in1.x, in1.y, in1.z, 0.0,
in2.x, in2.y, in2.z, 0.0,
in3.x, in3.y, in3.z, 0.0,
0.0, 0.0, 0.0, 1.0);
}
void mx_creatematrix_vector3_matrix44(vector in1, vector in2, vector in3, vector in4, output matrix result)
{
result = matrix(in1.x, in1.y, in1.z, 0.0,
in2.x, in2.y, in2.z, 0.0,
in3.x, in3.y, in3.z, 0.0,
in4.x, in4.y, in4.z, 1.0);
}
void mx_creatematrix_vector4_matrix44(vector4 in1, vector4 in2, vector4 in3, vector4 in4, output matrix result)
{
result = matrix(in1.x, in1.y, in1.z, in1.w,
in2.x, in2.y, in2.z, in2.w,
in3.x, in3.y, in3.z, in3.w,
in4.x, in4.y, in4.z, in4.w);
}
void mx_disjointover_color4(color4 fg, color4 bg, float mix, output color4 result)
{
float summedAlpha = fg.a + bg.a;
if (summedAlpha <= 1)
{
result.rgb = fg.rgb + bg.rgb;
}
else
{
if (abs(bg.a) < M_FLOAT_EPS)
{
result.rgb = 0.0;
}
else
{
float x = (1 - fg.a) / bg.a;
result.rgb = fg.rgb + bg.rgb * x;
}
}
result.a = min(summedAlpha, 1.0);
result.rgb = result.rgb * mix + (1.0 - mix) * bg.rgb;
result.a = result.a * mix + (1.0 - mix) * bg.a;
}
#include "mx_dodge_float.osl"
void mx_dodge_color3(color fg, color bg, float mix, output color result)
{
mx_dodge_float(fg[0], bg[0], mix, result[0]);
mx_dodge_float(fg[1], bg[1], mix, result[1]);
mx_dodge_float(fg[2], bg[2], mix, result[2]);
}
#include "mx_dodge_float.osl"
void mx_dodge_color4(color4 fg , color4 bg , float mix , output color4 result)
{
mx_dodge_float(fg.rgb[0], bg.rgb[0], mix, result.rgb[0]);
mx_dodge_float(fg.rgb[1], bg.rgb[1], mix, result.rgb[1]);
mx_dodge_float(fg.rgb[2], bg.rgb[2], mix, result.rgb[2]);
mx_dodge_float(fg.a, bg.a, mix, result.a);
}
void mx_dodge_float(float fg, float bg, float mix, output float out)
{
if (abs(1.0 - fg) < M_FLOAT_EPS)
{
out = 0.0;
return;
}
out = mix*(bg / (1.0 - fg)) + ((1.0-mix)*bg);
}
void mx_fractal2d_float(float amplitude, int octaves, float lacunarity, float diminish, vector2 texcoord, output float result)
{
float f = mx_fbm(texcoord.x, texcoord.y, octaves, lacunarity, diminish, "snoise");
result = f * amplitude;
}
void mx_fractal2d_vector2(vector2 amplitude, int octaves, float lacunarity, float diminish, vector2 texcoord, output vector2 result)
{
vector2 f = mx_fbm(texcoord.x, texcoord.y, octaves, lacunarity, diminish, "snoise");
result = f * amplitude;
}
void mx_fractal2d_vector3(vector amplitude, int octaves, float lacunarity, float diminish, vector2 texcoord, output vector result)
{
vector f = mx_fbm(texcoord.x, texcoord.y, octaves, lacunarity, diminish, "snoise");
result = f * amplitude;
}
void mx_fractal2d_vector4(vector4 amplitude, int octaves, float lacunarity, float diminish, vector2 texcoord, output vector4 result)
{
vector4 f = mx_fbm(texcoord.x, texcoord.y, octaves, lacunarity, diminish, "snoise");
result = f * amplitude;
}
void mx_fractal3d_float(float amplitude, int octaves, float lacunarity, float diminish, vector position, output float result)
{
float f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
result = f * amplitude;
}
void mx_fractal3d_vector2(vector2 amplitude, int octaves, float lacunarity, float diminish, vector position, output vector2 result)
{
vector2 f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
result = f * amplitude;
}
void mx_fractal3d_vector3(vector amplitude, int octaves, float lacunarity, float diminish, vector position, output vector result)
{
vector f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
result = f * amplitude;
}
void mx_fractal3d_vector4(vector4 amplitude, int octaves, float lacunarity, float diminish, vector position, output vector4 result)
{
vector4 f = mx_fbm(position, octaves, lacunarity, diminish, "snoise");
result = f * amplitude;
}
void mx_frame_float(output float result)
{
// Use the standard default value if the attribute is not present.
result = 1.0;
getattribute("frame", result);
}
void mx_geomcolor_color3(int index, output color result)
{
getattribute("color", result);
}
void mx_geomcolor_color4(int index, output color4 result)
{
float value[4];
getattribute("color", value);
result.rgb[0] = value[0];
result.rgb[1] = value[1];
result.rgb[2] = value[2];
result.a = value[3];
}
void mx_geomcolor_float(int index, output float result)
{
getattribute("color", result);
}
void mx_geompropvalue_boolean(string geomprop, int defaultVal, output int out)
{
if (getattribute(geomprop, out) == 0)
out = defaultVal;
}
void mx_geompropvalue_color(string geomprop, color defaultVal, output color out)
{
if (getattribute(geomprop, out) == 0)
out = defaultVal;
}
void mx_geompropvalue_color4(string geomprop, color4 defaultVal, output color4 out)
{
float value[4];
if (getattribute(geomprop, value) == 0)
{
out.rgb = defaultVal.rgb;
out.a = defaultVal.a;
}
else
{
out.rgb[0] = value[0];
out.rgb[1] = value[1];
out.rgb[2] = value[2];
out.a = value[3];
}
}
void mx_geompropvalue_float(string geomprop, float defaultVal, output float result)
{
if (getattribute(geomprop, result) == 0)
{
result = defaultVal;
}
}
void mx_geompropvalue_integer(string geomprop, int defaultVal, output int out)
{
if (getattribute(geomprop, out) == 0)
out = defaultVal;
}
void mx_geompropvalue_string(string geomprop, string defaultVal, output string out)
{
if (getattribute(geomprop, out) == 0)
out = defaultVal;
}
void mx_geompropvalue_vector2(string geomprop, vector2 defaultVal, output vector2 out)
{
float value[2];
if (getattribute(geomprop, value) == 0)
{
out = defaultVal;
}
else
{
out.x = value[0];
out.y = value[1];
}
}
void mx_geompropvalue_vector(string geomprop, vector defaultVal, output vector out)
{
if (getattribute(geomprop, out) == 0)
out = defaultVal;
}
void mx_geompropvalue_vector4(string geomprop, vector4 defaultVal, output vector4 out)
{
float value[4];
if (getattribute(geomprop, value) == 0)
{
out = defaultVal;
}
else
{
out.x = value[0];
out.y = value[1];
out.z = value[2];
out.w = value[3];
}
}
void mx_heighttonormal_vector3(float height, float scale, vector2 texcoord, output vector result)
{
// Scale factor for parity with traditional Sobel filtering.
float SOBEL_SCALE_FACTOR = 1.0 / 16.0;
// Compute screen-space gradients of the heightfield and texture coordinates.
vector2 dHdS = vector2(Dx(height), Dy(height)) * scale * SOBEL_SCALE_FACTOR;
vector2 dUdS = vector2(Dx(texcoord.x), Dy(texcoord.x));
vector2 dVdS = vector2(Dx(texcoord.y), Dy(texcoord.y));
// Construct a screen-space tangent frame.
vector tangent = vector(dUdS.x, dVdS.x, dHdS.x);
vector bitangent = vector(dUdS.y, dVdS.y, dHdS.y);
vector n = cross(tangent, bitangent);
// Handle invalid and mirrored texture coordinates.
if (dot(n, n) < M_FLOAT_EPS * M_FLOAT_EPS)
{
n = vector(0, 0, 1);
}
else if (n[2] < 0.0)
{
n *= -1.0;
}
// Normalize and encode the results.
result = normalize(n) * 0.5 + 0.5;
}
void mx_hsvtorgb_color3(vector _in, output vector result)
{
result = transformc("hsv","rgb", _in);
}
void mx_hsvtorgb_color4(color4 _in, output color4 result)
{
result = color4(transformc("hsv","rgb", _in.rgb), 1.0);
}
#include "lib/$fileTransformUv"
void mx_image_color3(textureresource file, string layer, color default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output color out)
{
if (file.filename == "" ||
(uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
(vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
{
out = default_value;
return;
}
color missingColor = default_value;
vector2 st = mx_transform_uv(texcoord);
out = texture(file.filename, st.x, st.y,
"subimage", layer, "interp", filtertype,
"missingcolor", missingColor,
"swrap", uaddressmode, "twrap", vaddressmode
#if OSL_VERSION_MAJOR >= 1 && OSL_VERSION_MINOR >= 14
, "colorspace", file.colorspace
#endif
);
}
#include "lib/$fileTransformUv"
void mx_image_color4(textureresource file, string layer, color4 default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output color4 out)
{
if (file.filename == "" ||
(uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
(vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
{
out = default_value;
return;
}
color missingColor = default_value.rgb;
float missingAlpha = default_value.a;
vector2 st = mx_transform_uv(texcoord);
float alpha;
color rgb = texture(file.filename, st.x, st.y, "alpha", alpha,
"subimage", layer, "interp", filtertype,
"missingcolor", missingColor, "missingalpha", missingAlpha,
"swrap", uaddressmode, "twrap", vaddressmode
#if OSL_VERSION_MAJOR >= 1 && OSL_VERSION_MINOR >= 14
, "colorspace", file.colorspace
#endif
);
out = color4(rgb, alpha);
}
#include "lib/$fileTransformUv"
void mx_image_float(textureresource file, string layer, float default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output float out)
{
if (file.filename == "" ||
(uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
(vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
{
out = default_value;
return;
}
color missingColor = color(default_value);
vector2 st = mx_transform_uv(texcoord);
color rgb = texture(file.filename, st.x, st.y,
"subimage", layer, "interp", filtertype,
"missingcolor", missingColor,
"swrap", uaddressmode, "twrap", vaddressmode);
out = rgb[0];
}
#include "lib/$fileTransformUv"
void mx_image_vector2(textureresource file, string layer, vector2 default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output vector2 out)
{
if (file.filename == "" ||
(uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
(vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
{
out = default_value;
return;
}
color missingColor = color(default_value.x, default_value.y, 0.0);
vector2 st = mx_transform_uv(texcoord);
color rgb = texture(file.filename, st.x, st.y,
"subimage", layer, "interp", filtertype,
"missingcolor", missingColor,
"swrap", uaddressmode, "twrap", vaddressmode);
out.x = rgb[0];
out.y = rgb[1];
}
#include "lib/$fileTransformUv"
void mx_image_vector3(textureresource file, string layer, vector default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output vector out)
{
if (file.filename == "" ||
(uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
(vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
{
out = default_value;
return;
}
color missingColor = default_value;
vector2 st = mx_transform_uv(texcoord);
out = texture(file.filename, st.x, st.y,
"subimage", layer, "interp", filtertype,
"missingcolor", missingColor,
"swrap", uaddressmode, "twrap", vaddressmode);
}
#include "lib/$fileTransformUv"
void mx_image_vector4(textureresource file, string layer, vector4 default_value, vector2 texcoord, string uaddressmode, string vaddressmode, string filtertype, string framerange, int frameoffset, string frameendaction, output vector4 out)
{
if (file.filename == "" ||
(uaddressmode == "constant" && (texcoord.x<0.0 || texcoord.x>1.0)) ||
(vaddressmode == "constant" && (texcoord.y<0.0 || texcoord.y>1.0)))
{
out = default_value;
return;
}
color missingColor = color(default_value.x, default_value.y, default_value.z);
float missingAlpha = default_value.w;
vector2 st = mx_transform_uv(texcoord);
float alpha;
color rgb = texture(file.filename, st.x, st.y, "alpha", alpha,
"subimage", layer, "interp", filtertype,
"missingcolor", missingColor, "missingalpha", missingAlpha,
"swrap", uaddressmode, "twrap", vaddressmode);
out = vector4(rgb[0], rgb[1], rgb[2], alpha);
}
void mx_luminance_color3(color in, color lumacoeffs, output color result)
{
result = dot(in, lumacoeffs);
}
void mx_luminance_color4(color4 in, color lumacoeffs, output color4 result)
{
result = color4(dot(in.rgb, lumacoeffs), in.a);
}
void mx_mix_surfaceshader(surfaceshader fg, surfaceshader bg, float w, output surfaceshader result)
{
result.bsdf = mix(bg.bsdf, fg.bsdf, w);
result.edf = mix(bg.edf, fg.edf, w);
result.opacity = mix(bg.opacity, fg.opacity, w);
}
void mx_noise2d_float(float amplitude, float pivot, vector2 texcoord, output float result)
{
float value = noise("snoise", texcoord.x, texcoord.y);
result = value * amplitude + pivot;
}
void mx_noise2d_vector2(vector2 amplitude, float pivot, vector2 texcoord, output vector2 result)
{
vector2 value = mx_noise("snoise", texcoord.x, texcoord.y);
result = value * amplitude + pivot;
}
void mx_noise2d_vector3(vector amplitude, float pivot, vector2 texcoord, output vector result)
{
vector value = noise("snoise", texcoord.x, texcoord.y);
result = value * amplitude + pivot;
}
void mx_noise2d_vector4(vector4 amplitude, float pivot, vector2 texcoord, output vector4 result)
{
vector4 value = mx_noise("snoise", texcoord.x, texcoord.y);
result = value * amplitude + pivot;
}
void mx_noise3d_float(float amplitude, float pivot, vector position, output float result)
{
float value = noise("snoise", position);
result = value * amplitude + pivot;
}
void mx_noise3d_vector2(vector2 amplitude, float pivot, vector position, output vector2 result)
{
vector2 value = mx_noise("snoise", position);
result = value * amplitude + pivot;
}
void mx_noise3d_vector3(vector amplitude, float pivot, vector position, output vector result)
{
vector value = noise("snoise", position);
result = value * amplitude + pivot;
}
void mx_noise3d_vector4(vector4 amplitude, float pivot, vector position, output vector4 result)
{
vector4 value = mx_noise("snoise", position);
result = value * amplitude + pivot;
}
void mx_normalmap_vector2(vector value, vector2 normal_scale, vector N, vector T, vector B, output vector result)
{
if (value == vector(0.0))
{
result = N;
}
else
{
// The OSL backend uses dPdu and dPdv for tangents and bitangents, but these vectors are not
// guaranteed to be orthonormal.
//
// Orthogonalize the tangent frame using Gram-Schmidt, unlike in the other backends.
//
vector v = value * 2.0 - 1.0;
vector Tn = normalize(T - dot(T, N) * N);
vector Bn = normalize(B - dot(B, N) * N - dot(B, Tn) * Tn);
result = normalize(Tn * v[0] * normal_scale.x + Bn * v[1] * normal_scale.y + N * v[2]);
}
}
void mx_normalmap_float(vector value, float normal_scale, vector N, vector T, vector B, output vector result)
{
mx_normalmap_vector2(value, vector2(normal_scale, normal_scale), N, T, B, result);
}
void mx_premult_color4(color4 in, output color4 result)
{
result = color4(in.rgb * in.a, in.a);
}
void mx_rgbtohsv_color3(vector _in, output vector result)
{
result = transformc("rgb","hsv", _in);
}
void mx_rgbtohsv_color4(color4 _in, output color4 result)
{
result = color4(transformc("rgb","hsv", _in.rgb), 1.0);
}
void mx_rotate_vector2(vector2 _in, float amount, output vector2 result)
{
float rotationRadians = radians(amount);
float sa = sin(rotationRadians);
float ca = cos(rotationRadians);
result = vector2(ca*_in.x + sa*_in.y, -sa*_in.x + ca*_in.y);
}
matrix rotationMatrix(vector axis, float angle)
{
vector nAxis = normalize(axis);
float s = sin(angle);
float c = cos(angle);
float oc = 1.0 - c;
return matrix(oc * nAxis[0] * nAxis[0] + c, oc * nAxis[0] * nAxis[1] - nAxis[2] * s, oc * nAxis[2] * nAxis[0] + nAxis[1] * s, 0.0,
oc * nAxis[0] * nAxis[1] + nAxis[2] * s, oc * nAxis[1] * nAxis[1] + c, oc * nAxis[1] * nAxis[2] - nAxis[0] * s, 0.0,
oc * nAxis[2] * nAxis[0] - nAxis[1] * s, oc * nAxis[1] * nAxis[2] + nAxis[0] * s, oc * nAxis[2] * nAxis[2] + c, 0.0,
0.0, 0.0, 0.0, 1.0);
}
void mx_rotate_vector3(vector _in, float amount, vector axis, output vector result)
{
float rotationRadians = radians(amount);
matrix m = rotationMatrix(axis, rotationRadians);
vector4 trans = transform(m, vector4(_in[0], _in[1], _in[2], 1.0));
result = vector(trans.x, trans.y, trans.z);
}
void mx_surface_unlit(float emission_weight, color emission_color, float transmission_weight, color transmission_color, float opacity, output surfaceshader result)
{
float trans = clamp(transmission_weight, 0.0, 1.0);
result.bsdf = trans * transmission_color * transparent();
result.edf = (1.0 - trans) * emission_weight * emission_color * emission();
result.opacity = clamp(opacity, 0.0, 1.0);
}
void mx_surfacematerial(surfaceshader surface, surfaceshader back, displacementshader disp, output MATERIAL result)
{
float opacity_weight = clamp(surface.opacity, 0.0, 1.0);
result = (surface.bsdf + surface.edf) * opacity_weight + transparent() * (1.0 - opacity_weight);
}
void mx_time_float(float fps, output float result)
{
// Use the standard default value if the attribute is not present.
result = 0.0;
getattribute("time", result);
}
void mx_transformmatrix_vector2M3(vector2 val, matrix m, output vector2 result)
{
point res = transform(m, point(val.x, val.y, 1.0));
result.x = res[0];
result.y = res[1];
}
void mx_unpremult_color4(color4 in, output color4 result)
{
result = color4(in.rgb / in.a, in.a);
}
void mx_worleynoise2d_float(vector2 texcoord, float jitter, int style, output float result)
{
result = mx_worley_noise_float(texcoord, jitter, style, 0);
}
void mx_worleynoise2d_vector2(vector2 texcoord, float jitter, int style, output vector2 result)
{
result = mx_worley_noise_vector2(texcoord, jitter, style, 0);
}
void mx_worleynoise2d_vector3(vector2 texcoord, float jitter, int style, output vector result)
{
result = mx_worley_noise_vector3(texcoord, jitter, style, 0);
}
void mx_worleynoise3d_float(vector position, float jitter, int style, output float result)
{
result = mx_worley_noise_float(position, jitter, style, 0);
}
void mx_worleynoise3d_vector2(vector position, float jitter, int style, output vector2 result)
{
result = mx_worley_noise_vector2(position, jitter, style, 0);
}
void mx_worleynoise3d_vector3(vector position, float jitter, int style, output vector result)
{
result = mx_worley_noise_vector3(position, jitter, style, 0);
}