diff --git a/packages/dev/core/src/Materials/PBR/openPbrMaterial.ts b/packages/dev/core/src/Materials/PBR/openPbrMaterial.ts index 7c8e2c680ec..e04fe0a7118 100644 --- a/packages/dev/core/src/Materials/PBR/openPbrMaterial.ts +++ b/packages/dev/core/src/Materials/PBR/openPbrMaterial.ts @@ -232,6 +232,7 @@ export class OpenPBRMaterialDefines extends ImageProcessingDefinesMixin(OpenPBRM public SPECULAR_ROUGHNESS_ANISOTROPY_FROM_TANGENT_TEXTURE = false; public COAT_ROUGHNESS_ANISOTROPY_FROM_TANGENT_TEXTURE = false; public USE_GLTF_STYLE_ANISOTROPY = false; + public THIN_FILM_THICKNESS_FROM_THIN_FILM_TEXTURE = false; public ENVIRONMENTBRDF = false; public ENVIRONMENTBRDF_RGBD = false; @@ -247,6 +248,8 @@ export class OpenPBRMaterialDefines extends ImageProcessingDefinesMixin(OpenPBRM public ANISOTROPIC_OPENPBR = true; // Tells the shader to use OpenPBR's anisotropic roughness remapping public ANISOTROPIC_BASE = false; // Tells the shader to apply anisotropy to the base layer public ANISOTROPIC_COAT = false; // Tells the shader to apply anisotropy to the coat layer + public THIN_FILM = false; // Enables thin film layer + public IRIDESCENCE = false; // Enables iridescence layer public REFLECTION = false; public REFLECTIONMAP_3D = false; @@ -787,6 +790,56 @@ export class OpenPBRMaterial extends OpenPBRMaterialBase { // eslint-disable-next-line @typescript-eslint/no-unused-vars private _emissionColorTexture: Sampler = new Sampler("emission_color", "emissionColor", "EMISSION_COLOR"); + /** + * Defines the weight of the thin film layer on top of the base layer for iridescent effects. + */ + public thinFilmWeight: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "thinFilmWeight") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _thinFilmWeight: Property = new Property("thin_film_weight", 0.0, "vThinFilmWeight", 1, 0); + + /** + * Thin film weight texture. + */ + public thinFilmWeightTexture: Nullable; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "thinFilmWeightTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _thinFilmWeightTexture: Sampler = new Sampler("thin_film_weight", "thinFilmWeight", "THIN_FILM_WEIGHT"); + + /** + * Defines the thickness of the thin film layer in μm. If a texture is provided for thinFilmWeightTexture, + * this value will act as a multiplier to the texture values. + * See OpenPBR's specs for thin_film_thickness + */ + public thinFilmThickness: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "thinFilmThickness") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _thinFilmThickness: Property = new Property("thin_film_thickness", 0.5, "vThinFilmThickness", 2, 0); + + /** + * Defines the minimum thickness of the thin film layer in μm. + */ + public thinFilmThicknessMin: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "thinFilmThicknessMin") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _thinFilmThicknessMin: Property = new Property("thin_film_thickness_min", 0.0, "vThinFilmThickness", 2, 1); + + /** + * Defines the maximum thickness of the thin film layer in μm. + */ + public thinFilmThicknessTexture: Nullable; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "thinFilmThicknessTexture") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _thinFilmThicknessTexture: Sampler = new Sampler("thin_film_thickness", "thinFilmThickness", "THIN_FILM_THICKNESS"); + + /** + * Defines the index of refraction of the thin film layer. + */ + public thinFilmIor: number; + @addAccessorsForMaterialProperty("_markAllSubMeshesAsTexturesDirty", "thinFilmIor") + // eslint-disable-next-line @typescript-eslint/no-unused-vars + private _thinFilmIor: Property = new Property("thin_film_ior", 1.4, "vThinFilmIor", 1, 0); + /** * Defines the ambient occlusion texture. */ @@ -1144,6 +1197,11 @@ export class OpenPBRMaterial extends OpenPBRMaterialBase { */ public _useMetallicFromMetallicTextureBlue = false; + /** + * Specifies if the thin film thickness is stored in the green channel of the thin film thickness texture. + */ + public _useThinFilmThicknessFromTextureGreen = false; + /** * Defines the falloff type used in this material. * It by default is Physical. @@ -1463,6 +1521,12 @@ export class OpenPBRMaterial extends OpenPBRMaterialBase { this._geometryCoatTangentTexture; this._geometryOpacity; this._geometryOpacityTexture; + this._thinFilmWeight; + this._thinFilmWeightTexture; + this._thinFilmThickness; + this._thinFilmThicknessMin; + this._thinFilmThicknessTexture; + this._thinFilmIor; this._emissionLuminance; this._emissionColor; this._emissionColorTexture; @@ -2426,6 +2490,7 @@ export class OpenPBRMaterial extends OpenPBRMaterialBase { defines.COAT_ROUGHNESS_ANISOTROPY_FROM_TANGENT_TEXTURE = this._useCoatRoughnessAnisotropyFromTangentTexture; defines.ROUGHNESSSTOREINMETALMAPGREEN = this._useRoughnessFromMetallicTextureGreen; defines.METALLNESSSTOREINMETALMAPBLUE = this._useMetallicFromMetallicTextureBlue; + defines.THIN_FILM_THICKNESS_FROM_THIN_FILM_TEXTURE = this._useThinFilmThicknessFromTextureGreen; if (this.geometryNormalTexture) { if (this._useParallax && this.baseColorTexture && MaterialFlags.DiffuseTextureEnabled) { @@ -2522,6 +2587,9 @@ export class OpenPBRMaterial extends OpenPBRMaterialBase { defines.ANISOTROPIC_COAT = false; } + defines.THIN_FILM = this.thinFilmWeight > 0.0; + defines.IRIDESCENCE = this.thinFilmWeight > 0.0; + // Misc. if (defines._areMiscDirty) { PrepareDefinesForMisc( diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrDielectricReflectance.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDielectricReflectance.fx index f37eb493bf0..8902d364261 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/openpbrDielectricReflectance.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDielectricReflectance.fx @@ -14,6 +14,24 @@ ReflectanceParams dielectricReflectance( ReflectanceParams outParams; float dielectricF0 = pow((insideIOR - outsideIOR) / (insideIOR + outsideIOR), 2.0); + float dielectricF0_NoSpec = pow((1.0 - outsideIOR) / (1.0 + outsideIOR), 2.0); + + // Scale the reflectanceF90 by the IOR for values less than 1.5. + // This is an empirical hack to account for the fact that Schlick is tuned for IOR = 1.5 + // and an IOR of 1.0 should result in no visible glancing specular. + float f90Scale = clamp(2.0 * abs(insideIOR - outsideIOR), 0.0, 1.0); + float f90Scale_NoSpec = clamp(2.0 * abs(1.0 - outsideIOR), 0.0, 1.0); + + // Now, compute the coloured reflectance at glancing angles based on the specular model. + #if (DIELECTRIC_SPECULAR_MODEL == DIELECTRIC_SPECULAR_MODEL_OPENPBR) + // In OpenPBR, the F90 is coloured using the specular colour for dielectrics. + vec3 dielectricColorF90 = specularColor.rgb * vec3(f90Scale); + vec3 dielectricColorF90_NoSpec = specularColor.rgb * vec3(f90Scale_NoSpec); + #else + // In glTF, the F90 is white for dielectrics. + vec3 dielectricColorF90 = vec3(f90Scale); + vec3 dielectricColorF90_NoSpec = vec3(f90Scale_NoSpec); + #endif // Compute non-coloured reflectance. // reflectanceF0 is the non-coloured reflectance used for blending between the diffuse and specular components. @@ -21,33 +39,21 @@ ReflectanceParams dielectricReflectance( // In glTF's material model, the F0 value is multiplied by the maximum component of the specular colour. #if DIELECTRIC_SPECULAR_MODEL == DIELECTRIC_SPECULAR_MODEL_GLTF float maxF0 = max(specularColor.r, max(specularColor.g, specularColor.b)); - outParams.F0 = dielectricF0 * maxF0 * specularWeight; + outParams.F0 = mix(dielectricF0_NoSpec, dielectricF0, specularWeight) * maxF0; #else - outParams.F0 = dielectricF0 * specularWeight; + outParams.F0 = mix(dielectricF0_NoSpec, dielectricF0, specularWeight); #endif - // Scale the reflectanceF90 by the IOR for values less than 1.5. - // This is an empirical hack to account for the fact that Schlick is tuned for IOR = 1.5 - // and an IOR of 1.0 should result in no visible glancing specular. - float f90Scale = clamp(2.0 * abs(insideIOR - outsideIOR), 0.0, 1.0); - outParams.F90 = f90Scale * specularWeight; + + outParams.F90 = mix(f90Scale_NoSpec, f90Scale, specularWeight); // Compute the coloured F0 reflectance. // The coloured reflectance is the percentage of light reflected by the specular lobe at normal incidence. // In glTF and OpenPBR, it is not the same thing as the percentage of light blocked from penetrating // down to the layer below. The non-coloured F0 will be used for this (see below). - outParams.coloredF0 = vec3(dielectricF0 * specularWeight) * specularColor.rgb; - - // Now, compute the coloured reflectance at glancing angles based on the specular model. - #if (DIELECTRIC_SPECULAR_MODEL == DIELECTRIC_SPECULAR_MODEL_OPENPBR) - // In OpenPBR, the F90 is coloured using the specular colour for dielectrics. - vec3 dielectricColorF90 = specularColor.rgb * vec3(f90Scale) * specularWeight; - #else - // In glTF, the F90 is white for dielectrics. - vec3 dielectricColorF90 = vec3(f90Scale) * specularWeight; - #endif - outParams.coloredF90 = dielectricColorF90; + outParams.coloredF0 = mix(vec3(dielectricF0_NoSpec), vec3(dielectricF0), specularWeight) * specularColor.rgb; + outParams.coloredF90 = mix(dielectricColorF90_NoSpec, dielectricColorF90, specularWeight); return outParams; } diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLighting.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLighting.fx index 995fba1beda..c6b3c5482ab 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLighting.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrDirectLighting.fx @@ -5,6 +5,7 @@ vec3 slab_translucent = vec3(0., 0., 0.); vec3 slab_glossy = vec3(0., 0., 0.); float specularFresnel = 0.0; + vec3 specularColoredFresnel = vec3(0., 0., 0.); vec3 slab_metal = vec3(0., 0., 0.); vec3 slab_coat = vec3(0., 0., 0.); float coatFresnel = 0.0; @@ -35,11 +36,19 @@ baseGeoInfo.anisotropicTangent, baseGeoInfo.anisotropicBitangent, baseGeoInfo.anisotropy, 0.0, lightColor{X}.rgb); #else - slab_glossy = computeSpecularLighting(preInfo{X}, normalW, baseDielectricReflectance.coloredF0, baseDielectricReflectance.coloredF90, specular_roughness, lightColor{X}.rgb); + // We're passing in vec3(1.0) for both F0 and F90 here because the actual Fresnel is computed below + // Also computeSpecularLighting does some iridescence work using these values that we don't want. + slab_glossy = computeSpecularLighting(preInfo{X}, normalW, vec3(1.0), vec3(1.0), specular_roughness, lightColor{X}.rgb); #endif - float NdotH = dot(normalW, preInfo{X}.H); specularFresnel = fresnelSchlickGGX(NdotH, baseDielectricReflectance.F0, baseDielectricReflectance.F90); + specularColoredFresnel = specularFresnel * specular_color; + #ifdef THIN_FILM + // Scale the thin film effect based on how different the IOR is from 1.0 (no thin film effect) + float thinFilmIorScale = clamp(2.0f * abs(thin_film_ior - 1.0f), 0.0f, 1.0f); + vec3 thinFilmDielectricFresnel = evalIridescence(thin_film_outside_ior, thin_film_ior, preInfo{X}.VdotH, thin_film_thickness, baseDielectricReflectance.coloredF0); + specularColoredFresnel = mix(specularColoredFresnel, thinFilmDielectricFresnel * specular_color, thin_film_weight * thinFilmIorScale); + #endif } #endif @@ -51,16 +60,25 @@ // For OpenPBR, we use the F82 specular model for metallic materials and mix with the // usual Schlick lobe. #if (CONDUCTOR_SPECULAR_MODEL == CONDUCTOR_SPECULAR_MODEL_OPENPBR) - vec3 coloredFresnel = specular_weight * getF82Specular(preInfo{X}.VdotH, baseConductorReflectance.coloredF0, baseConductorReflectance.coloredF90, specular_roughness); + vec3 coloredFresnel = getF82Specular(preInfo{X}.VdotH, baseConductorReflectance.coloredF0, baseConductorReflectance.coloredF90, specular_roughness); #else vec3 coloredFresnel = fresnelSchlickGGX(preInfo{X}.VdotH, baseConductorReflectance.coloredF0, baseConductorReflectance.coloredF90); #endif + #ifdef THIN_FILM + // Scale the thin film effect based on how different the IOR is from 1.0 (no thin film effect) + float thinFilmIorScale = clamp(2.0f * abs(thin_film_ior - 1.0f), 0.0f, 1.0f); + vec3 thinFilmConductorFresnel = evalIridescence(thin_film_outside_ior, thin_film_ior, preInfo{X}.VdotH, thin_film_thickness, baseConductorReflectance.coloredF0); + coloredFresnel = mix(coloredFresnel, specular_weight * thinFilmIorScale * thinFilmConductorFresnel, thin_film_weight); + #endif + #ifdef ANISOTROPIC_BASE slab_metal = computeAnisotropicSpecularLighting(preInfo{X}, viewDirectionW, normalW, baseGeoInfo.anisotropicTangent, baseGeoInfo.anisotropicBitangent, baseGeoInfo.anisotropy, 0.0, lightColor{X}.rgb); #else - slab_metal = computeSpecularLighting(preInfo{X}, normalW, baseConductorReflectance.coloredF0, coloredFresnel, specular_roughness, lightColor{X}.rgb); + slab_metal = computeSpecularLighting(preInfo{X}, normalW, vec3(1.0), coloredFresnel, specular_roughness, lightColor{X}.rgb); #endif + + } #endif @@ -119,7 +137,7 @@ slab_diffuse *= base_color.rgb; vec3 material_opaque_base = mix(slab_diffuse, slab_subsurface, subsurface_weight); vec3 material_dielectric_base = mix(material_opaque_base, slab_translucent, transmission_weight); - vec3 material_dielectric_gloss = layer(material_dielectric_base, slab_glossy, specularFresnel, vec3(1.0), specular_color); + vec3 material_dielectric_gloss = material_dielectric_base * (1.0 - specularFresnel) + slab_glossy * specularColoredFresnel; vec3 material_base_substrate = mix(material_dielectric_gloss, slab_metal, base_metalness); vec3 material_coated_base = layer(material_base_substrate, slab_coat, coatFresnel, coatAbsorption, vec3(1.0)); material_surface_direct += mix(material_coated_base, slab_fuzz, fuzz_weight); diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrEnvironmentLighting.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrEnvironmentLighting.fx index 3f7d845498b..b869e8aadfc 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/openpbrEnvironmentLighting.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrEnvironmentLighting.fx @@ -107,11 +107,22 @@ // The colored fresnel represents the % of light reflected by the base specular lobe // The non-colored fresnel represents the % of light that doesn't penetrate through // the base specular lobe. i.e. the specular lobe isn't energy conserving for coloured specular. + float dielectricIblFresnel = getReflectanceFromBRDFLookup(vec3(baseDielectricReflectance.F0), vec3(baseDielectricReflectance.F90), baseGeoInfo.environmentBrdf).r; - vec3 dielectricIblColoredFresnel = getReflectanceFromBRDFLookup(baseDielectricReflectance.coloredF0, baseDielectricReflectance.coloredF90, baseGeoInfo.environmentBrdf); + vec3 dielectricIblColoredFresnel = dielectricIblFresnel * specular_color; + #ifdef THIN_FILM + // Scale the thin film effect based on how different the IOR is from 1.0 (no thin film effect) + float thinFilmIorScale = clamp(2.0f * abs(thin_film_ior - 1.0f), 0.0f, 1.0f); + vec3 thinFilmDielectricFresnel = evalIridescence(thin_film_outside_ior, thin_film_ior, baseGeoInfo.NdotV, thin_film_thickness, baseDielectricReflectance.coloredF0); + dielectricIblColoredFresnel = mix(dielectricIblColoredFresnel, thinFilmDielectricFresnel * specular_color, thin_film_weight * thinFilmIorScale); + #endif // Conductor IBL Fresnel vec3 conductorIblFresnel = conductorIblFresnel(baseConductorReflectance, baseGeoInfo.NdotV, specular_roughness, baseGeoInfo.environmentBrdf); + #ifdef THIN_FILM + vec3 thinFilmConductorFresnel = specular_weight * evalIridescence(thin_film_outside_ior, thin_film_ior, baseGeoInfo.NdotV, thin_film_thickness, baseConductorReflectance.coloredF0); + conductorIblFresnel = mix(conductorIblFresnel, thinFilmConductorFresnel, thin_film_weight * thinFilmIorScale); + #endif // Coat IBL Fresnel float coatIblFresnel = 0.0; @@ -181,7 +192,7 @@ #define CUSTOM_FRAGMENT_BEFORE_IBLLAYERCOMPOSITION vec3 material_opaque_base_ibl = mix(slab_diffuse_ibl, slab_subsurface_ibl, subsurface_weight); vec3 material_dielectric_base_ibl = mix(material_opaque_base_ibl, slab_translucent_base_ibl, transmission_weight); - vec3 material_dielectric_gloss_ibl = layer(material_dielectric_base_ibl, slab_glossy_ibl, dielectricIblFresnel, vec3(1.0), specular_color); + vec3 material_dielectric_gloss_ibl = material_dielectric_base_ibl * (1.0 - dielectricIblFresnel) + slab_glossy_ibl * dielectricIblColoredFresnel; vec3 material_base_substrate_ibl = mix(material_dielectric_gloss_ibl, slab_metal_ibl, base_metalness); vec3 material_coated_base_ibl = layer(material_base_substrate_ibl, slab_coat_ibl, coatIblFresnel, coatAbsorption, vec3(1.0)); material_surface_ibl = mix(material_coated_base_ibl, slab_fuzz_ibl, fuzz_weight); diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentDeclaration.fx index 86ac11739b0..66016854476 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentDeclaration.fx @@ -14,6 +14,9 @@ uniform float vCoatIor; uniform float vCoatDarkening; uniform vec2 vGeometryCoatTangent; uniform vec3 vEmissionColor; +uniform float vThinFilmWeight; +uniform vec2 vThinFilmThickness; +uniform float vThinFilmIor; // CUSTOM CONTROLS uniform vec4 vLightingIntensity; @@ -109,6 +112,14 @@ uniform vec2 vCoatDarkeningInfos; uniform vec2 vGeometryCoatTangentInfos; #endif +#ifdef THIN_FILM_WEIGHT +uniform vec2 vThinFilmWeightInfos; +#endif + +#ifdef THIN_FILM_THICKNESS +uniform vec2 vThinFilmThicknessInfos; +#endif + // Refraction Reflection #if defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_PROJECTION) || defined(SS_REFRACTION) || defined(PREPASS) uniform mat4 view; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentSamplersDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentSamplersDeclaration.fx index 5f82de119fb..f932f625410 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentSamplersDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrFragmentSamplersDeclaration.fx @@ -16,6 +16,8 @@ #include(_DEFINENAME_,GEOMETRY_TANGENT,_VARYINGNAME_,GeometryTangent,_SAMPLERNAME_,geometryTangent) #include(_DEFINENAME_,GEOMETRY_COAT_TANGENT,_VARYINGNAME_,GeometryCoatTangent,_SAMPLERNAME_,geometryCoatTangent) #include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor,_SAMPLERNAME_,emissionColor) +#include(_DEFINENAME_,THIN_FILM_WEIGHT,_VARYINGNAME_,ThinFilmWeight,_SAMPLERNAME_,thinFilmWeight) +#include(_DEFINENAME_,THIN_FILM_THICKNESS,_VARYINGNAME_,ThinFilmThickness,_SAMPLERNAME_,thinFilmThickness) #include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion,_SAMPLERNAME_,ambientOcclusion) #include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal,_SAMPLERNAME_,decal) diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrThinFilmLayerData.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrThinFilmLayerData.fx new file mode 100644 index 00000000000..fa5fa6db3af --- /dev/null +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrThinFilmLayerData.fx @@ -0,0 +1,20 @@ +// This code reads uniforms and samples textures to fill up the base and specular +// layer properties for OpenPBR +#ifdef THIN_FILM +// Thin Film Layer Properties +float thin_film_weight = vThinFilmWeight; +float thin_film_thickness = vThinFilmThickness.r * 1000.0; // Convert from microns to nanometers +float thin_film_ior = vThinFilmIor; +#ifdef THIN_FILM_WEIGHT + float thinFilmWeightFromTexture = texture2D(thinFilmWeightSampler, vThinFilmWeightUV + uvOffset).r * vThinFilmWeightInfos.y; +#endif +#ifdef THIN_FILM_THICKNESS + float thinFilmThicknessFromTexture = texture2D(thinFilmThicknessSampler, vThinFilmThicknessUV + uvOffset).g * vThinFilmThicknessInfos.y; +#endif +#ifdef THIN_FILM_WEIGHT + thin_film_weight *= thinFilmWeightFromTexture; +#endif +#ifdef THIN_FILM_THICKNESS + thin_film_thickness *= thinFilmThicknessFromTexture; +#endif +#endif \ No newline at end of file diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrUboDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrUboDeclaration.fx index a736ae0e8eb..4d87d0de0a3 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/openpbrUboDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrUboDeclaration.fx @@ -1,27 +1,5 @@ layout(std140, column_major) uniform; -// layout(set = 0, binding = 0) uniform Harmonics -// { -// uniform vec3 vSphericalL00; -// uniform vec3 vSphericalL1_1; -// uniform vec3 vSphericalL10; -// uniform vec3 vSphericalL11; -// uniform vec3 vSphericalL2_2; -// uniform vec3 vSphericalL2_1; -// uniform vec3 vSphericalL20; -// uniform vec3 vSphericalL21; -// uniform vec3 vSphericalL22; -// uniform vec3 vSphericalX; -// uniform vec3 vSphericalY; -// uniform vec3 vSphericalZ; -// uniform vec3 vSphericalXX_ZZ; -// uniform vec3 vSphericalYY_ZZ; -// uniform vec3 vSphericalZZ; -// uniform vec3 vSphericalXY; -// uniform vec3 vSphericalYZ; -// uniform vec3 vSphericalZX; -// } - uniform Material { vec2 vTangentSpaceParams; vec4 vLightingIntensity; @@ -74,6 +52,9 @@ uniform Material { float vCoatDarkening; vec2 vGeometryCoatTangent; vec3 vEmissionColor; + float vThinFilmWeight; + vec2 vThinFilmThickness; + float vThinFilmIor; vec2 vBaseWeightInfos; mat4 baseWeightMatrix; @@ -97,6 +78,8 @@ uniform Material { mat4 coatColorMatrix; vec2 vCoatRoughnessInfos; mat4 coatRoughnessMatrix; + vec2 vCoatRoughnessAnisotropyInfos; + mat4 coatRoughnessAnisotropyMatrix; vec2 vCoatDarkeningInfos; mat4 coatDarkeningMatrix; vec2 vGeometryNormalInfos; @@ -111,6 +94,10 @@ uniform Material { mat4 geometryOpacityMatrix; vec2 vEmissionColorInfos; mat4 emissionColorMatrix; + vec2 vThinFilmWeightInfos; + mat4 thinFilmWeightMatrix; + vec2 vThinFilmThicknessInfos; + mat4 thinFilmThicknessMatrix; vec2 vAmbientOcclusionInfos; mat4 ambientOcclusionMatrix; diff --git a/packages/dev/core/src/Shaders/ShadersInclude/openpbrVertexDeclaration.fx b/packages/dev/core/src/Shaders/ShadersInclude/openpbrVertexDeclaration.fx index 230a889ddf5..43eaa9e7d92 100644 --- a/packages/dev/core/src/Shaders/ShadersInclude/openpbrVertexDeclaration.fx +++ b/packages/dev/core/src/Shaders/ShadersInclude/openpbrVertexDeclaration.fx @@ -106,6 +106,16 @@ uniform vec2 vGeometryCoatNormalInfos; uniform mat4 geometryCoatNormalMatrix; #endif +#ifdef THIN_FILM_WEIGHT +uniform vec2 vThinFilmWeightInfos; +uniform mat4 thinFilmWeightMatrix; +#endif + +#ifdef THIN_FILM_THICKNESS +uniform vec2 vThinFilmThicknessInfos; +uniform mat4 thinFilmThicknessMatrix; +#endif + #ifdef GEOMETRY_OPACITY uniform mat4 geometryOpacityMatrix; uniform vec2 vGeometryOpacityInfos; diff --git a/packages/dev/core/src/Shaders/openpbr.fragment.fx b/packages/dev/core/src/Shaders/openpbr.fragment.fx index 6057346cc5e..638691e13b6 100644 --- a/packages/dev/core/src/Shaders/openpbr.fragment.fx +++ b/packages/dev/core/src/Shaders/openpbr.fragment.fx @@ -94,6 +94,8 @@ void main(void) { // _____________________________ Read Coat Layer properties ______________________ #include + #include + // TEMP float subsurface_weight = 0.0; float transmission_weight = 0.0; @@ -157,6 +159,11 @@ void main(void) { , coat_weight ); +#ifdef THIN_FILM + // Thin Film + float thin_film_outside_ior = mix(1.0, coat_ior, coat_weight); +#endif + // Base Dielectric ReflectanceParams baseDielectricReflectance; { @@ -172,7 +179,7 @@ void main(void) { // Base Metallic ReflectanceParams baseConductorReflectance; baseConductorReflectance = conductorReflectance(base_color, specular_color, specular_weight); - + // ________________________ Environment (IBL) Lighting ____________________________ vec3 material_surface_ibl = vec3(0., 0., 0.); #include diff --git a/packages/dev/core/src/Shaders/openpbr.vertex.fx b/packages/dev/core/src/Shaders/openpbr.vertex.fx index aa2d1154cf2..3806c074c62 100644 --- a/packages/dev/core/src/Shaders/openpbr.vertex.fx +++ b/packages/dev/core/src/Shaders/openpbr.vertex.fx @@ -52,6 +52,8 @@ attribute vec4 color; #include(_DEFINENAME_,GEOMETRY_COAT_NORMAL,_VARYINGNAME_,GeometryCoatNormal) #include(_DEFINENAME_,GEOMETRY_OPACITY,_VARYINGNAME_,GeometryOpacity) #include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor) +#include(_DEFINENAME_,THIN_FILM_WEIGHT,_VARYINGNAME_,ThinFilmWeight) +#include(_DEFINENAME_,THIN_FILM_THICKNESS,_VARYINGNAME_,ThinFilmThickness) #include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion) #include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal) @@ -239,6 +241,8 @@ void main(void) { #include(_DEFINENAME_,GEOMETRY_OPACITY,_VARYINGNAME_,GeometryOpacity,_MATRIXNAME_,geometryOpacity,_INFONAME_,GeometryOpacityInfos.x) #include(_DEFINENAME_,GEOMETRY_TANGENT,_VARYINGNAME_,GeometryTangent,_MATRIXNAME_,geometryTangent,_INFONAME_,GeometryTangentInfos.x) #include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor,_MATRIXNAME_,emissionColor,_INFONAME_,EmissionColorInfos.x) + #include(_DEFINENAME_,THIN_FILM_WEIGHT,_VARYINGNAME_,ThinFilmWeight,_MATRIXNAME_,thinFilmWeight,_INFONAME_,ThinFilmWeightInfos.x) + #include(_DEFINENAME_,THIN_FILM_THICKNESS,_VARYINGNAME_,ThinFilmThickness,_MATRIXNAME_,thinFilmThickness,_INFONAME_,ThinFilmThicknessInfos.x) #include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion,_MATRIXNAME_,ambientOcclusion,_INFONAME_,AmbientOcclusionInfos.x) #include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal,_MATRIXNAME_,decal,_INFONAME_,DecalInfos.x) diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.fx index 73df7c0ebc8..45192d17bd0 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrBaseLayerData.fx @@ -79,10 +79,10 @@ var geometry_tangent: vec2f = vec2f(1.0, 0.0); // Initalize base layer properties from uniforms base_color = uniforms.vBaseColor.rgb; #if defined(VERTEXCOLOR) || defined(INSTANCESCOLOR) && defined(INSTANCES) - base_color *= uniforms.vColor.rgb; + base_color *= fragmentInputs.vColor.rgb; #endif #if defined(VERTEXALPHA) || defined(INSTANCESCOLOR) && defined(INSTANCES) - alpha *= uniforms.vColor.a; + alpha *= fragmentInputs.vColor.a; #endif base_color *= vec3(uniforms.vBaseWeight); alpha = uniforms.vBaseColor.a; diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDirectLighting.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDirectLighting.fx index 5b6162fbb47..59204feb58e 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDirectLighting.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrDirectLighting.fx @@ -5,6 +5,7 @@ var slab_translucent: vec3f = vec3f(0.f, 0.f, 0.f); var slab_glossy: vec3f = vec3f(0.f, 0.f, 0.f); var specularFresnel: f32 = 0.0f; + var specularColoredFresnel: vec3f = vec3f(0.f, 0.f, 0.f); var slab_metal: vec3f = vec3f(0.f, 0.f, 0.f); var slab_coat: vec3f = vec3f(0.f, 0.f, 0.f); var coatFresnel: f32 = 0.0f; @@ -35,11 +36,19 @@ baseGeoInfo.anisotropicTangent, baseGeoInfo.anisotropicBitangent, baseGeoInfo.anisotropy, 0.0f, lightColor{X}.rgb); #else - slab_glossy = computeSpecularLighting(preInfo{X}, normalW, baseDielectricReflectance.coloredF0, baseDielectricReflectance.coloredF90, specular_roughness, lightColor{X}.rgb); + // We're passing in vec3(1.0) for both F0 and F90 here because the actual Fresnel is computed below + // Also computeSpecularLighting does some iridescence work using these values that we don't want. + slab_glossy = computeSpecularLighting(preInfo{X}, normalW, vec3(1.0), vec3(1.0), specular_roughness, lightColor{X}.rgb); #endif let NdotH: f32 = dot(normalW, preInfo{X}.H); specularFresnel = fresnelSchlickGGX(NdotH, baseDielectricReflectance.F0, baseDielectricReflectance.F90); + specularColoredFresnel = specularFresnel * specular_color; + #ifdef THIN_FILM + let thinFilmIorScale: f32 = clamp(2.0f * abs(thin_film_ior - 1.0f), 0.0f, 1.0f); + let thinFilmDielectricFresnel: vec3f = evalIridescence(thin_film_outside_ior, thin_film_ior, preInfo{X}.VdotH, thin_film_thickness, baseDielectricReflectance.coloredF0); + specularColoredFresnel = mix(specularColoredFresnel, thinFilmDielectricFresnel * specular_color, thin_film_weight * thinFilmIorScale); + #endif } #endif @@ -51,9 +60,16 @@ // For OpenPBR, we use the F82 specular model for metallic materials and mix with the // usual Schlick lobe. #if (CONDUCTOR_SPECULAR_MODEL == CONDUCTOR_SPECULAR_MODEL_OPENPBR) - let coloredFresnel: vec3f = specular_weight * getF82Specular(preInfo{X}.VdotH, baseConductorReflectance.coloredF0, baseConductorReflectance.coloredF90, specular_roughness); + var coloredFresnel: vec3f = getF82Specular(preInfo{X}.VdotH, baseConductorReflectance.coloredF0, baseConductorReflectance.coloredF90, specular_roughness); #else - let coloredFresnel: vec3f = fresnelSchlickGGX(preInfo{X}.VdotH, baseConductorReflectance.coloredF0, baseConductorReflectance.coloredF90); + var coloredFresnel: vec3f = fresnelSchlickGGX(preInfo{X}.VdotH, baseConductorReflectance.coloredF0, baseConductorReflectance.coloredF90); + #endif + + #ifdef THIN_FILM + // Scale the thin film effect based on how different the IOR is from 1.0 (no thin film effect) + let thinFilmIorScale: f32 = clamp(2.0f * abs(thin_film_ior - 1.0f), 0.0f, 1.0f); + let thinFilmConductorFresnel = evalIridescence(thin_film_outside_ior, thin_film_ior, preInfo{X}.VdotH, thin_film_thickness, baseConductorReflectance.coloredF0); + coloredFresnel = mix(coloredFresnel, specular_weight * thinFilmIorScale * thinFilmConductorFresnel, thin_film_weight); #endif #ifdef ANISOTROPIC_BASE @@ -119,7 +135,7 @@ slab_diffuse *= base_color.rgb; let material_opaque_base: vec3f = mix(slab_diffuse, slab_subsurface, subsurface_weight); let material_dielectric_base: vec3f = mix(material_opaque_base, slab_translucent, transmission_weight); - let material_dielectric_gloss: vec3f = layer(material_dielectric_base, slab_glossy, specularFresnel, vec3f(1.0), specular_color); + let material_dielectric_gloss: vec3f = material_dielectric_base * (1.0f - specularFresnel) + slab_glossy * specularColoredFresnel; let material_base_substrate: vec3f = mix(material_dielectric_gloss, slab_metal, base_metalness); let material_coated_base: vec3f = layer(material_base_substrate, slab_coat, coatFresnel, coatAbsorption, vec3f(1.0)); material_surface_direct += mix(material_coated_base, slab_fuzz, fuzz_weight); diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrEnvironmentLighting.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrEnvironmentLighting.fx index 8f544c329dd..450ab3a081f 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrEnvironmentLighting.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrEnvironmentLighting.fx @@ -115,10 +115,20 @@ // The non-colored fresnel represents the % of light that doesn't penetrate through // the base specular lobe. i.e. the specular lobe isn't energy conserving for coloured specular. let dielectricIblFresnel: f32 = getReflectanceFromBRDFWithEnvLookup(vec3f(baseDielectricReflectance.F0), vec3f(baseDielectricReflectance.F90), baseGeoInfo.environmentBrdf).r; - let dielectricIblColoredFresnel: vec3f = getReflectanceFromBRDFWithEnvLookup(baseDielectricReflectance.coloredF0, baseDielectricReflectance.coloredF90, baseGeoInfo.environmentBrdf); + var dielectricIblColoredFresnel: vec3f = dielectricIblFresnel * specular_color; + #ifdef THIN_FILM + // Scale the thin film effect based on how different the IOR is from 1.0 (no thin film effect) + let thinFilmIorScale: f32 = clamp(2.0f * abs(thin_film_ior - 1.0f), 0.0f, 1.0f); + let thin_film_dielectric: vec3f = evalIridescence(thin_film_outside_ior, thin_film_ior, baseGeoInfo.NdotV, thin_film_thickness, baseDielectricReflectance.coloredF0); + dielectricIblColoredFresnel = mix(dielectricIblColoredFresnel, thin_film_dielectric * specular_color, thin_film_weight * thinFilmIorScale); + #endif // Conductor IBL Fresnel - let conductorIblFresnel: vec3f = conductorIblFresnel(baseConductorReflectance, baseGeoInfo.NdotV, specular_roughness, baseGeoInfo.environmentBrdf); + var conductorIblFresnel: vec3f = conductorIblFresnel(baseConductorReflectance, baseGeoInfo.NdotV, specular_roughness, baseGeoInfo.environmentBrdf); + #ifdef THIN_FILM + let thinFilmConductorFresnel: vec3f = specular_weight * evalIridescence(thin_film_outside_ior, thin_film_ior, baseGeoInfo.NdotV, thin_film_thickness, baseConductorReflectance.coloredF0); + conductorIblFresnel = mix(conductorIblFresnel, thinFilmConductorFresnel, thin_film_weight * thinFilmIorScale); + #endif // Coat IBL Fresnel var coatIblFresnel: f32 = 0.0; @@ -186,7 +196,7 @@ #define CUSTOM_FRAGMENT_BEFORE_IBLLAYERCOMPOSITION let material_opaque_base_ibl: vec3f = mix(slab_diffuse_ibl, slab_subsurface_ibl, subsurface_weight); let material_dielectric_base_ibl: vec3f = mix(material_opaque_base_ibl, slab_translucent_base_ibl, transmission_weight); - let material_dielectric_gloss_ibl: vec3f = layer(material_dielectric_base_ibl, slab_glossy_ibl, dielectricIblFresnel, vec3f(1.0f), specular_color); + let material_dielectric_gloss_ibl: vec3f = material_dielectric_base_ibl * (1.0 - dielectricIblFresnel) + slab_glossy_ibl * dielectricIblColoredFresnel; let material_base_substrate_ibl: vec3f = mix(material_dielectric_gloss_ibl, slab_metal_ibl, base_metalness); let material_coated_base_ibl: vec3f = layer(material_base_substrate_ibl, slab_coat_ibl, coatIblFresnel, coatAbsorption, vec3f(1.0f)); material_surface_ibl = mix(material_coated_base_ibl, slab_fuzz_ibl, fuzz_weight); diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrFragmentSamplersDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrFragmentSamplersDeclaration.fx index a6f6c9ca4ea..516de4828fb 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrFragmentSamplersDeclaration.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrFragmentSamplersDeclaration.fx @@ -15,6 +15,8 @@ #include(_DEFINENAME_,GEOMETRY_TANGENT,_VARYINGNAME_,GeometryTangent,_SAMPLERNAME_,geometryTangent) #include(_DEFINENAME_,GEOMETRY_COAT_TANGENT,_VARYINGNAME_,GeometryCoatTangent,_SAMPLERNAME_,geometryCoatTangent) #include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor,_SAMPLERNAME_,emissionColor) +#include(_DEFINENAME_,THIN_FILM_WEIGHT,_VARYINGNAME_,ThinFilmWeight,_SAMPLERNAME_,thinFilmWeight) +#include(_DEFINENAME_,THIN_FILM_THICKNESS,_VARYINGNAME_,ThinFilmThickness,_SAMPLERNAME_,thinFilmThickness) #include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion,_SAMPLERNAME_,ambientOcclusion) #include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal,_SAMPLERNAME_,decal) diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrThinFilmLayerData.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrThinFilmLayerData.fx new file mode 100644 index 00000000000..c59fdd45806 --- /dev/null +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrThinFilmLayerData.fx @@ -0,0 +1,20 @@ +// This code reads uniforms and samples textures to fill up the base and specular +// layer properties for OpenPBR +#ifdef THIN_FILM +// Thin Film Layer Properties +var thin_film_weight: f32 = uniforms.vThinFilmWeight; +var thin_film_thickness: f32 = uniforms.vThinFilmThickness.r * 1000.0f; // Convert from microns to nanometers +var thin_film_ior: f32 = uniforms.vThinFilmIor; +#ifdef THIN_FILM_WEIGHT + var thinFilmWeightFromTexture: f32 = textureSample(thinFilmWeightSampler, thinFilmWeightSamplerSampler, fragmentInputs.vThinFilmWeightUV + uvOffset).r * uniforms.vThinFilmWeightInfos.y; +#endif +#ifdef THIN_FILM_THICKNESS + var thinFilmThicknessFromTexture: f32 = textureSample(thinFilmThicknessSampler, thinFilmThicknessSamplerSampler, fragmentInputs.vThinFilmThicknessUV + uvOffset).g * uniforms.vThinFilmThicknessInfos.y; +#endif +#ifdef THIN_FILM_WEIGHT + thin_film_weight *= thinFilmWeightFromTexture; +#endif +#ifdef THIN_FILM_THICKNESS + thin_film_thickness *= thinFilmThicknessFromTexture; +#endif +#endif \ No newline at end of file diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrUboDeclaration.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrUboDeclaration.fx index 468a6923246..48c3cfc9313 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrUboDeclaration.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/openpbrUboDeclaration.fx @@ -50,6 +50,9 @@ uniform vCoatIor: f32; uniform vCoatDarkening : f32; uniform vGeometryCoatTangent: vec2f; uniform vEmissionColor: vec3f; +uniform vThinFilmWeight: f32; +uniform vThinFilmThickness: vec2f; +uniform vThinFilmIor: f32; uniform vBaseWeightInfos: vec2f; uniform baseWeightMatrix: mat4x4f; @@ -73,6 +76,8 @@ uniform vCoatColorInfos: vec2f; uniform coatColorMatrix: mat4x4f; uniform vCoatRoughnessInfos: vec2f; uniform coatRoughnessMatrix: mat4x4f; +uniform vCoatRoughnessAnisotropyInfos: vec2f; +uniform coatRoughnessAnisotropyMatrix: mat4x4f; uniform vCoatDarkeningInfos : vec2f; uniform coatDarkeningMatrix : mat4x4f; uniform vGeometryNormalInfos: vec2f; @@ -87,6 +92,10 @@ uniform vGeometryOpacityInfos: vec2f; uniform geometryOpacityMatrix: mat4x4f; uniform vEmissionInfos: vec2f; uniform emissionMatrix: mat4x4f; +uniform vThinFilmWeightInfos: vec2f; +uniform thinFilmWeightMatrix: mat4x4f; +uniform vThinFilmThicknessInfos: vec2f; +uniform thinFilmThicknessMatrix: mat4x4f; uniform vAmbientOcclusionInfos: vec2f; uniform ambientOcclusionMatrix: mat4x4f; diff --git a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.fx b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.fx index 6098f91c53e..f1bd228a8f0 100644 --- a/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.fx +++ b/packages/dev/core/src/ShadersWGSL/ShadersInclude/pbrDirectLightingFunctions.fx @@ -105,9 +105,9 @@ fn computeProjectionTextureDiffuseLighting(projectionLightTexture: texture_2d FragmentOutputs { // _____________________________ Read Coat Layer properties ______________________ #include + #include + // TEMP var subsurface_weight: f32 = 0.0f; var transmission_weight: f32 = 0.0f; @@ -141,6 +143,11 @@ fn main(input: FragmentInputs) -> FragmentOutputs { , coat_weight ); +#ifdef THIN_FILM + // Thin Film + let thin_film_outside_ior: f32 = mix(1.0f, coat_ior, coat_weight); +#endif + // Base Dielectric let baseDielectricReflectance: ReflectanceParams = dielectricReflectance( specular_ior // inside IOR diff --git a/packages/dev/core/src/ShadersWGSL/openpbr.vertex.fx b/packages/dev/core/src/ShadersWGSL/openpbr.vertex.fx index 874e29ecf04..843b5a0a65e 100644 --- a/packages/dev/core/src/ShadersWGSL/openpbr.vertex.fx +++ b/packages/dev/core/src/ShadersWGSL/openpbr.vertex.fx @@ -47,6 +47,8 @@ attribute color: vec4f; #include(_DEFINENAME_,GEOMETRY_COAT_NORMAL,_VARYINGNAME_,GeometryCoatNormal) #include(_DEFINENAME_,GEOMETRY_OPACITY,_VARYINGNAME_,GeometryOpacity) #include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor) +#include(_DEFINENAME_,THIN_FILM_WEIGHT,_VARYINGNAME_,ThinFilmWeight) +#include(_DEFINENAME_,THIN_FILM_THICKNESS,_VARYINGNAME_,ThinFilmThickness) #include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion) #include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal) @@ -226,6 +228,8 @@ fn main(input : VertexInputs) -> FragmentInputs { #include(_DEFINENAME_,GEOMETRY_COAT_NORMAL,_VARYINGNAME_,GeometryCoatNormal,_MATRIXNAME_,geometryCoatNormal,_INFONAME_,GeometryCoatNormalInfos.x) #include(_DEFINENAME_,GEOMETRY_OPACITY,_VARYINGNAME_,GeometryOpacity,_MATRIXNAME_,geometryOpacity,_INFONAME_,GeometryOpacityInfos.x) #include(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor,_MATRIXNAME_,emissionColor,_INFONAME_,EmissionColorInfos.x) + #include(_DEFINENAME_,THIN_FILM_WEIGHT,_VARYINGNAME_,ThinFilmWeight,_MATRIXNAME_,thinFilmWeight,_INFONAME_,ThinFilmWeightInfos.x) + #include(_DEFINENAME_,THIN_FILM_THICKNESS,_VARYINGNAME_,ThinFilmThickness,_MATRIXNAME_,thinFilmThickness,_INFONAME_,ThinFilmThicknessInfos.x) #include(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion,_MATRIXNAME_,ambientOcclusion,_INFONAME_,AmbientOcclusionInfos.x) #include(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal,_MATRIXNAME_,decal,_INFONAME_,DecalInfos.x) diff --git a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/materials/openpbrMaterialPropertyGridComponent.tsx b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/materials/openpbrMaterialPropertyGridComponent.tsx index cd7a9b01a3b..4fb5591a478 100644 --- a/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/materials/openpbrMaterialPropertyGridComponent.tsx +++ b/packages/dev/inspector/src/components/actionTabs/tabs/propertyGrids/materials/openpbrMaterialPropertyGridComponent.tsx @@ -181,6 +181,22 @@ export class OpenPBRMaterialPropertyGridComponent extends React.Component + + ); } @@ -555,6 +571,54 @@ export class OpenPBRMaterialPropertyGridComponent extends React.Component + + + + + + + >(); // Set non-texture properties immediately - const iridescenceWeight = properties.iridescenceFactor ?? 0; - const iridescenceIor = properties.iridescenceIor ?? (properties as any).iridescenceIOR ?? 1.3; - const iridescenceThicknessMinimum = properties.iridescenceThicknessMinimum ?? 100; - const iridescenceThicknessMaximum = properties.iridescenceThicknessMaximum ?? 400; - - adapter.iridescenceWeight = iridescenceWeight; - adapter.iridescenceIor = iridescenceIor; - adapter.iridescenceThicknessMinimum = iridescenceThicknessMinimum; - adapter.iridescenceThicknessMaximum = iridescenceThicknessMaximum; + adapter.thinFilmWeight = properties.iridescenceFactor ?? 0; + adapter.thinFilmIor = properties.iridescenceIor ?? (properties as any).iridescenceIOR ?? 1.3; + adapter.thinFilmThicknessMinimum = properties.iridescenceThicknessMinimum ?? 100; + adapter.thinFilmThicknessMaximum = properties.iridescenceThicknessMaximum ?? 400; // Load textures if (properties.iridescenceTexture) { promises.push( this._loader.loadTextureInfoAsync(`${context}/iridescenceTexture`, properties.iridescenceTexture, (texture) => { texture.name = `${babylonMaterial.name} (Iridescence)`; - adapter.iridescenceTexture = texture; + adapter.thinFilmWeightTexture = texture; }) ); } @@ -98,7 +93,7 @@ export class KHR_materials_iridescence implements IGLTFLoaderExtension { promises.push( this._loader.loadTextureInfoAsync(`${context}/iridescenceThicknessTexture`, properties.iridescenceThicknessTexture, (texture) => { texture.name = `${babylonMaterial.name} (Iridescence Thickness)`; - adapter.iridescenceThicknessTexture = texture; + adapter.thinFilmThicknessTexture = texture; }) ); } diff --git a/packages/dev/loaders/src/glTF/2.0/materialLoadingAdapter.ts b/packages/dev/loaders/src/glTF/2.0/materialLoadingAdapter.ts index 2811e7f1e1a..c9579d511d5 100644 --- a/packages/dev/loaders/src/glTF/2.0/materialLoadingAdapter.ts +++ b/packages/dev/loaders/src/glTF/2.0/materialLoadingAdapter.ts @@ -383,34 +383,34 @@ export interface IMaterialLoadingAdapter { // ======================================== /** - * Sets the iridescence weight (OpenPBR: iridescenceWeight, PBR: iridescence.intensity) + * Sets the thin film weight */ - iridescenceWeight: number; + thinFilmWeight: number; /** - * Sets the iridescence IOR (OpenPBR: iridescenceIor, PBR: iridescence.indexOfRefraction) + * Sets the thin film IOR */ - iridescenceIor: number; + thinFilmIor: number; /** - * Sets the iridescence thickness minimum (OpenPBR: iridescenceThicknessMinimum, PBR: iridescence.minimumThickness) + * Sets the thin film thickness minimum */ - iridescenceThicknessMinimum: number; + thinFilmThicknessMinimum: number; /** - * Sets the iridescence thickness maximum (OpenPBR: iridescenceThicknessMaximum, PBR: iridescence.maximumThickness) + * Sets the thin film thickness maximum */ - iridescenceThicknessMaximum: number; + thinFilmThicknessMaximum: number; /** - * Sets the iridescence texture (OpenPBR: iridescenceTexture, PBR: iridescence.intensityTexture) + * Sets the thin film iridescence texture */ - iridescenceTexture: Nullable; + thinFilmWeightTexture: Nullable; /** - * Sets the iridescence thickness texture (OpenPBR: iridescenceThicknessTexture, PBR: iridescence.thicknessTexture) + * Sets the thin film thickness texture */ - iridescenceThicknessTexture: Nullable; + thinFilmThicknessTexture: Nullable; // ======================================== // UNLIT MATERIALS diff --git a/packages/dev/loaders/src/glTF/2.0/openPbrMaterialLoadingAdapter.ts b/packages/dev/loaders/src/glTF/2.0/openPbrMaterialLoadingAdapter.ts index 78c828f9f13..4e0ca0c903a 100644 --- a/packages/dev/loaders/src/glTF/2.0/openPbrMaterialLoadingAdapter.ts +++ b/packages/dev/loaders/src/glTF/2.0/openPbrMaterialLoadingAdapter.ts @@ -898,63 +898,52 @@ export class OpenPBRMaterialLoadingAdapter implements IMaterialLoadingAdapter { // ======================================== /** - * Sets the iridescence weight. - * TODO: Implementation pending OpenPBR iridescence feature availability. - * @param value The iridescence intensity value + * Sets the thin film weight. + * @param value The thin film weight value */ - public set iridescenceWeight(value: number) { - // TODO: Implement when OpenPBR iridescence is available - // this._material.iridescenceWeight = value; + public set thinFilmWeight(value: number) { + this._material.thinFilmWeight = value; } /** - * Sets the iridescence IOR. - * TODO: Implementation pending OpenPBR iridescence feature availability. - * @param value The iridescence IOR value + * Sets the thin film IOR. + * @param value The thin film IOR value */ - public set iridescenceIor(value: number) { - // TODO: Implement when OpenPBR iridescence is available - // this._material.iridescenceIor = value; + public set thinFilmIor(value: number) { + this._material.thinFilmIor = value; } /** - * Sets the iridescence thickness minimum. - * TODO: Implementation pending OpenPBR iridescence feature availability. + * Sets the thin film thickness minimum. * @param value The minimum thickness value in nanometers */ - public set iridescenceThicknessMinimum(value: number) { - // TODO: Implement when OpenPBR iridescence is available - // this._material.iridescenceThicknessMinimum = value; + public set thinFilmThicknessMinimum(value: number) { + this._material.thinFilmThicknessMin = value / 1000.0; // Convert to micrometers for OpenPBR } /** - * Sets the iridescence thickness maximum. - * TODO: Implementation pending OpenPBR iridescence feature availability. + * Sets the thin film thickness maximum. * @param value The maximum thickness value in nanometers */ - public set iridescenceThicknessMaximum(value: number) { - // TODO: Implement when OpenPBR iridescence is available - // this._material.iridescenceThicknessMaximum = value; + public set thinFilmThicknessMaximum(value: number) { + this._material.thinFilmThickness = value / 1000.0; // Convert to micrometers for OpenPBR } /** - * Sets the iridescence texture. - * TODO: Implementation pending OpenPBR iridescence feature availability. - * @param value The iridescence intensity texture or null + * Sets the thin film weight texture. + * @param value The thin film weight texture or null */ - public set iridescenceTexture(value: Nullable) { - // TODO: Implement when OpenPBR iridescence is available - // this._material.iridescenceTexture = value; + public set thinFilmWeightTexture(value: Nullable) { + this._material.thinFilmWeightTexture = value; } /** - * Sets the iridescence thickness texture. - * TODO: Implementation pending OpenPBR iridescence feature availability. - * @param value The iridescence thickness texture or null + * Sets the thin film thickness texture. + * @param value The thin film thickness texture or null */ - public set iridescenceThicknessTexture(value: Nullable) { - // TODO: Implement when OpenPBR iridescence is available - // this._material.iridescenceThicknessTexture = value; + public set thinFilmThicknessTexture(value: Nullable) { + this._material.thinFilmThicknessTexture = value; + this._material._useThinFilmThicknessFromTextureGreen = true; } // ======================================== diff --git a/packages/dev/loaders/src/glTF/2.0/pbrMaterialLoadingAdapter.ts b/packages/dev/loaders/src/glTF/2.0/pbrMaterialLoadingAdapter.ts index ebf7f0e6071..4ce3344b4c4 100644 --- a/packages/dev/loaders/src/glTF/2.0/pbrMaterialLoadingAdapter.ts +++ b/packages/dev/loaders/src/glTF/2.0/pbrMaterialLoadingAdapter.ts @@ -956,58 +956,48 @@ export class PBRMaterialLoadingAdapter implements IMaterialLoadingAdapter { * Automatically enables iridescence. * @param value The iridescence intensity value */ - public set iridescenceWeight(value: number) { - this._material.iridescence.isEnabled = true; + public set thinFilmWeight(value: number) { + this._material.iridescence.isEnabled = value > 0; this._material.iridescence.intensity = value; } /** * Sets the iridescence IOR (mapped to PBR iridescence.indexOfRefraction). - * Automatically enables iridescence. * @param value The iridescence IOR value */ - public set iridescenceIor(value: number) { - this._material.iridescence.isEnabled = true; + public set thinFilmIor(value: number) { this._material.iridescence.indexOfRefraction = value; } /** * Sets the iridescence thickness minimum (mapped to PBR iridescence.minimumThickness). - * Automatically enables iridescence. * @param value The minimum thickness value in nanometers */ - public set iridescenceThicknessMinimum(value: number) { - this._material.iridescence.isEnabled = true; + public set thinFilmThicknessMinimum(value: number) { this._material.iridescence.minimumThickness = value; } /** * Sets the iridescence thickness maximum (mapped to PBR iridescence.maximumThickness). - * Automatically enables iridescence. * @param value The maximum thickness value in nanometers */ - public set iridescenceThicknessMaximum(value: number) { - this._material.iridescence.isEnabled = true; + public set thinFilmThicknessMaximum(value: number) { this._material.iridescence.maximumThickness = value; } /** - * Sets the iridescence texture (mapped to PBR iridescence.texture). - * Automatically enables iridescence. - * @param value The iridescence intensity texture or null + * Sets the thin film weight texture (mapped to PBR iridescence.texture). + * @param value The thin film weight texture or null */ - public set iridescenceTexture(value: Nullable) { - this._material.iridescence.isEnabled = true; + public set thinFilmWeightTexture(value: Nullable) { this._material.iridescence.texture = value; } /** * Sets the iridescence thickness texture (mapped to PBR iridescence.thicknessTexture). - * Automatically enables iridescence. * @param value The iridescence thickness texture or null */ - public set iridescenceThicknessTexture(value: Nullable) { - this._material.iridescence.isEnabled = true; + public set thinFilmThicknessTexture(value: Nullable) { this._material.iridescence.thicknessTexture = value; } diff --git a/packages/dev/serializers/src/glTF/2.0/Extensions/KHR_materials_iridescence.ts b/packages/dev/serializers/src/glTF/2.0/Extensions/KHR_materials_iridescence.ts index 40f532cc2d3..7e144e854aa 100644 --- a/packages/dev/serializers/src/glTF/2.0/Extensions/KHR_materials_iridescence.ts +++ b/packages/dev/serializers/src/glTF/2.0/Extensions/KHR_materials_iridescence.ts @@ -3,6 +3,7 @@ import type { IGLTFExporterExtensionV2 } from "../glTFExporterExtension"; import { GLTFExporter } from "../glTFExporter"; import type { Material } from "core/Materials/material"; import { PBRBaseMaterial } from "core/Materials/PBR/pbrBaseMaterial"; +import { OpenPBRMaterial } from "core/Materials/PBR/openPbrMaterial"; import type { BaseTexture } from "core/Materials/Textures/baseTexture"; const NAME = "KHR_materials_iridescence"; @@ -48,8 +49,17 @@ export class KHR_materials_iridescence implements IGLTFExporterExtensionV2 { } return additionalTextures; } + } else if (babylonMaterial instanceof OpenPBRMaterial) { + if (babylonMaterial.thinFilmWeight > 0) { + if (babylonMaterial.thinFilmWeightTexture) { + additionalTextures.push(babylonMaterial.thinFilmWeightTexture); + } + if (babylonMaterial.thinFilmThicknessTexture && babylonMaterial.thinFilmThicknessTexture !== babylonMaterial.thinFilmWeightTexture) { + additionalTextures.push(babylonMaterial.thinFilmThicknessTexture); + } + return additionalTextures; + } } - return []; } @@ -83,6 +93,34 @@ export class KHR_materials_iridescence implements IGLTFExporterExtensionV2 { this._exporter._materialNeedsUVsSet.add(babylonMaterial); } + node.extensions[NAME] = iridescenceInfo; + } else if (babylonMaterial instanceof OpenPBRMaterial) { + if (babylonMaterial.thinFilmWeight <= 0) { + resolve(node); + return; + } + + this._wasUsed = true; + + node.extensions = node.extensions || {}; + + const thinFilmWeightTextureInfo = this._exporter._materialExporter.getTextureInfo(babylonMaterial.thinFilmWeightTexture); + const thinFilmThicknessTextureInfo = this._exporter._materialExporter.getTextureInfo(babylonMaterial.thinFilmThicknessTexture); + + const iridescenceInfo: IKHRMaterialsIridescence = { + iridescenceFactor: babylonMaterial.thinFilmWeight, + iridescenceIor: babylonMaterial.thinFilmIor, + iridescenceThicknessMinimum: babylonMaterial.thinFilmThicknessMin * 1000, // Convert to nanometers for glTF + iridescenceThicknessMaximum: babylonMaterial.thinFilmThickness * 1000, // Convert to nanometers for glTF + + iridescenceTexture: thinFilmWeightTextureInfo ?? undefined, + iridescenceThicknessTexture: thinFilmThicknessTextureInfo ?? undefined, + }; + + if (iridescenceInfo.iridescenceTexture !== null || iridescenceInfo.iridescenceThicknessTexture !== null) { + this._exporter._materialNeedsUVsSet.add(babylonMaterial); + } + node.extensions[NAME] = iridescenceInfo; } resolve(node); diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-Weight-vs-Metalness.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-Weight-vs-Metalness.png index baaab50b422..64f0208b4ce 100644 Binary files a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-Weight-vs-Metalness.png and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Analytic-Lights-Specular-Weight-vs-Metalness.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Textures---IBL.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Textures---IBL.png new file mode 100644 index 00000000000..5cbcff4df9c Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Textures---IBL.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR---Analytic-Lights.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR---Analytic-Lights.png new file mode 100644 index 00000000000..ae80d1618fb Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR---Analytic-Lights.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR---Furnace-Test.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR---Furnace-Test.png new file mode 100644 index 00000000000..2dc64580f31 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR---Furnace-Test.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR---IBL.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR---IBL.png new file mode 100644 index 00000000000..bf4e4686fe4 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR---IBL.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR-Metal---Furnace-Test.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR-Metal---Furnace-Test.png new file mode 100644 index 00000000000..f5a88884c55 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Thickness-VS-IOR-Metal---Furnace-Test.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Weight-VS-IOR---Analytic-Lights.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Weight-VS-IOR---Analytic-Lights.png new file mode 100644 index 00000000000..92439aa5fb5 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Weight-VS-IOR---Analytic-Lights.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Weight-VS-IOR---Furnace-Test.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Weight-VS-IOR---Furnace-Test.png new file mode 100644 index 00000000000..22a9ab831de Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Weight-VS-IOR---Furnace-Test.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Weight-VS-IOR---IBL.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Weight-VS-IOR---IBL.png new file mode 100644 index 00000000000..0d7019ad3af Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-Weight-VS-IOR---IBL.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight---Analytic-Lights.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight---Analytic-Lights.png new file mode 100644 index 00000000000..7faba1fab6a Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight---Analytic-Lights.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight---IBL.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight---IBL.png new file mode 100644 index 00000000000..462652af2a0 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight---IBL.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight-Metals---Analytic-Lights.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight-Metals---Analytic-Lights.png new file mode 100644 index 00000000000..aaf590a8400 Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight-Metals---Analytic-Lights.png differ diff --git a/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight-Metals---IBL.png b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight-Metals---IBL.png new file mode 100644 index 00000000000..e0287d9043e Binary files /dev/null and b/packages/tools/tests/test/visualization/ReferenceImages/OpenPBR-Thin-Film-and-Specular-Weight-Metals---IBL.png differ diff --git a/packages/tools/tests/test/visualization/config.json b/packages/tools/tests/test/visualization/config.json index ef407e8e176..8fd36713e32 100644 --- a/packages/tools/tests/test/visualization/config.json +++ b/packages/tools/tests/test/visualization/config.json @@ -3022,6 +3022,67 @@ "playgroundId": "#GRQHVV#81", "excludedEngines": ["webgl1"] }, + { + "title": "OpenPBR Thin Film Weight VS IOR - IBL", + "playgroundId": "#GRQHVV#86", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Thin Film Weight VS IOR - Analytic Lights", + "playgroundId": "#GRQHVV#87", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Thin Film Thickness VS IOR - Analytic Lights", + "playgroundId": "#GRQHVV#88", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Thin Film Thickness VS IOR - IBL", + "playgroundId": "#GRQHVV#89", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Thin Film Weight VS IOR - Furnace Test", + "playgroundId": "#GRQHVV#90", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Thin Film Thickness VS IOR - Furnace Test", + "playgroundId": "#GRQHVV#91", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Thin Film Thickness VS IOR Metal - Furnace Test", + "playgroundId": "#GRQHVV#94", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Thin Film and Specular Weight - Analytic Lights", + "playgroundId": "#GRQHVV#95", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Thin Film and Specular Weight Metals - Analytic Lights", + "playgroundId": "#GRQHVV#96", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Thin Film and Specular Weight Metals - IBL", + "playgroundId": "#GRQHVV#97", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Thin Film and Specular Weight - IBL", + "playgroundId": "#GRQHVV#98", + "excludedEngines": ["webgl1"] + }, + { + "title": "OpenPBR Thin Film Textures - IBL", + "playgroundId": "#GRQHVV#101", + "excludedEngines": ["webgl1"], + "renderCount": 2 + }, { "title": "Background material blur", "playgroundId": "#UU7RQ#4458"