Explorar o código

KHR_materials ior and specular

sebavan %!s(int64=5) %!d(string=hai) anos
pai
achega
d94a6d2d83

+ 17 - 6
Tools/Gulp/tasks/gulpTasks-localRun.js

@@ -28,13 +28,23 @@ gulp.task("webserver", function () {
         middleware: function (connect, opt) {
             return [function (req, res, next) {
                 const baseUrl =  (req.url.indexOf('dist') !== -1 || req.url.indexOf('Tools') !== -1  || req.url.indexOf('temp/') !== -1);
-                if (!baseUrl && req.headers['referer'] && req.headers['referer'].indexOf('/Playground/') !== -1 && req.url.indexOf('/Playground/') === -1) {
-                    req.url = "/Playground/" + req.url;
-                    res.writeHead(301, {
-                        'Location': req.url
-                    });
-                    return res.end();
+                let referer = req.headers['referer'];
+                if (!baseUrl && referer) {
+                    referer = referer.toLowerCase();
+                    if (referer.indexOf('/playground/') !== -1 && req.url.indexOf('/Playground/') === -1) {
+                        req.url = "/Playground/" + req.url;
+                        res.writeHead(301, {
+                            'Location': req.url
+                        });
+                        return res.end();
+                    }
+                    if (referer.indexOf('/localdev/') !== -1) {
+                        if (!fs.existsSync(rootRelativePath + req.originalUrl)) {
+                            req.url = "/Playground/" + req.url.replace(/localDev/ig, "");
+                        }
+                    }
                 }
+
                 const pgMath = req.url.match(/\/Playground\/pg\/(.*)/);
                 if (pgMath) {
                     const isAFile = req.url.indexOf('.') !== -1;
@@ -61,6 +71,7 @@ gulp.task("webserver", function () {
                         req.url += ".js";
                     }
                 }
+
                 next();
             }]
         }

+ 2 - 0
dist/preview release/what's new.md

@@ -69,6 +69,8 @@
 - Get the list of cameras retrieved from a gLTF file when loaded through the asset container ([Popov72](https://github.com/Popov72))
 - Fixed SceneLoader.ImportAnimations. Now targets nodes based on "targetProperty" ([#7931](https://github.com/BabylonJS/Babylon.js/issues/7931)) ([phenry20](https://github.com/phenry20))
 - Renamed KHR_mesh_instancing extension to EXT_mesh_gpu_instancing ([#7945](https://github.com/BabylonJS/Babylon.js/issues/7945)) ([drigax](https://github.com/Drigax))
+- Added support for KHR_materials_ior for glTF loader. ([Sebavan](https://github.com/sebavan/))
+- Added support for KHR_materials_specular for glTF loader. ([Sebavan](https://github.com/sebavan/))
 
 ### Navigation
 

+ 4 - 4
inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx

@@ -151,11 +151,11 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="METALLIC WORKFLOW">
                     <SliderLineComponent label="Metallic" target={material} propertyName="metallic" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <SliderLineComponent label="Metallic F0" target={material} propertyName="metallicF0Factor" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <CheckBoxLineComponent label="Metallic F0 From Map" target={material} propertyName="useMetallicF0FactorFromMetallicTexture"
-                        onValueChanged={() => this.forceUpdate()}
-                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Roughness" target={material} propertyName="roughness" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Index of Refraction" target={material} propertyName="indexOfRefraction" minimum={1} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="F0 Factor" target={material} propertyName="metallicF0Factor" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <Color3LineComponent label="Reflectance Color" target={material} propertyName="metallicReflectanceColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} isLinear={true} />
+                    <TextureLinkLineComponent label="Reflectance Texture" texture={material.metallicReflectanceTexture} onTextureCreated={(texture) => material.metallicReflectanceTexture = texture} onTextureRemoved={() => material.metallicReflectanceTexture = null} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={this._onDebugSelectionChangeObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="CLEAR COAT">
                     <CheckBoxLineComponent label="Enabled" target={material.clearCoat} propertyName="isEnabled"

+ 79 - 0
loaders/src/glTF/2.0/Extensions/KHR_materials_ior.ts

@@ -0,0 +1,79 @@
+import { Nullable } from "babylonjs/types";
+import { PBRMaterial } from "babylonjs/Materials/PBR/pbrMaterial";
+import { Material } from "babylonjs/Materials/material";
+
+import { IMaterial } from "../glTFLoaderInterfaces";
+import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
+import { GLTFLoader } from "../glTFLoader";
+
+const NAME = "KHR_materials_ior";
+
+interface IKHR_materials_ior {
+    ior: number;
+}
+
+/**
+ * [Proposed Specification](https://github.com/KhronosGroup/glTF/pull/1718)
+ * !!! Experimental Extension Subject to Changes !!!
+ */
+export class KHR_materials_ior implements IGLTFLoaderExtension {
+    /**
+     * Default ior Value from the spec.
+     */
+    private static readonly _DEFAULT_IOR = 1.5;
+
+    /**
+     * The name of this extension.
+     */
+    public readonly name = NAME;
+
+    /**
+     * Defines whether this extension is enabled.
+     */
+    public enabled: boolean;
+
+    /**
+     * Defines a number that determines the order the extensions are applied.
+     */
+    public order = 180;
+
+    private _loader: GLTFLoader;
+
+    /** @hidden */
+    constructor(loader: GLTFLoader) {
+        this._loader = loader;
+        this.enabled = this._loader.isExtensionUsed(NAME);
+    }
+
+    /** @hidden */
+    public dispose() {
+        delete this._loader;
+    }
+
+    /** @hidden */
+    public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>> {
+        return GLTFLoader.LoadExtensionAsync<IKHR_materials_ior>(context, material, this.name, (extensionContext, extension) => {
+            const promises = new Array<Promise<any>>();
+            promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial));
+            promises.push(this._loadIorPropertiesAsync(extensionContext, extension, babylonMaterial));
+            return Promise.all(promises).then(() => { });
+        });
+    }
+
+    private _loadIorPropertiesAsync(context: string, properties: IKHR_materials_ior, babylonMaterial: Material): Promise<void> {
+        if (!(babylonMaterial instanceof PBRMaterial)) {
+            throw new Error(`${context}: Material type not supported`);
+        }
+
+        if (properties.ior !== undefined) {
+            babylonMaterial.indexOfRefraction = properties.ior;
+        }
+        else {
+            babylonMaterial.indexOfRefraction = KHR_materials_ior._DEFAULT_IOR;
+        }
+
+        return Promise.resolve();
+    }
+}
+
+GLTFLoader.RegisterExtension(NAME, (loader) => new KHR_materials_ior(loader));

+ 14 - 5
loaders/src/glTF/2.0/Extensions/KHR_materials_specular.ts

@@ -5,17 +5,18 @@ import { Material } from "babylonjs/Materials/material";
 import { IMaterial, ITextureInfo } from "../glTFLoaderInterfaces";
 import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
 import { GLTFLoader } from "../glTFLoader";
+import { Color3 } from 'babylonjs/Maths/math.color';
 
 const NAME = "KHR_materials_specular";
 
 interface IKHR_materials_specular {
     specularFactor: number;
+    specularColorFactor: number[];
     specularTexture: ITextureInfo;
 }
 
 /**
- * [Proposed Specification](https://github.com/KhronosGroup/glTF/pull/1677)
- * [Playground Sample](https://www.babylonjs-playground.com/frame.html#BNIZX6#4)
+ * [Proposed Specification](https://github.com/KhronosGroup/glTF/pull/1719)
  * !!! Experimental Extension Subject to Changes !!!
  */
 export class KHR_materials_specular implements IGLTFLoaderExtension {
@@ -62,16 +63,24 @@ export class KHR_materials_specular implements IGLTFLoaderExtension {
             throw new Error(`${context}: Material type not supported`);
         }
 
+        const promises = new Array<Promise<any>>();
+
         if (properties.specularFactor !== undefined) {
             babylonMaterial.metallicF0Factor = properties.specularFactor;
         }
 
+        if (properties.specularColorFactor !== undefined) {
+            babylonMaterial.metallicReflectanceColor = Color3.FromArray(properties.specularColorFactor);
+        }
+
         if (properties.specularTexture) {
-            // This does not allow a separate sampler for it at the moment but is currently under discussion.
-            babylonMaterial.useMetallicF0FactorFromMetallicTexture = true;
+            promises.push(this._loader.loadTextureInfoAsync(`${context}/specularTexture`, properties.specularTexture, (texture) => {
+                texture.name = `${babylonMaterial.name} (Specular F0 Color)`;
+                babylonMaterial.metallicReflectanceTexture = texture;
+            }));
         }
 
-        return Promise.resolve();
+        return Promise.all(promises).then(() => { });
     }
 }
 

+ 2 - 11
src/Materials/Node/Blocks/PBR/reflectivityBlock.ts

@@ -40,12 +40,6 @@ export class ReflectivityBlock extends NodeMaterialBlock {
     public useRoughnessFromMetallicTextureGreen: boolean = true;
 
     /**
-     * Specifies whether the F0 factor can be fetched from the mettalic texture.
-     */
-    @editableInPropertyPage("Metallic F0 from alpha channel", PropertyTypeForEdition.Boolean, "METALLIC WORKFLOW", { "notifiers": { "update": true }})
-    public useMetallicF0FactorFromMetallicTexture: boolean = false;
-
-    /**
      * Create a new ReflectivityBlock
      * @param name defines the block name
      */
@@ -122,9 +116,10 @@ export class ReflectivityBlock extends NodeMaterialBlock {
             reflectivityOutParams reflectivityOut;
 
             reflectivityBlock(
-                vec4(${this.metallic.associatedVariableName}, ${this.roughness.associatedVariableName}, 0., 0.04),
+                vec4(${this.metallic.associatedVariableName}, ${this.roughness.associatedVariableName}, 0., 0.),
             #ifdef METALLICWORKFLOW
                 surfaceAlbedo,
+                vec4(1.),
             #endif
             #ifdef REFLECTIVITY
                 vec3(0., 0., ${aoIntensityVarName}),
@@ -160,7 +155,6 @@ export class ReflectivityBlock extends NodeMaterialBlock {
         defines.setValue("METALLNESSSTOREINMETALMAPBLUE", this.useMetallnessFromMetallicTextureBlue, true);
         defines.setValue("ROUGHNESSSTOREINMETALMAPALPHA", this.useRoughnessFromMetallicTextureAlpha, true);
         defines.setValue("ROUGHNESSSTOREINMETALMAPGREEN",  !this.useRoughnessFromMetallicTextureAlpha && this.useRoughnessFromMetallicTextureGreen, true);
-        defines.setValue("METALLICF0FACTORFROMMETALLICMAP", this.useMetallicF0FactorFromMetallicTexture, true);
     }
 
     protected _buildBlock(state: NodeMaterialBuildState) {
@@ -178,7 +172,6 @@ export class ReflectivityBlock extends NodeMaterialBlock {
         codeString += `${this._codeVariableName}.useMetallnessFromMetallicTextureBlue = ${this.useMetallnessFromMetallicTextureBlue};\r\n`;
         codeString += `${this._codeVariableName}.useRoughnessFromMetallicTextureAlpha = ${this.useRoughnessFromMetallicTextureAlpha};\r\n`;
         codeString += `${this._codeVariableName}.useRoughnessFromMetallicTextureGreen = ${this.useRoughnessFromMetallicTextureGreen};\r\n`;
-        codeString += `${this._codeVariableName}.useMetallicF0FactorFromMetallicTexture = ${this.useMetallicF0FactorFromMetallicTexture};\r\n`;
 
         return codeString;
     }
@@ -190,7 +183,6 @@ export class ReflectivityBlock extends NodeMaterialBlock {
         serializationObject.useMetallnessFromMetallicTextureBlue = this.useMetallnessFromMetallicTextureBlue;
         serializationObject.useRoughnessFromMetallicTextureAlpha = this.useRoughnessFromMetallicTextureAlpha;
         serializationObject.useRoughnessFromMetallicTextureGreen = this.useRoughnessFromMetallicTextureGreen;
-        serializationObject.useMetallicF0FactorFromMetallicTexture = this.useMetallicF0FactorFromMetallicTexture;
 
         return serializationObject;
     }
@@ -202,7 +194,6 @@ export class ReflectivityBlock extends NodeMaterialBlock {
         this.useMetallnessFromMetallicTextureBlue = serializationObject.useMetallnessFromMetallicTextureBlue;
         this.useRoughnessFromMetallicTextureAlpha = serializationObject.useRoughnessFromMetallicTextureAlpha;
         this.useRoughnessFromMetallicTextureGreen = serializationObject.useRoughnessFromMetallicTextureGreen;
-        this.useMetallicF0FactorFromMetallicTexture = serializationObject.useMetallicF0FactorFromMetallicTexture;
     }
 }
 

+ 84 - 55
src/Materials/PBR/pbrBaseMaterial.ts

@@ -101,7 +101,8 @@ export class PBRMaterialDefines extends MaterialDefines
     public ROUGHNESSSTOREINMETALMAPGREEN = false;
     public METALLNESSSTOREINMETALMAPBLUE = false;
     public AOSTOREINMETALMAPRED = false;
-    public METALLICF0FACTORFROMMETALLICMAP = false;
+    public METALLIC_REFLECTANCE = false;
+    public METALLIC_REFLECTANCEDIRECTUV = 0;
 
     public ENVIRONMENTBRDF = false;
     public ENVIRONMENTBRDF_RGBD = false;
@@ -406,19 +407,32 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     protected _roughness: Nullable<number> = null;
 
     /**
-     * Specifies the an F0 factor to help configuring the material F0.
-     * Instead of the default 4%, 8% * factor will be used. As the factor is defaulting
-     * to 0.5 the previously hard coded value stays the same.
-     * Can also be used to scale the F0 values of the metallic texture.
+     * In metallic workflow, specifies an F0 factor to help configuring the material F0.
+     * By default the indexOfrefraction is used to compute F0;
+     *
+     * This is used as a factor against the default reflectance at normal incidence to tweak it.
+     *
+     * F0 = defaultF0 * metallicF0Factor * metallicReflectanceColor;
+     * F90 = metallicReflectanceColor;
      */
-    protected _metallicF0Factor = 0.5;
+    protected _metallicF0Factor = 1;
 
     /**
-     * Specifies whether the F0 factor can be fetched from the mettalic texture.
-     * If set to true, please adapt the metallicF0Factor to ensure it fits with
-     * your expectation as it multiplies with the texture data.
+     * In metallic workflow, specifies an F90 color to help configuring the material F90.
+     * By default the F90 is always 1;
+     *
+     * Please note that this factor is also used as a factor against the default reflectance at normal incidence.
+     *
+     * F0 = defaultF0 * metallicF0Factor * metallicReflectanceColor
+     * F90 = metallicReflectanceColor;
      */
-    protected _useMetallicF0FactorFromMetallicTexture = false;
+    protected _metallicReflectanceColor = Color3.White();
+
+    /**
+     * Defines to store metallicReflectanceColor in RGB and metallicF0Factor in A
+     * This is multiply against the scalar values defined in the material.
+     */
+    protected _metallicReflectanceTexture: Nullable<BaseTexture> = null;
 
     /**
      * Used to enable roughness/glossiness fetch from a separate channel depending on the current mode.
@@ -938,6 +952,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         }
                     }
 
+                    if (this._metallicReflectanceTexture) {
+                        if (!this._metallicReflectanceTexture.isReadyOrNotBlocking()) {
+                            return false;
+                        }
+                    }
+
                     if (this._microSurfaceTexture) {
                         if (!this._microSurfaceTexture.isReadyOrNotBlocking()) {
                             return false;
@@ -1156,12 +1176,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
         var shaderName = "pbr";
 
-        var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vAlbedoColor", "vReflectivityColor", "vEmissiveColor", "visibility", "vReflectionColor",
+        var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vAlbedoColor", "vReflectivityColor", "vMetallicReflectanceFactors", "vEmissiveColor", "visibility", "vReflectionColor",
             "vFogInfos", "vFogColor", "pointSize",
-            "vAlbedoInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vReflectionPosition", "vReflectionSize", "vEmissiveInfos", "vReflectivityInfos",
+            "vAlbedoInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vReflectionPosition", "vReflectionSize", "vEmissiveInfos", "vReflectivityInfos", "vMetallicReflectanceInfos",
             "vMicroSurfaceSamplerInfos", "vBumpInfos", "vLightmapInfos",
             "mBones",
-            "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "normalMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix",
+            "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "normalMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix", "metallicReflectanceMatrix",
             "vLightingIntensity",
             "logarithmicDepthConstant",
             "vSphericalX", "vSphericalY", "vSphericalZ",
@@ -1178,7 +1198,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         var samplers = ["albedoSampler", "reflectivitySampler", "ambientSampler", "emissiveSampler",
             "bumpSampler", "lightmapSampler", "opacitySampler",
             "reflectionSampler", "reflectionSamplerLow", "reflectionSamplerHigh", "irradianceSampler",
-            "microSurfaceSampler", "environmentBrdfSampler", "boneSampler"];
+            "microSurfaceSampler", "environmentBrdfSampler", "boneSampler", "metallicReflectanceSampler"];
 
         var uniformBuffers = ["Material", "Scene"];
 
@@ -1387,7 +1407,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         defines.ROUGHNESSSTOREINMETALMAPGREEN = !this._useRoughnessFromMetallicTextureAlpha && this._useRoughnessFromMetallicTextureGreen;
                         defines.METALLNESSSTOREINMETALMAPBLUE = this._useMetallnessFromMetallicTextureBlue;
                         defines.AOSTOREINMETALMAPRED = this._useAmbientOcclusionFromMetallicTextureRed;
-                        defines.METALLICF0FACTORFROMMETALLICMAP = this._useMetallicF0FactorFromMetallicTexture;
                     }
                     else if (this._reflectivityTexture) {
                         MaterialHelper.PrepareDefinesForMergedUV(this._reflectivityTexture, defines, "REFLECTIVITY");
@@ -1397,6 +1416,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         defines.REFLECTIVITY = false;
                     }
 
+                    if (this._metallicReflectanceTexture) {
+                        MaterialHelper.PrepareDefinesForMergedUV(this._metallicReflectanceTexture, defines, "METALLIC_REFLECTANCE");
+                    } else {
+                        defines.METALLIC_REFLECTANCE = false;
+                    }
+
                     if (this._microSurfaceTexture) {
                         MaterialHelper.PrepareDefinesForMergedUV(this._microSurfaceTexture, defines, "MICROSURFACEMAP");
                     } else {
@@ -1572,6 +1597,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         ubo.addUniform("vReflectivityColor", 4);
         ubo.addUniform("vEmissiveColor", 3);
         ubo.addUniform("visibility", 1);
+        ubo.addUniform("vMetallicReflectanceFactors", 4);
+        ubo.addUniform("vMetallicReflectanceInfos", 2);
+        ubo.addUniform("metallicReflectanceMatrix", 16);
 
         PBRClearCoatConfiguration.PrepareUniformBuffer(ubo);
         PBRAnisotropicConfiguration.PrepareUniformBuffer(ubo);
@@ -1740,6 +1768,11 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                             MaterialHelper.BindTextureMatrix(this._reflectivityTexture, ubo, "reflectivity");
                         }
 
+                        if (this._metallicReflectanceTexture) {
+                            ubo.updateFloat2("vMetallicReflectanceInfos", this._metallicReflectanceTexture.coordinatesIndex, this._metallicReflectanceTexture.level);
+                            MaterialHelper.BindTextureMatrix(this._metallicReflectanceTexture, ubo, "metallicReflectance");
+                        }
+
                         if (this._microSurfaceTexture) {
                             ubo.updateFloat2("vMicroSurfaceSamplerInfos", this._microSurfaceTexture.coordinatesIndex, this._microSurfaceTexture.level);
                             MaterialHelper.BindTextureMatrix(this._microSurfaceTexture, ubo, "microSurfaceSampler");
@@ -1767,13 +1800,21 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                 if (defines.METALLICWORKFLOW) {
                     TmpColors.Color3[0].r = (this._metallic === undefined || this._metallic === null) ? 1 : this._metallic;
                     TmpColors.Color3[0].g = (this._roughness === undefined || this._roughness === null) ? 1 : this._roughness;
+                    ubo.updateColor4("vReflectivityColor", TmpColors.Color3[0], 1);
+
+                    const ior = this.subSurface.indexOfRefraction;
+                    const outside_ior = 1; // consider air as clear coat and other layaers would remap in the shader.
 
                     // We are here deriving our default reflectance from a common value for none metallic surface.
-                    // Default specular reflectance at normal incidence.
-                    // 4% corresponds to index of refraction (IOR) of 1.50, approximately equal to glass.
-                    // We then use 8% combined with a factor of 0.5 to allow some variations around the 0.04 default value.
-                    const metallicF0 = 0.08 * this._metallicF0Factor;
-                    ubo.updateColor4("vReflectivityColor", TmpColors.Color3[0], metallicF0);
+                    // Based of the schlick fresnel approximation model
+                    // for dielectrics.
+                    const f0 = Math.pow((ior - outside_ior) / (ior + outside_ior), 2);
+
+                    // Tweak the default F0 and F90 based on our given setup
+                    this._metallicReflectanceColor.scaleToRef(f0 * this._metallicF0Factor, TmpColors.Color3[0]);
+                    const metallicF90 = this._metallicF0Factor;
+
+                    ubo.updateColor4("vMetallicReflectanceFactors", TmpColors.Color3[0], metallicF90);
                 }
                 else {
                     ubo.updateColor4("vReflectivityColor", this._reflectivityColor, this._microSurface);
@@ -1849,6 +1890,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         ubo.setTexture("reflectivitySampler", this._reflectivityTexture);
                     }
 
+                    if (this._metallicReflectanceTexture) {
+                        ubo.setTexture("metallicReflectanceSampler", this._metallicReflectanceTexture);
+                    }
+
                     if (this._microSurfaceTexture) {
                         ubo.setTexture("microSurfaceSampler", this._microSurfaceTexture);
                     }
@@ -2010,6 +2055,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             activeTextures.push(this._metallicTexture);
         }
 
+        if (this._metallicReflectanceTexture) {
+            activeTextures.push(this._metallicReflectanceTexture);
+        }
+
         if (this._microSurfaceTexture) {
             activeTextures.push(this._microSurfaceTexture);
         }
@@ -2064,6 +2113,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             return true;
         }
 
+        if (this._metallicReflectanceTexture === texture) {
+            return true;
+        }
+
         if (this._microSurfaceTexture === texture) {
             return true;
         }
@@ -2089,45 +2142,21 @@ export abstract class PBRBaseMaterial extends PushMaterial {
      */
     public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
         if (forceDisposeTextures) {
-            if (this._albedoTexture) {
-                this._albedoTexture.dispose();
-            }
-
-            if (this._ambientTexture) {
-                this._ambientTexture.dispose();
-            }
-
-            if (this._opacityTexture) {
-                this._opacityTexture.dispose();
-            }
-
-            if (this._reflectionTexture) {
-                this._reflectionTexture.dispose();
-            }
-
             if (this._environmentBRDFTexture && this.getScene().environmentBRDFTexture !== this._environmentBRDFTexture) {
                 this._environmentBRDFTexture.dispose();
             }
 
-            if (this._emissiveTexture) {
-                this._emissiveTexture.dispose();
-            }
-
-            if (this._metallicTexture) {
-                this._metallicTexture.dispose();
-            }
-
-            if (this._reflectivityTexture) {
-                this._reflectivityTexture.dispose();
-            }
-
-            if (this._bumpTexture) {
-                this._bumpTexture.dispose();
-            }
-
-            if (this._lightmapTexture) {
-                this._lightmapTexture.dispose();
-            }
+            this._albedoTexture?.dispose();
+            this._ambientTexture?.dispose();
+            this._opacityTexture?.dispose();
+            this._reflectionTexture?.dispose();
+            this._emissiveTexture?.dispose();
+            this._metallicTexture?.dispose();
+            this._reflectivityTexture?.dispose();
+            this._bumpTexture?.dispose();
+            this._lightmapTexture?.dispose();
+            this._metallicReflectanceTexture?.dispose();
+            this._microSurfaceTexture?.dispose();
         }
 
         this.subSurface.dispose(forceDisposeTextures);

+ 33 - 13
src/Materials/PBR/pbrMaterial.ts

@@ -165,23 +165,38 @@ export class PBRMaterial extends PBRBaseMaterial {
     public roughness: Nullable<number>;
 
     /**
-     * Specifies the an F0 factor to help configuring the material F0.
-     * Instead of the default 4%, 8% * factor will be used. As the factor is defaulting
-     * to 0.5 the previously hard coded value stays the same.
-     * Can also be used to scale the F0 values of the metallic texture.
+     * In metallic workflow, specifies an F0 factor to help configuring the material F0.
+     * By default the indexOfrefraction is used to compute F0;
+     *
+     * This is used as a factor against the default reflectance at normal incidence to tweak it.
+     *
+     * F0 = defaultF0 * metallicF0Factor * metallicReflectanceColor;
+     * F90 = metallicReflectanceColor;
      */
     @serialize()
     @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public metallicF0Factor = 0.5;
+    public metallicF0Factor = 1;
 
     /**
-     * Specifies whether the F0 factor can be fetched from the mettalic texture.
-     * If set to true, please adapt the metallicF0Factor to ensure it fits with
-     * your expectation as it multiplies with the texture data.
+     * In metallic workflow, specifies an F90 color to help configuring the material F90.
+     * By default the F90 is always 1;
+     *
+     * Please note that this factor is also used as a factor against the default reflectance at normal incidence.
+     *
+     * F0 = defaultF0 * metallicF0Factor * metallicReflectanceColor
+     * F90 = metallicReflectanceColor;
      */
-    @serialize()
+    @serializeAsColor3()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public metallicReflectanceColor = Color3.White();
+
+    /**
+     * Defines to store metallicReflectanceColor in RGB and metallicF0Factor in A
+     * This is multiply against the scalar values defined in the material.
+     */
+    @serializeAsTexture()
     @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public useMetallicF0FactorFromMetallicTexture = false;
+    public metallicReflectanceTexture: BaseTexture;
 
     /**
      * Used to enable roughness/glossiness fetch from a separate channel depending on the current mode.
@@ -264,13 +279,18 @@ export class PBRMaterial extends PBRBaseMaterial {
     public microSurface = 1.0;
 
     /**
-     * source material index of refraction (IOR)' / 'destination material IOR.
+     * Index of refraction of the material base layer.
+     * https://en.wikipedia.org/wiki/List_of_refractive_indices
+     *
+     * This does not only impact refraction but also the Base F0 of Dielectric Materials.
+     *
+     * From dielectric fresnel rules: F0 = square((iorT - iorI) / (iorT + iorI))
      */
     public get indexOfRefraction(): number {
-        return 1 / this.subSurface.indexOfRefraction;
+        return this.subSurface.indexOfRefraction;
     }
     public set indexOfRefraction(value: number) {
-        this.subSurface.indexOfRefraction = 1 / value;
+        this.subSurface.indexOfRefraction = value;
     }
 
     /**

+ 7 - 3
src/Materials/PBR/pbrSubSurfaceConfiguration.ts

@@ -113,14 +113,18 @@ export class PBRSubSurfaceConfiguration {
     @expandToProperty("_markAllSubMeshesAsTexturesDirty")
     public refractionTexture: Nullable<BaseTexture> = null;
 
-    private _indexOfRefraction = 1;
+    private _indexOfRefraction = 1.5;
     /**
-     * Defines the index of refraction used in the material.
+     * Index of refraction of the material base layer.
      * https://en.wikipedia.org/wiki/List_of_refractive_indices
+     *
+     * This does not only impact refraction but also the Base F0 of Dielectric Materials.
+     *
+     * From dielectric fresnel rules: F0 = square((iorT - iorI) / (iorT + iorI))
      */
     @serialize()
     @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public indexOfRefraction = 1;
+    public indexOfRefraction = 1.5;
 
     private _invertRefractionY = false;
     /**

+ 20 - 7
src/Shaders/ShadersInclude/pbrBRDFFunctions.fx

@@ -29,9 +29,19 @@
         return brdfLookup.rgb;
     }
 
+    vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0, const vec3 specularEnvironmentR90, const vec3 environmentBrdf) {
+        #ifdef BRDF_V_HEIGHT_CORRELATED
+            vec3 reflectance = (specularEnvironmentR90 - specularEnvironmentR0) * environmentBrdf.x + specularEnvironmentR0 * environmentBrdf.y;
+            // Simplification if F90 = 1 vec3 reflectance = (specularEnvironmentR90 - specularEnvironmentR0) * environmentBrdf.xxx + specularEnvironmentR0 * environmentBrdf.yyy;
+        #else
+            vec3 reflectance = specularEnvironmentR0 * environmentBrdf.x + specularEnvironmentR90 * environmentBrdf.y;
+        #endif
+        return reflectance;
+    }
+
     vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0, const vec3 environmentBrdf) {
         #ifdef BRDF_V_HEIGHT_CORRELATED
-            vec3 reflectance = mix(environmentBrdf.xxx, environmentBrdf.yyy, specularEnvironmentR0);
+            vec3 reflectance = mix(environmentBrdf.xxx, specularEnvironmentR0, environmentBrdf.yyy);
         #else
             vec3 reflectance = specularEnvironmentR0 * environmentBrdf.x + environmentBrdf.y;
         #endif
@@ -76,18 +86,21 @@ float getBRDFLookupCharlieSheen(float NdotV, float perceptualRoughness)
 //                              Schlick/Fresnel
 // ______________________________________________________________________
 
+// iorI incident iorT transmitted
+
 // Schlick's approximation for R0 (Fresnel Reflectance Values)
 // Keep for references
-// vec3 getR0fromAirToSurfaceIOR(vec3 ior1) {
-//     return getR0fromIOR(ior1, vec3(1.0));
-// }
 
-// vec3 getR0fromIOR(vec3 ior1, vec3 ior2) {
-//     vec3 t = (ior1 - ior2) / (ior1 + ior2);
+// vec3 getR0fromIORs(vec3 iorT, vec3 iorI) { 
+//     vec3 t = (iorT - iorI) / (iorT + iorI);
 //     return t * t;
 // }
 
-// vec3 getIORfromAirToSurfaceR0(vec3 f0) {
+// vec3 getR0fromAirToSurfaceIORT(vec3 iorT) {
+//     return getR0fromIOR(iorT, vec3(1.0));
+// }
+
+// vec3 getIORTfromAirToSurfaceR0(vec3 f0) {
 //     vec3 s = sqrt(f0);
 //     return (1.0 + s) / (1.0 - s);
 // }

+ 1 - 1
src/Shaders/ShadersInclude/pbrBlockReflectance.fx

@@ -1,5 +1,5 @@
 #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX)
-    vec3 specularEnvironmentReflectance = getReflectanceFromBRDFLookup(clearcoatOut.specularEnvironmentR0, environmentBrdf);
+    vec3 specularEnvironmentReflectance = getReflectanceFromBRDFLookup(clearcoatOut.specularEnvironmentR0, specularEnvironmentR90, environmentBrdf);
 
     #ifdef RADIANCEOCCLUSION
         specularEnvironmentReflectance *= seo;

+ 12 - 2
src/Shaders/ShadersInclude/pbrBlockReflectance0.fx

@@ -1,4 +1,14 @@
 float reflectance = max(max(reflectivityOut.surfaceReflectivityColor.r, reflectivityOut.surfaceReflectivityColor.g), reflectivityOut.surfaceReflectivityColor.b);
-float reflectance90 = fresnelGrazingReflectance(reflectance);
 vec3 specularEnvironmentR0 = reflectivityOut.surfaceReflectivityColor.rgb;
-vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
+
+#ifdef METALLICWORKFLOW
+    vec3 specularEnvironmentR90 = vec3(metallicReflectanceFactors.a);
+#else 
+    vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0);
+#endif
+
+// Back Compat
+#ifdef ALPHAFRESNEL
+    float reflectance90 = fresnelGrazingReflectance(reflectance);
+    specularEnvironmentR90 = specularEnvironmentR90 * reflectance90;
+#endif

+ 5 - 8
src/Shaders/ShadersInclude/pbrBlockReflectivity.fx

@@ -21,6 +21,7 @@ void reflectivityBlock(
     const in vec4 vReflectivityColor,
 #ifdef METALLICWORKFLOW
     const in vec3 surfaceAlbedo,
+    const in vec4 metallicReflectanceFactors,
 #endif
 #ifdef REFLECTIVITY
     const in vec3 vReflectivityInfos,
@@ -82,7 +83,7 @@ void reflectivityBlock(
         // Diffuse is used as the base of the reflectivity.
         vec3 baseColor = surfaceAlbedo;
 
-        #ifdef REFLECTANCE
+        #ifdef FROSTBITE_REFLECTANCE
             // *** NOT USED ANYMORE ***
             // Following Frostbite Remapping,
             // https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf page 115
@@ -95,19 +96,15 @@ void reflectivityBlock(
             // Compute the converted reflectivity.
             surfaceReflectivityColor = mix(0.16 * reflectance * reflectance, baseColor, metallicRoughness.r);
         #else
-            vec3 metallicF0 = vec3(vReflectivityColor.a, vReflectivityColor.a, vReflectivityColor.a);
-            #ifdef METALLICF0FACTORFROMMETALLICMAP
-                #ifdef REFLECTIVITY
-                    metallicF0 *= surfaceMetallicOrReflectivityColorMap.a;
-                #endif
-            #endif
+            vec3 metallicF0 = metallicReflectanceFactors.rgb;
+            float metallicF90 = metallicReflectanceFactors.a;
 
             #if DEBUGMODE > 0
                 outParams.metallicF0 = metallicF0;
             #endif
 
             // Compute the converted diffuse.
-            outParams.surfaceAlbedo = mix(baseColor.rgb * (1.0 - metallicF0.r), vec3(0., 0., 0.), metallicRoughness.r);
+            outParams.surfaceAlbedo = mix(baseColor.rgb * (1.0 - metallicF0), vec3(0., 0., 0.), metallicRoughness.r);
 
             // Compute the converted reflectivity.
             surfaceReflectivityColor = mix(metallicF0, baseColor, metallicRoughness.r);

+ 1 - 0
src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx

@@ -5,6 +5,7 @@ uniform vec4 vAlbedoColor;
 uniform vec4 vLightingIntensity;
 
 uniform vec4 vReflectivityColor;
+uniform vec4 vMetallicReflectanceFactors;
 uniform vec3 vEmissiveColor;
 
 uniform float visibility;

+ 11 - 0
src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx

@@ -75,6 +75,17 @@
     uniform sampler2D microSurfaceSampler;
 #endif
 
+#ifdef METALLIC_REFLECTANCE
+    #if METALLIC_REFLECTANCEDIRECTUV == 1
+        #define vMetallicReflectanceUV vMainUV1
+    #elif METALLIC_REFLECTANCEDIRECTUV == 2
+        #define vMetallicReflectanceUV vMainUV2
+    #else
+        varying vec2 vMetallicReflectanceUV;
+    #endif
+    uniform sampler2D metallicReflectanceSampler;
+#endif
+
 #ifdef CLEARCOAT
     #ifdef CLEARCOAT_TEXTURE
         #if CLEARCOAT_TEXTUREDIRECTUV == 1

+ 4 - 0
src/Shaders/ShadersInclude/pbrUboDeclaration.fx

@@ -33,6 +33,10 @@ uniform Material
 
     uniform float visibility;
 
+    uniform vec4 vMetallicReflectanceFactors;
+    uniform vec2 vMetallicReflectanceInfos;
+    uniform mat4 metallicReflectanceMatrix;
+
     uniform vec2 vClearCoatParams;
     uniform vec4 vClearCoatRefractionParams;
     uniform vec2 vClearCoatInfos;

+ 5 - 0
src/Shaders/ShadersInclude/pbrVertexDeclaration.fx

@@ -31,6 +31,11 @@ uniform vec3 vReflectivityInfos;
 uniform mat4 reflectivityMatrix;
 #endif
 
+#ifdef METALLIC_REFLECTANCE
+uniform vec2 vMetallicReflectanceInfos;
+uniform mat4 metallicReflectanceMatrix;
+#endif
+
 #ifdef MICROSURFACEMAP
 uniform vec2 vMicroSurfaceSamplerInfos;
 uniform mat4 microSurfaceSamplerMatrix;

+ 11 - 0
src/Shaders/pbr.fragment.fx

@@ -142,10 +142,21 @@ void main(void) {
     vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
 #endif
 
+#ifdef METALLICWORKFLOW
+    vec4 metallicReflectanceFactors = vMetallicReflectanceFactors;
+    #ifdef METALLIC_REFLECTANCE
+        vec4 metallicReflectanceFactorsMap = texture2D(metallicReflectanceSampler, vMetallicReflectanceUV + uvOffset);
+        metallicReflectanceFactorsMap = toLinearSpace(metallicReflectanceFactorsMap);
+
+        metallicReflectanceFactors *= metallicReflectanceFactorsMap;
+    #endif
+#endif
+
     reflectivityBlock(
         vReflectivityColor,
     #ifdef METALLICWORKFLOW
         surfaceAlbedo,
+        metallicReflectanceFactors,
     #endif
     #ifdef REFLECTIVITY
         vReflectivityInfos,

+ 15 - 0
src/Shaders/pbr.vertex.fx

@@ -62,6 +62,10 @@ varying vec2 vReflectivityUV;
 varying vec2 vMicroSurfaceSamplerUV;
 #endif
 
+#if defined(METALLIC_REFLECTANCE) && METALLIC_REFLECTANCEDIRECTUV == 0
+varying vec2 vMetallicReflectanceUV;
+#endif
+
 #if defined(BUMP) && BUMPDIRECTUV == 0
 varying vec2 vBumpUV;
 #endif
@@ -297,6 +301,17 @@ void main(void) {
     }
 #endif
 
+#if defined(METALLIC_REFLECTANCE) && METALLIC_REFLECTANCEDIRECTUV == 0 
+    if (vMetallicReflectanceInfos.x == 0.)
+    {
+        vMetallicReflectanceUV = vec2(metallicReflectanceMatrix * vec4(uvUpdated, 1.0, 0.0));
+    }
+    else
+    {
+        vMetallicReflectanceUV = vec2(metallicReflectanceMatrix * vec4(uv2, 1.0, 0.0));
+    }
+#endif
+
 #if defined(BUMP) && BUMPDIRECTUV == 0 
     if (vBumpInfos.x == 0.)
     {