# 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. 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) 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; } void mx_directional_light(LightData light, float3 position, thread lightshader& result) { result.direction = -light.direction; result.intensity = light.color * light.intensity; } void mx_spot_light(LightData light, vec3 position, out lightshader result) { result.direction = light.position - position; float distance = length(result.direction) + M_FLOAT_EPS; float attenuation = pow(distance + 1.0, light.decay_rate + M_FLOAT_EPS); result.intensity = light.color * light.intensity / attenuation; result.direction /= distance; float low = min(light.inner_angle, light.outer_angle); float high = light.inner_angle; float cosDir = dot(result.direction, -light.direction); float spotAttenuation = smoothstep(low, high, cosDir); result.intensity *= spotAttenuation; } void mx_directional_light(LightData light, vec3 position, out lightshader result) { result.direction = -light.direction; result.intensity = light.color * light.intensity; } void mx_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; } // Restrict to 7x7 kernel size for performance reasons #define MX_MAX_SAMPLE_COUNT 49 // Size of all weights for all levels (including level 1) #define MX_WEIGHT_ARRAY_SIZE 84 // // Function to compute the sample size relative to a texture coordinate // vec2 mx_compute_sample_size_uv(vec2 uv, float filterSize, float filterOffset) { vec2 derivUVx = dFdx(uv) * 0.5f; vec2 derivUVy = dFdy(uv) * 0.5f; float derivX = abs(derivUVx.x) + abs(derivUVy.x); float derivY = abs(derivUVx.y) + abs(derivUVy.y); float sampleSizeU = 2.0f * filterSize * derivX + filterOffset; if (sampleSizeU < 1.0E-05f) sampleSizeU = 1.0E-05f; float sampleSizeV = 2.0f * filterSize * derivY + filterOffset; if (sampleSizeV < 1.0E-05f) sampleSizeV = 1.0E-05f; return vec2(sampleSizeU, sampleSizeV); } // // Apply filter for float samples S, using weights W. // sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 } // float mx_convolution_float(float S[MX_MAX_SAMPLE_COUNT], constant float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount) { float result = 0.0; for (int i = 0; i < sampleCount; i++) { result += S[i]*W[i+offset]; } return result; } // // Apply filter for vec2 samples S, using weights W. // sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 } // vec2 mx_convolution_vec2(vec2 S[MX_MAX_SAMPLE_COUNT], constant float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount) { vec2 result = vec2(0.0); for (int i=0; i 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); } 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()); } #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; } 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_splittb_float(float valuet, float valueb, float center, vec2 texcoord, out float result) { result = mix(valuet, valueb, mx_aastep(center, texcoord.y)); } #include "lib/$fileTransformUv" #ifdef HW_SEPARATE_SAMPLERS void mx_image_vector3(texture2D tex_texture, sampler tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(sampler2D(tex_texture, tex_sampler), uv).rgb; } #else void mx_image_vector3(sampler2D tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(tex_sampler, uv).rgb; } #endif #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_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; } 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); } 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; } void mx_premult_color4(vec4 _in, out vec4 result) { result = vec4(_in.rgb * _in.a, _in.a); } #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; } 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); } #include "lib/mx_noise.glsl" void mx_cellnoise3d_float(vec3 position, out float result) { result = mx_cell_noise_float(position); } #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_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; } 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); } #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)); } void mx_transformmatrix_vector3M4(vec3 val, mat4 transform, out vec3 result) { vec4 res = transform * vec4(val, 1.0); result = res.xyz; } #include "lib/mx_noise.glsl" void mx_noise3d_vector3(vec3 amplitude, float pivot, vec3 position, out vec3 result) { vec3 value = mx_perlin_noise_vec3(position); result = value * amplitude + pivot; } #include "mx_aastep.glsl" void mx_splitlr_vector3(vec3 valuel, vec3 valuer, float center, vec2 texcoord, out vec3 result) { result = mix(valuel, valuer, mx_aastep(center, texcoord.x)); } void mx_ramplr_vector2(vec2 valuel, vec2 valuer, vec2 texcoord, out vec2 result) { result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) ); } #include "lib/mx_noise.glsl" void mx_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_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; } #include "lib/$fileTransformUv" #ifdef HW_SEPARATE_SAMPLERS void mx_image_float(texture2D tex_texture, sampler tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out float result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(sampler2D(tex_texture, tex_sampler), uv).r; } #else void mx_image_float(sampler2D tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out float result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(tex_sampler, uv).r; } #endif void mx_ramptb_vector3(vec3 valuet, vec3 valueb, vec2 texcoord, out vec3 result) { result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) ); } #include "lib/mx_hsv.glsl" void mx_rgbtohsv_color3(vec3 _in, out vec3 result) { result = mx_rgbtohsv(_in); } void mx_ramptb_vector2(vec2 valuet, vec2 valueb, vec2 texcoord, out vec2 result) { result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) ); } #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; } 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); } 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_ramplr_vector3(vec3 valuel, vec3 valuer, vec2 texcoord, out vec3 result) { result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) ); } #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( #ifdef HW_SEPARATE_SAMPLERS texture2D tex_texture, sampler tex_sampler, #else sampler2D tex_sampler, #endif 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); #ifdef HW_SEPARATE_SAMPLERS vec3 nm1 = textureGrad(sampler2D(tex_texture, tex_sampler), tile_data.coord1, tile_data.ddx1, tile_data.ddy1).xyz; vec3 nm2 = textureGrad(sampler2D(tex_texture, tex_sampler), tile_data.coord2, tile_data.ddx2, tile_data.ddy2).xyz; vec3 nm3 = textureGrad(sampler2D(tex_texture, tex_sampler), tile_data.coord3, tile_data.ddx3, tile_data.ddy3).xyz; #else vec3 nm1 = textureGrad(tex_sampler, tile_data.coord1, tile_data.ddx1, tile_data.ddy1).xyz; vec3 nm2 = textureGrad(tex_sampler, tile_data.coord2, tile_data.ddx2, tile_data.ddy2).xyz; vec3 nm3 = textureGrad(tex_sampler, tile_data.coord3, tile_data.ddx3, tile_data.ddy3).xyz; #endif 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); } void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result) { result = vec3(dot(_in, lumacoeffs)); } #include "lib/mx_hsv.glsl" void mx_hsvtorgb_color4(vec4 _in, out vec4 result) { result = vec4(mx_hsvtorgb(_in.rgb), 1.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/$fileTransformUv" #ifdef HW_SEPARATE_SAMPLERS void mx_image_color4(texture2D tex_texture, sampler tex_sampler, int layer, vec4 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec4 result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(sampler2D(tex_texture, tex_sampler), uv); } #else void mx_image_color4(sampler2D tex_sampler, int layer, vec4 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec4 result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(tex_sampler, uv); } #endif #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_noise3d_vector4(vec4 amplitude, float pivot, vec3 position, out vec4 result) { vec3 xyz = mx_perlin_noise_vec3(position); float w = mx_perlin_noise_float(position + vec3(19, 73, 29)); result = vec4(xyz, w) * amplitude + pivot; } #include "lib/$fileTransformUv" #ifdef HW_SEPARATE_SAMPLERS void mx_image_color3(texture2D tex_texture, sampler tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(sampler2D(tex_texture, tex_sampler), uv).rgb; } #else void mx_image_color3(sampler2D tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(tex_sampler, uv).rgb; } #endif 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_worleynoise3d_float(vec3 position, float jitter, int style, out float result) { result = mx_worley_noise_float(position, jitter, style, 0); } #include "lib/$fileTransformUv" #ifdef HW_SEPARATE_SAMPLERS void mx_image_vector2(texture2D tex_texture, sampler tex_sampler, int layer, vec2 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec2 result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(sampler2D(tex_texture, tex_sampler), uv).rg; } #else void mx_image_vector2(sampler2D tex_sampler, int layer, vec2 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec2 result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(tex_sampler, uv).rg; } #endif #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( #ifdef HW_SEPARATE_SAMPLERS texture2D tex_texture, sampler tex_sampler, #else sampler2D tex_sampler, #endif 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); #ifdef HW_SEPARATE_SAMPLERS vec3 c1 = textureGrad(sampler2D(tex_texture, tex_sampler), tile_data.coord1, tile_data.ddx1, tile_data.ddy1).rgb; vec3 c2 = textureGrad(sampler2D(tex_texture, tex_sampler), tile_data.coord2, tile_data.ddx2, tile_data.ddy2).rgb; vec3 c3 = textureGrad(sampler2D(tex_texture, tex_sampler), tile_data.coord3, tile_data.ddx3, tile_data.ddy3).rgb; #else vec3 c1 = textureGrad(tex_sampler, tile_data.coord1, tile_data.ddx1, tile_data.ddy1).rgb; vec3 c2 = textureGrad(tex_sampler, tile_data.coord2, tile_data.ddx2, tile_data.ddy2).rgb; vec3 c3 = textureGrad(tex_sampler, tile_data.coord3, tile_data.ddx3, tile_data.ddy3).rgb; #endif // 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( #ifdef HW_SEPARATE_SAMPLERS texture2D tex_texture, sampler tex_sampler, #else sampler2D tex_sampler, #endif 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); #ifdef HW_SEPARATE_SAMPLERS vec4 c1 = textureGrad(sampler2D(tex_texture, tex_sampler), tile_data.coord1, tile_data.ddx1, tile_data.ddy1); vec4 c2 = textureGrad(sampler2D(tex_texture, tex_sampler), tile_data.coord2, tile_data.ddx2, tile_data.ddy2); vec4 c3 = textureGrad(sampler2D(tex_texture, tex_sampler), tile_data.coord3, tile_data.ddx3, tile_data.ddy3); #else vec4 c1 = textureGrad(tex_sampler, tile_data.coord1, tile_data.ddx1, tile_data.ddy1); vec4 c2 = textureGrad(tex_sampler, tile_data.coord2, tile_data.ddx2, tile_data.ddy2); vec4 c3 = textureGrad(tex_sampler, tile_data.coord3, tile_data.ddx3, tile_data.ddy3); #endif // 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/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 "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_ramptb_vector4(vec4 valuet, vec4 valueb, vec2 texcoord, out vec4 result) { result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) ); } #include "mx_aastep.glsl" void mx_splittb_vector2(vec2 valuet, vec2 valueb, float center, vec2 texcoord, out vec2 result) { result = mix(valuet, valueb, mx_aastep(center, texcoord.y)); } #include "mx_aastep.glsl" void mx_splitlr_vector4(vec4 valuel, vec4 valuer, float center, vec2 texcoord, out vec4 result) { result = mix(valuel, valuer, mx_aastep(center, texcoord.x)); } void mx_ramptb_float(float valuet, float valueb, vec2 texcoord, out float result) { result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) ); } #include "lib/mx_hsv.glsl" void mx_hsvtorgb_color3(vec3 _in, out vec3 result) { result = mx_hsvtorgb(_in); } void mx_transformmatrix_vector2M3(vec2 val, mat3 transform, out vec2 result) { vec3 res = transform * vec3(val, 1.0); result = res.xy; } #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 "lib/$fileTransformUv" #ifdef HW_SEPARATE_SAMPLERS void mx_image_vector4(texture2D tex_texture, sampler tex_sampler, int layer, vec4 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec4 result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(sampler2D(tex_texture, tex_sampler), uv); } #else void mx_image_vector4(sampler2D tex_sampler, int layer, vec4 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec4 result) { vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); result = texture(tex_sampler, uv); } #endif #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_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_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; } void mx_luminance_color4(vec4 _in, vec3 lumacoeffs, out vec4 result) { result = vec4(vec3(dot(_in.rgb, lumacoeffs)), _in.a); } #include "lib/mx_hsv.glsl" void mx_rgbtohsv_color4(vec4 _in, out vec4 result) { result = vec4(mx_rgbtohsv(_in.rgb), 1.0); } void mx_unpremult_color4(vec4 _in, out vec4 result) { result = vec4(_in.rgb / _in.a, _in.a); } #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; } 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_aastep.glsl" void mx_splittb_vector4(vec4 valuet, vec4 valueb, float center, vec2 texcoord, out vec4 result) { result = mix(valuet, valueb, mx_aastep(center, texcoord.y)); } #include "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); } void mx_ramplr_float(float valuel, float valuer, vec2 texcoord, out float result) { result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) ); } #include "lib/mx_noise.glsl" void mx_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; } void mx_ramplr_vector4(vec4 valuel, vec4 valuer, vec2 texcoord, out vec4 result) { result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) ); } #include "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_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 "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; } 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/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_worleynoise3d_vector3(vec3 position, float jitter, int style, out vec3 result) { result = mx_worley_noise_vec3(position, jitter, style, 0); } 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); } #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; } 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); } 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); } // Restrict to 7x7 kernel size for performance reasons #define MX_MAX_SAMPLE_COUNT 49 // Size of all weights for all levels (including level 1) #define MX_WEIGHT_ARRAY_SIZE 84 // // Function to compute the sample size relative to a texture coordinate // vec2 mx_compute_sample_size_uv(vec2 uv, float filterSize, float filterOffset) { vec2 derivUVx = dFdx(uv) * 0.5f; vec2 derivUVy = dFdy(uv) * 0.5f; float derivX = abs(derivUVx.x) + abs(derivUVy.x); float derivY = abs(derivUVx.y) + abs(derivUVy.y); float sampleSizeU = 2.0f * filterSize * derivX + filterOffset; if (sampleSizeU < 1.0E-05f) sampleSizeU = 1.0E-05f; float sampleSizeV = 2.0f * filterSize * derivY + filterOffset; if (sampleSizeV < 1.0E-05f) sampleSizeV = 1.0E-05f; return vec2(sampleSizeU, sampleSizeV); } // // Apply filter for float samples S, using weights W. // sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 } // float mx_convolution_float(float S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount) { float result = 0.0; for (int i = 0; i < sampleCount; i++) { result += S[i]*W[i+offset]; } return result; } // // Apply filter for vec2 samples S, using weights W. // sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 } // vec2 mx_convolution_vec2(vec2 S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount) { vec2 result = vec2(0.0); for (int i=0; i>(32-k)); } void mx_bjmix(inout uint a, inout uint b, inout uint c) { a -= c; a ^= mx_rotl32(c, 4); c += b; b -= a; b ^= mx_rotl32(a, 6); a += c; c -= b; c ^= mx_rotl32(b, 8); b += a; a -= c; a ^= mx_rotl32(c,16); c += b; b -= a; b ^= mx_rotl32(a,19); a += c; c -= b; c ^= mx_rotl32(b, 4); b += a; } // Mix up and combine the bits of a, b, and c (doesn't change them, but // returns a hash of those three original values). uint mx_bjfinal(uint a, uint b, uint c) { c ^= b; c -= mx_rotl32(b,14); a ^= c; a -= mx_rotl32(c,11); b ^= a; b -= mx_rotl32(a,25); c ^= b; c -= mx_rotl32(b,16); a ^= c; a -= mx_rotl32(c,4); b ^= a; b -= mx_rotl32(a,14); c ^= b; c -= mx_rotl32(b,24); return c; } // Convert a 32 bit integer into a floating point number in [0,1] float mx_bits_to_01(uint bits) { return float(bits) / float(uint(0xffffffff)); } float mx_fade(float t) { return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); } uint mx_hash_int(int x) { uint len = 1u; uint seed = uint(0xdeadbeef) + (len << 2u) + 13u; return mx_bjfinal(seed+uint(x), seed, seed); } uint mx_hash_int(int x, int y) { uint len = 2u; uint a, b, c; a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u; a += uint(x); b += uint(y); return mx_bjfinal(a, b, c); } uint mx_hash_int(int x, int y, int z) { uint len = 3u; uint a, b, c; a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u; a += uint(x); b += uint(y); c += uint(z); return mx_bjfinal(a, b, c); } uint mx_hash_int(int x, int y, int z, int xx) { uint len = 4u; uint a, b, c; a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u; a += uint(x); b += uint(y); c += uint(z); mx_bjmix(a, b, c); a += uint(xx); return mx_bjfinal(a, b, c); } uint mx_hash_int(int x, int y, int z, int xx, int yy) { uint len = 5u; uint a, b, c; a = b = c = uint(0xdeadbeef) + (len << 2u) + 13u; a += uint(x); b += uint(y); c += uint(z); mx_bjmix(a, b, c); a += uint(xx); b += uint(yy); return mx_bjfinal(a, b, c); } uvec3 mx_hash_vec3(int x, int y) { uint h = mx_hash_int(x, y); // we only need the low-order bits to be random, so split out // the 32 bit result into 3 parts for each channel uvec3 result; result.x = (h ) & 0xFFu; result.y = (h >> 8 ) & 0xFFu; result.z = (h >> 16) & 0xFFu; return result; } uvec3 mx_hash_vec3(int x, int y, int z) { uint h = mx_hash_int(x, y, z); // we only need the low-order bits to be random, so split out // the 32 bit result into 3 parts for each channel uvec3 result; result.x = (h ) & 0xFFu; result.y = (h >> 8 ) & 0xFFu; result.z = (h >> 16) & 0xFFu; return result; } float mx_perlin_noise_float(vec2 p) { int X, Y; float fx = mx_floorfrac(p.x, X); float fy = mx_floorfrac(p.y, Y); float u = mx_fade(fx); float v = mx_fade(fy); float result = mx_bilerp( mx_gradient_float(mx_hash_int(X , Y ), fx , fy ), mx_gradient_float(mx_hash_int(X+1, Y ), fx-1.0, fy ), mx_gradient_float(mx_hash_int(X , Y+1), fx , fy-1.0), mx_gradient_float(mx_hash_int(X+1, Y+1), fx-1.0, fy-1.0), u, v); return mx_gradient_scale2d(result); } float mx_perlin_noise_float(vec3 p) { int X, Y, Z; float fx = mx_floorfrac(p.x, X); float fy = mx_floorfrac(p.y, Y); float fz = mx_floorfrac(p.z, Z); float u = mx_fade(fx); float v = mx_fade(fy); float w = mx_fade(fz); float result = mx_trilerp( mx_gradient_float(mx_hash_int(X , Y , Z ), fx , fy , fz ), mx_gradient_float(mx_hash_int(X+1, Y , Z ), fx-1.0, fy , fz ), mx_gradient_float(mx_hash_int(X , Y+1, Z ), fx , fy-1.0, fz ), mx_gradient_float(mx_hash_int(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ), mx_gradient_float(mx_hash_int(X , Y , Z+1), fx , fy , fz-1.0), mx_gradient_float(mx_hash_int(X+1, Y , Z+1), fx-1.0, fy , fz-1.0), mx_gradient_float(mx_hash_int(X , Y+1, Z+1), fx , fy-1.0, fz-1.0), mx_gradient_float(mx_hash_int(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0), u, v, w); return mx_gradient_scale3d(result); } vec3 mx_perlin_noise_vec3(vec2 p) { int X, Y; float fx = mx_floorfrac(p.x, X); float fy = mx_floorfrac(p.y, Y); float u = mx_fade(fx); float v = mx_fade(fy); vec3 result = mx_bilerp( mx_gradient_vec3(mx_hash_vec3(X , Y ), fx , fy ), mx_gradient_vec3(mx_hash_vec3(X+1, Y ), fx-1.0, fy ), mx_gradient_vec3(mx_hash_vec3(X , Y+1), fx , fy-1.0), mx_gradient_vec3(mx_hash_vec3(X+1, Y+1), fx-1.0, fy-1.0), u, v); return mx_gradient_scale2d(result); } vec3 mx_perlin_noise_vec3(vec3 p) { int X, Y, Z; float fx = mx_floorfrac(p.x, X); float fy = mx_floorfrac(p.y, Y); float fz = mx_floorfrac(p.z, Z); float u = mx_fade(fx); float v = mx_fade(fy); float w = mx_fade(fz); vec3 result = mx_trilerp( mx_gradient_vec3(mx_hash_vec3(X , Y , Z ), fx , fy , fz ), mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z ), fx-1.0, fy , fz ), mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z ), fx , fy-1.0, fz ), mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z ), fx-1.0, fy-1.0, fz ), mx_gradient_vec3(mx_hash_vec3(X , Y , Z+1), fx , fy , fz-1.0), mx_gradient_vec3(mx_hash_vec3(X+1, Y , Z+1), fx-1.0, fy , fz-1.0), mx_gradient_vec3(mx_hash_vec3(X , Y+1, Z+1), fx , fy-1.0, fz-1.0), mx_gradient_vec3(mx_hash_vec3(X+1, Y+1, Z+1), fx-1.0, fy-1.0, fz-1.0), u, v, w); return mx_gradient_scale3d(result); } float mx_cell_noise_float(float p) { int ix = mx_floor(p); return mx_bits_to_01(mx_hash_int(ix)); } float mx_cell_noise_float(vec2 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); return mx_bits_to_01(mx_hash_int(ix, iy)); } float mx_cell_noise_float(vec3 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); int iz = mx_floor(p.z); return mx_bits_to_01(mx_hash_int(ix, iy, iz)); } float mx_cell_noise_float(vec4 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); int iz = mx_floor(p.z); int iw = mx_floor(p.w); return mx_bits_to_01(mx_hash_int(ix, iy, iz, iw)); } vec3 mx_cell_noise_vec3(float p) { int ix = mx_floor(p); return vec3( mx_bits_to_01(mx_hash_int(ix, 0)), mx_bits_to_01(mx_hash_int(ix, 1)), mx_bits_to_01(mx_hash_int(ix, 2)) ); } vec3 mx_cell_noise_vec3(vec2 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); return vec3( mx_bits_to_01(mx_hash_int(ix, iy, 0)), mx_bits_to_01(mx_hash_int(ix, iy, 1)), mx_bits_to_01(mx_hash_int(ix, iy, 2)) ); } vec3 mx_cell_noise_vec3(vec3 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); int iz = mx_floor(p.z); return vec3( mx_bits_to_01(mx_hash_int(ix, iy, iz, 0)), mx_bits_to_01(mx_hash_int(ix, iy, iz, 1)), mx_bits_to_01(mx_hash_int(ix, iy, iz, 2)) ); } vec3 mx_cell_noise_vec3(vec4 p) { int ix = mx_floor(p.x); int iy = mx_floor(p.y); int iz = mx_floor(p.z); int iw = mx_floor(p.w); return vec3( mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 0)), mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 1)), mx_bits_to_01(mx_hash_int(ix, iy, iz, iw, 2)) ); } float mx_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; } // 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; } // 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 ); } /* 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); } void mx_worleynoise3d_vector2(vector position, float jitter, int style, output vector2 result) { result = mx_worley_noise_vector2(position, jitter, style, 0); } void mx_geompropvalue_string(string geomprop, string defaultVal, output string out) { if (getattribute(geomprop, out) == 0) out = defaultVal; } void mx_noise3d_vector3(vector amplitude, float pivot, vector position, output vector result) { vector value = noise("snoise", position); result = value * amplitude + pivot; } void mx_worleynoise3d_float(vector position, float jitter, int style, output float result) { result = mx_worley_noise_float(position, 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_luminance_color4(color4 in, color lumacoeffs, output color4 result) { result = color4(dot(in.rgb, lumacoeffs), in.a); } 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_frame_float(output float result) { // Use the standard default value if the attribute is not present. result = 1.0; getattribute("frame", result); } void mx_hsvtorgb_color3(vector _in, output vector result) { result = transformc("hsv","rgb", _in); } 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]; } } #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 ); } void mx_luminance_color3(color in, color lumacoeffs, output color result) { result = dot(in, lumacoeffs); } 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_geompropvalue_boolean(string geomprop, int defaultVal, output int out) { if (getattribute(geomprop, out) == 0) out = defaultVal; } 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; } #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]; } void mx_worleynoise2d_vector2(vector2 texcoord, float jitter, int style, output vector2 result) { result = mx_worley_noise_vector2(texcoord, 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); } 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); } 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_premult_color4(color4 in, output color4 result) { result = color4(in.rgb * in.a, in.a); } 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_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_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_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_geompropvalue_integer(string geomprop, int defaultVal, output int out) { if (getattribute(geomprop, out) == 0) out = defaultVal; } void mx_cellnoise3d_float(vector position, output float result) { result = cellnoise(position); } void mx_unpremult_color4(color4 in, output color4 result) { result = color4(in.rgb / in.a, in.a); } void mx_rotate_vector2(vector2 _in, float amount, output vector2 result) { float rotationRadians = radians(amount); float sa = sin(rotationRadians); float ca = cos(rotationRadians); result = vector2(ca*_in.x + sa*_in.y, -sa*_in.x + ca*_in.y); } void mx_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_noise3d_vector4(vector4 amplitude, float pivot, vector position, output vector4 result) { vector4 value = mx_noise("snoise", position); result = value * amplitude + pivot; } void mx_dodge_float(float fg, float bg, float mix, output float out) { if (abs(1.0 - fg) < M_FLOAT_EPS) { out = 0.0; return; } out = mix*(bg / (1.0 - fg)) + ((1.0-mix)*bg); } void mx_fractal3d_vector2(vector2 amplitude, int octaves, float lacunarity, float diminish, vector position, output vector2 result) { vector2 f = mx_fbm(position, octaves, lacunarity, diminish, "snoise"); result = f * amplitude; } void mx_geompropvalue_vector(string geomprop, vector defaultVal, output vector out) { if (getattribute(geomprop, out) == 0) out = defaultVal; } void mx_geompropvalue_float(string geomprop, float defaultVal, output float result) { if (getattribute(geomprop, result) == 0) { result = defaultVal; } } 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_geompropvalue_color(string geomprop, color defaultVal, output color out) { if (getattribute(geomprop, out) == 0) out = defaultVal; } void mx_noise3d_vector2(vector2 amplitude, float pivot, vector position, output vector2 result) { vector2 value = mx_noise("snoise", position); result = value * amplitude + pivot; } #include "mx_dodge_float.osl" void mx_dodge_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]); } void mx_rgbtohsv_color3(vector _in, output vector result) { result = transformc("rgb","hsv", _in); } void mx_worleynoise2d_float(vector2 texcoord, float jitter, int style, output float result) { result = mx_worley_noise_float(texcoord, jitter, style, 0); } void mx_noise3d_float(float amplitude, float pivot, vector position, output float result) { float value = noise("snoise", position); result = value * amplitude + pivot; } void mx_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_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_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_noise2d_vector3(vector amplitude, float pivot, vector2 texcoord, output vector result) { vector value = noise("snoise", texcoord.x, texcoord.y); result = value * amplitude + pivot; } #include "mx_dodge_float.osl" void mx_dodge_color4(color4 fg , color4 bg , float mix , output color4 result) { mx_dodge_float(fg.rgb[0], bg.rgb[0], mix, result.rgb[0]); mx_dodge_float(fg.rgb[1], bg.rgb[1], mix, result.rgb[1]); mx_dodge_float(fg.rgb[2], bg.rgb[2], mix, result.rgb[2]); mx_dodge_float(fg.a, bg.a, mix, result.a); } #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 "mx_burn_float.osl" void mx_burn_color3(color fg, color bg, float mix, output color result) { mx_burn_float(fg[0], bg[0], mix, result[0]); mx_burn_float(fg[1], bg[1], mix, result[1]); mx_burn_float(fg[2], bg[2], mix, result[2]); } void mx_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_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_hsvtorgb_color4(color4 _in, output color4 result) { result = color4(transformc("hsv","rgb", _in.rgb), 1.0); } #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 "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_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_noise2d_vector2(vector2 amplitude, float pivot, vector2 texcoord, output vector2 result) { vector2 value = mx_noise("snoise", texcoord.x, texcoord.y); result = value * amplitude + pivot; } #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); } void mx_rgbtohsv_color4(color4 _in, output color4 result) { result = color4(transformc("rgb","hsv", _in.rgb), 1.0); } void mx_cellnoise2d_float(vector2 texcoord, output float result) { result = cellnoise(texcoord.x, texcoord.y); } void mx_geomcolor_color3(int index, output color result) { getattribute("color", result); } 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_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 "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_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_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_geomcolor_float(int index, output float result) { getattribute("color", result); } // 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 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); } // 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 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); } vector2 mx_transform_uv(vector2 texcoord) { return vector2(texcoord.x, 1.0 - texcoord.y); } vector2 mx_transform_uv(vector2 texcoord) { return texcoord; } // 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)); } // Restrict to 7x7 kernel size for performance reasons #define MX_MAX_SAMPLE_COUNT 49 // Size of all weights for all levels (including level 1) #define MX_WEIGHT_ARRAY_SIZE 84 // // Function to compute the sample size relative to a texture coordinate // vector2 mx_compute_sample_size_uv(vector2 uv, float filterSize, float filterOffset) { vector derivUVx = Dx(vector(uv.x, uv.y, 0.0)) * 0.5; vector derivUVy = Dy(vector(uv.x, uv.y, 0.0)) * 0.5; float derivX = abs(derivUVx[0]) + abs(derivUVy[0]); float derivY = abs(derivUVx[1]) + abs(derivUVy[1]); float sampleSizeU = filterSize * derivX + filterOffset; if (sampleSizeU < 1.0E-05) sampleSizeU = 1.0E-05; float sampleSizeV = filterSize * derivY + filterOffset; if (sampleSizeV < 1.0E-05) sampleSizeV = 1.0E-05; return vector2(sampleSizeU, sampleSizeV); } // Kernel weights for box filter void mx_get_box_weights(output float W[MX_MAX_SAMPLE_COUNT], int filterSize) { int sampleCount = filterSize*filterSize; float value = 1.0 / float(sampleCount); for (int i=0; i= 7) { W[0] = 0.000036; W[1] = 0.000363; W[2] = 0.001446; W[3] = 0.002291; W[4] = 0.001446; W[5] = 0.000363; W[6] = 0.000036; W[7] = 0.000363; W[8] = 0.003676; W[9] = 0.014662; W[10] = 0.023226; W[11] = 0.014662; W[12] = 0.003676; W[13] = 0.000363; W[14] = 0.001446; W[15] = 0.014662; W[16] = 0.058488; W[17] = 0.092651; W[18] = 0.058488; W[19] = 0.014662; W[20] = 0.001446; W[21] = 0.002291; W[22] = 0.023226; W[23] = 0.092651; W[24] = 0.146768; W[25] = 0.092651; W[26] = 0.023226; W[27] = 0.002291; W[28] = 0.001446; W[29] = 0.014662; W[30] = 0.058488; W[31] = 0.092651; W[32] = 0.058488; W[33] = 0.014662; W[34] = 0.001446; W[35] = 0.000363; W[36] = 0.003676; W[37] = 0.014662; W[38] = 0.023226; W[39] = 0.014662; W[40] = 0.003676; W[41] = 0.000363; W[42] = 0.000036; W[43] = 0.000363; W[44] = 0.001446; W[45] = 0.002291; W[46] = 0.001446; W[47] = 0.000363; W[48] = 0.000036; } else if (filterSize >= 5) { W[0] = 0.003765; W[1] = 0.015019; W[2] = 0.023792; W[3] = 0.015019; W[4] = 0.003765; W[5] = 0.015019; W[6] = 0.059912; W[7] = 0.094907; W[8] = 0.059912; W[9] = 0.015019; W[10] = 0.023792; W[11] = 0.094907; W[12] = 0.150342; W[13] = 0.094907; W[14] = 0.023792; W[15] = 0.015019; W[16] = 0.059912; W[17] = 0.094907; W[18] = 0.059912; W[19] = 0.015019; W[20] = 0.003765; W[21] = 0.015019; W[22] = 0.023792; W[23] = 0.015019; W[24] = 0.003765; } else if (filterSize >= 3) { W[0] = 0.0625; W[1] = 0.125; W[2] = 0.0625; W[3] = 0.125; W[4] = 0.25; W[5] = 0.125; W[6] = 0.0625; W[7] = 0.125; W[8] = 0.0625; } else { W[0] = 1.0; } } // // Apply filter for float samples S, using weights W. // sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 } // float mx_convolution_float(float S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount) { float result = 0.0; for (int i = 0; i < sampleCount; i++) { result += S[i]*W[i+offset]; } return result; } // // Apply filter for vector2 samples S, using weights W. // sampleCount should be a square of a odd number in the range { 1, 3, 5, 7 } // vector2 mx_convolution_vector2(vector2 S[MX_MAX_SAMPLE_COUNT], float W[MX_WEIGHT_ARRAY_SIZE], int offset, int sampleCount) { vector2 result = vector2(0.0, 0.0); for (int i=0; i #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; } } void mx_displacement_vector3(vec3 disp, float scale, out displacementshader result) { result.offset = disp; result.scale = scale; } #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. } #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" void mx_multiply_edf_color3(ClosureData closureData, EDF in1, vec3 in2, out EDF result) { result = in1 * in2; } void mx_displacement_float(float disp, float scale, out displacementshader result) { result.offset = vec3(disp); result.scale = scale; } #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" 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_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" 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_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; } } void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, out vec3 ior, out vec3 extinction) { // "Artist Friendly Metallic Fresnel", Ole Gulbrandsen, 2014 // http://jcgt.org/published/0003/04/03/paper.pdf vec3 r = clamp(reflectivity, 0.0, 0.99); vec3 r_sqrt = sqrt(r); vec3 n_min = (1.0 - r) / (1.0 + r); vec3 n_max = (1.0 + r_sqrt) / (1.0 - r_sqrt); ior = mix(n_max, n_min, edge_color); vec3 np1 = ior + 1.0; vec3 nm1 = ior - 1.0; vec3 k2 = (np1*np1 * r - nm1*nm1) / (1.0 - r); k2 = max(k2, 0.0); extinction = sqrt(k2); } #include "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_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_uniform_edf(ClosureData closureData, vec3 color, out EDF result) { if (closureData.closureType == CLOSURE_TYPE_EMISSION) { result = color; } } #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; } } 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" void mx_add_edf(ClosureData closureData, EDF in1, EDF in2, out EDF result) { result = in1 + in2; } #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" #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; } } #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); } 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; } } /// 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" 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" #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; } } #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.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_mix_edf(ClosureData closureData, EDF fg, EDF bg, float mixValue, out EDF result) { result = mix(bg, fg, mixValue); } #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. #ifdef HW_SEPARATE_SAMPLERS vec2 uv = gl_FragCoord.xy * pow(2.0, $envPrefilterMip) / vec2(textureSize(sampler2D($envRadiance_texture, $envRadiance_sampler), 0)); #else vec2 uv = gl_FragCoord.xy * pow(2.0, $envPrefilterMip) / vec2(textureSize($envRadiance, 0)); #endif 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); #ifdef HW_SEPARATE_SAMPLERS vec3 sampleColor = mx_latlong_map_lookup(Lw, $envMatrix, lod, $envRadiance_texture, $envRadiance_sampler); #else vec3 sampleColor = mx_latlong_map_lookup(Lw, $envMatrix, lod, $envRadiance); #endif // Add the radiance contribution of this sample. radiance += G * sampleColor; weight += G; } return radiance / weight; } #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; #ifdef HW_SEPARATE_SAMPLERS vec3 Li = mx_latlong_map_lookup(L, $envMatrix, mx_latlong_alpha_to_lod(avgAlpha), $envRadiance_texture, $envRadiance_sampler); #else vec3 Li = mx_latlong_map_lookup(L, $envMatrix, mx_latlong_alpha_to_lod(avgAlpha), $envRadiance); #endif return Li * FG * $envLightIntensity; } vec3 mx_environment_irradiance(vec3 N) { #ifdef HW_SEPARATE_SAMPLERS vec3 Li = mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance_texture, $envIrradiance_sampler); #else vec3 Li = mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance); #endif 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); } #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" // 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; } // 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_surface_transmission(vec3 N, vec3 V, vec3 X, vec2 alpha, int distribution, FresnelData fd, vec3 tint) { return tint; } #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_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); #ifdef HW_SEPARATE_SAMPLERS vec3 sampleColor = mx_latlong_map_lookup(Lw, $envMatrix, lod, $envRadiance_texture, $envRadiance_sampler); #else vec3 sampleColor = mx_latlong_map_lookup(Lw, $envMatrix, lod, $envRadiance); #endif // 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) { #ifdef HW_SEPARATE_SAMPLERS vec3 Li = mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance_texture, $envIrradiance_sampler); #else vec3 Li = mx_latlong_map_lookup(N, $envMatrix, 0.0, $envIrradiance); #endif return Li * $envLightIntensity; } // 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) { // 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 "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); } #ifdef HW_SEPARATE_SAMPLERS vec3 mx_latlong_map_lookup(vec3 dir, mat4 transform, float lod, texture2D env_texture, sampler env_sampler) { vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz); vec2 uv = mx_latlong_projection(envDir); return textureLod(sampler2D(env_texture, env_sampler), uv, lod).rgb; } #else vec3 mx_latlong_map_lookup(vec3 dir, mat4 transform, float lod, sampler2D envSampler) { vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz); vec2 uv = mx_latlong_projection(envDir); return textureLod(envSampler, uv, lod).rgb; } #endif // 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); } 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_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_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); } 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_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_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_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_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_blackbody(float temp, output color color_value) { float xc, yc; float t, t2, t3, xc2, xc3; // if value outside valid range of approximation clamp to accepted temperature range float temperature = clamp(temp, 1667.0, 25000.0); t = 1000.0 / temperature; t2 = t * t; t3 = t * t * t; // Cubic spline approximation for Kelvin temperature to sRGB conversion // (https://en.wikipedia.org/wiki/Planckian_locus#Approximation) if (temperature < 4000.0) { // 1667K <= temperature < 4000K xc = -0.2661239 * t3 - 0.2343580 * t2 + 0.8776956 * t + 0.179910; } else { // 4000K <= temperature <= 25000K xc = -3.0258469 * t3 + 2.1070379 * t2 + 0.2226347 * t + 0.240390; } xc2 = xc * xc; xc3 = xc * xc * xc; if (temperature < 2222.0) { // 1667K <= temperature < 2222K yc = -1.1063814 * xc3 - 1.34811020 * xc2 + 2.18555832 * xc - 0.20219683; } else if (temperature < 4000.0) { // 2222K <= temperature < 4000K yc = -0.9549476 * xc3 - 1.37418593 * xc2 + 2.09137015 * xc - 0.16748867; } else { // 4000K <= temperature <= 25000K yc = 3.0817580 * xc3 - 5.87338670 * xc2 + 3.75112997 * xc - 0.37001483; } if (yc <= 0.0) { // avoid division by zero color_value = color(1.0); return; } vector XYZ = vector(xc / yc, 1.0, (1 - xc - yc) / yc); /// XYZ to Rec.709 RGB colorspace conversion matrix XYZ_to_RGB = matrix( 3.2406, -0.9689, 0.0557, 0.0, -1.5372, 1.8758, -0.2040, 0.0, -0.4986, 0.0415, 1.0570, 0.0, 0.0, 0.0, 0.0, 1.0); color_value = transform(XYZ_to_RGB, XYZ); color_value = max(color_value, vector(0.0)); } #include "lib/mx_microfacet.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_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); } void mx_roughness_anisotropy(float roughness, float anisotropy, output vector2 result) { float roughness_sqr = clamp(roughness*roughness, M_FLOAT_EPS, 1.0); if (anisotropy > 0.0) { float aspect = sqrt(1.0 - clamp(anisotropy, 0.0, 0.98)); result.x = min(roughness_sqr / aspect, 1.0); result.y = roughness_sqr * aspect; } else { result.x = roughness_sqr; result.y = roughness_sqr; } } float mx_square(float x) { return x*x; } vector2 mx_square(vector2 x) { return x*x; } vector mx_square(vector x) { return x*x; } vector4 mx_square(vector4 x) { return x*x; } float mx_pow5(float x) { return mx_square(mx_square(x)) * x; } color mx_fresnel_conductor(float cosTheta, vector n, vector k) { float c2 = cosTheta*cosTheta; vector n2_k2 = n*n + k*k; vector nc2 = 2.0 * n * cosTheta; vector rs_a = n2_k2 + c2; vector rp_a = n2_k2 * c2 + 1.0; vector rs = (rs_a - nc2) / (rs_a + nc2); vector rp = (rp_a - nc2) / (rp_a + nc2); return 0.5 * (rs + rp); } // Standard Schlick Fresnel float mx_fresnel_schlick(float cosTheta, float F0) { float x = clamp(1.0 - cosTheta, 0.0, 1.0); float x5 = mx_pow5(x); return F0 + (1.0 - F0) * x5; } color mx_fresnel_schlick(float cosTheta, color F0) { float x = clamp(1.0 - cosTheta, 0.0, 1.0); float x5 = mx_pow5(x); return F0 + (1.0 - F0) * x5; } // Generalized Schlick Fresnel float mx_fresnel_schlick(float cosTheta, float F0, float F90) { float x = clamp(1.0 - cosTheta, 0.0, 1.0); float x5 = mx_pow5(x); return mix(F0, F90, x5); } color mx_fresnel_schlick(float cosTheta, color F0, color F90) { float x = clamp(1.0 - cosTheta, 0.0, 1.0); float x5 = mx_pow5(x); return mix(F0, F90, x5); } // Generalized Schlick Fresnel with a variable exponent color mx_fresnel_schlick(float cosTheta, float f0, float f90, float exponent) { float x = clamp(1.0 - cosTheta, 0.0, 1.0); return mix(f0, f90, pow(x, exponent)); } color mx_fresnel_schlick(float cosTheta, color f0, color f90, float exponent) { float x = clamp(1.0 - cosTheta, 0.0, 1.0); return mix(f0, f90, pow(x, exponent)); } />