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;