Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions packages/dev/core/src/Materials/PBR/openPbrMaterial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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<number> = new Property<number>("thin_film_weight", 0.0, "vThinFilmWeight", 1, 0);

/**
* Thin film weight texture.
*/
public thinFilmWeightTexture: Nullable<BaseTexture>;
@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<number> = new Property<number>("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<number> = new Property<number>("thin_film_thickness_min", 0.0, "vThinFilmThickness", 2, 1);

/**
* Defines the maximum thickness of the thin film layer in μm.
*/
public thinFilmThicknessTexture: Nullable<BaseTexture>;
@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<number> = new Property<number>("thin_film_ior", 1.4, "vThinFilmIor", 1, 0);

/**
* Defines the ambient occlusion texture.
*/
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,40 +14,46 @@ 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.
// It represents the total percentage of light reflected by the specular lobe at normal incidence.
// 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include<samplerFragmentDeclaration>(_DEFINENAME_,GEOMETRY_TANGENT,_VARYINGNAME_,GeometryTangent,_SAMPLERNAME_,geometryTangent)
#include<samplerFragmentDeclaration>(_DEFINENAME_,GEOMETRY_COAT_TANGENT,_VARYINGNAME_,GeometryCoatTangent,_SAMPLERNAME_,geometryCoatTangent)
#include<samplerFragmentDeclaration>(_DEFINENAME_,EMISSION_COLOR,_VARYINGNAME_,EmissionColor,_SAMPLERNAME_,emissionColor)
#include<samplerFragmentDeclaration>(_DEFINENAME_,THIN_FILM_WEIGHT,_VARYINGNAME_,ThinFilmWeight,_SAMPLERNAME_,thinFilmWeight)
#include<samplerFragmentDeclaration>(_DEFINENAME_,THIN_FILM_THICKNESS,_VARYINGNAME_,ThinFilmThickness,_SAMPLERNAME_,thinFilmThickness)

#include<samplerFragmentDeclaration>(_DEFINENAME_,AMBIENT_OCCLUSION,_VARYINGNAME_,AmbientOcclusion,_SAMPLERNAME_,ambientOcclusion)
#include<samplerFragmentDeclaration>(_DEFINENAME_,DECAL,_VARYINGNAME_,Decal,_SAMPLERNAME_,decal)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Loading