From f21a4db9cd6bbd524a36f1407b0e63da6c339de5 Mon Sep 17 00:00:00 2001 From: muenstc <84033270+muenstc@users.noreply.github.com> Date: Thu, 1 Jul 2021 16:19:56 +0200 Subject: [PATCH] Add EsslShaderGenerator and Three.js based sample app (JsMaterialXView) (#1244) * Rough GLES generator setup. * Fix incorrect target inheritance. * fixed copying of library folder * initial materialx web viewer implementation with dummy shader (#1207) * Adapt ESSLShaderGenerator for WebGL2 (#1211) * Minimize required code changes between Glsl and Essl shadergen by exposing more `emit` functions * Get correct vertex prefix for Essl in the node implementations. * Update three js sample to render standard_surface_default material Co-authored-by: dunkels Co-authored-by: kohakukun * fix: Cleanup essl context for new documents (#1226) Co-authored-by: kohakukun * fix: Add explicit casting for switch statement (#1227) Co-authored-by: kohakukun * clean up, add entry for Essl shader generation option to Viewer.md (#1228) * fix: stdlib explicit cast for essl compatibility (#1229) * add precission to vertex shader * fix: Add explicit cast for ess compatibility * address comments * quick fix Co-authored-by: kohakukun * Enable mipmap generation for JsMaterialXView (#1242) * Throw if EsslShaderGenerator has access to a resource binding context (#1230) * clean up (#1247) * clean up pass 2 Co-authored-by: bernardkwok Co-authored-by: dunkels Co-authored-by: Sebastian Dunkel Co-authored-by: kohakukun Co-authored-by: Aura Munoz --- CMakeLists.txt | 12 +- documents/DeveloperGuide/Viewer.md | 1 + .../genglsl/lib/mx_environment_fis.glsl | 4 +- .../genglsl/lib/mx_environment_prefilter.glsl | 2 +- .../genglsl/lib/mx_microfacet_diffuse.glsl | 6 +- .../genglsl/lib/mx_microfacet_sheen.glsl | 8 +- .../genglsl/lib/mx_microfacet_specular.glsl | 34 +- .../pbrlib/genglsl/mx_conductor_bsdf.glsl | 16 +- .../pbrlib/genglsl/mx_dielectric_bsdf.glsl | 23 +- .../genglsl/mx_generalized_schlick_bsdf.glsl | 2 +- libraries/stdlib/genglsl/lib/mx_noise.glsl | 2 +- .../genglsl/mx_disjointover_color4.glsl | 4 +- .../stdlib/genglsl/mx_fractal3d_vector4.glsl | 2 +- libraries/stdlib/genglsl/mx_overlay.glsl | 2 +- .../stdlib/genglsl/mx_overlay_color3.glsl | 2 +- .../stdlib/genglsl/mx_overlay_color4.glsl | 2 +- libraries/stdlib/genglsl/mx_ramplr_float.glsl | 2 +- .../stdlib/genglsl/mx_ramplr_vector2.glsl | 2 +- .../stdlib/genglsl/mx_ramplr_vector3.glsl | 2 +- .../stdlib/genglsl/mx_ramplr_vector4.glsl | 2 +- libraries/stdlib/genglsl/mx_ramptb_float.glsl | 2 +- .../stdlib/genglsl/mx_ramptb_vector2.glsl | 2 +- .../stdlib/genglsl/mx_ramptb_vector3.glsl | 2 +- .../stdlib/genglsl/mx_ramptb_vector4.glsl | 2 +- libraries/stdlib/genglsl/mx_screen.inline | 2 +- source/JsMaterialX/JsMaterialXView/.gitignore | 1 + source/JsMaterialX/JsMaterialXView/index.html | 13 + .../JsMaterialX/JsMaterialXView/package.json | 20 + .../JsMaterialXView/shader-frag.glsl | 1446 +++++++++++++++++ .../JsMaterialXView/shader-vert.glsl | 26 + .../JsMaterialX/JsMaterialXView/src/index.js | 313 ++++ .../JsMaterialXView/webpack.config.js | 19 + source/MaterialXGenEssl/CMakeLists.txt | 59 + .../MaterialXGenEssl/EsslShaderGenerator.cpp | 110 ++ source/MaterialXGenEssl/EsslShaderGenerator.h | 50 + source/MaterialXGenEssl/EsslSyntax.cpp | 20 + source/MaterialXGenEssl/EsslSyntax.h | 28 + .../MaterialXGenEssl/libraries/CMakeLists.txt | 3 + .../libraries/targets/essl.mtlx | 14 + .../MaterialXGenGlsl/GlslShaderGenerator.cpp | 220 +-- source/MaterialXGenGlsl/GlslShaderGenerator.h | 18 +- .../Nodes/BitangentNodeGlsl.cpp | 6 +- .../Nodes/GeomColorNodeGlsl.cpp | 6 +- .../Nodes/GeomPropValueNodeGlsl.cpp | 6 +- .../MaterialXGenGlsl/Nodes/NormalNodeGlsl.cpp | 6 +- .../Nodes/PositionNodeGlsl.cpp | 6 +- .../Nodes/SurfaceNodeGlsl.cpp | 17 +- .../Nodes/SurfaceShaderNodeGlsl.cpp | 4 +- .../Nodes/TangentNodeGlsl.cpp | 6 +- .../Nodes/TexCoordNodeGlsl.cpp | 8 +- .../MaterialXGenShader/Nodes/SwitchNode.cpp | 4 +- source/MaterialXTest/CMakeLists.txt | 5 + source/MaterialXView/CMakeLists.txt | 9 + source/MaterialXView/Viewer.cpp | 55 +- source/MaterialXView/Viewer.h | 3 + 55 files changed, 2456 insertions(+), 185 deletions(-) create mode 100644 source/JsMaterialX/JsMaterialXView/.gitignore create mode 100644 source/JsMaterialX/JsMaterialXView/index.html create mode 100644 source/JsMaterialX/JsMaterialXView/package.json create mode 100644 source/JsMaterialX/JsMaterialXView/shader-frag.glsl create mode 100644 source/JsMaterialX/JsMaterialXView/shader-vert.glsl create mode 100644 source/JsMaterialX/JsMaterialXView/src/index.js create mode 100644 source/JsMaterialX/JsMaterialXView/webpack.config.js create mode 100644 source/MaterialXGenEssl/CMakeLists.txt create mode 100644 source/MaterialXGenEssl/EsslShaderGenerator.cpp create mode 100644 source/MaterialXGenEssl/EsslShaderGenerator.h create mode 100644 source/MaterialXGenEssl/EsslSyntax.cpp create mode 100644 source/MaterialXGenEssl/EsslSyntax.h create mode 100644 source/MaterialXGenEssl/libraries/CMakeLists.txt create mode 100644 source/MaterialXGenEssl/libraries/targets/essl.mtlx diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ee659a649..1889c9e6b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,6 +116,7 @@ option(MATERIALX_BUILD_CONTRIB "Build contribution folder." ON) option(MATERIALX_BUILD_CROSS "Build MaterialXCross folder." OFF) option(MATERIALX_BUILD_GEN_OGSXML "Build the OGSXML shader generator back-end." ON) option(MATERIALX_BUILD_GEN_OGSFX "Build the OGSFX shader generator back-end." ON) +option(MATERIALX_BUILD_GEN_ESSL "Build the ESSL shader generator back-end." ON) option(MATERIALX_BUILD_GEN_ARNOLD "Build the Arnold OSL shader generator back-end." ON) option(MATERIALX_BUILD_RUNTIME "Build the MaterialX Runtime data model library." OFF) @@ -142,11 +143,15 @@ if (MATERIALX_BUILD_GEN_OGSFX) set(MATERIALX_BUILD_GEN_GLSL ON) endif() +if (MATERIALX_BUILD_GEN_ESSL) + set(MATERIALX_BUILD_GEN_GLSL ON) +endif() + if (MATERIALX_BUILD_GEN_OGSXML) set(MATERIALX_BUILD_GEN_GLSL ON) endif() -if (MATERIALX_BUILD_GEN_GLSL OR MATERIALX_BUILD_GEN_OSL OR MATERIALX_BUILD_GEN_OGSXML OR MATERIALX_BUILD_GEN_OGSFX OR MATERIALX_BUILD_GEN_ARNOLD) +if (MATERIALX_BUILD_GEN_GLSL OR MATERIALX_BUILD_GEN_OSL OR MATERIALX_BUILD_GEN_OGSXML OR MATERIALX_BUILD_GEN_OGSFX OR MATERIALX_BUILD_GEN_ESSL OR MATERIALX_BUILD_GEN_ARNOLD) set(MATERIALX_BUILD_GEN ON) else() set(MATERIALX_BUILD_GEN OFF) @@ -154,6 +159,7 @@ endif() mark_as_advanced(MATERIALX_BUILD_GEN_OGSXML) mark_as_advanced(MATERIALX_BUILD_GEN_OGSFX) +mark_as_advanced(MATERIALX_BUILD_GEN_ESSL) mark_as_advanced(MATERIALX_BUILD_GEN_ARNOLD) mark_as_advanced(MATERIALX_BUILD_CROSS) mark_as_advanced(MATERIALX_BUILD_GEN) @@ -291,6 +297,10 @@ if (MATERIALX_BUILD_GEN) add_definitions(-DMATERIALX_BUILD_GEN_OGSFX) add_subdirectory(source/MaterialXGenOgsFx) endif() + if (MATERIALX_BUILD_GEN_ESSL) + add_definitions(-DMATERIALX_BUILD_GEN_ESSL) + add_subdirectory(source/MaterialXGenEssl) + endif() if (MATERIALX_BUILD_GEN_ARNOLD) add_definitions(-DMATERIALX_BUILD_GEN_ARNOLD) add_subdirectory(source/MaterialXGenArnold) diff --git a/documents/DeveloperGuide/Viewer.md b/documents/DeveloperGuide/Viewer.md index 4f438dbf31..ce6c8b0d10 100644 --- a/documents/DeveloperGuide/Viewer.md +++ b/documents/DeveloperGuide/Viewer.md @@ -65,6 +65,7 @@ By default, the MaterialX viewer loads and saves image files using `stb_image`, - `R`: Reload the current material from file. Hold `SHIFT` to reload all standard libraries as well. - `G`: Save the current GLSL shader source to file. +- `E`: Save the current ESSL (OpenGL ES 3.0) shader source to file. - `O`: Save the current OSL shader source to file. - `M`: Save the current MDL shader source to file. - `L`: Load GLSL shader source from file. Editing the source files before loading provides a way to debug and experiment with shader source code. diff --git a/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl b/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl index 4f2ed7c621..3712dba31e 100644 --- a/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl +++ b/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl @@ -12,7 +12,7 @@ float mx_latlong_compute_lod(vec3 dir, float pdf, float maxMipLevel, int envSamp 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(envSamples * pdf * distortion), 0.0); + return max(effectiveMaxMipLevel - 0.5 * log2(float(envSamples) * pdf * distortion), 0.0); } vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 roughness, int distribution, FresnelData fd) @@ -43,7 +43,7 @@ vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 roughness, int distrib // Sample the environment light from the given direction. float pdf = mx_ggx_PDF(X, Y, H, NdotH, LdotH, roughness.x, roughness.y); - float lod = mx_latlong_compute_lod(L, pdf, $envRadianceMips - 1, envRadianceSamples); + float lod = mx_latlong_compute_lod(L, pdf, float($envRadianceMips - 1), envRadianceSamples); vec3 sampleColor = mx_latlong_map_lookup(L, $envMatrix, lod, $envRadiance); // Compute the Fresnel term. diff --git a/libraries/pbrlib/genglsl/lib/mx_environment_prefilter.glsl b/libraries/pbrlib/genglsl/lib/mx_environment_prefilter.glsl index ff226d3f53..e931e673bf 100644 --- a/libraries/pbrlib/genglsl/lib/mx_environment_prefilter.glsl +++ b/libraries/pbrlib/genglsl/lib/mx_environment_prefilter.glsl @@ -4,7 +4,7 @@ float mx_latlong_compute_lod(float roughness) { // Select a mip level based on input roughness. float lodBias = roughness < 0.25 ? sqrt(roughness) : 0.5*roughness + 0.375; - return lodBias * $envRadianceMips; + return lodBias * float($envRadianceMips); } vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 roughness, int distribution, FresnelData fd) diff --git a/libraries/pbrlib/genglsl/lib/mx_microfacet_diffuse.glsl b/libraries/pbrlib/genglsl/lib/mx_microfacet_diffuse.glsl index 985486cd7a..98106f5903 100644 --- a/libraries/pbrlib/genglsl/lib/mx_microfacet_diffuse.glsl +++ b/libraries/pbrlib/genglsl/lib/mx_microfacet_diffuse.glsl @@ -35,7 +35,7 @@ float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness) float mx_burley_diffuse_directional_albedo(float NdotV, float roughness) { float x = NdotV; - float fit0 = 0.97619 - 0.488095 * mx_pow5(1 - x); + 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); } @@ -63,10 +63,10 @@ vec3 mx_integrate_burley_diffusion(vec3 N, vec3 L, float radius, vec3 mfp) vec3 sumD = vec3(0.0); vec3 sumR = vec3(0.0); const int SAMPLE_COUNT = 32; - const float SAMPLE_WIDTH = (2.0 * M_PI) / SAMPLE_COUNT; + const float SAMPLE_WIDTH = (2.0 * M_PI) / float(SAMPLE_COUNT); for (int i = 0; i < SAMPLE_COUNT; i++) { - float x = -M_PI + (i + 0.5) * SAMPLE_WIDTH; + float x = -M_PI + (float(i) + 0.5) * SAMPLE_WIDTH; float dist = radius * abs(2.0 * sin(x * 0.5)); vec3 R = mx_burley_diffusion_profile(dist, shape); sumD += R * max(cos(theta + x), 0.0); diff --git a/libraries/pbrlib/genglsl/lib/mx_microfacet_sheen.glsl b/libraries/pbrlib/genglsl/lib/mx_microfacet_sheen.glsl index c88bb16e4e..5fe5670c52 100644 --- a/libraries/pbrlib/genglsl/lib/mx_microfacet_sheen.glsl +++ b/libraries/pbrlib/genglsl/lib/mx_microfacet_sheen.glsl @@ -34,14 +34,14 @@ const float u_sheenAlbedo[SHEEN_ALBEDO_TABLE_SIZE*SHEEN_ALBEDO_TABLE_SIZE] = flo float mx_imageworks_sheen_directional_albedo(float cosTheta, float roughness) { - float x = cosTheta * (SHEEN_ALBEDO_TABLE_SIZE - 1); - float y = roughness * (SHEEN_ALBEDO_TABLE_SIZE - 1); + float x = cosTheta * float(SHEEN_ALBEDO_TABLE_SIZE - 1); + float y = roughness * float(SHEEN_ALBEDO_TABLE_SIZE - 1); int ix = int(x); int iy = int(y); int ix2 = clamp(ix + 1, 0, SHEEN_ALBEDO_TABLE_SIZE - 1); int iy2 = clamp(iy + 1, 0, SHEEN_ALBEDO_TABLE_SIZE - 1); - float fx = x - ix; - float fy = y - iy; + float fx = x - float(ix); + float fy = y - float(iy); // Bi-linear interpolation of the LUT values float v1 = mix(u_sheenAlbedo[iy * SHEEN_ALBEDO_TABLE_SIZE + ix], u_sheenAlbedo[iy * SHEEN_ALBEDO_TABLE_SIZE + ix2], fx); diff --git a/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl b/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl index 5aad7d3dea..14e3435d4f 100644 --- a/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl +++ b/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl @@ -91,11 +91,11 @@ vec3 mx_ggx_directional_albedo_importance_sample(float NdotV, float roughness, v float Gvis = mx_ggx_smith_G(NdotL, NdotV, roughness) * VdotH / (NdotH * NdotV); // Add the contribution of this sample. - AB += vec2(Gvis * (1 - Fc), Gvis * Fc); + AB += vec2(Gvis * (1.0 - Fc), Gvis * Fc); } // Normalize integrated terms. - AB /= SAMPLE_COUNT; + AB /= float(SAMPLE_COUNT); // Return the final directional albedo. return F0 * AB.x + F90 * AB.y; @@ -184,7 +184,7 @@ void mx_fresnel_dielectric_polarized(float cosTheta, float n1, float n2, out vec return; } - float cosTheta_t = sqrt(1 - eta2 * st2); + float cosTheta_t = sqrt(1.0 - eta2 * st2); vec2 r = vec2((n2*cosTheta - n1*cosTheta_t) / (n2*cosTheta + n1*cosTheta_t), (n1*cosTheta - n2*cosTheta_t) / (n1*cosTheta + n2*cosTheta_t)); F = mx_square(r); @@ -196,7 +196,7 @@ void mx_fresnel_dielectric_polarized(float cosTheta, float n1, float n2, out vec // TODO: Optimize this functions and support wavelength dependent complex refraction index. void mx_fresnel_conductor_polarized(float cosTheta, float n1, float n2, float k, out vec2 F, out vec2 phi) { - if (k == 0) + if (k == 0.0) { // Use dielectric formula to avoid numerical issues mx_fresnel_dielectric_polarized(cosTheta, n1, n2, F, phi); @@ -213,7 +213,7 @@ void mx_fresnel_conductor_polarized(float cosTheta, float n1, float n2, float k, F.x = (mx_square(mx_square(n2) * (1.0 - mx_square(k)) * cosTheta - n1*U) + mx_square(2.0 * mx_square(n2) * k * cosTheta - n1*V)) / (mx_square(mx_square(n2) * (1.0 - mx_square(k)) * cosTheta + n1*U) + mx_square(2.0 * mx_square(n2) * k * cosTheta + n1*V)); - phi.x = atan(2.0 * n1 * mx_square(n2) * cosTheta * (2*k*U - (1.0 - mx_square(k)) * V), mx_square(mx_square(n2) * (1.0 + mx_square(k)) * cosTheta) - mx_square(n1) * (mx_square(U) + mx_square(V))); + phi.x = atan(2.0 * n1 * mx_square(n2) * cosTheta * (2.0*k*U - (1.0 - mx_square(k)) * V), mx_square(mx_square(n2) * (1.0 + mx_square(k)) * cosTheta) - mx_square(n1) * (mx_square(U) + mx_square(V))); } // XYZ to CIE 1931 RGB color space (using neutral E illuminant) @@ -233,12 +233,12 @@ vec3 mx_depolarize(vec3 s, vec3 p) vec3 mx_eval_sensitivity(float opd, float shift) { // Use Gaussian fits, given by 3 parameters: val, pos and var - float phase = 2*M_PI * opd; + 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*M_PI * var) * cos(pos * phase + shift) * exp(- var * phase*phase); - xyz.x += 9.7470e-14 * sqrt(2*M_PI * 4.5282e+09) * cos(2.2399e+06 * phase + shift) * exp(- 4.5282e+09 * phase*phase); + vec3 xyz = val * sqrt(2.0*M_PI * var) * cos(pos * phase + shift) * exp(- var * phase*phase); + xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * cos(2.2399e+06 * phase + shift) * exp(- 4.5282e+09 * phase*phase); return xyz / 1.0685e-7; } @@ -275,7 +275,7 @@ vec3 mx_fresnel_airy(float cosTheta, vec3 ior, vec3 extinction, float tf_thickne vec3 R = vec3(0.0); vec2 R123 = R12*R23; vec2 r123 = sqrt(R123); - vec2 Rs = mx_square(T121)*R23 / (1-R123); + vec2 Rs = mx_square(T121)*R23 / (1.0-R123); // Reflectance term for m=0 (DC term amplitude) vec2 C0 = R12 + Rs; @@ -287,8 +287,8 @@ vec3 mx_fresnel_airy(float cosTheta, vec3 ior, vec3 extinction, float tf_thickne for (int m=1; m<=3; ++m) { Cm *= r123; - vec3 SmS = 2.0 * mx_eval_sensitivity(m*D, m*phi2.x); - vec3 SmP = 2.0 * mx_eval_sensitivity(m*D, m*phi2.y); + vec3 SmS = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*phi2.x); + vec3 SmP = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*phi2.y); R += mx_depolarize(Cm.x*SmS, Cm.y*SmP); } @@ -311,7 +311,7 @@ struct FresnelData FresnelData mx_init_fresnel_dielectric(float ior) { - FresnelData fd = FresnelData(vec3(0), vec3(0), 0, 0, 0, -1); + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); fd.model = 0; fd.ior = vec3(ior); fd.tf_thickness = 0.0f; @@ -320,7 +320,7 @@ FresnelData mx_init_fresnel_dielectric(float ior) FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction) { - FresnelData fd = FresnelData(vec3(0), vec3(0), 0, 0, 0, -1); + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); fd.model = 1; fd.ior = ior; fd.extinction = extinction; @@ -330,7 +330,7 @@ FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction) FresnelData mx_init_fresnel_schlick(vec3 F0) { - FresnelData fd = FresnelData(vec3(0), vec3(0), 0, 0, 0, -1); + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); fd.model = 2; fd.ior = F0; fd.extinction = vec3(1.0); @@ -341,7 +341,7 @@ FresnelData mx_init_fresnel_schlick(vec3 F0) FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent) { - FresnelData fd = FresnelData(vec3(0), vec3(0), 0, 0, 0, -1); + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); fd.model = 2; fd.ior = F0; fd.extinction = F90; @@ -352,7 +352,7 @@ FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent) FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior) { - FresnelData fd = FresnelData(vec3(0), vec3(0), 0, 0, 0, -1); + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); fd.model = 3; fd.ior = vec3(ior); fd.extinction = vec3(0.0); @@ -363,7 +363,7 @@ FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior) { - FresnelData fd = FresnelData(vec3(0), vec3(0), 0, 0, 0, -1); + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); fd.model = 3; fd.ior = ior; fd.extinction = extinction; diff --git a/libraries/pbrlib/genglsl/mx_conductor_bsdf.glsl b/libraries/pbrlib/genglsl/mx_conductor_bsdf.glsl index 18daeaae0c..702f08f7cf 100644 --- a/libraries/pbrlib/genglsl/mx_conductor_bsdf.glsl +++ b/libraries/pbrlib/genglsl/mx_conductor_bsdf.glsl @@ -18,7 +18,12 @@ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float float NdotH = clamp(dot(N, H), M_FLOAT_EPS, 1.0); float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0); - FresnelData fd = tf.thickness > 0.0 ? mx_init_fresnel_conductor_airy(ior_n, ior_k, tf.thickness, tf.ior) : mx_init_fresnel_conductor(ior_n, ior_k); + FresnelData fd; + if (tf.thickness > 0.0) + fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, tf.thickness, tf.ior); + else + fd = mx_init_fresnel_conductor(ior_n, ior_k); + vec3 F = mx_compute_fresnel(VdotH, fd); float avgRoughness = mx_average_roughness(roughness); @@ -28,7 +33,7 @@ void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float vec3 comp = mx_ggx_energy_compensation(NdotV, avgRoughness, F); // Note: NdotL is cancelled out - result = D * F * G * comp * occlusion * weight / (4 * NdotV); + result = D * F * G * comp * occlusion * weight / (4.0 * NdotV); } void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thinfilm tf, out BSDF result) @@ -43,7 +48,12 @@ void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, ve float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); - FresnelData fd = tf.thickness > 0.0 ? mx_init_fresnel_conductor_airy(ior_n, ior_k, tf.thickness, tf.ior) : mx_init_fresnel_conductor(ior_n, ior_k); + FresnelData fd; + if (tf.thickness > 0.0) + fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, tf.thickness, tf.ior); + else + fd = mx_init_fresnel_conductor(ior_n, ior_k); + vec3 F = mx_compute_fresnel(NdotV, fd); vec3 Li = mx_environment_radiance(N, V, X, roughness, distribution, fd); diff --git a/libraries/pbrlib/genglsl/mx_dielectric_bsdf.glsl b/libraries/pbrlib/genglsl/mx_dielectric_bsdf.glsl index d0c77a26ad..0c502d77b0 100644 --- a/libraries/pbrlib/genglsl/mx_dielectric_bsdf.glsl +++ b/libraries/pbrlib/genglsl/mx_dielectric_bsdf.glsl @@ -20,7 +20,12 @@ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, floa float avgRoughness = mx_average_roughness(roughness); - FresnelData fd = tf.thickness > 0.0 ? mx_init_fresnel_dielectric_airy(ior, tf.thickness, tf.ior) : mx_init_fresnel_dielectric(ior); + FresnelData fd; + if (tf.thickness > 0.0) + fd = mx_init_fresnel_dielectric_airy(ior, tf.thickness, tf.ior); + else + fd = mx_init_fresnel_dielectric(ior); + vec3 F = mx_compute_fresnel(VdotH, fd); float D = mx_ggx_NDF(X, Y, H, NdotH, roughness.x, roughness.y); float G = mx_ggx_smith_G(NdotL, NdotV, avgRoughness); @@ -30,7 +35,7 @@ void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, floa vec3 dirAlbedo = mx_ggx_directional_albedo(NdotV, avgRoughness, F0, 1.0) * comp; // Note: NdotL is cancelled out - result = D * F * G * comp * tint * occlusion * weight / (4 * NdotV) // Top layer reflection + result = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV) // Top layer reflection + base * (1.0 - dirAlbedo * weight); // Base layer reflection attenuated by top layer } @@ -58,7 +63,12 @@ void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, N = mx_forward_facing_normal(N, V); float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); - FresnelData fd = tf.thickness > 0.0 ? mx_init_fresnel_dielectric_airy(ior, tf.thickness, tf.ior) : mx_init_fresnel_dielectric(ior); + FresnelData fd; + if (tf.thickness > 0.0) + fd = mx_init_fresnel_dielectric_airy(ior, tf.thickness, tf.ior); + else + fd = mx_init_fresnel_dielectric(ior); + vec3 F = mx_compute_fresnel(NdotV, fd); float avgRoughness = mx_average_roughness(roughness); @@ -81,7 +91,12 @@ void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); - FresnelData fd = tf.thickness > 0.0 ? mx_init_fresnel_dielectric_airy(ior, tf.thickness, tf.ior) : mx_init_fresnel_dielectric(ior); + FresnelData fd; + if (tf.thickness > 0.0) + fd = mx_init_fresnel_dielectric_airy(ior, tf.thickness, tf.ior); + else + fd = mx_init_fresnel_dielectric(ior); + vec3 F = mx_compute_fresnel(NdotV, fd); float avgRoughness = mx_average_roughness(roughness); diff --git a/libraries/pbrlib/genglsl/mx_generalized_schlick_bsdf.glsl b/libraries/pbrlib/genglsl/mx_generalized_schlick_bsdf.glsl index cf567db2de..79938e62ce 100644 --- a/libraries/pbrlib/genglsl/mx_generalized_schlick_bsdf.glsl +++ b/libraries/pbrlib/genglsl/mx_generalized_schlick_bsdf.glsl @@ -30,7 +30,7 @@ void mx_generalized_schlick_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlus float avgDirAlbedo = dot(dirAlbedo, vec3(1.0 / 3.0)); // Note: NdotL is cancelled out - result = D * F * G * comp * occlusion * weight / (4 * NdotV) // Top layer reflection + result = D * F * G * comp * occlusion * weight / (4.0 * NdotV) // Top layer reflection + base * (1.0 - avgDirAlbedo * weight); // Base layer reflection attenuated by top layer } diff --git a/libraries/stdlib/genglsl/lib/mx_noise.glsl b/libraries/stdlib/genglsl/lib/mx_noise.glsl index d1944cd4bc..ee5d15a80f 100644 --- a/libraries/stdlib/genglsl/lib/mx_noise.glsl +++ b/libraries/stdlib/genglsl/lib/mx_noise.glsl @@ -59,7 +59,7 @@ int mx_floor(float x) float mx_floorfrac(float x, out int i) { i = mx_floor(x); - return x - i; + return x - float(i); } float mx_bilerp(float v0, float v1, float v2, float v3, float s, float t) diff --git a/libraries/stdlib/genglsl/mx_disjointover_color4.glsl b/libraries/stdlib/genglsl/mx_disjointover_color4.glsl index c835d7d5a3..7daaf351aa 100644 --- a/libraries/stdlib/genglsl/mx_disjointover_color4.glsl +++ b/libraries/stdlib/genglsl/mx_disjointover_color4.glsl @@ -2,7 +2,7 @@ void mx_disjointover_color4(vec4 fg, vec4 bg, float mixval, out vec4 result) { float summedAlpha = fg.w + bg.w; - if (summedAlpha <= 1) + if (summedAlpha <= 1.0) { result.xyz = fg.xyz + bg.xyz; } @@ -14,7 +14,7 @@ void mx_disjointover_color4(vec4 fg, vec4 bg, float mixval, out vec4 result) } else { - float x = (1 - fg.w) / bg.w; + float x = (1.0 - fg.w) / bg.w; result.xyz = fg.xyz + bg.xyz * x; } } diff --git a/libraries/stdlib/genglsl/mx_fractal3d_vector4.glsl b/libraries/stdlib/genglsl/mx_fractal3d_vector4.glsl index a131645390..fa3d07a597 100644 --- a/libraries/stdlib/genglsl/mx_fractal3d_vector4.glsl +++ b/libraries/stdlib/genglsl/mx_fractal3d_vector4.glsl @@ -3,6 +3,6 @@ void mx_fractal3d_vector4(vec4 amplitude, int octaves, float lacunarity, float diminish, vec3 position, out vec4 result) { vec3 xyz = mx_fractal_noise_vec3(position, octaves, lacunarity, diminish); - float w = mx_fractal_noise_float(position + vec3(19, 193, 17), octaves, lacunarity, diminish); + float w = mx_fractal_noise_float(position + vec3(19.0, 193.0, 17.0), octaves, lacunarity, diminish); result = vec4(xyz,w) * amplitude; } diff --git a/libraries/stdlib/genglsl/mx_overlay.glsl b/libraries/stdlib/genglsl/mx_overlay.glsl index fb9f42a243..e1bec8ac44 100644 --- a/libraries/stdlib/genglsl/mx_overlay.glsl +++ b/libraries/stdlib/genglsl/mx_overlay.glsl @@ -1,6 +1,6 @@ float mx_overlay(float fg, float bg) { - return (fg < 0.5) ? (2 * fg * bg) : (1 - (1 - fg) * (1 - bg)); + return (fg < 0.5) ? (2.0 * fg * bg) : (1.0 - (1.0 - fg) * (1.0 - bg)); } vec2 mx_overlay(vec2 fg, vec2 bg) diff --git a/libraries/stdlib/genglsl/mx_overlay_color3.glsl b/libraries/stdlib/genglsl/mx_overlay_color3.glsl index 8fb062519f..97c9f3b737 100644 --- a/libraries/stdlib/genglsl/mx_overlay_color3.glsl +++ b/libraries/stdlib/genglsl/mx_overlay_color3.glsl @@ -2,5 +2,5 @@ void mx_overlay_color3(vec3 fg, vec3 bg, float mix, out vec3 result) { - result = mix * mx_overlay(fg, bg) + (1-mix) * bg; + result = mix * mx_overlay(fg, bg) + (1.0-mix) * bg; } diff --git a/libraries/stdlib/genglsl/mx_overlay_color4.glsl b/libraries/stdlib/genglsl/mx_overlay_color4.glsl index 7b18db17bd..e331028a7f 100644 --- a/libraries/stdlib/genglsl/mx_overlay_color4.glsl +++ b/libraries/stdlib/genglsl/mx_overlay_color4.glsl @@ -2,5 +2,5 @@ void mx_overlay_color4(vec4 fg, vec4 bg, float mix, out vec4 result) { - result = mix * mx_overlay(fg, bg) + (1-mix) * bg; + result = mix * mx_overlay(fg, bg) + (1.0-mix) * bg; } diff --git a/libraries/stdlib/genglsl/mx_ramplr_float.glsl b/libraries/stdlib/genglsl/mx_ramplr_float.glsl index d19df8f75b..3040f138d4 100644 --- a/libraries/stdlib/genglsl/mx_ramplr_float.glsl +++ b/libraries/stdlib/genglsl/mx_ramplr_float.glsl @@ -1,4 +1,4 @@ void mx_ramplr_float(float valuel, float valuer, vec2 texcoord, out float result) { - result = mix (valuel, valuer, clamp(texcoord.x, 0, 1) ); + result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) ); } diff --git a/libraries/stdlib/genglsl/mx_ramplr_vector2.glsl b/libraries/stdlib/genglsl/mx_ramplr_vector2.glsl index 58b99a92d0..752e5bc3f5 100644 --- a/libraries/stdlib/genglsl/mx_ramplr_vector2.glsl +++ b/libraries/stdlib/genglsl/mx_ramplr_vector2.glsl @@ -1,4 +1,4 @@ void mx_ramplr_vector2(vec2 valuel, vec2 valuer, vec2 texcoord, out vec2 result) { - result = mix (valuel, valuer, clamp(texcoord.x, 0, 1) ); + result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) ); } diff --git a/libraries/stdlib/genglsl/mx_ramplr_vector3.glsl b/libraries/stdlib/genglsl/mx_ramplr_vector3.glsl index 1bf476173d..bc6b9f0bbd 100644 --- a/libraries/stdlib/genglsl/mx_ramplr_vector3.glsl +++ b/libraries/stdlib/genglsl/mx_ramplr_vector3.glsl @@ -1,4 +1,4 @@ void mx_ramplr_vector3(vec3 valuel, vec3 valuer, vec2 texcoord, out vec3 result) { - result = mix (valuel, valuer, clamp(texcoord.x, 0, 1) ); + result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) ); } diff --git a/libraries/stdlib/genglsl/mx_ramplr_vector4.glsl b/libraries/stdlib/genglsl/mx_ramplr_vector4.glsl index 458a2d749d..a3c4c161db 100644 --- a/libraries/stdlib/genglsl/mx_ramplr_vector4.glsl +++ b/libraries/stdlib/genglsl/mx_ramplr_vector4.glsl @@ -1,4 +1,4 @@ void mx_ramplr_vector4(vec4 valuel, vec4 valuer, vec2 texcoord, out vec4 result) { - result = mix (valuel, valuer, clamp(texcoord.x, 0, 1) ); + result = mix (valuel, valuer, clamp(texcoord.x, 0.0, 1.0) ); } diff --git a/libraries/stdlib/genglsl/mx_ramptb_float.glsl b/libraries/stdlib/genglsl/mx_ramptb_float.glsl index 41d8546508..bd452ffa5f 100644 --- a/libraries/stdlib/genglsl/mx_ramptb_float.glsl +++ b/libraries/stdlib/genglsl/mx_ramptb_float.glsl @@ -1,4 +1,4 @@ void mx_ramptb_float(float valuet, float valueb, vec2 texcoord, out float result) { - result = mix (valuet, valueb, clamp(texcoord.y, 0, 1) ); + result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) ); } diff --git a/libraries/stdlib/genglsl/mx_ramptb_vector2.glsl b/libraries/stdlib/genglsl/mx_ramptb_vector2.glsl index f305eb652b..494b5f665b 100644 --- a/libraries/stdlib/genglsl/mx_ramptb_vector2.glsl +++ b/libraries/stdlib/genglsl/mx_ramptb_vector2.glsl @@ -1,4 +1,4 @@ void mx_ramptb_vector2(vec2 valuet, vec2 valueb, vec2 texcoord, out vec2 result) { - result = mix (valuet, valueb, clamp(texcoord.y, 0, 1) ); + result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) ); } diff --git a/libraries/stdlib/genglsl/mx_ramptb_vector3.glsl b/libraries/stdlib/genglsl/mx_ramptb_vector3.glsl index 870049f54f..180200a925 100644 --- a/libraries/stdlib/genglsl/mx_ramptb_vector3.glsl +++ b/libraries/stdlib/genglsl/mx_ramptb_vector3.glsl @@ -1,4 +1,4 @@ void mx_ramptb_vector3(vec3 valuet, vec3 valueb, vec2 texcoord, out vec3 result) { - result = mix (valuet, valueb, clamp(texcoord.y, 0, 1) ); + result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) ); } diff --git a/libraries/stdlib/genglsl/mx_ramptb_vector4.glsl b/libraries/stdlib/genglsl/mx_ramptb_vector4.glsl index 367daae362..ba6f9c6def 100644 --- a/libraries/stdlib/genglsl/mx_ramptb_vector4.glsl +++ b/libraries/stdlib/genglsl/mx_ramptb_vector4.glsl @@ -1,4 +1,4 @@ void mx_ramptb_vector4(vec4 valuet, vec4 valueb, vec2 texcoord, out vec4 result) { - result = mix (valuet, valueb, clamp(texcoord.y, 0, 1) ); + result = mix (valuet, valueb, clamp(texcoord.y, 0.0, 1.0) ); } diff --git a/libraries/stdlib/genglsl/mx_screen.inline b/libraries/stdlib/genglsl/mx_screen.inline index 741b58d953..cbfa5872e1 100644 --- a/libraries/stdlib/genglsl/mx_screen.inline +++ b/libraries/stdlib/genglsl/mx_screen.inline @@ -1 +1 @@ -({{mix}}*((1.0 - (1.0 - {{fg}})) * (1 - {{bg}}))) + ((1.0-{{mix}})*{{bg}}) +({{mix}}*((1.0 - (1.0 - {{fg}})) * (1.0 - {{bg}}))) + ((1.0-{{mix}})*{{bg}}) diff --git a/source/JsMaterialX/JsMaterialXView/.gitignore b/source/JsMaterialX/JsMaterialXView/.gitignore new file mode 100644 index 0000000000..2ccbe4656c --- /dev/null +++ b/source/JsMaterialX/JsMaterialXView/.gitignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/source/JsMaterialX/JsMaterialXView/index.html b/source/JsMaterialX/JsMaterialXView/index.html new file mode 100644 index 0000000000..7da2f3f8fa --- /dev/null +++ b/source/JsMaterialX/JsMaterialXView/index.html @@ -0,0 +1,13 @@ + + + + + Getting Started + + +
+ +
+ + + \ No newline at end of file diff --git a/source/JsMaterialX/JsMaterialXView/package.json b/source/JsMaterialX/JsMaterialXView/package.json new file mode 100644 index 0000000000..a9ff24ded3 --- /dev/null +++ b/source/JsMaterialX/JsMaterialXView/package.json @@ -0,0 +1,20 @@ +{ + "name": "jsmaterialxview", + "version": "1.0.0", + "description": "MaterialX Web Viewer", + "main": "src/index.js", + "scripts": { + "start": "webpack serve" + }, + "author": "", + "license": "ISC", + "dependencies": { + "three": "^0.128.0", + "webpack": "^5.37.1" + }, + "devDependencies": { + "copy-webpack-plugin": "^8.1.1", + "webpack-cli": "^4.7.0", + "webpack-dev-server": "^3.11.2" + } +} diff --git a/source/JsMaterialX/JsMaterialXView/shader-frag.glsl b/source/JsMaterialX/JsMaterialXView/shader-frag.glsl new file mode 100644 index 0000000000..f9ddb2a2f9 --- /dev/null +++ b/source/JsMaterialX/JsMaterialXView/shader-frag.glsl @@ -0,0 +1,1446 @@ +#version 300 es + +precision mediump float; + +#define M_PI 3.1415926535897932384626433832795 +#define M_PI_INV 1.0/3.1415926535897932384626433832795 +#define M_GOLDEN_RATIO 1.6180339887498948482045868343656 +#define M_FLOAT_EPS 1e-8 +#define MAX_LIGHT_SOURCES 1 +#define DIRECTIONAL_ALBEDO_METHOD 0 +#define MAX_ENV_RADIANCE_SAMPLES 1024 + +#define BSDF vec3 +#define EDF vec3 +struct VDF { vec3 absorption; vec3 scattering; }; +struct surfaceshader { vec3 color; vec3 transparency; }; +struct volumeshader { VDF vdf; EDF edf; }; +struct displacementshader { vec3 offset; float scale; }; +struct lightshader { vec3 intensity; vec3 direction; }; +struct thinfilm { float thickness; float ior; }; + +// Uniform block: PrivateUniforms +uniform mat4 u_envMatrix; +uniform sampler2D u_envRadiance; +uniform int u_envRadianceMips; +uniform int u_envRadianceSamples; +uniform sampler2D u_envIrradiance; +uniform vec3 u_viewPosition; +uniform int u_numActiveLightSources; + +// Uniform block: PublicUniforms +uniform float base; +uniform vec3 base_color; +uniform float diffuse_roughness; +uniform float metalness; +uniform float specular; +uniform vec3 specular_color; +uniform float specular_roughness; +uniform float specular_IOR; +uniform float specular_anisotropy; +uniform float specular_rotation; +uniform float transmission; +uniform vec3 transmission_color; +uniform float transmission_depth; +uniform vec3 transmission_scatter; +uniform float transmission_scatter_anisotropy; +uniform float transmission_dispersion; +uniform float transmission_extra_roughness; +uniform float subsurface; +uniform vec3 subsurface_color; +uniform vec3 subsurface_radius; +uniform float subsurface_scale; +uniform float subsurface_anisotropy; +uniform float sheen; +uniform vec3 sheen_color; +uniform float sheen_roughness; +uniform float coat; +uniform vec3 coat_color; +uniform float coat_roughness; +uniform float coat_anisotropy; +uniform float coat_rotation; +uniform float coat_IOR; +uniform float coat_affect_color; +uniform float coat_affect_roughness; +uniform float thin_film_thickness; +uniform float thin_film_IOR; +uniform float emission; +uniform vec3 emission_color; +uniform vec3 opacity; +uniform bool thin_walled; + +struct LightData +{ + int type; + vec3 direction; + vec3 color; + float intensity; +}; + +uniform LightData u_lightData[MAX_LIGHT_SOURCES]; + +in vec3 normalWorld; +in vec3 tangentWorld; +in vec3 positionWorld; + +// Pixel shader outputs +out vec4 out1; + +float mx_square(float x) +{ + return x*x; +} + +vec2 mx_square(vec2 x) +{ + return x*x; +} + +vec3 mx_square(vec3 x) +{ + return x*x; +} + +vec4 mx_square(vec4 x) +{ + return x*x; +} + +float mx_pow5(float x) +{ + return mx_square(mx_square(x)) * x; +} + +float mx_max_component(vec2 v) +{ + return max(v.x, v.y); +} + +float mx_max_component(vec3 v) +{ + return max(max(v.x, v.y), v.z); +} + +float mx_max_component(vec4 v) +{ + return max(max(max(v.x, v.y), v.z), v.w); +} + +bool mx_is_tiny(float v) +{ + return abs(v) < M_FLOAT_EPS; +} + +bool mx_is_tiny(vec3 v) +{ + return all(lessThan(abs(v), vec3(M_FLOAT_EPS))); +} + +float mx_mix(float v00, float v01, float v10, float v11, + float x, float y) +{ + float v0_ = mix(v00, v01, x); + float v1_ = mix(v10, v11, x); + return mix(v0_, v1_, y); +} + +vec2 mx_latlong_projection(vec3 dir) +{ + float latitude = -asin(dir.y) * M_PI_INV + 0.5; + float longitude = atan(dir.x, -dir.z) * M_PI_INV * 0.5 + 0.5; + return vec2(longitude, latitude); +} + +vec3 mx_latlong_map_lookup(vec3 dir, mat4 transform, float lod, sampler2D sampler) +{ + vec3 envDir = normalize((transform * vec4(dir,0.0)).xyz); + vec2 uv = mx_latlong_projection(envDir); + return textureLod(sampler, uv, lod).rgb; +} + +vec3 mx_forward_facing_normal(vec3 N, vec3 V) +{ + if (dot(N, V) < 0.0) + { + return -N; + } + else + { + return N; + } +} + +// 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)); +} + +// Compute the average of an anisotropic roughness pair +float mx_average_roughness(vec2 roughness) +{ + return sqrt(roughness.x * roughness.y); +} + +// https://www.graphics.rwth-aachen.de/publication/2/jgt.pdf +float mx_golden_ratio_sequence(int i) +{ + return fract((float(i) + 1.0) * M_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)); +} + +// https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf +// Appendix B.2 Equation 13 +float mx_ggx_NDF(vec3 X, vec3 Y, vec3 H, float NdotH, float alphaX, float alphaY) +{ + float XdotH = dot(X, H); + float YdotH = dot(Y, H); + float denom = mx_square(XdotH / alphaX) + mx_square(YdotH / alphaY) + mx_square(NdotH); + return 1.0 / (M_PI * alphaX * alphaY * mx_square(denom)); +} + +// https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf +// Appendix B.1 Equation 3 +float mx_ggx_PDF(vec3 X, vec3 Y, vec3 H, float NdotH, float LdotH, float alphaX, float alphaY) +{ + return mx_ggx_NDF(X, Y, H, NdotH, alphaX, alphaY) * NdotH / (4.0 * LdotH); +} + +// https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf +// Appendix B.2 Equation 15 +vec3 mx_ggx_importance_sample_NDF(vec2 Xi, vec3 X, vec3 Y, vec3 N, float alphaX, float alphaY) +{ + float phi = 2.0 * M_PI * Xi.x; + float tanTheta = sqrt(Xi.y / (1.0 - Xi.y)); + vec3 H = X * (tanTheta * alphaX * cos(phi)) + + Y * (tanTheta * alphaY * sin(phi)) + + N; + return normalize(H); +} + +// http://jcgt.org/published/0003/02/03/paper.pdf +// Equations 72 and 99 +float mx_ggx_smith_G(float NdotL, float NdotV, float alpha) +{ + float alpha2 = mx_square(alpha); + float lambdaL = sqrt(alpha2 + (1.0 - alpha2) * mx_square(NdotL)); + float lambdaV = sqrt(alpha2 + (1.0 - alpha2) * mx_square(NdotV)); + return 2.0 / (lambdaL / NdotL + lambdaV / NdotV); +} + +// https://www.unrealengine.com/blog/physically-based-shading-on-mobile +vec3 mx_ggx_directional_albedo_curve_fit(float NdotV, float roughness, vec3 F0, vec3 F90) +{ + const vec4 c0 = vec4(-1, -0.0275, -0.572, 0.022); + const vec4 c1 = vec4( 1, 0.0425, 1.04, -0.04 ); + vec4 r = roughness * c0 + c1; + float a004 = min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x + r.y; + vec2 AB = vec2(-1.04, 1.04) * a004 + r.zw; + return F0 * AB.x + F90 * AB.y; +} + +vec3 mx_ggx_directional_albedo_table_lookup(float NdotV, float roughness, vec3 F0, vec3 F90) +{ +#if DIRECTIONAL_ALBEDO_METHOD == 1 + vec2 res = textureSize(u_albedoTable, 0); + if (res.x > 1) + { + vec2 AB = texture(u_albedoTable, vec2(NdotV, roughness)).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_directional_albedo_importance_sample(float NdotV, float roughness, vec3 F0, vec3 F90) +{ + NdotV = clamp(NdotV, M_FLOAT_EPS, 1.0); + vec3 V = vec3(sqrt(1.0f - 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_NDF(Xi, vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1), roughness, roughness); + vec3 L = -reflect(V, H); + + // 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); + 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 geometric visibility term. + float Gvis = mx_ggx_smith_G(NdotL, NdotV, roughness) * VdotH / (NdotH * NdotV); + + // Add the contribution of this sample. + AB += vec2(Gvis * (1.0 - Fc), Gvis * Fc); + } + + // Normalize integrated terms. + AB /= float(SAMPLE_COUNT); + + // Return the final directional albedo. + return F0 * AB.x + F90 * AB.y; +} + +vec3 mx_ggx_directional_albedo(float NdotV, float roughness, vec3 F0, vec3 F90) +{ +#if DIRECTIONAL_ALBEDO_METHOD == 0 + return mx_ggx_directional_albedo_curve_fit(NdotV, roughness, F0, F90); +#elif DIRECTIONAL_ALBEDO_METHOD == 1 + return mx_ggx_directional_albedo_table_lookup(NdotV, roughness, F0, F90); +#else + return mx_ggx_directional_albedo_importance_sample(NdotV, roughness, F0, F90); +#endif +} + +float mx_ggx_directional_albedo(float NdotV, float roughness, float F0, float F90) +{ + return mx_ggx_directional_albedo(NdotV, roughness, 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 roughness, vec3 Fss) +{ + float Ess = mx_ggx_directional_albedo(NdotV, roughness, 1.0, 1.0); + return 1.0 + Fss * (1.0 - Ess) / Ess; +} + +float mx_ggx_energy_compensation(float NdotV, float roughness, float Fss) +{ + return mx_ggx_energy_compensation(NdotV, roughness, vec3(Fss)).x; +} + +// 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)); +} + +// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/ +float mx_fresnel_dielectric(float cosTheta, float ior) +{ + if (cosTheta < 0.0) + return 1.0; + + float g = ior*ior + cosTheta*cosTheta - 1.0; + // Check for total internal reflection + if (g < 0.0) + return 1.0; + + g = sqrt(g); + float gmc = g - cosTheta; + float gpc = g + cosTheta; + float x = gmc / gpc; + float y = (gpc * cosTheta - 1.0) / (gmc * cosTheta + 1.0); + return 0.5 * x * x * (1.0 + y * y); +} + +vec3 mx_fresnel_conductor(float cosTheta, vec3 n, vec3 k) +{ + float c2 = cosTheta*cosTheta; + vec3 n2_k2 = n*n + k*k; + vec3 nc2 = 2.0 * n * cosTheta; + + vec3 rs_a = n2_k2 + c2; + vec3 rp_a = n2_k2 * c2 + 1.0; + vec3 rs = (rs_a - nc2) / (rs_a + nc2); + vec3 rp = (rp_a - nc2) / (rp_a + nc2); + + return 0.5 * (rs + rp); +} + +// Fresnel for dielectric/dielectric interface and polarized light. +void mx_fresnel_dielectric_polarized(float cosTheta, float n1, float n2, out vec2 F, out vec2 phi) +{ + float eta2 = mx_square(n1 / n2); + float st2 = 1.0 - cosTheta*cosTheta; + + // Check for total internal reflection + if(eta2*st2 > 1.0) + { + F = vec2(1.0); + float s = sqrt(st2 - 1.0/eta2) / cosTheta; + phi = 2.0 * atan(vec2(-eta2 * s, -s)); + return; + } + + float cosTheta_t = sqrt(1.0 - eta2 * st2); + vec2 r = vec2((n2*cosTheta - n1*cosTheta_t) / (n2*cosTheta + n1*cosTheta_t), + (n1*cosTheta - n2*cosTheta_t) / (n1*cosTheta + n2*cosTheta_t)); + F = mx_square(r); + phi.x = (r.x < 0.0) ? M_PI : 0.0; + phi.y = (r.y < 0.0) ? M_PI : 0.0; +} + +// Fresnel for dielectric/conductor interface and polarized light. +// TODO: Optimize this functions and support wavelength dependent complex refraction index. +void mx_fresnel_conductor_polarized(float cosTheta, float n1, float n2, float k, out vec2 F, out vec2 phi) +{ + if (k == 0.0) + { + // Use dielectric formula to avoid numerical issues + mx_fresnel_dielectric_polarized(cosTheta, n1, n2, F, phi); + return; + } + + float A = mx_square(n2) * (1.0 - mx_square(k)) - mx_square(n1) * (1.0 - mx_square(cosTheta)); + float B = sqrt(mx_square(A) + mx_square(2.0 * mx_square(n2) * k)); + float U = sqrt((A+B) / 2.0); + float V = sqrt((B-A) / 2.0); + + F.y = (mx_square(n1*cosTheta - U) + mx_square(V)) / (mx_square(n1*cosTheta + U) + mx_square(V)); + phi.y = atan(2.0*n1 * V*cosTheta, mx_square(U) + mx_square(V) - mx_square(n1*cosTheta)) + M_PI; + + F.x = (mx_square(mx_square(n2) * (1.0 - mx_square(k)) * cosTheta - n1*U) + mx_square(2.0 * mx_square(n2) * k * cosTheta - n1*V)) / + (mx_square(mx_square(n2) * (1.0 - mx_square(k)) * cosTheta + n1*U) + mx_square(2.0 * mx_square(n2) * k * cosTheta + n1*V)); + phi.x = atan(2.0 * n1 * mx_square(n2) * cosTheta * (2.0*k*U - (1.0 - mx_square(k)) * V), mx_square(mx_square(n2) * (1.0 + mx_square(k)) * cosTheta) - mx_square(n1) * (mx_square(U) + mx_square(V))); +} + +// 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); + +// Depolarization functions for natural light +float mx_depolarize(vec2 v) +{ + return 0.5 * (v.x + v.y); +} +vec3 mx_depolarize(vec3 s, vec3 p) +{ + return 0.5 * (s + p); +} + +// Evaluation XYZ sensitivity curves in Fourier space +vec3 mx_eval_sensitivity(float opd, float shift) +{ + // Use Gaussian fits, given by 3 parameters: val, pos and var + float phase = 2.0*M_PI * opd; + vec3 val = vec3(5.4856e-13, 4.4201e-13, 5.2481e-13); + vec3 pos = vec3(1.6810e+06, 1.7953e+06, 2.2084e+06); + vec3 var = vec3(4.3278e+09, 9.3046e+09, 6.6121e+09); + vec3 xyz = val * sqrt(2.0*M_PI * var) * cos(pos * phase + shift) * exp(- var * phase*phase); + xyz.x += 9.7470e-14 * sqrt(2.0*M_PI * 4.5282e+09) * cos(2.2399e+06 * phase + shift) * 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/2017/05/01/brdf-thin-film.html +vec3 mx_fresnel_airy(float cosTheta, vec3 ior, vec3 extinction, float tf_thickness, float tf_ior) +{ + // Convert nm -> m + float d = tf_thickness * 1.0e-9; + + // Assume vacuum on the outside + float eta1 = 1.0; + float eta2 = tf_ior; + + // Optical path difference + float cosTheta2 = sqrt(1.0 - mx_square(eta1/eta2) * (1.0 - mx_square(cosTheta))); + float D = 2.0 * eta2 * d * cosTheta2; + + // First interface + vec2 R12, phi12; + mx_fresnel_dielectric_polarized(cosTheta, eta1, eta2, R12, phi12); + vec2 R21 = R12; + vec2 T121 = vec2(1.0) - R12; + vec2 phi21 = vec2(M_PI) - phi12; + + // Second interface + vec2 R23, phi23; + mx_fresnel_conductor_polarized(cosTheta2, eta2, ior.x, extinction.x, R23, phi23); + + // Phase shift + vec2 phi2 = phi21 + phi23; + + // Compound terms + vec3 R = vec3(0.0); + vec2 R123 = R12*R23; + vec2 r123 = sqrt(R123); + vec2 Rs = mx_square(T121)*R23 / (1.0-R123); + + // Reflectance term for m=0 (DC term amplitude) + vec2 C0 = R12 + Rs; + vec3 S0 = mx_eval_sensitivity(0.0, 0.0); + R += mx_depolarize(C0) * S0; + + // Reflectance term for m>0 (pairs of diracs) + vec2 Cm = Rs - T121; + for (int m=1; m<=3; ++m) + { + Cm *= r123; + vec3 SmS = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*phi2.x); + vec3 SmP = 2.0 * mx_eval_sensitivity(float(m)*D, float(m)*phi2.y); + R += mx_depolarize(Cm.x*SmS, Cm.y*SmP); + } + + // Convert back to RGB reflectance + R = clamp(XYZ_TO_RGB * R, vec3(0.0), vec3(1.0)); + + return R; +} + +// Parameters for Fresnel calculations. +struct FresnelData +{ + vec3 ior; // In Schlick Fresnel mode these two + vec3 extinction; // hold F0 and F90 reflectance values + float exponent; + float tf_thickness; + float tf_ior; + int model; +}; + +FresnelData mx_init_fresnel_dielectric(float ior) +{ + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); + fd.model = 0; + fd.ior = vec3(ior); + fd.tf_thickness = 0.0f; + return fd; +} + +FresnelData mx_init_fresnel_conductor(vec3 ior, vec3 extinction) +{ + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); + fd.model = 1; + fd.ior = ior; + fd.extinction = extinction; + fd.tf_thickness = 0.0f; + return fd; +} + +FresnelData mx_init_fresnel_schlick(vec3 F0) +{ + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); + fd.model = 2; + fd.ior = F0; + fd.extinction = vec3(1.0); + fd.exponent = 5.0f; + fd.tf_thickness = 0.0f; + return fd; +} + +FresnelData mx_init_fresnel_schlick(vec3 F0, vec3 F90, float exponent) +{ + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); + fd.model = 2; + fd.ior = F0; + fd.extinction = F90; + fd.exponent = exponent; + fd.tf_thickness = 0.0f; + return fd; +} + +FresnelData mx_init_fresnel_dielectric_airy(float ior, float tf_thickness, float tf_ior) +{ + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); + fd.model = 3; + fd.ior = vec3(ior); + fd.extinction = vec3(0.0); + fd.tf_thickness = tf_thickness; + fd.tf_ior = tf_ior; + return fd; +} + +FresnelData mx_init_fresnel_conductor_airy(vec3 ior, vec3 extinction, float tf_thickness, float tf_ior) +{ + FresnelData fd = FresnelData(vec3(0.0), vec3(0.0), 0.0, 0.0, 0.0, -1); + fd.model = 3; + fd.ior = ior; + fd.extinction = extinction; + fd.tf_thickness = tf_thickness; + fd.tf_ior = tf_ior; + return fd; +} + +vec3 mx_compute_fresnel(float cosTheta, FresnelData fd) +{ + if (fd.model == 0) + return vec3(mx_fresnel_dielectric(cosTheta, fd.ior.x)); + else if (fd.model == 1) + return mx_fresnel_conductor(cosTheta, fd.ior, fd.extinction); + else if (fd.model == 2) + // ior & extinction holds F0 & F90 + return mx_fresnel_schlick(cosTheta, fd.ior, fd.extinction, fd.exponent); + else + return mx_fresnel_airy(cosTheta, fd.ior, fd.extinction, fd.tf_thickness, fd.tf_ior); +} + +int numRadianceSamples() +{ + return min(u_envRadianceSamples, MAX_ENV_RADIANCE_SAMPLES); +} + +// https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch20.html +// Section 20.4 Equation 13 +float mx_latlong_compute_lod(vec3 dir, float pdf, float maxMipLevel, int envSamples) +{ + const float MIP_LEVEL_OFFSET = 1.5; + float effectiveMaxMipLevel = maxMipLevel - MIP_LEVEL_OFFSET; + float distortion = sqrt(1.0 - mx_square(dir.y)); + return max(effectiveMaxMipLevel - 0.5 * log2(float(envSamples) * pdf * distortion), 0.0); +} + +vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 roughness, int distribution, FresnelData fd) +{ + vec3 Y = normalize(cross(N, X)); + X = cross(Y, N); + + // Compute shared dot products. + float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); + + // 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 = numRadianceSamples(); + 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_NDF(Xi, X, Y, N, roughness.x, roughness.y); + vec3 L = -reflect(V, H); + + // Compute dot products for this sample. + float NdotH = clamp(dot(N, H), M_FLOAT_EPS, 1.0); + float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0); + float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0); + float LdotH = VdotH; + + // Sample the environment light from the given direction. + float pdf = mx_ggx_PDF(X, Y, H, NdotH, LdotH, roughness.x, roughness.y); + float lod = mx_latlong_compute_lod(L, pdf, float(u_envRadianceMips - 1), envRadianceSamples); + vec3 sampleColor = mx_latlong_map_lookup(L, u_envMatrix, lod, u_envRadiance); + + // Compute the Fresnel term. + vec3 F = mx_compute_fresnel(VdotH, fd); + + // Compute the geometric term. + float G = mx_ggx_smith_G(NdotL, NdotV, mx_average_roughness(roughness)); + + // 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 * NdotH / (4 * VdotH) + // radiance = incidentLight * microfacetSpecular / pdf + radiance += sampleColor * F * G * VdotH / (NdotV * NdotH); + } + + // Normalize and return the final radiance. + radiance /= float(envRadianceSamples); + return radiance; +} + +vec3 mx_environment_irradiance(vec3 N) +{ + return mx_latlong_map_lookup(N, u_envMatrix, 0.0, u_envIrradiance); +} + +void mx_directional_light(LightData light, vec3 position, out lightshader result) +{ + result.direction = -light.direction; + result.intensity = light.color * light.intensity; +} + +int numActiveLightSources() +{ + return min(u_numActiveLightSources, MAX_LIGHT_SOURCES) ; +} + +void sampleLightSource(LightData light, vec3 position, out lightshader result) +{ + result.intensity = vec3(0.0); + result.direction = vec3(0.0); + if (light.type == 1) + { + mx_directional_light(light, position, result); + } +} + +void mx_roughness_anisotropy(float roughness, float anisotropy, out vec2 result) +{ + float roughness_sqr = clamp(roughness*roughness, M_FLOAT_EPS, 1.0); + if (anisotropy > 0.0) + { + float aspect = sqrt(1.0 - clamp(anisotropy, 0.0, 0.98)); + result.x = min(roughness_sqr / aspect, 1.0); + result.y = roughness_sqr * aspect; + } + else + { + result.x = roughness_sqr; + result.y = roughness_sqr; + } +} + +void mx_luminance_color3(vec3 _in, vec3 lumacoeffs, out vec3 result) +{ + result = vec3(dot(_in, lumacoeffs)); +} + + +// http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf +// Equation 2 +float mx_imageworks_sheen_NDF(float cosTheta, float roughness) +{ + float invRoughness = 1.0 / max(roughness, 0.0001); + float cos2 = cosTheta * cosTheta; + float sin2 = 1.0 - cos2; + return (2.0 + invRoughness) * pow(sin2, invRoughness * 0.5) / (2.0 * M_PI); +} + +// LUT for sheen directional albedo. +// A 2D table parameterized with 'cosTheta' (cosine of angle to normal) on x-axis and 'roughness' on y-axis. +#define SHEEN_ALBEDO_TABLE_SIZE 16 +const float u_sheenAlbedo[SHEEN_ALBEDO_TABLE_SIZE*SHEEN_ALBEDO_TABLE_SIZE] = float[]( + 1.6177, 0.978927, 0.618938, 0.391714, 0.245177, 0.150234, 0.0893475, 0.0511377, 0.0280191, 0.0144204, 0.00687674, 0.00295935, 0.00111049, 0.000336768, 7.07119e-05, 6.22646e-06, + 1.1084, 0.813928, 0.621389, 0.479304, 0.370299, 0.284835, 0.21724, 0.163558, 0.121254, 0.0878921, 0.0619052, 0.0419894, 0.0270556, 0.0161443, 0.00848212, 0.00342323, + 0.930468, 0.725652, 0.586532, 0.479542, 0.393596, 0.322736, 0.26353, 0.213565, 0.171456, 0.135718, 0.105481, 0.0800472, 0.0588117, 0.0412172, 0.0268329, 0.0152799, + 0.833791, 0.671201, 0.558957, 0.471006, 0.398823, 0.337883, 0.285615, 0.240206, 0.200696, 0.16597, 0.135422, 0.10859, 0.0850611, 0.0644477, 0.0464763, 0.0308878, + 0.771692, 0.633819, 0.537877, 0.461939, 0.398865, 0.344892, 0.297895, 0.256371, 0.219562, 0.186548, 0.156842, 0.130095, 0.10598, 0.0841919, 0.0645311, 0.04679, + 0.727979, 0.606373, 0.52141, 0.453769, 0.397174, 0.348337, 0.305403, 0.267056, 0.232655, 0.201398, 0.17286, 0.146756, 0.122808, 0.100751, 0.0804254, 0.0616485, + 0.695353, 0.585281, 0.508227, 0.44667, 0.394925, 0.350027, 0.310302, 0.274561, 0.242236, 0.212604, 0.185281, 0.16002, 0.13657, 0.114693, 0.0942543, 0.0750799, + 0.669981, 0.568519, 0.497442, 0.440542, 0.392567, 0.350786, 0.313656, 0.280075, 0.249533, 0.221359, 0.195196, 0.170824, 0.148012, 0.126537, 0.106279, 0.0870713, + 0.649644, 0.554855, 0.488453, 0.435237, 0.390279, 0.351028, 0.316036, 0.284274, 0.255266, 0.228387, 0.203297, 0.179796, 0.157665, 0.136695, 0.116774, 0.0977403, + 0.632951, 0.543489, 0.480849, 0.430619, 0.388132, 0.350974, 0.317777, 0.287562, 0.259885, 0.234153, 0.210041, 0.187365, 0.165914, 0.145488, 0.125983, 0.10724, + 0.61899, 0.533877, 0.47433, 0.426573, 0.386145, 0.35075, 0.319078, 0.290197, 0.263681, 0.238971, 0.215746, 0.193838, 0.173043, 0.153167, 0.134113, 0.115722, + 0.607131, 0.52564, 0.468678, 0.423001, 0.38432, 0.35043, 0.320072, 0.292349, 0.266856, 0.243055, 0.220636, 0.199438, 0.179264, 0.159926, 0.141332, 0.123323, + 0.596927, 0.518497, 0.463731, 0.419829, 0.382647, 0.350056, 0.320842, 0.294137, 0.269549, 0.246564, 0.224875, 0.204331, 0.18474, 0.165919, 0.147778, 0.130162, + 0.588052, 0.512241, 0.459365, 0.416996, 0.381114, 0.349657, 0.321448, 0.295641, 0.271862, 0.24961, 0.228584, 0.208643, 0.189596, 0.171266, 0.153566, 0.136341, + 0.580257, 0.506717, 0.455481, 0.41445, 0.379708, 0.34925, 0.321929, 0.296923, 0.273869, 0.252279, 0.231859, 0.212472, 0.193933, 0.176066, 0.158788, 0.141945, + 0.573355, 0.5018, 0.452005, 0.412151, 0.378416, 0.348844, 0.322316, 0.298028, 0.275627, 0.254638, 0.234772, 0.215896, 0.197828, 0.180398, 0.163522, 0.147049 +); + +float mx_imageworks_sheen_directional_albedo(float cosTheta, float roughness) +{ + float x = cosTheta * float(SHEEN_ALBEDO_TABLE_SIZE - 1); + float y = roughness * float(SHEEN_ALBEDO_TABLE_SIZE - 1); + int ix = int(x); + int iy = int(y); + int ix2 = clamp(ix + 1, 0, SHEEN_ALBEDO_TABLE_SIZE - 1); + int iy2 = clamp(iy + 1, 0, SHEEN_ALBEDO_TABLE_SIZE - 1); + float fx = x - float(ix); + float fy = y - float(iy); + + // Bi-linear interpolation of the LUT values + float v1 = mix(u_sheenAlbedo[iy * SHEEN_ALBEDO_TABLE_SIZE + ix], u_sheenAlbedo[iy * SHEEN_ALBEDO_TABLE_SIZE + ix2], fx); + float v2 = mix(u_sheenAlbedo[iy2 * SHEEN_ALBEDO_TABLE_SIZE + ix], u_sheenAlbedo[iy2 * SHEEN_ALBEDO_TABLE_SIZE + ix2], fx); + float albedo = mix(v1, v2, fy); + + return clamp(albedo, 0.0, 1.0); +} + +void mx_sheen_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 N, BSDF base, out BSDF result) +{ + if (weight < M_FLOAT_EPS) + { + result = base; + return; + } + + N = mx_forward_facing_normal(N, V); + + vec3 H = normalize(L + V); + + float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0); + float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); + float NdotH = clamp(dot(N, H), M_FLOAT_EPS, 1.0); + + float D = mx_imageworks_sheen_NDF(NdotH, roughness); + + // Geometry term is skipped and we use a smoother denominator, as in: + // https://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf + vec3 fr = D * color / (4.0 * (NdotL + NdotV - NdotL*NdotV)); + + float dirAlbedo = mx_imageworks_sheen_directional_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. + result = fr * NdotL * occlusion * weight // Top layer reflection + + base * (1.0 - dirAlbedo * weight); // Base layer reflection attenuated by top layer +} + +void mx_sheen_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 N, BSDF base, out vec3 result) +{ + if (weight <= 0.0) + { + result = base; + return; + } + + N = mx_forward_facing_normal(N, V); + + float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); + + float dirAlbedo = mx_imageworks_sheen_directional_albedo(NdotV, roughness); + + vec3 Li = mx_environment_irradiance(N); + result = Li * color * dirAlbedo * weight // Top layer reflection + + base * (1.0 - dirAlbedo * weight); // Base layer reflection attenuated by top layer +} + +mat4 mx_rotationMatrix(vec3 axis, float angle) +{ + axis = normalize(axis); + float s = sin(angle); + float c = cos(angle); + float oc = 1.0 - c; + + return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0, + oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0, + oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0, + 0.0, 0.0, 0.0, 1.0); +} + +void mx_rotate_vector3(vec3 _in, float amount, vec3 axis, out vec3 result) +{ + float rotationRadians = radians(amount); + mat4 m = mx_rotationMatrix(axis, rotationRadians); + result = (m * vec4(_in, 1.0)).xyz; +} + +// "Artist Friendly Metallic Fresnel", Ole Gulbrandsen, 2014 +// http://jcgt.org/published/0003/04/03/paper.pdf + +void mx_complex_to_artistic_ior(vec3 ior, vec3 extinction, out vec3 reflectivity, out vec3 edge_color) +{ + vec3 nm1 = ior - 1.0; + vec3 np1 = ior + 1.0; + vec3 k2 = extinction * extinction; + vec3 r = (nm1*nm1 + k2) / (np1*np1 + k2); + reflectivity = r; + + 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); + edge_color = (n_max - ior) / (n_max - n_min); +} + +void mx_artistic_to_complex_ior(vec3 reflectivity, vec3 edge_color, out vec3 ior, out vec3 extinction) +{ + vec3 r = clamp(reflectivity, 0.0, 0.99); + vec3 r_sqrt = sqrt(r); + vec3 n_min = (1.0 - r) / (1.0 + r); + vec3 n_max = (1.0 + r_sqrt) / (1.0 - r_sqrt); + ior = mix(n_max, n_min, edge_color); + + vec3 np1 = ior + 1.0; + vec3 nm1 = ior - 1.0; + vec3 k2 = (np1*np1 * r - nm1*nm1) / (1.0 - r); + k2 = max(k2, 0.0); + extinction = sqrt(k2); +} + +void mx_artistic_ior(vec3 reflectivity, vec3 edge_color, out vec3 ior, out vec3 extinction) +{ + mx_artistic_to_complex_ior(reflectivity, edge_color, ior, extinction); +} + +void mx_uniform_edf(vec3 N, vec3 L, vec3 color, out EDF result) +{ + result = color; +} + + +void mx_dielectric_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, BSDF base, thinfilm tf, out BSDF result) +{ + if (weight < M_FLOAT_EPS) + { + result = base; + return; + } + + N = mx_forward_facing_normal(N, V); + + vec3 Y = normalize(cross(N, X)); + vec3 H = normalize(L + V); + + float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0); + float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); + float NdotH = clamp(dot(N, H), M_FLOAT_EPS, 1.0); + float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0); + + float avgRoughness = mx_average_roughness(roughness); + + FresnelData fd; + if (tf.thickness > 0.0) + fd = mx_init_fresnel_dielectric_airy(ior, tf.thickness, tf.ior); + else + fd = mx_init_fresnel_dielectric(ior); + + vec3 F = mx_compute_fresnel(VdotH, fd); + float D = mx_ggx_NDF(X, Y, H, NdotH, roughness.x, roughness.y); + float G = mx_ggx_smith_G(NdotL, NdotV, avgRoughness); + + float F0 = mx_ior_to_f0(ior); + vec3 comp = mx_ggx_energy_compensation(NdotV, avgRoughness, F); + vec3 dirAlbedo = mx_ggx_directional_albedo(NdotV, avgRoughness, F0, 1.0) * comp; + + // Note: NdotL is cancelled out + result = D * F * G * comp * tint * occlusion * weight / (4.0 * NdotV) // Top layer reflection + + base * (1.0 - dirAlbedo * weight); // Base layer reflection attenuated by top layer +} + +void mx_dielectric_bsdf_transmission(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, BSDF base, thinfilm tf, out BSDF result) +{ + if (scatter_mode == 1) + { + result = tint * weight; + return; + } + + if (scatter_mode == 2) + { + // No external layering in RT mode, + // the base is always T in this case. + base = tint * weight; + } + + if (weight < M_FLOAT_EPS) + { + result = base; + return; + } + + N = mx_forward_facing_normal(N, V); + float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); + + FresnelData fd; + if (tf.thickness > 0.0) + fd = mx_init_fresnel_dielectric_airy(ior, tf.thickness, tf.ior); + else + fd = mx_init_fresnel_dielectric(ior); + + vec3 F = mx_compute_fresnel(NdotV, fd); + + float avgRoughness = mx_average_roughness(roughness); + float F0 = mx_ior_to_f0(ior); + vec3 comp = mx_ggx_energy_compensation(NdotV, avgRoughness, F); + vec3 dirAlbedo = mx_ggx_directional_albedo(NdotV, avgRoughness, F0, 1.0) * comp; + + result = base * (1.0 - dirAlbedo * weight); // Transmission attenuated by reflection amount +} + +void mx_dielectric_bsdf_indirect(vec3 V, float weight, vec3 tint, float ior, vec2 roughness, vec3 N, vec3 X, int distribution, int scatter_mode, BSDF base, thinfilm tf, out BSDF result) +{ + if (weight < M_FLOAT_EPS) + { + result = base; + return; + } + + N = mx_forward_facing_normal(N, V); + + float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); + + FresnelData fd; + if (tf.thickness > 0.0) + fd = mx_init_fresnel_dielectric_airy(ior, tf.thickness, tf.ior); + else + fd = mx_init_fresnel_dielectric(ior); + + vec3 F = mx_compute_fresnel(NdotV, fd); + + float avgRoughness = mx_average_roughness(roughness); + float F0 = mx_ior_to_f0(ior); + vec3 comp = mx_ggx_energy_compensation(NdotV, avgRoughness, F); + vec3 dirAlbedo = mx_ggx_directional_albedo(NdotV, avgRoughness, F0, 1.0) * comp; + + vec3 Li = mx_environment_radiance(N, V, X, roughness, distribution, fd); + + result = Li * tint * comp * weight // Top layer reflection + + base * (1.0 - dirAlbedo * weight); // Base layer reflection attenuated by top layer +} + + +void mx_conductor_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thinfilm tf, out BSDF result) +{ + if (weight < M_FLOAT_EPS) + { + result = BSDF(0.0); + return; + } + + N = mx_forward_facing_normal(N, V); + + vec3 Y = normalize(cross(N, X)); + vec3 H = normalize(L + V); + + float NdotL = clamp(dot(N, L), M_FLOAT_EPS, 1.0); + float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); + float NdotH = clamp(dot(N, H), M_FLOAT_EPS, 1.0); + float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0); + + FresnelData fd; + if (tf.thickness > 0.0) + fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, tf.thickness, tf.ior); + else + fd = mx_init_fresnel_conductor(ior_n, ior_k); + + vec3 F = mx_compute_fresnel(VdotH, fd); + + float avgRoughness = mx_average_roughness(roughness); + float D = mx_ggx_NDF(X, Y, H, NdotH, roughness.x, roughness.y); + float G = mx_ggx_smith_G(NdotL, NdotV, avgRoughness); + + vec3 comp = mx_ggx_energy_compensation(NdotV, avgRoughness, F); + + // Note: NdotL is cancelled out + result = D * F * G * comp * occlusion * weight / (4.0 * NdotV); +} + +void mx_conductor_bsdf_indirect(vec3 V, float weight, vec3 ior_n, vec3 ior_k, vec2 roughness, vec3 N, vec3 X, int distribution, thinfilm tf, out BSDF result) +{ + if (weight < M_FLOAT_EPS) + { + result = BSDF(0.0); + return; + } + + N = mx_forward_facing_normal(N, V); + + float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); + + FresnelData fd; + if (tf.thickness > 0.0) + fd = mx_init_fresnel_conductor_airy(ior_n, ior_k, tf.thickness, tf.ior); + else + fd = mx_init_fresnel_conductor(ior_n, ior_k); + + vec3 F = mx_compute_fresnel(NdotV, fd); + + vec3 Li = mx_environment_radiance(N, V, X, roughness, distribution, fd); + + float avgRoughness = mx_average_roughness(roughness); + vec3 comp = mx_ggx_energy_compensation(NdotV, avgRoughness, F); + + result = Li * comp * weight; +} + +// We fake diffuse transmission by using diffuse reflection from the opposite side. +// So this BTDF is really a BRDF. +void mx_translucent_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 normal, out BSDF result) +{ + // Invert normal since we're transmitting light from the other side + float NdotL = dot(L, -normal); + if (NdotL <= 0.0 || weight < M_FLOAT_EPS) + { + result = BSDF(0.0); + return; + } + + result = color * weight * NdotL * M_PI_INV; +} + +void mx_translucent_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 normal, out BSDF result) +{ + if (weight < M_FLOAT_EPS) + { + result = BSDF(0.0); + return; + } + + // Invert normal since we're transmitting light from the other side + vec3 Li = mx_environment_irradiance(-normal); + result = Li * color * weight; +} + + +// Based on the OSL implementation of Oren-Nayar diffuse, which is in turn +// based on https://mimosa-pudica.net/improved-oren-nayar.html. +float mx_oren_nayar_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness) +{ + float LdotV = clamp(dot(L, V), M_FLOAT_EPS, 1.0); + float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); + float s = LdotV - NdotL * NdotV; + float stinv = (s > 0.0f) ? s / max(NdotL, NdotV) : 0.0; + + float sigma2 = mx_square(roughness * M_PI); + float A = 1.0 - 0.5 * (sigma2 / (sigma2 + 0.33)); + float B = 0.45 * sigma2 / (sigma2 + 0.09); + + return A + B * stinv; +} + +// https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf +// Section 5.3 +float mx_burley_diffuse(vec3 L, vec3 V, vec3 N, float NdotL, float roughness) +{ + vec3 H = normalize(L + V); + float LdotH = clamp(dot(L, H), M_FLOAT_EPS, 1.0); + float NdotV = clamp(dot(N, V), M_FLOAT_EPS, 1.0); + + float F90 = 0.5 + (2.0 * roughness * mx_square(LdotH)); + float refL = mx_fresnel_schlick(NdotL, 1.0, F90); + float refV = mx_fresnel_schlick(NdotV, 1.0, F90); + return refL * refV; +} + +// Compute the directional albedo component of Burley diffuse for the given +// view angle and roughness. Curve fit provided by Stephen Hill. +float mx_burley_diffuse_directional_albedo(float NdotV, float roughness) +{ + float x = NdotV; + float fit0 = 0.97619 - 0.488095 * mx_pow5(1.0 - x); + float fit1 = 1.55754 + (-2.02221 + (2.56283 - 1.06244 * x) * x) * x; + return mix(fit0, fit1, roughness); +} + +// Evaluate the Burley diffusion profile for the given distance and diffusion shape. +// Based on https://graphics.pixar.com/library/ApproxBSSRDF/ +vec3 mx_burley_diffusion_profile(float dist, vec3 shape) +{ + vec3 num1 = exp(-shape * dist); + vec3 num2 = exp(-shape * dist / 3.0); + float denom = max(dist, M_FLOAT_EPS); + return (num1 + num2) / denom; +} + +// Integrate the Burley diffusion profile over a sphere of the given radius. +// Inspired by Eric Penner's presentation in http://advances.realtimerendering.com/s2011/ +vec3 mx_integrate_burley_diffusion(vec3 N, vec3 L, float radius, vec3 mfp) +{ + float theta = acos(dot(N, L)); + + // Estimate the Burley diffusion shape from mean free path. + vec3 shape = vec3(1.0) / max(mfp, 0.1); + + // Integrate the profile over the sphere. + vec3 sumD = vec3(0.0); + vec3 sumR = vec3(0.0); + const int SAMPLE_COUNT = 32; + const float SAMPLE_WIDTH = (2.0 * M_PI) / float(SAMPLE_COUNT); + for (int i = 0; i < SAMPLE_COUNT; i++) + { + float x = -M_PI + (float(i) + 0.5) * SAMPLE_WIDTH; + float dist = radius * abs(2.0 * sin(x * 0.5)); + vec3 R = mx_burley_diffusion_profile(dist, shape); + sumD += R * max(cos(theta + x), 0.0); + sumR += R; + } + + return sumD / sumR; +} + +vec3 mx_subsurface_scattering_approx(vec3 N, vec3 L, vec3 P, vec3 albedo, vec3 mfp) +{ + float curvature = length(fwidth(N)) / length(fwidth(P)); + float radius = 1.0 / max(curvature, 0.01); + return albedo * mx_integrate_burley_diffusion(N, L, radius, mfp) / vec3(M_PI); +} + +void mx_subsurface_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, out BSDF result) +{ + if (weight < M_FLOAT_EPS) + { + result = BSDF(0.0); + return; + } + + normal = mx_forward_facing_normal(normal, V); + + vec3 sss = mx_subsurface_scattering_approx(normal, L, P, color, radius); + float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0); + float visibleOcclusion = 1.0 - NdotL * (1.0 - occlusion); + result = sss * visibleOcclusion * weight; +} + +void mx_subsurface_bsdf_indirect(vec3 V, float weight, vec3 color, vec3 radius, float anisotropy, vec3 normal, out BSDF result) +{ + if (weight < M_FLOAT_EPS) + { + result = BSDF(0.0); + return; + } + + normal = mx_forward_facing_normal(normal, V); + + // For now, we render indirect subsurface as simple indirect diffuse. + vec3 Li = mx_environment_irradiance(normal); + result = Li * color * weight; +} + + +void mx_oren_nayar_diffuse_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, float weight, vec3 color, float roughness, vec3 normal, out BSDF result) +{ + if (weight < M_FLOAT_EPS) + { + result = BSDF(0.0); + return; + } + + normal = mx_forward_facing_normal(normal, V); + + float NdotL = clamp(dot(normal, L), M_FLOAT_EPS, 1.0); + + result = color * occlusion * weight * NdotL * M_PI_INV; + if (roughness > 0.0) + { + result *= mx_oren_nayar_diffuse(L, V, normal, NdotL, roughness); + } +} + +void mx_oren_nayar_diffuse_bsdf_indirect(vec3 V, float weight, vec3 color, float roughness, vec3 normal, out vec3 result) +{ + if (weight < M_FLOAT_EPS) + { + result = BSDF(0.0); + return; + } + + normal = mx_forward_facing_normal(normal, V); + + vec3 Li = mx_environment_irradiance(normal); + result = Li * color * weight; +} + +void mx_mix_bsdf_reflection(vec3 L, vec3 V, vec3 P, float occlusion, BSDF fg, BSDF bg, float w, out BSDF result) +{ + result = mix(bg, fg, clamp(w, 0.0, 1.0)); +} + +void mx_mix_bsdf_transmission(vec3 V, BSDF fg, BSDF bg, float w, out BSDF result) +{ + result = mix(bg, fg, clamp(w, 0.0, 1.0)); +} + +void mx_mix_bsdf_indirect(vec3 V, vec3 fg, vec3 bg, float w, out vec3 result) +{ + result = mix(bg, fg, clamp(w, 0.0, 1.0)); +} + +void mx_multiply_bsdf_color_reflection(vec3 L, vec3 V, vec3 P, float occlusion, BSDF in1, vec3 in2, out BSDF result) +{ + result = in1 * clamp(in2, 0.0, 1.0); +} + +void mx_multiply_bsdf_color_transmission(vec3 V, BSDF in1, vec3 in2, out BSDF result) +{ + result = in1 * clamp(in2, 0.0, 1.0); +} + +void mx_multiply_bsdf_color_indirect(vec3 V, vec3 in1, vec3 in2, out vec3 result) +{ + result = in1 * clamp(in2, 0.0, 1.0); +} + +void IMPL_standard_surface_surfaceshader(float base, vec3 base_color, float diffuse_roughness, float metalness, float specular, vec3 specular_color, float specular_roughness, float specular_IOR, float specular_anisotropy, float specular_rotation, float transmission, vec3 transmission_color, float transmission_depth, vec3 transmission_scatter, float transmission_scatter_anisotropy, float transmission_dispersion, float transmission_extra_roughness, float subsurface, vec3 subsurface_color, vec3 subsurface_radius, float subsurface_scale, float subsurface_anisotropy, float sheen, vec3 sheen_color, float sheen_roughness, float coat, vec3 coat_color, float coat_roughness, float coat_anisotropy, float coat_rotation, float coat_IOR, vec3 coat_normal, float coat_affect_color, float coat_affect_roughness, float thin_film_thickness, float thin_film_IOR, float emission, vec3 emission_color, vec3 opacity, bool thin_walled, vec3 normal1, vec3 tangent1, out surfaceshader out1) +{ + vec3 emission_weight_out = emission_color * emission; + vec3 metal_reflectivity_out = base_color * base; + const float coat_tangent_rotate_degree_in2_tmp = 360.000000; + float coat_tangent_rotate_degree_out = coat_rotation * coat_tangent_rotate_degree_in2_tmp; + vec2 coat_roughness_vector_out = vec2(0.0); + mx_roughness_anisotropy(coat_roughness, coat_anisotropy, coat_roughness_vector_out); + vec3 subsurface_radius_vector_out = vec3(subsurface_radius.x, subsurface_radius.y, subsurface_radius.z); + vec3 opacity_luminance_out = vec3(0.0); + mx_luminance_color3(opacity, vec3(0.272229, 0.674082, 0.053689), opacity_luminance_out); + const vec3 coat_emission_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000); + vec3 coat_emission_attenuation_out = mix(coat_emission_attenuation_bg_tmp, coat_color, coat); + vec3 metal_edgecolor_out = specular_color * specular; + float coat_affect_roughness_multiply1_out = coat_affect_roughness * coat; + float subsurface_selector_out = float(thin_walled); + const float tangent_rotate_degree_in2_tmp = 360.000000; + float tangent_rotate_degree_out = specular_rotation * tangent_rotate_degree_in2_tmp; + const float coat_clamped_low_tmp = 0.000000; + const float coat_clamped_high_tmp = 1.000000; + float coat_clamped_out = clamp(coat, coat_clamped_low_tmp, coat_clamped_high_tmp); + const vec3 coat_attenuation_bg_tmp = vec3(1.000000, 1.000000, 1.000000); + vec3 coat_attenuation_out = mix(coat_attenuation_bg_tmp, coat_color, coat); + vec3 coat_tangent_rotate_out = vec3(0.0); + mx_rotate_vector3(tangent1, coat_tangent_rotate_degree_out, coat_normal, coat_tangent_rotate_out); + vec3 subsurface_radius_scaled_out = subsurface_radius_vector_out * subsurface_scale; + vec3 emission_weight_attenuated_out = emission_weight_out * coat_emission_attenuation_out; + vec3 artistic_ior_ior = vec3(0.0); + vec3 artistic_ior_extinction = vec3(0.0); + mx_artistic_ior(metal_reflectivity_out, metal_edgecolor_out, artistic_ior_ior, artistic_ior_extinction); + float coat_affect_roughness_multiply2_out = coat_affect_roughness_multiply1_out * coat_roughness; + vec3 tangent_rotate_out = vec3(0.0); + mx_rotate_vector3(tangent1, tangent_rotate_degree_out, normal1, tangent_rotate_out); + float coat_gamma_multiply_out = coat_clamped_out * coat_affect_color; + vec3 coat_tangent_rotate_normalize_out = normalize(coat_tangent_rotate_out); + const float coat_affected_roughness_fg_tmp = 1.000000; + float coat_affected_roughness_out = mix(specular_roughness, coat_affected_roughness_fg_tmp, coat_affect_roughness_multiply2_out); + vec3 tangent_rotate_normalize_out = normalize(tangent_rotate_out); + const float coat_gamma_in2_tmp = 1.000000; + float coat_gamma_out = coat_gamma_multiply_out + coat_gamma_in2_tmp; + vec3 coat_tangent_out = vec3(0.0); + if (coat_anisotropy > 0.000000) + { + coat_tangent_out = coat_tangent_rotate_normalize_out; + } + else + { + coat_tangent_out = tangent1; + } + vec2 main_roughness_out = vec2(0.0); + mx_roughness_anisotropy(coat_affected_roughness_out, specular_anisotropy, main_roughness_out); + vec3 main_tangent_out = vec3(0.0); + if (specular_anisotropy > 0.000000) + { + main_tangent_out = tangent_rotate_normalize_out; + } + else + { + main_tangent_out = tangent1; + } + vec3 coat_affected_subsurface_color_out = pow(subsurface_color, vec3(coat_gamma_out)); + vec3 coat_affected_diffuse_color_out = pow(base_color, vec3(coat_gamma_out)); + + surfaceshader shader_constructor_out = surfaceshader(vec3(0.0),vec3(0.0)); + { + // Shadow occlusion + float occlusion = 1.0; + + vec3 N = normalize(normalWorld); + vec3 V = normalize(u_viewPosition - positionWorld); + vec3 P = positionWorld; + // Light loop + int numLights = numActiveLightSources(); + lightshader lightShader; + for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex) + { + sampleLightSource(u_lightData[activeLightIndex], positionWorld, lightShader); + vec3 L = lightShader.direction; + + // Calculate the BSDF response for this light source + thinfilm thin_film_bsdf_out; + thin_film_bsdf_out.thickness = thin_film_thickness; + thin_film_bsdf_out.ior = thin_film_IOR; + BSDF metal_bsdf_out = BSDF(0.0); + mx_conductor_bsdf_reflection(L, V, P, occlusion, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, 0, thinfilm(0.0,1.5), metal_bsdf_out); + BSDF transmission_bsdf_out = BSDF(0.0); + BSDF translucent_bsdf_out = BSDF(0.0); + mx_translucent_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out); + BSDF subsurface_bsdf_out = BSDF(0.0); + mx_subsurface_bsdf_reflection(L, V, P, occlusion, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out); + BSDF diffuse_bsdf_out = BSDF(0.0); + mx_oren_nayar_diffuse_bsdf_reflection(L, V, P, occlusion, base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out); + BSDF selected_subsurface_bsdf_out = BSDF(0.0); + mx_mix_bsdf_reflection(L, V, P, occlusion, translucent_bsdf_out, subsurface_bsdf_out, subsurface_selector_out, selected_subsurface_bsdf_out); + BSDF subsurface_mix_out = BSDF(0.0); + mx_mix_bsdf_reflection(L, V, P, occlusion, selected_subsurface_bsdf_out, diffuse_bsdf_out, subsurface, subsurface_mix_out); + BSDF sheen_layer_out = BSDF(0.0); + mx_sheen_bsdf_reflection(L, V, P, occlusion, sheen, sheen_color, sheen_roughness, normal1, subsurface_mix_out, sheen_layer_out); + BSDF transmission_mix_out = BSDF(0.0); + mx_mix_bsdf_reflection(L, V, P, occlusion, transmission_bsdf_out, sheen_layer_out, transmission, transmission_mix_out); + BSDF specular_layer_out = BSDF(0.0); + mx_dielectric_bsdf_reflection(L, V, P, occlusion, specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, 0, 0, transmission_mix_out, thin_film_bsdf_out, specular_layer_out); + BSDF metalness_mix_out = BSDF(0.0); + mx_mix_bsdf_reflection(L, V, P, occlusion, metal_bsdf_out, specular_layer_out, metalness, metalness_mix_out); + BSDF metalness_mix_attenuated_out = BSDF(0.0); + mx_multiply_bsdf_color_reflection(L, V, P, occlusion, metalness_mix_out, coat_attenuation_out, metalness_mix_attenuated_out); + BSDF coat_layer_out = BSDF(0.0); + mx_dielectric_bsdf_reflection(L, V, P, occlusion, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, metalness_mix_attenuated_out, thinfilm(0.0,1.5), coat_layer_out); + + // Accumulate the light's contribution + shader_constructor_out.color += lightShader.intensity * coat_layer_out; + } + + // Ambient occlusion + occlusion = 1.0; + + // Add surface emission + { + EDF emission_edf_out = EDF(0.0); + mx_uniform_edf(N, V, emission_weight_attenuated_out, emission_edf_out); + shader_constructor_out.color += emission_edf_out; + } + + // Add indirect contribution + { + thinfilm thin_film_bsdf_out; + thin_film_bsdf_out.thickness = thin_film_thickness; + thin_film_bsdf_out.ior = thin_film_IOR; + BSDF metal_bsdf_out = BSDF(0.0); + mx_conductor_bsdf_indirect(V, 1.000000, artistic_ior_ior, artistic_ior_extinction, main_roughness_out, normal1, main_tangent_out, 0, thinfilm(0.0,1.5), metal_bsdf_out); + BSDF transmission_bsdf_out = BSDF(0.0); + BSDF translucent_bsdf_out = BSDF(0.0); + mx_translucent_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, normal1, translucent_bsdf_out); + BSDF subsurface_bsdf_out = BSDF(0.0); + mx_subsurface_bsdf_indirect(V, 1.000000, coat_affected_subsurface_color_out, subsurface_radius_scaled_out, subsurface_anisotropy, normal1, subsurface_bsdf_out); + BSDF diffuse_bsdf_out = BSDF(0.0); + mx_oren_nayar_diffuse_bsdf_indirect(V, base, coat_affected_diffuse_color_out, diffuse_roughness, normal1, diffuse_bsdf_out); + BSDF selected_subsurface_bsdf_out = BSDF(0.0); + mx_mix_bsdf_indirect(V, translucent_bsdf_out, subsurface_bsdf_out, subsurface_selector_out, selected_subsurface_bsdf_out); + BSDF subsurface_mix_out = BSDF(0.0); + mx_mix_bsdf_indirect(V, selected_subsurface_bsdf_out, diffuse_bsdf_out, subsurface, subsurface_mix_out); + BSDF sheen_layer_out = BSDF(0.0); + mx_sheen_bsdf_indirect(V, sheen, sheen_color, sheen_roughness, normal1, subsurface_mix_out, sheen_layer_out); + BSDF transmission_mix_out = BSDF(0.0); + mx_mix_bsdf_indirect(V, transmission_bsdf_out, sheen_layer_out, transmission, transmission_mix_out); + BSDF specular_layer_out = BSDF(0.0); + mx_dielectric_bsdf_indirect(V, specular, specular_color, specular_IOR, main_roughness_out, normal1, main_tangent_out, 0, 0, transmission_mix_out, thin_film_bsdf_out, specular_layer_out); + BSDF metalness_mix_out = BSDF(0.0); + mx_mix_bsdf_indirect(V, metal_bsdf_out, specular_layer_out, metalness, metalness_mix_out); + BSDF metalness_mix_attenuated_out = BSDF(0.0); + mx_multiply_bsdf_color_indirect(V, metalness_mix_out, coat_attenuation_out, metalness_mix_attenuated_out); + BSDF coat_layer_out = BSDF(0.0); + mx_dielectric_bsdf_indirect(V, coat, vec3(1.000000, 1.000000, 1.000000), coat_IOR, coat_roughness_vector_out, coat_normal, coat_tangent_out, 0, 0, metalness_mix_attenuated_out, thinfilm(0.0,1.5), coat_layer_out); + + shader_constructor_out.color += occlusion * coat_layer_out; + } + + shader_constructor_out.transparency = vec3(0.0); + } + + out1 = shader_constructor_out; +} + +void main() +{ + vec3 geomprop_Nworld_out = normalize(normalWorld); + vec3 geomprop_Tworld_out = normalize(tangentWorld); + + surfaceshader SR_default_out = surfaceshader(vec3(0.0),vec3(0.0)); + IMPL_standard_surface_surfaceshader(base, base_color, diffuse_roughness, metalness, specular, specular_color, specular_roughness, specular_IOR, specular_anisotropy, specular_rotation, transmission, transmission_color, transmission_depth, transmission_scatter, transmission_scatter_anisotropy, transmission_dispersion, transmission_extra_roughness, subsurface, subsurface_color, subsurface_radius, subsurface_scale, subsurface_anisotropy, sheen, sheen_color, sheen_roughness, coat, coat_color, coat_roughness, coat_anisotropy, coat_rotation, coat_IOR, geomprop_Nworld_out, coat_affect_color, coat_affect_roughness, thin_film_thickness, thin_film_IOR, emission, emission_color, opacity, thin_walled, geomprop_Nworld_out, geomprop_Tworld_out, SR_default_out); + out1 = vec4(SR_default_out.color, 1.0); +} diff --git a/source/JsMaterialX/JsMaterialXView/shader-vert.glsl b/source/JsMaterialX/JsMaterialXView/shader-vert.glsl new file mode 100644 index 0000000000..7862e30666 --- /dev/null +++ b/source/JsMaterialX/JsMaterialXView/shader-vert.glsl @@ -0,0 +1,26 @@ +#version 300 es + +precision mediump float; + +// Uniform block: PrivateUniforms +uniform mat4 u_worldMatrix; +uniform mat4 u_viewProjectionMatrix; +uniform mat4 u_worldInverseTransposeMatrix; + +// Inputs block: VertexInputs +in vec3 position; +in vec3 normal; +in vec3 tangent; + +out vec3 normalWorld; +out vec3 tangentWorld; +out vec3 positionWorld; + +void main() +{ + vec4 hPositionWorld = u_worldMatrix * vec4(position, 1.0); + gl_Position = u_viewProjectionMatrix * hPositionWorld; + normalWorld = (u_worldInverseTransposeMatrix * vec4(normal,0.0)).xyz; + tangentWorld = (u_worldInverseTransposeMatrix * vec4(tangent,0.0)).xyz; + positionWorld = hPositionWorld.xyz; +} diff --git a/source/JsMaterialX/JsMaterialXView/src/index.js b/source/JsMaterialX/JsMaterialXView/src/index.js new file mode 100644 index 0000000000..a84047744f --- /dev/null +++ b/source/JsMaterialX/JsMaterialXView/src/index.js @@ -0,0 +1,313 @@ +import * as THREE from 'three'; +import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js'; +import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; +import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'; +import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'; +import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'; +import { GammaCorrectionShader } from 'three/examples/jsm/shaders/GammaCorrectionShader.js'; + +let camera, scene, model, renderer, composer, controls; + +let normalMat = new THREE.Matrix3(); +let viewProjMat = new THREE.Matrix4(); +let worldViewPos = new THREE.Vector3(); + +init(); + +/** + * Adds a tangent BufferAttribute to the passed in geometry. + * See MaterialXRender/Mesh.cpp + * @param {THREE.BufferGeometry} geometry + */ +function generateTangents(geometry) { + let p0 = new THREE.Vector3(); + let p1 = new THREE.Vector3(); + let p2 = new THREE.Vector3(); + + let n0 = new THREE.Vector3(); + let n1 = new THREE.Vector3(); + let n2 = new THREE.Vector3(); + + let w0 = new THREE.Vector2(); + let w1 = new THREE.Vector2(); + let w2 = new THREE.Vector2(); + + let e1 = new THREE.Vector3(); + let e2 = new THREE.Vector3(); + + let tangent = new THREE.Vector3(); + let t0 = new THREE.Vector3(); + let t1 = new THREE.Vector3(); + let t2 = new THREE.Vector3(); + + const positions = geometry.attributes.position; + const normals = geometry.attributes.normal; + const uvs = geometry.attributes.uv; + const length = positions.count * positions.itemSize; + + const tangentsdata = new Float32Array(length); + + for(let i = 0; i < positions.count; i += 3) { + const idx = i * positions.itemSize; + const uvidx = i * uvs.itemSize; + + p0.set(positions.array[idx], positions.array[idx + 1], positions.array[idx + 2]); + p1.set(positions.array[idx + 3], positions.array[idx + 4], positions.array[idx + 5]); + p2.set(positions.array[idx + 6], positions.array[idx + 7], positions.array[idx + 8]); + + n0.set(normals.array[idx], normals.array[idx + 1], normals.array[idx + 2]); + n1.set(normals.array[idx + 3], normals.array[idx + 4], normals.array[idx + 5]); + n2.set(normals.array[idx + 6], normals.array[idx + 7], normals.array[idx + 8]); + + w0.set(uvs.array[uvidx], uvs.array[uvidx + 1]); + w1.set(uvs.array[uvidx + 2], uvs.array[uvidx + 3]); + w2.set(uvs.array[uvidx + 4], uvs.array[uvidx + 5]); + + // Based on Eric Lengyel at http://www.terathon.com/code/tangent.html + + e1.subVectors(p1, p0) + e2.subVectors(p2, p0); + + const x1 = w1.x - w0.x; + const x2 = w2.x - w0.x; + const y1 = w1.y - w0.y; + const y2 = w2.y - w0.y; + + const denom = x1 * y2 - x2 * y1; + const r = denom ? (1.0 / denom) : 0.0; + + tangent.subVectors(e1.clone().multiplyScalar(y2), e2.clone().multiplyScalar(y1)).multiplyScalar(r); + + // Gram-Schmidt process + t0.subVectors(tangent, n0.multiplyScalar(n0.dot(tangent))).normalize(); + t1.subVectors(tangent, n1.multiplyScalar(n1.dot(tangent))).normalize(); + t2.subVectors(tangent, n2.multiplyScalar(n2.dot(tangent))).normalize(); + + tangentsdata[idx] = t0.x; + tangentsdata[idx + 1] = t0.y; + tangentsdata[idx + 2] = t0.z; + tangentsdata[idx + 3] = t1.x; + tangentsdata[idx + 4] = t1.y; + tangentsdata[idx + 5] = t1.z; + tangentsdata[idx + 6] = t2.x; + tangentsdata[idx + 7] = t2.y; + tangentsdata[idx + 8] = t2.z; + } + + geometry.setAttribute('tangent', new THREE.BufferAttribute( tangentsdata, 3)); +} + +/** + * Create a new (half)float texture containing an alpha channel with a value of 1 from a RGB (half)float texture. + * @param {THREE.Texture} texture + */ +function RGBToRGBA_Float(texture) { + const rgbData = texture.image.data; + const length = (rgbData.length / 3) * 4; + let rgbaData; + + switch (texture.type) { + case THREE.FloatType: + rgbaData = new Float32Array(length); + break; + case THREE.HalfFloatType: + rgbaData = new Uint16Array(length); + break; + default: + break; + } + + if (rgbaData) { + for (let i = 0; i < length / 4; i++) { + rgbaData[(i * 4) + 0] = rgbData[(i * 3) + 0]; + rgbaData[(i * 4) + 1] = rgbData[(i * 3) + 1]; + rgbaData[(i * 4) + 2] = rgbData[(i * 3) + 2]; + rgbaData[(i * 4) + 3] = 1.0; + } + return new THREE.DataTexture(rgbaData, texture.image.width, texture.image.height, THREE.RGBAFormat, texture.type); + } + + return texture; +} + +function prepareEnvTexture(texture, capabilities) { + const rgbaTexture = RGBToRGBA_Float(texture); + // RGBELoader sets flipY to true by default + rgbaTexture.flipY = false; + rgbaTexture.wrapS = THREE.RepeatWrapping; + rgbaTexture.anisotropy = capabilities.getMaxAnisotropy(); + rgbaTexture.minFilter = THREE.LinearMipmapLinearFilter; + rgbaTexture.magFilter = THREE.LinearFilter; + rgbaTexture.generateMipmaps = true; + rgbaTexture.needsUpdate = true; + + return rgbaTexture; +} + +function init() { + let canvas = document.getElementById('webglcanvas'); + let context = canvas.getContext('webgl2'); + + camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 100); + + // Set up scene + scene = new THREE.Scene(); + scene.background = new THREE.Color(0x4c4c52); + scene.background.convertSRGBToLinear(); + + scene.add(new THREE.AmbientLight( 0x222222)); + const directionalLight = new THREE.DirectionalLight(new THREE.Color(1, 0.894474, 0.567234), 2.52776); + directionalLight.position.set(-1, 1, 1).normalize(); + scene.add(directionalLight); + const lightData = { + type: 1, + direction: directionalLight.position.negate(), + color: new THREE.Vector3(...directionalLight.color.toArray()), + intensity: directionalLight.intensity + }; + + renderer = new THREE.WebGLRenderer({canvas, context}); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.setSize(window.innerWidth, window.innerHeight); + + composer = new EffectComposer( renderer ); + const renderPass = new RenderPass( scene, camera ); + composer.addPass( renderPass ); + const gammaCorrectionPass = new ShaderPass( GammaCorrectionShader ); + composer.addPass( gammaCorrectionPass ); + + window.addEventListener('resize', onWindowResize); + + // controls + controls = new OrbitControls(camera, renderer.domElement); + + // Load model and shaders + var fileloader = new THREE.FileLoader(); + const objLoader = new OBJLoader(); + const hdrloader = new RGBELoader(); + + Promise.all([ + new Promise(resolve => hdrloader.setDataType(THREE.FloatType).load('san_giuseppe_bridge_split.hdr', resolve)), + new Promise(resolve => hdrloader.setDataType(THREE.FloatType).load('irradiance/san_giuseppe_bridge_split.hdr', resolve)), + new Promise(resolve => objLoader.load('shaderball.obj', resolve)), + new Promise(resolve => fileloader.load('shader-frag.glsl', resolve)), + new Promise(resolve => fileloader.load('shader-vert.glsl', resolve)) + ]).then(([loadedRadianceTexture, loadedIrradianceTexture, obj, fShader, vShader]) => { + + const radianceTexture = prepareEnvTexture(loadedRadianceTexture, renderer.capabilities); + const irradianceTexture = prepareEnvTexture(loadedIrradianceTexture, renderer.capabilities); + + const material = new THREE.RawShaderMaterial({ + uniforms: { + time: { value: 0.0 }, + + base: {value: 1.0}, + base_color: {value: new THREE.Vector3(0.8, 0.8, 0.8)}, + diffuse_roughness: {value: 0.0}, + metalness: {value: 0.0}, + specular: {value: 1.0}, + specular_color: {value: new THREE.Vector3(1.0, 1.0, 1.0)}, + specular_roughness: {value: 0.2}, + specular_IOR: {value: 1.5}, + specular_anisotropy: {value: 0.0}, + specular_rotation: {value: 0.0}, + transmission: {value: 0.0}, + transmission_color: {value: new THREE.Vector3(1.0, 1.0, 1.0)}, + transmission_depth: {value: 0.0}, + transmission_scatter: {value: new THREE.Vector3(0.0, 0.0, 0.0)}, + transmission_scatter_anisotropy: {value: 0.0}, + transmission_dispersion: {value: 0.0}, + transmission_extra_roughness: {value: 0.0}, + subsurface: {value: 0.0}, + subsurface_color: {value: new THREE.Vector3(1.0, 1.0, 1.0)}, + subsurface_radius: {value: new THREE.Vector3(1.0, 1.0, 1.0)}, + subsurface_scale: {value: 1.0}, + subsurface_anisotropy: {value: 0.0}, + sheen: {value: 0.0}, + sheen_color: {value: new THREE.Vector3(1.0, 1.0, 1.0)}, + sheen_roughness: {value: 0.3}, + coat: {value: 0.0}, + coat_color: {value: new THREE.Vector3(1.0, 1.0, 1.0)}, + coat_roughness: {value: 0.1}, + coat_anisotropy: {value: 0.0}, + coat_rotation: {value: 0.0}, + coat_IOR: {value: 1.5}, + coat_affect_color: {value: 0.0}, + coat_affect_roughness: {value: 0.0}, + thin_film_thickness: {value: 0.0}, + thin_film_IOR: {value: 1.5}, + emission: {value: 0.0}, + emission_color: {value: new THREE.Vector3(1.0, 1.0, 1.0)}, + opacity: {value: new THREE.Vector3(1.0, 1.0, 1.0)}, + thin_walled: {value: false}, + + u_numActiveLightSources: {value: 1}, + u_lightData: {value: [ lightData ]}, + + u_envMatrix: {value: new THREE.Matrix4().makeRotationY(Math.PI)}, + u_envRadiance: {value: radianceTexture}, + u_envRadianceMips: {value: Math.trunc(Math.log2(Math.max(radianceTexture.image.width, radianceTexture.image.height))) + 1}, + u_envRadianceSamples: {value: 16}, + u_envIrradiance: {value: irradianceTexture}, + + u_viewPosition: new THREE.Uniform( new THREE.Vector3() ), + + u_worldMatrix: new THREE.Uniform( new THREE.Matrix4() ), + u_viewProjectionMatrix: new THREE.Uniform( new THREE.Matrix4() ), + u_worldInverseTransposeMatrix: new THREE.Uniform( new THREE.Matrix4() ) + }, + vertexShader: vShader, + fragmentShader: fShader, + }); + + obj.traverse((child) => { + if (child.isMesh) { + generateTangents(child.geometry); + child.geometry.attributes.uv_0 = child.geometry.attributes.uv + child.material = material; + } + }); + model = obj; + scene.add(model); + + const bbox = new THREE.Box3().setFromObject(model); + const bsphere = new THREE.Sphere(); + bbox.getBoundingSphere(bsphere); + + controls.target = bsphere.center; + camera.position.y = camera.position.z = bsphere.radius * 2.5; + controls.update(); + + camera.far = bsphere.radius * 10; + camera.updateProjectionMatrix(); + + }).then(() => { + animate(); + }) + +} + +function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight); +} + +function animate() { + requestAnimationFrame(animate); + composer.render(); + + model.traverse((child) => { + if (child.isMesh) { + const uniforms = child.material.uniforms; + if(uniforms) { + uniforms.time.value = performance.now() / 1000; + uniforms.u_viewPosition.value = camera.getWorldPosition(worldViewPos); + uniforms.u_worldMatrix.value = child.matrixWorld; + uniforms.u_viewProjectionMatrix.value = viewProjMat.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); + uniforms.u_worldInverseTransposeMatrix.value.setFromMatrix3(normalMat.getNormalMatrix(child.matrixWorld)); + } + } + }); +} diff --git a/source/JsMaterialX/JsMaterialXView/webpack.config.js b/source/JsMaterialX/JsMaterialXView/webpack.config.js new file mode 100644 index 0000000000..e895085d27 --- /dev/null +++ b/source/JsMaterialX/JsMaterialXView/webpack.config.js @@ -0,0 +1,19 @@ +const path = require('path'); +const CopyPlugin = require("copy-webpack-plugin"); + +module.exports = { + entry: './src/index.js', + output: { + filename: 'main.js', + path: path.resolve(__dirname, 'dist') + }, + mode: "development", + plugins: [ + new CopyPlugin({ + patterns: [ + { from: "../../../resources/Geometry" }, + { from: "../../../resources/Lights" }, + ], + }), + ] +}; \ No newline at end of file diff --git a/source/MaterialXGenEssl/CMakeLists.txt b/source/MaterialXGenEssl/CMakeLists.txt new file mode 100644 index 0000000000..2bf45f6bbd --- /dev/null +++ b/source/MaterialXGenEssl/CMakeLists.txt @@ -0,0 +1,59 @@ +file(GLOB_RECURSE materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") +file(GLOB_RECURSE materialx_header "${CMAKE_CURRENT_SOURCE_DIR}/*.h") + +function(assign_source_group prefix) + foreach(_source IN ITEMS ${ARGN}) + if (IS_ABSOLUTE "${_source}") + file(RELATIVE_PATH _source_rel "${CMAKE_CURRENT_SOURCE_DIR}" "${_source}") + else() + set(_source_rel "${_source}") + endif() + get_filename_component(_source_path "${_source_rel}" PATH) + string(REPLACE "/" "\\" _source_path_msvc "${_source_path}") + source_group("${prefix}\\${_source_path_msvc}" FILES "${_source}") + endforeach() +endfunction(assign_source_group) + +assign_source_group("Header Files" ${materialx_header}) +assign_source_group("Source Files" ${materialx_source}) + +add_library(MaterialXGenEssl STATIC + ${materialx_source} + ${materialx_header} +) + +set_target_properties( + MaterialXGenEssl PROPERTIES + OUTPUT_NAME MaterialXGenEssl + COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" + LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" + VERSION "${MATERIALX_LIBRARY_VERSION}" + SOVERSION "${MATERIALX_MAJOR_VERSION}") + +target_link_libraries( + MaterialXGenEssl + MaterialXGenGlsl + MaterialXGenShader + MaterialXCore + ${CMAKE_DL_LIBS}) + +target_include_directories(MaterialXGenEssl + PUBLIC + $ + $ + PRIVATE + ${EXTERNAL_INCLUDE_DIRS} +) + +install(TARGETS MaterialXGenEssl + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH}) + +install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${CMAKE_INSTALL_PREFIX}/${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXGenEssl/ MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h*") + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXGenEssl.pdb" + DESTINATION "${CMAKE_INSTALL_PREFIX}/${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + +add_subdirectory(libraries) diff --git a/source/MaterialXGenEssl/EsslShaderGenerator.cpp b/source/MaterialXGenEssl/EsslShaderGenerator.cpp new file mode 100644 index 0000000000..2148119c11 --- /dev/null +++ b/source/MaterialXGenEssl/EsslShaderGenerator.cpp @@ -0,0 +1,110 @@ +// +// TM & (c) 2021 Lucasfilm Entertainment Company Ltd. and Lucasfilm Ltd. +// All rights reserved. See LICENSE.txt for license. +// + +#include +#include + +#include + +namespace MaterialX +{ + +const string EsslShaderGenerator::TARGET = "essl"; +const string EsslShaderGenerator::VERSION = "300 es"; // Target WebGL 2.0 + +EsslShaderGenerator::EsslShaderGenerator() + : GlslShaderGenerator() +{ + _syntax = EsslSyntax::create(); + // ESSL specific keywords + const StringSet reservedWords = { "precision", "highp", "mediump", "lowp" }; + _syntax->registerReservedWords(reservedWords); + + // Temporary overwrites for three.js + _tokenSubstitutions[HW::T_IN_POSITION] = "position"; + _tokenSubstitutions[HW::T_IN_NORMAL] = "normal"; + _tokenSubstitutions[HW::T_IN_TEXCOORD] = "uv"; + _tokenSubstitutions[HW::T_IN_TANGENT] = "tangent"; +} + +void EsslShaderGenerator::emitDirectives(GenContext&, ShaderStage& stage) const +{ + emitLine("#version " + getVersion(), stage, false); + emitLineBreak(stage); + emitLine("precision mediump float", stage); +} + +void EsslShaderGenerator::emitUniforms(GenContext& context, ShaderStage& stage, HwResourceBindingContextPtr&) const +{ + for (const auto& it : stage.getUniformBlocks()) + { + const VariableBlock& uniforms = *it.second; + + // Skip light uniforms as they are handled separately + if (!uniforms.empty() && uniforms.getName() != HW::LIGHT_DATA) + { + emitComment("Uniform block: " + uniforms.getName(), stage); + emitVariableDeclarations(uniforms, _syntax->getUniformQualifier(), Syntax::SEMICOLON, context, stage, false); + emitLineBreak(stage); + } + } +} + +void EsslShaderGenerator::emitInputs(GenContext& context, ShaderStage& stage) const +{ +BEGIN_SHADER_STAGE(stage, Stage::VERTEX) + const VariableBlock& vertexInputs = stage.getInputBlock(HW::VERTEX_INPUTS); + if (!vertexInputs.empty()) + { + emitComment("Inputs block: " + vertexInputs.getName(), stage); + emitVariableDeclarations(vertexInputs, _syntax->getInputQualifier(), Syntax::SEMICOLON, context, stage, false); + emitLineBreak(stage); + } +END_SHADER_STAGE(stage, Stage::VERTEX) + +BEGIN_SHADER_STAGE(stage, Stage::PIXEL) + const VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); + if (!vertexData.empty()) + { + emitVariableDeclarations(vertexData, _syntax->getInputQualifier(), Syntax::SEMICOLON, context, stage, false); + emitLineBreak(stage); + } +END_SHADER_STAGE(stage, Stage::PIXEL) +} + +void EsslShaderGenerator::emitOutputs(GenContext& context, ShaderStage& stage) const +{ +BEGIN_SHADER_STAGE(stage, Stage::VERTEX) + const VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); + if (!vertexData.empty()) + { + emitVariableDeclarations(vertexData, _syntax->getOutputQualifier(), Syntax::SEMICOLON, context, stage, false); + emitLineBreak(stage); + } +END_SHADER_STAGE(stage, Stage::VERTEX) + +BEGIN_SHADER_STAGE(stage, Stage::PIXEL) + emitComment("Pixel shader outputs", stage); + const VariableBlock& outputs = stage.getOutputBlock(HW::PIXEL_OUTPUTS); + emitVariableDeclarations(outputs, _syntax->getOutputQualifier(), Syntax::SEMICOLON, context, stage, false); + emitLineBreak(stage); +END_SHADER_STAGE(stage, Stage::PIXEL) +} + +const string EsslShaderGenerator::getVertexDataPrefix(const VariableBlock&) const +{ + return ""; +} + +const HwResourceBindingContextPtr EsslShaderGenerator::getResourceBindingContext(GenContext& context) const +{ + HwResourceBindingContextPtr resoureBindingCtx = GlslShaderGenerator::getResourceBindingContext(context); + if (resoureBindingCtx) { + throw ExceptionShaderGenError("The EsslShaderGenerator does not support resource binding."); + } + return resoureBindingCtx; +} + +} diff --git a/source/MaterialXGenEssl/EsslShaderGenerator.h b/source/MaterialXGenEssl/EsslShaderGenerator.h new file mode 100644 index 0000000000..3092323363 --- /dev/null +++ b/source/MaterialXGenEssl/EsslShaderGenerator.h @@ -0,0 +1,50 @@ +// +// TM & (c) 2021 Lucasfilm Entertainment Company Ltd. and Lucasfilm Ltd. +// All rights reserved. See LICENSE.txt for license. +// + +#ifndef MATERIALX_ESSLSHADERGENERATOR_H +#define MATERIALX_ESSLSHADERGENERATOR_H + +/// @file +/// ESSL shader generator + +#include + +namespace MaterialX +{ + +using EsslShaderGeneratorPtr = shared_ptr; + +/// @class EsslShaderGenerator +/// An ESSL (OpenGL ES Shading Language) shader generator +class EsslShaderGenerator : public GlslShaderGenerator +{ + public: + EsslShaderGenerator(); + + static ShaderGeneratorPtr create() { return std::make_shared(); } + + /// Return a unique identifier for the target this generator is for + const string& getTarget() const override { return TARGET; } + + /// Return the version string for the ESSL version this generator is for + const string& getVersion() const override { return VERSION; } + + const string getVertexDataPrefix(const VariableBlock& vertexData) const override; + + /// Unique identifier for this generator target + static const string TARGET; + static const string VERSION; + + protected: + void emitDirectives(GenContext& context, ShaderStage& stage) const override; + void emitUniforms(GenContext& context, ShaderStage& stage, HwResourceBindingContextPtr &resourceBindingCtx) const override; + void emitInputs(GenContext& context, ShaderStage& stage) const override; + void emitOutputs(GenContext& context, ShaderStage& stage) const override; + const HwResourceBindingContextPtr getResourceBindingContext(GenContext& context) const override; +}; + +} + +#endif diff --git a/source/MaterialXGenEssl/EsslSyntax.cpp b/source/MaterialXGenEssl/EsslSyntax.cpp new file mode 100644 index 0000000000..46e8407866 --- /dev/null +++ b/source/MaterialXGenEssl/EsslSyntax.cpp @@ -0,0 +1,20 @@ +// +// TM & (c) 2021 Lucasfilm Entertainment Company Ltd. and Lucasfilm Ltd. +// All rights reserved. See LICENSE.txt for license. +// + +#include + +#include +#include + +#include + +namespace MaterialX +{ + +EsslSyntax::EsslSyntax() +{ +} + +} diff --git a/source/MaterialXGenEssl/EsslSyntax.h b/source/MaterialXGenEssl/EsslSyntax.h new file mode 100644 index 0000000000..f33fb8e6b8 --- /dev/null +++ b/source/MaterialXGenEssl/EsslSyntax.h @@ -0,0 +1,28 @@ +// +// TM & (c) 2017 Lucasfilm Entertainment Company Ltd. and Lucasfilm Ltd. +// All rights reserved. See LICENSE.txt for license. +// + +#ifndef MATERIALX_ESSLSYNTAX_H +#define MATERIALX_ESSLSYNTAX_H + +/// @file +/// ESSL syntax class + +#include + +namespace MaterialX +{ + +/// Syntax class for ESSL (OpenGL ES Shading Language) +class EsslSyntax : public GlslSyntax +{ +public: + EsslSyntax(); + + static SyntaxPtr create() { return std::make_shared(); } +}; + +} // namespace MaterialX + +#endif diff --git a/source/MaterialXGenEssl/libraries/CMakeLists.txt b/source/MaterialXGenEssl/libraries/CMakeLists.txt new file mode 100644 index 0000000000..2b2521f519 --- /dev/null +++ b/source/MaterialXGenEssl/libraries/CMakeLists.txt @@ -0,0 +1,3 @@ +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ + DESTINATION "${CMAKE_INSTALL_PREFIX}/${MATERIALX_INSTALL_STDLIB_PATH}" + PATTERN "CMakeLists.txt" EXCLUDE) diff --git a/source/MaterialXGenEssl/libraries/targets/essl.mtlx b/source/MaterialXGenEssl/libraries/targets/essl.mtlx new file mode 100644 index 0000000000..30c28d6bb0 --- /dev/null +++ b/source/MaterialXGenEssl/libraries/targets/essl.mtlx @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/source/MaterialXGenGlsl/GlslShaderGenerator.cpp b/source/MaterialXGenGlsl/GlslShaderGenerator.cpp index 462145b356..3448c8829f 100644 --- a/source/MaterialXGenGlsl/GlslShaderGenerator.cpp +++ b/source/MaterialXGenGlsl/GlslShaderGenerator.cpp @@ -275,7 +275,7 @@ ShaderPtr GlslShaderGenerator::generate(const string& name, ElementPtr element, ScopedFloatFormatting fmt(Value::FloatFormatFixed); // Make sure we initialize/reset the binding context before generation. - HwResourceBindingContextPtr resourceBindingCtx = context.getUserData(HW::USER_DATA_BINDING_CONTEXT); + HwResourceBindingContextPtr resourceBindingCtx = getResourceBindingContext(context); if (resourceBindingCtx) { resourceBindingCtx->initialize(); @@ -296,10 +296,9 @@ ShaderPtr GlslShaderGenerator::generate(const string& name, ElementPtr element, void GlslShaderGenerator::emitVertexStage(const ShaderGraph& graph, GenContext& context, ShaderStage& stage) const { - HwResourceBindingContextPtr resourceBindingCtx = context.getUserData(HW::USER_DATA_BINDING_CONTEXT); + HwResourceBindingContextPtr resourceBindingCtx = getResourceBindingContext(context); - // Add directives - emitLine("#version " + getVersion(), stage, false); + emitDirectives(context, stage); if (resourceBindingCtx) { resourceBindingCtx->emitDirectives(context, stage); @@ -307,53 +306,16 @@ void GlslShaderGenerator::emitVertexStage(const ShaderGraph& graph, GenContext& emitLineBreak(stage); // Add all constants - const VariableBlock& constants = stage.getConstantBlock(); - if (!constants.empty()) - { - emitVariableDeclarations(constants, _syntax->getConstantQualifier(), Syntax::SEMICOLON, context, stage); - emitLineBreak(stage); - } + emitConstants(context, stage); // Add all uniforms - for (const auto& it : stage.getUniformBlocks()) - { - const VariableBlock& uniforms = *it.second; - if (!uniforms.empty()) - { - emitComment("Uniform block: " + uniforms.getName(), stage); - if (resourceBindingCtx) - { - resourceBindingCtx->emitResourceBindings(context, uniforms, stage); - } - else - { - emitVariableDeclarations(uniforms, _syntax->getUniformQualifier(), Syntax::SEMICOLON, context, stage); - emitLineBreak(stage); - } - } - } + emitUniforms(context, stage, resourceBindingCtx); // Add vertex inputs - const VariableBlock& vertexInputs = stage.getInputBlock(HW::VERTEX_INPUTS); - if (!vertexInputs.empty()) - { - emitComment("Inputs block: " + vertexInputs.getName(), stage); - emitVariableDeclarations(vertexInputs, _syntax->getInputQualifier(), Syntax::SEMICOLON, context, stage, false); - emitLineBreak(stage); - } + emitInputs(context, stage); // Add vertex data outputs block - const VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); - if (!vertexData.empty()) - { - emitLine("out " + vertexData.getName(), stage, false); - emitScopeBegin(stage); - emitVariableDeclarations(vertexData, EMPTY_STRING, Syntax::SEMICOLON, context, stage, false); - emitScopeEnd(stage, false, false); - emitString(" " + vertexData.getInstance() + Syntax::SEMICOLON, stage); - emitLineBreak(stage); - emitLineBreak(stage); - } + emitOutputs(context, stage); emitFunctionDefinitions(graph, context, stage); @@ -396,40 +358,23 @@ bool GlslShaderGenerator::requiresLighting(const ShaderGraph& graph) const graph.hasClassification(ShaderNode::Classification::BSDF); } -void GlslShaderGenerator::emitPixelStage(const ShaderGraph& graph, GenContext& context, ShaderStage& stage) const +void GlslShaderGenerator::emitDirectives(GenContext&, ShaderStage& stage) const { - HwResourceBindingContextPtr resourceBindingCtx = context.getUserData(HW::USER_DATA_BINDING_CONTEXT); - - // Add directives emitLine("#version " + getVersion(), stage, false); - if (resourceBindingCtx) - { - resourceBindingCtx->emitDirectives(context, stage); - } - emitLineBreak(stage); - - // Add global constants and type definitions - emitInclude("pbrlib/" + GlslShaderGenerator::TARGET + "/lib/mx_defines.glsl", context, stage); - - if (context.getOptions().hwMaxActiveLightSources > 0) - { - const unsigned int maxLights = std::max(1u, context.getOptions().hwMaxActiveLightSources); - emitLine("#define " + HW::LIGHT_DATA_MAX_LIGHT_SOURCES + " " + std::to_string(maxLights), stage, false); - } - emitLine("#define DIRECTIONAL_ALBEDO_METHOD " + std::to_string(int(context.getOptions().hwDirectionalAlbedoMethod)), stage, false); - emitLine("#define " + HW::ENV_RADIANCE_MAX_SAMPLES + " " + std::to_string(context.getOptions().hwMaxRadianceSamples), stage, false); - emitLineBreak(stage); - emitTypeDefinitions(context, stage); +} - // Add all constants +void GlslShaderGenerator::emitConstants(GenContext& context, ShaderStage& stage) const +{ const VariableBlock& constants = stage.getConstantBlock(); if (!constants.empty()) { emitVariableDeclarations(constants, _syntax->getConstantQualifier(), Syntax::SEMICOLON, context, stage); emitLineBreak(stage); } +} - // Add all uniforms +void GlslShaderGenerator::emitUniforms(GenContext& context, ShaderStage& stage, HwResourceBindingContextPtr& resourceBindingCtx) const +{ for (const auto& it : stage.getUniformBlocks()) { const VariableBlock& uniforms = *it.second; @@ -449,36 +394,43 @@ void GlslShaderGenerator::emitPixelStage(const ShaderGraph& graph, GenContext& c } } } +} - bool lighting = requiresLighting(graph); - - bool shadowing = (lighting && context.getOptions().hwShadowMap) || - context.getOptions().hwWriteDepthMoments; +void GlslShaderGenerator::emitLightData(GenContext& context, ShaderStage& stage, HwResourceBindingContextPtr& resourceBindingCtx) const +{ + const VariableBlock& lightData = stage.getUniformBlock(HW::LIGHT_DATA); + const string structArraySuffix = "[" + HW::LIGHT_DATA_MAX_LIGHT_SOURCES + "]"; + const string structName = lightData.getInstance(); + if (resourceBindingCtx) + { + resourceBindingCtx->emitStructuredResourceBindings( + context, lightData, stage, structName, structArraySuffix); + } + else + { + emitLine("struct " + lightData.getName(), stage, false); + emitScopeBegin(stage); + emitVariableDeclarations(lightData, EMPTY_STRING, Syntax::SEMICOLON, context, stage, false); + emitScopeEnd(stage, true); + emitLineBreak(stage); + emitLine("uniform " + lightData.getName() + " " + structName + structArraySuffix, stage); + } + emitLineBreak(stage); +} - // Add light data block if needed - if (lighting && context.getOptions().hwMaxActiveLightSources > 0) +void GlslShaderGenerator::emitInputs(GenContext& context, ShaderStage& stage) const +{ +BEGIN_SHADER_STAGE(stage, Stage::VERTEX) + const VariableBlock& vertexInputs = stage.getInputBlock(HW::VERTEX_INPUTS); + if (!vertexInputs.empty()) { - const VariableBlock& lightData = stage.getUniformBlock(HW::LIGHT_DATA); - const string structArraySuffix = "[" + HW::LIGHT_DATA_MAX_LIGHT_SOURCES + "]"; - const string structName = lightData.getInstance(); - if (resourceBindingCtx) - { - resourceBindingCtx->emitStructuredResourceBindings( - context, lightData, stage, structName, structArraySuffix); - } - else - { - emitLine("struct " + lightData.getName(), stage, false); - emitScopeBegin(stage); - emitVariableDeclarations(lightData, EMPTY_STRING, Syntax::SEMICOLON, context, stage, false); - emitScopeEnd(stage, true); - emitLineBreak(stage); - emitLine("uniform " + lightData.getName() + " " + structName + structArraySuffix, stage); - } + emitComment("Inputs block: " + vertexInputs.getName(), stage); + emitVariableDeclarations(vertexInputs, _syntax->getInputQualifier(), Syntax::SEMICOLON, context, stage, false); emitLineBreak(stage); } +END_SHADER_STAGE(stage, Stage::VERTEX) - // Add vertex data inputs block +BEGIN_SHADER_STAGE(stage, Stage::PIXEL) const VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); if (!vertexData.empty()) { @@ -490,13 +442,91 @@ void GlslShaderGenerator::emitPixelStage(const ShaderGraph& graph, GenContext& c emitLineBreak(stage); emitLineBreak(stage); } +END_SHADER_STAGE(stage, Stage::PIXEL) +} - // Add the pixel shader output. This needs to be a vec4 for rendering - // and upstream connection will be converted to vec4 if needed in emitFinalOutput() +void GlslShaderGenerator::emitOutputs(GenContext& context, ShaderStage& stage) const +{ +BEGIN_SHADER_STAGE(stage, Stage::VERTEX) + const VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); + if (!vertexData.empty()) + { + emitLine("out " + vertexData.getName(), stage, false); + emitScopeBegin(stage); + emitVariableDeclarations(vertexData, EMPTY_STRING, Syntax::SEMICOLON, context, stage, false); + emitScopeEnd(stage, false, false); + emitString(" " + vertexData.getInstance() + Syntax::SEMICOLON, stage); + emitLineBreak(stage); + emitLineBreak(stage); + } +END_SHADER_STAGE(stage, Stage::VERTEX) + +BEGIN_SHADER_STAGE(stage, Stage::PIXEL) emitComment("Pixel shader outputs", stage); const VariableBlock& outputs = stage.getOutputBlock(HW::PIXEL_OUTPUTS); emitVariableDeclarations(outputs, _syntax->getOutputQualifier(), Syntax::SEMICOLON, context, stage, false); emitLineBreak(stage); +END_SHADER_STAGE(stage, Stage::PIXEL) +} + +const HwResourceBindingContextPtr GlslShaderGenerator::getResourceBindingContext(GenContext& context) const +{ + return context.getUserData(HW::USER_DATA_BINDING_CONTEXT); +} + +const string GlslShaderGenerator::getVertexDataPrefix(const VariableBlock& vertexData) const +{ + return vertexData.getInstance() + "."; +} + +void GlslShaderGenerator::emitPixelStage(const ShaderGraph& graph, GenContext& context, ShaderStage& stage) const +{ + HwResourceBindingContextPtr resourceBindingCtx = getResourceBindingContext(context); + + // Add directives + emitDirectives(context, stage); + if (resourceBindingCtx) + { + resourceBindingCtx->emitDirectives(context, stage); + } + emitLineBreak(stage); + + // Add global constants and type definitions + emitInclude("pbrlib/" + GlslShaderGenerator::TARGET + "/lib/mx_defines.glsl", context, stage); + + if (context.getOptions().hwMaxActiveLightSources > 0) + { + const unsigned int maxLights = std::max(1u, context.getOptions().hwMaxActiveLightSources); + emitLine("#define " + HW::LIGHT_DATA_MAX_LIGHT_SOURCES + " " + std::to_string(maxLights), stage, false); + } + emitLine("#define DIRECTIONAL_ALBEDO_METHOD " + std::to_string(int(context.getOptions().hwDirectionalAlbedoMethod)), stage, false); + emitLine("#define " + HW::ENV_RADIANCE_MAX_SAMPLES + " " + std::to_string(context.getOptions().hwMaxRadianceSamples), stage, false); + emitLineBreak(stage); + emitTypeDefinitions(context, stage); + + // Add all constants + emitConstants(context, stage); + + // Add all uniforms + emitUniforms(context, stage, resourceBindingCtx); + + bool lighting = requiresLighting(graph); + + bool shadowing = (lighting && context.getOptions().hwShadowMap) || + context.getOptions().hwWriteDepthMoments; + + // Add light data block if needed + if (lighting && context.getOptions().hwMaxActiveLightSources > 0) + { + emitLightData(context, stage, resourceBindingCtx); + } + + // Add vertex data inputs block + emitInputs(context, stage); + + // Add the pixel shader output. This needs to be a vec4 for rendering + // and upstream connection will be converted to vec4 if needed in emitFinalOutput() + emitOutputs(context, stage); // Emit common math functions emitInclude("pbrlib/" + GlslShaderGenerator::TARGET + "/lib/mx_math.glsl", context, stage); diff --git a/source/MaterialXGenGlsl/GlslShaderGenerator.h b/source/MaterialXGenGlsl/GlslShaderGenerator.h index 206a0c426d..e4051f00e7 100644 --- a/source/MaterialXGenGlsl/GlslShaderGenerator.h +++ b/source/MaterialXGenGlsl/GlslShaderGenerator.h @@ -40,13 +40,16 @@ class MX_GENGLSL_API GlslShaderGenerator : public HwShaderGenerator /// Emit function definitions for all nodes void emitFunctionDefinitions(const ShaderGraph& graph, GenContext& context, ShaderStage& stage) const override; - /// Emit all functon calls constructing the shader body + /// Emit all function calls constructing the shader body void emitFunctionCalls(const ShaderGraph& graph, GenContext& context, ShaderStage& stage) const override; /// Emit a shader variable. void emitVariableDeclaration(const ShaderPort* variable, const string& qualifier, GenContext& context, ShaderStage& stage, bool assignValue = true) const override; + /// Determine the prefix of vertex data variables. + virtual const string getVertexDataPrefix(const VariableBlock& vertexData) const; + public: /// Unique identifier for this generator target static const string TARGET; @@ -58,10 +61,19 @@ class MX_GENGLSL_API GlslShaderGenerator : public HwShaderGenerator virtual void emitVertexStage(const ShaderGraph& graph, GenContext& context, ShaderStage& stage) const; virtual void emitPixelStage(const ShaderGraph& graph, GenContext& context, ShaderStage& stage) const; - bool requiresLighting(const ShaderGraph& graph) const; + virtual void emitDirectives(GenContext& context, ShaderStage& stage) const; + virtual void emitConstants(GenContext& context, ShaderStage& stage) const; + virtual void emitUniforms(GenContext& context, ShaderStage& stage, HwResourceBindingContextPtr &resourceBindingCtx) const; + virtual void emitLightData(GenContext& context, ShaderStage& stage, HwResourceBindingContextPtr& resourceBindingCtx) const; + virtual void emitInputs(GenContext& context, ShaderStage& stage) const; + virtual void emitOutputs(GenContext& context, ShaderStage& stage) const; + + virtual const HwResourceBindingContextPtr getResourceBindingContext(GenContext& context) const; + + virtual bool requiresLighting(const ShaderGraph& graph) const; /// Emit specular environment lookup code - void emitSpecularEnvironment(GenContext& context, ShaderStage& stage) const; + virtual void emitSpecularEnvironment(GenContext& context, ShaderStage& stage) const; /// Override the compound implementation creator in order to handle light compounds. ShaderNodeImplPtr createCompoundImplementation(const NodeGraph& impl) const override; diff --git a/source/MaterialXGenGlsl/Nodes/BitangentNodeGlsl.cpp b/source/MaterialXGenGlsl/Nodes/BitangentNodeGlsl.cpp index d14916208e..041059f62c 100644 --- a/source/MaterialXGenGlsl/Nodes/BitangentNodeGlsl.cpp +++ b/source/MaterialXGenGlsl/Nodes/BitangentNodeGlsl.cpp @@ -38,14 +38,14 @@ void BitangentNodeGlsl::createVariables(const ShaderNode& node, GenContext&, Sha void BitangentNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& context, ShaderStage& stage) const { - const ShaderGenerator& shadergen = context.getShaderGenerator(); + const GlslShaderGenerator& shadergen = static_cast(context.getShaderGenerator()); const ShaderInput* spaceInput = node.getInput(SPACE); const int space = spaceInput ? spaceInput->getValue()->asA() : OBJECT_SPACE; BEGIN_SHADER_STAGE(stage, Stage::VERTEX) VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); if (space == WORLD_SPACE) { ShaderPort* bitangent = vertexData[HW::T_BITANGENT_WORLD]; @@ -69,7 +69,7 @@ void BitangentNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& con BEGIN_SHADER_STAGE(stage, Stage::PIXEL) VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); shadergen.emitLineBegin(stage); shadergen.emitOutput(node.getOutput(), true, false, context, stage); if (space == WORLD_SPACE) diff --git a/source/MaterialXGenGlsl/Nodes/GeomColorNodeGlsl.cpp b/source/MaterialXGenGlsl/Nodes/GeomColorNodeGlsl.cpp index 5e01801228..08c629d5ed 100644 --- a/source/MaterialXGenGlsl/Nodes/GeomColorNodeGlsl.cpp +++ b/source/MaterialXGenGlsl/Nodes/GeomColorNodeGlsl.cpp @@ -28,7 +28,7 @@ void GeomColorNodeGlsl::createVariables(const ShaderNode& node, GenContext&, Sha void GeomColorNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& context, ShaderStage& stage) const { - const ShaderGenerator& shadergen = context.getShaderGenerator(); + const GlslShaderGenerator& shadergen = static_cast(context.getShaderGenerator()); const ShaderOutput* output = node.getOutput(); const ShaderInput* indexInput = node.getInput(INDEX); @@ -37,7 +37,7 @@ void GeomColorNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& con BEGIN_SHADER_STAGE(stage, Stage::VERTEX) VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); ShaderPort* color = vertexData[variable]; if (!color->isEmitted()) { @@ -57,7 +57,7 @@ void GeomColorNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& con suffix = ".rgb"; } VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); ShaderPort* color = vertexData[variable]; shadergen.emitLineBegin(stage); shadergen.emitOutput(node.getOutput(), true, false, context, stage); diff --git a/source/MaterialXGenGlsl/Nodes/GeomPropValueNodeGlsl.cpp b/source/MaterialXGenGlsl/Nodes/GeomPropValueNodeGlsl.cpp index deac5844a6..5f86ef3d76 100644 --- a/source/MaterialXGenGlsl/Nodes/GeomPropValueNodeGlsl.cpp +++ b/source/MaterialXGenGlsl/Nodes/GeomPropValueNodeGlsl.cpp @@ -34,7 +34,7 @@ void GeomPropValueNodeGlsl::createVariables(const ShaderNode& node, GenContext&, void GeomPropValueNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& context, ShaderStage& stage) const { - const ShaderGenerator& shadergen = context.getShaderGenerator(); + const GlslShaderGenerator& shadergen = static_cast(context.getShaderGenerator()); const ShaderInput* geomPropInput = node.getInput(GEOMPROP); if (!geomPropInput) @@ -46,7 +46,7 @@ void GeomPropValueNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& BEGIN_SHADER_STAGE(stage, Stage::VERTEX) VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); ShaderPort* geomprop = vertexData[variable]; if (!geomprop->isEmitted()) { @@ -57,7 +57,7 @@ void GeomPropValueNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& BEGIN_SHADER_STAGE(stage, Stage::PIXEL) VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); ShaderPort* geomprop = vertexData[variable]; shadergen.emitLineBegin(stage); shadergen.emitOutput(node.getOutput(), true, false, context, stage); diff --git a/source/MaterialXGenGlsl/Nodes/NormalNodeGlsl.cpp b/source/MaterialXGenGlsl/Nodes/NormalNodeGlsl.cpp index 9a2408d95d..e26f916fd8 100644 --- a/source/MaterialXGenGlsl/Nodes/NormalNodeGlsl.cpp +++ b/source/MaterialXGenGlsl/Nodes/NormalNodeGlsl.cpp @@ -37,14 +37,14 @@ void NormalNodeGlsl::createVariables(const ShaderNode& node, GenContext&, Shader void NormalNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& context, ShaderStage& stage) const { - const ShaderGenerator& shadergen = context.getShaderGenerator(); + const GlslShaderGenerator& shadergen = static_cast(context.getShaderGenerator()); const ShaderInput* spaceInput = node.getInput(SPACE); const int space = spaceInput ? spaceInput->getValue()->asA() : OBJECT_SPACE; BEGIN_SHADER_STAGE(stage, Stage::VERTEX) VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); if (space == WORLD_SPACE) { ShaderPort* normal = vertexData[HW::T_NORMAL_WORLD]; @@ -67,7 +67,7 @@ void NormalNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& contex BEGIN_SHADER_STAGE(stage, Stage::PIXEL) VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); shadergen.emitLineBegin(stage); shadergen.emitOutput(node.getOutput(), true, false, context, stage); if (space == WORLD_SPACE) diff --git a/source/MaterialXGenGlsl/Nodes/PositionNodeGlsl.cpp b/source/MaterialXGenGlsl/Nodes/PositionNodeGlsl.cpp index 97517268ce..e1ff484950 100644 --- a/source/MaterialXGenGlsl/Nodes/PositionNodeGlsl.cpp +++ b/source/MaterialXGenGlsl/Nodes/PositionNodeGlsl.cpp @@ -36,14 +36,14 @@ void PositionNodeGlsl::createVariables(const ShaderNode& node, GenContext&, Shad void PositionNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& context, ShaderStage& stage) const { - const ShaderGenerator& shadergen = context.getShaderGenerator(); + const GlslShaderGenerator& shadergen = static_cast(context.getShaderGenerator()); const ShaderInput* spaceInput = node.getInput(SPACE); const int space = spaceInput ? spaceInput->getValue()->asA() : OBJECT_SPACE; BEGIN_SHADER_STAGE(stage, Stage::VERTEX) VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); if (space == WORLD_SPACE) { ShaderPort* position = vertexData[HW::T_POSITION_WORLD]; @@ -66,7 +66,7 @@ void PositionNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& cont BEGIN_SHADER_STAGE(stage, Stage::PIXEL) VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); shadergen.emitLineBegin(stage); shadergen.emitOutput(node.getOutput(), true, false, context, stage); if (space == WORLD_SPACE) diff --git a/source/MaterialXGenGlsl/Nodes/SurfaceNodeGlsl.cpp b/source/MaterialXGenGlsl/Nodes/SurfaceNodeGlsl.cpp index 1855d8a3a1..69152272bf 100644 --- a/source/MaterialXGenGlsl/Nodes/SurfaceNodeGlsl.cpp +++ b/source/MaterialXGenGlsl/Nodes/SurfaceNodeGlsl.cpp @@ -73,7 +73,7 @@ void SurfaceNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& conte BEGIN_SHADER_STAGE(stage, Stage::VERTEX) VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); ShaderPort* position = vertexData[HW::T_POSITION_WORLD]; if (!position->isEmitted()) { @@ -99,7 +99,7 @@ void SurfaceNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& conte BEGIN_SHADER_STAGE(stage, Stage::PIXEL) VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); // Declare the output variable shadergen.emitLineBegin(stage); @@ -128,7 +128,7 @@ void SurfaceNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& conte shadergen.emitComment("Shadow occlusion", stage); if (context.getOptions().hwShadowMap) { - shadergen.emitLine("vec3 shadowCoord = (" + HW::T_SHADOW_MATRIX + " * vec4(" + HW::T_VERTEX_DATA_INSTANCE + "." + HW::T_POSITION_WORLD + ", 1.0)).xyz", stage); + shadergen.emitLine("vec3 shadowCoord = (" + HW::T_SHADOW_MATRIX + " * vec4(" + prefix + HW::T_POSITION_WORLD + ", 1.0)).xyz", stage); shadergen.emitLine("shadowCoord = shadowCoord * 0.5 + 0.5", stage); shadergen.emitLine("vec2 shadowMoments = texture(" + HW::T_SHADOW_MAP + ", shadowCoord.xy).xy", stage); shadergen.emitLine("float occlusion = mx_variance_shadow_occlusion(shadowMoments, shadowCoord.z)", stage); @@ -139,9 +139,9 @@ void SurfaceNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& conte } shadergen.emitLineBreak(stage); - shadergen.emitLine("vec3 N = normalize(" + HW::T_VERTEX_DATA_INSTANCE + "." + HW::T_NORMAL_WORLD + ")", stage); - shadergen.emitLine("vec3 V = normalize(" + HW::T_VIEW_POSITION + " - " + HW::T_VERTEX_DATA_INSTANCE + "." + HW::T_POSITION_WORLD + ")", stage); - shadergen.emitLine("vec3 P = " + HW::T_VERTEX_DATA_INSTANCE + "." + HW::T_POSITION_WORLD, stage); + shadergen.emitLine("vec3 N = normalize(" + prefix + HW::T_NORMAL_WORLD + ")", stage); + shadergen.emitLine("vec3 V = normalize(" + HW::T_VIEW_POSITION + " - " + prefix + HW::T_POSITION_WORLD + ")", stage); + shadergen.emitLine("vec3 P = " + prefix + HW::T_POSITION_WORLD, stage); emitLightLoop(node, context, stage, outColor); @@ -216,6 +216,8 @@ void SurfaceNodeGlsl::emitLightLoop(const ShaderNode& node, GenContext& context, { const GlslShaderGenerator& shadergen = static_cast(context.getShaderGenerator()); const ShaderGraph& graph = *node.getParent(); + VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); + const string prefix = shadergen.getVertexDataPrefix(vertexData); shadergen.emitComment("Light loop", stage); shadergen.emitLine("int numLights = numActiveLightSources()", stage); @@ -223,8 +225,7 @@ void SurfaceNodeGlsl::emitLightLoop(const ShaderNode& node, GenContext& context, shadergen.emitLine("for (int activeLightIndex = 0; activeLightIndex < numLights; ++activeLightIndex)", stage, false); shadergen.emitScopeBegin(stage); - - shadergen.emitLine("sampleLightSource(" + HW::T_LIGHT_DATA_INSTANCE + "[activeLightIndex], " + HW::T_VERTEX_DATA_INSTANCE + "." + HW::T_POSITION_WORLD + ", lightShader)", stage); + shadergen.emitLine("sampleLightSource(" + HW::T_LIGHT_DATA_INSTANCE + "[activeLightIndex], " + prefix + HW::T_POSITION_WORLD + ", lightShader)", stage); shadergen.emitLine("vec3 L = lightShader.direction", stage); shadergen.emitLineBreak(stage); diff --git a/source/MaterialXGenGlsl/Nodes/SurfaceShaderNodeGlsl.cpp b/source/MaterialXGenGlsl/Nodes/SurfaceShaderNodeGlsl.cpp index dd4b156dbb..e69a480963 100644 --- a/source/MaterialXGenGlsl/Nodes/SurfaceShaderNodeGlsl.cpp +++ b/source/MaterialXGenGlsl/Nodes/SurfaceShaderNodeGlsl.cpp @@ -43,9 +43,11 @@ void SurfaceShaderNodeGlsl::createVariables(const ShaderNode&, GenContext& conte void SurfaceShaderNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& context, ShaderStage& stage) const { + const GlslShaderGenerator& shadergen = static_cast(context.getShaderGenerator()); + BEGIN_SHADER_STAGE(stage, Stage::VERTEX) VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); ShaderPort* position = vertexData[HW::T_POSITION_WORLD]; if (!position->isEmitted()) { diff --git a/source/MaterialXGenGlsl/Nodes/TangentNodeGlsl.cpp b/source/MaterialXGenGlsl/Nodes/TangentNodeGlsl.cpp index 8724048efe..d174aea582 100644 --- a/source/MaterialXGenGlsl/Nodes/TangentNodeGlsl.cpp +++ b/source/MaterialXGenGlsl/Nodes/TangentNodeGlsl.cpp @@ -37,14 +37,14 @@ void TangentNodeGlsl::createVariables(const ShaderNode& node, GenContext&, Shade void TangentNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& context, ShaderStage& stage) const { - const ShaderGenerator& shadergen = context.getShaderGenerator(); + const GlslShaderGenerator& shadergen = static_cast(context.getShaderGenerator()); const ShaderInput* spaceInput = node.getInput(SPACE); const int space = spaceInput ? spaceInput->getValue()->asA() : OBJECT_SPACE; BEGIN_SHADER_STAGE(stage, Stage::VERTEX) VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); if (space == WORLD_SPACE) { ShaderPort* tangent = vertexData[HW::T_TANGENT_WORLD]; @@ -67,7 +67,7 @@ void TangentNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& conte BEGIN_SHADER_STAGE(stage, Stage::PIXEL) VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); shadergen.emitLineBegin(stage); shadergen.emitOutput(node.getOutput(), true, false, context, stage); if (space == WORLD_SPACE) diff --git a/source/MaterialXGenGlsl/Nodes/TexCoordNodeGlsl.cpp b/source/MaterialXGenGlsl/Nodes/TexCoordNodeGlsl.cpp index aecc783d5b..76122a9b4d 100644 --- a/source/MaterialXGenGlsl/Nodes/TexCoordNodeGlsl.cpp +++ b/source/MaterialXGenGlsl/Nodes/TexCoordNodeGlsl.cpp @@ -30,7 +30,7 @@ void TexCoordNodeGlsl::createVariables(const ShaderNode& node, GenContext&, Shad void TexCoordNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& context, ShaderStage& stage) const { - const ShaderGenerator& shadergen = context.getShaderGenerator(); + const GlslShaderGenerator& shadergen = static_cast(context.getShaderGenerator()); const ShaderInput* indexInput = node.getInput(INDEX); const string index = indexInput ? indexInput->getValue()->getValueString() : "0"; @@ -38,7 +38,7 @@ void TexCoordNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& cont BEGIN_SHADER_STAGE(stage, Stage::VERTEX) VariableBlock& vertexData = stage.getOutputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); ShaderPort* texcoord = vertexData[variable]; if (!texcoord->isEmitted()) { @@ -49,9 +49,9 @@ void TexCoordNodeGlsl::emitFunctionCall(const ShaderNode& node, GenContext& cont BEGIN_SHADER_STAGE(stage, Stage::PIXEL) VariableBlock& vertexData = stage.getInputBlock(HW::VERTEX_DATA); - const string prefix = vertexData.getInstance() + "."; + const string prefix = shadergen.getVertexDataPrefix(vertexData); ShaderPort* texcoord = vertexData[variable]; - shadergen.emitLineBegin(stage); + shadergen.emitLineBegin(stage); shadergen.emitOutput(node.getOutput(), true, false, context, stage); shadergen.emitString(" = " + prefix + texcoord->getVariable(), stage); shadergen.emitLineEnd(stage); diff --git a/source/MaterialXGenShader/Nodes/SwitchNode.cpp b/source/MaterialXGenShader/Nodes/SwitchNode.cpp index c6bff3f4c3..f15575c377 100644 --- a/source/MaterialXGenShader/Nodes/SwitchNode.cpp +++ b/source/MaterialXGenShader/Nodes/SwitchNode.cpp @@ -52,9 +52,9 @@ void SwitchNode::emitFunctionCall(const ShaderNode& node, GenContext& context, S // input may be float, integer or boolean. shadergen.emitString("if (float(", stage); shadergen.emitInput(which, context, stage); - shadergen.emitString(") < ", stage); + shadergen.emitString(") < float(", stage); shadergen.emitValue(float(branch + 1), stage); - shadergen.emitString(")", stage); + shadergen.emitString("))", stage); shadergen.emitLineEnd(stage, false); shadergen.emitScopeBegin(stage); diff --git a/source/MaterialXTest/CMakeLists.txt b/source/MaterialXTest/CMakeLists.txt index 09206bc884..8ecd77e901 100644 --- a/source/MaterialXTest/CMakeLists.txt +++ b/source/MaterialXTest/CMakeLists.txt @@ -126,6 +126,11 @@ endif() add_custom_command(TARGET MaterialXTest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/../../libraries ${CMAKE_BINARY_DIR}/bin/libraries) +if(MATERIALX_BUILD_GEN_ESSL) + add_custom_command(TARGET MaterialXTest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/../../source/MaterialXGenEssl/libraries ${CMAKE_BINARY_DIR}/bin/libraries) +endif() if(MATERIALX_BUILD_GEN_OSL) install(DIRECTORY DESTINATION ${CMAKE_BINARY_DIR}/bin/libraries/stdlib/reference/osl) diff --git a/source/MaterialXView/CMakeLists.txt b/source/MaterialXView/CMakeLists.txt index 14000c0488..984dde4a96 100644 --- a/source/MaterialXView/CMakeLists.txt +++ b/source/MaterialXView/CMakeLists.txt @@ -77,6 +77,9 @@ endif() if (MATERIALX_BUILD_GEN_ARNOLD) LIST(APPEND LIBS MaterialXGenArnold) endif() +if (MATERIALX_BUILD_GEN_ESSL) + LIST(APPEND LIBS MaterialXGenEssl) +endif() target_link_libraries( ${LIBS} @@ -101,6 +104,7 @@ install(TARGETS MaterialXView RUNTIME DESTINATION bin) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXView.pdb" DESTINATION "${CMAKE_INSTALL_PREFIX}/bin/" OPTIONAL) + # Copy over libraries and resources to local build area add_custom_command(TARGET MaterialXView POST_BUILD @@ -109,3 +113,8 @@ add_custom_command(TARGET MaterialXView POST_BUILD add_custom_command(TARGET MaterialXView POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/../../resources ${CMAKE_BINARY_DIR}/bin/resources) +if(MATERIALX_BUILD_GEN_ESSL) + add_custom_command(TARGET MaterialXView POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/../../source/MaterialXGenEssl/libraries ${CMAKE_BINARY_DIR}/bin/libraries) +endif() diff --git a/source/MaterialXView/Viewer.cpp b/source/MaterialXView/Viewer.cpp index a760a067fa..3e4289e619 100644 --- a/source/MaterialXView/Viewer.cpp +++ b/source/MaterialXView/Viewer.cpp @@ -18,6 +18,9 @@ #ifdef MATERIALX_BUILD_GEN_ARNOLD #include #endif +#ifdef MATERIALX_BUILD_GEN_ESSL +#include +#endif #include #include @@ -234,6 +237,9 @@ Viewer::Viewer(const std::string& materialFilename, #endif #if MATERIALX_BUILD_GEN_ARNOLD _genContextArnold(mx::ArnoldShaderGenerator::create()), +#endif +#if MATERIALX_BUILD_GEN_ESSL + _genContextEssl(mx::EsslShaderGenerator::create()), #endif _unitRegistry(mx::UnitConverterRegistry::create()), _splitByUdims(true), @@ -280,6 +286,14 @@ Viewer::Viewer(const std::string& materialFilename, _genContextArnold.getOptions().targetColorSpaceOverride = "lin_rec709"; _genContextArnold.getOptions().fileTextureVerticalFlip = false; #endif +#if MATERIALX_BUILD_GEN_ESSL + _genContextEssl.getOptions().targetColorSpaceOverride = "lin_rec709"; + _genContextEssl.getOptions().fileTextureVerticalFlip = false; + _genContextEssl.getOptions().hwMaxActiveLightSources = 1; + _genContextEssl.getOptions().hwSpecularEnvironmentMethod = mx::SPECULAR_ENVIRONMENT_FIS; + _genContextEssl.getOptions().hwDirectionalAlbedoMethod = mx::DIRECTIONAL_ALBEDO_CURVE_FIT; +#endif + // Register the GLSL implementation for used by the environment shader. _genContext.getShaderGenerator().registerImplementation("IM_viewdir_vector3_" + mx::GlslShaderGenerator::TARGET, ViewDirGlsl::create); @@ -533,6 +547,7 @@ void Viewer::applyDirectLights(mx::DocumentPtr doc) std::vector lights; _lightHandler->findLights(doc, lights); _lightHandler->registerLights(doc, lights, _genContext); + _lightHandler->registerLights(doc, lights, _genContextEssl); _lightHandler->setLightSources(lights); } catch (std::exception& e) @@ -764,6 +779,9 @@ void Viewer::createAdvancedSettings(Widget* parent) #if MATERIALX_BUILD_GEN_ARNOLD _genContextArnold.getOptions().targetDistanceUnit = _distanceUnitOptions[index]; #endif +#if MATERIALX_BUILD_GEN_ESSL + _genContextEssl.getOptions().targetDistanceUnit = _distanceUnitOptions[index]; +#endif for (MaterialPtr material : _materials) { material->bindShader(); @@ -911,6 +929,8 @@ void Viewer::createAdvancedSettings(Widget* parent) referenceQualityBox->setCallback([this](bool enable) { _genContext.getOptions().hwDirectionalAlbedoMethod = enable ? mx::DIRECTIONAL_ALBEDO_IS : mx::DIRECTIONAL_ALBEDO_TABLE; + // No Albedo Table support for Essl yet. + _genContextEssl.getOptions().hwDirectionalAlbedoMethod = enable ? mx::DIRECTIONAL_ALBEDO_IS : mx::DIRECTIONAL_ALBEDO_CURVE_FIT; reloadShaders(); }); @@ -919,6 +939,7 @@ void Viewer::createAdvancedSettings(Widget* parent) importanceSampleBox->setCallback([this](bool enable) { _genContext.getOptions().hwSpecularEnvironmentMethod = enable ? mx::SPECULAR_ENVIRONMENT_FIS : mx::SPECULAR_ENVIRONMENT_PREFILTER; + _genContextEssl.getOptions().hwSpecularEnvironmentMethod = _genContext.getOptions().hwSpecularEnvironmentMethod; reloadShaders(); }); @@ -1169,6 +1190,9 @@ void Viewer::loadDocument(const mx::FilePath& filename, mx::DocumentPtr librarie // Clear user data on the generator. _genContext.clearUserData(); +#if MATERIALX_BUILD_GEN_ESSL + _genContextEssl.clearUserData(); +#endif // Clear materials if merging is not requested. if (!_mergeMaterials) @@ -1290,6 +1314,9 @@ void Viewer::loadDocument(const mx::FilePath& filename, mx::DocumentPtr librarie { // Clear cached implementations, in case libraries on the file system have changed. _genContext.clearNodeImplementations(); +#if MATERIALX_BUILD_GEN_ESSL + _genContextEssl.clearNodeImplementations(); +#endif mx::TypedElementPtr elem = mat->getElement(); @@ -1469,6 +1496,18 @@ void Viewer::saveShaderSource(mx::GenContext& context) new ng::MessageDialog(this, ng::MessageDialog::Type::Information, "Saved Arnold OSL source: ", sourceFilename); } #endif +#if MATERIALX_BUILD_GEN_ESSL + else if (context.getShaderGenerator().getTarget() == mx::EsslShaderGenerator::TARGET) + { + const std::string& pixelShader = shader->getSourceCode(mx::Stage::PIXEL); + const std::string& vertexShader = shader->getSourceCode(mx::Stage::VERTEX); + writeTextFile(vertexShader, sourceFilename.asString() + "_essl_vs.glsl"); + writeTextFile(pixelShader, sourceFilename.asString() + "_essl_ps.glsl"); + new ng::MessageDialog(this, ng::MessageDialog::Type::Information, "Saved Essl source: ", + sourceFilename.asString() + "_essl_*.glsl"); + } +#endif + } } } @@ -1632,6 +1671,9 @@ void Viewer::loadStandardLibraries() #if MATERIALX_BUILD_GEN_ARNOLD initContext(_genContextArnold); #endif +#if MATERIALX_BUILD_GEN_ESSL + initContext(_genContextEssl); +#endif } bool Viewer::keyboardEvent(int key, int scancode, int action, int modifiers) @@ -1703,8 +1745,17 @@ bool Viewer::keyboardEvent(int key, int scancode, int action, int modifiers) } #endif - // Load GLSL shader source from file. Editing the source files before - // loading provides a way to debug and experiment with shader source code. +#if MATERIALX_BUILD_GEN_ESSL + // Save Essl shader source to file. + if (key == GLFW_KEY_E && action == GLFW_PRESS) + { + saveShaderSource(_genContextEssl); + return true; + } +#endif + + // Load shader source from file. Editing the source files before loading + // provides a way to debug and experiment with shader source code. if (key == GLFW_KEY_L && action == GLFW_PRESS) { loadShaderSource(); diff --git a/source/MaterialXView/Viewer.h b/source/MaterialXView/Viewer.h index 6df65c682d..09726c9402 100644 --- a/source/MaterialXView/Viewer.h +++ b/source/MaterialXView/Viewer.h @@ -332,6 +332,9 @@ class Viewer : public ng::Screen #if MATERIALX_BUILD_GEN_ARNOLD mx::GenContext _genContextArnold; #endif +#if MATERIALX_BUILD_GEN_ESSL + mx::GenContext _genContextEssl; +#endif // Unit registry mx::UnitConverterRegistryPtr _unitRegistry;