瀏覽代碼

Clear Coat

sebavan 6 年之前
父節點
當前提交
c3998cb84e

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

@@ -24,6 +24,8 @@
   - Moved to a measure / draw mechanism ([Deltakosh](https://github.com/deltakosh))
   - Moved to a measure / draw mechanism ([Deltakosh](https://github.com/deltakosh))
   - Added support for [nine patch stretch](https://www.babylonjs-playground.com/#G5H9IN#2) mode for images. ([Deltakosh](https://github.com/deltakosh))
   - Added support for [nine patch stretch](https://www.babylonjs-playground.com/#G5H9IN#2) mode for images. ([Deltakosh](https://github.com/deltakosh))
   - InvalidateRect added to AdvancedDynamicTexture to improve perf for heavily populated GUIs, works with shadows ([TrevorDev](https://github.com/TrevorDev))
   - InvalidateRect added to AdvancedDynamicTexture to improve perf for heavily populated GUIs, works with shadows ([TrevorDev](https://github.com/TrevorDev))
+- Migrate the code to modules and deploy es6 npm packages ([Sebavan](https://github.com/Sebavan))
+- Add clear coat support to PBR ([Sebavan](https://github.com/Sebavan))
 
 
 ## Updates
 ## Updates
 
 

+ 142 - 3
src/Materials/PBR/pbrBaseMaterial.ts

@@ -14,6 +14,7 @@ import { AbstractMesh } from "../../Meshes/abstractMesh";
 import { Mesh } from "../../Meshes/mesh";
 import { Mesh } from "../../Meshes/mesh";
 import { _TimeToken } from "../../Instrumentation/timeToken";
 import { _TimeToken } from "../../Instrumentation/timeToken";
 import { _DepthCullingState, _StencilState, _AlphaState } from "../../States/index";
 import { _DepthCullingState, _StencilState, _AlphaState } from "../../States/index";
+import { IMaterialClearCoatDefines, PBRClearCoatConfiguration } from "./pbrClearCoatConfiguration";
 
 
 import { ImageProcessingConfiguration, IImageProcessingConfigurationDefines } from "../../Materials/imageProcessingConfiguration";
 import { ImageProcessingConfiguration, IImageProcessingConfigurationDefines } from "../../Materials/imageProcessingConfiguration";
 import { Effect, EffectFallbacks, EffectCreationOptions } from "../../Materials/effect";
 import { Effect, EffectFallbacks, EffectCreationOptions } from "../../Materials/effect";
@@ -37,7 +38,7 @@ import "../../Shaders/pbr.vertex";
  * Manages the defines for the PBR Material.
  * Manages the defines for the PBR Material.
  * @hiddenChildren
  * @hiddenChildren
  */
  */
-class PBRMaterialDefines extends MaterialDefines implements IImageProcessingConfigurationDefines {
+class PBRMaterialDefines extends MaterialDefines implements IImageProcessingConfigurationDefines, IMaterialClearCoatDefines {
     public PBR = true;
     public PBR = true;
 
 
     public MAINUV1 = false;
     public MAINUV1 = false;
@@ -177,6 +178,12 @@ class PBRMaterialDefines extends MaterialDefines implements IImageProcessingConf
 
 
     public SPECULARAA = false;
     public SPECULARAA = false;
 
 
+    public CLEARCOAT = false;
+    public CLEARCOAT_TEXTURE = false;
+    public CLEARCOAT_TEXTUREDIRECTUV = 0;
+    public CLEARCOAT_BUMP = false;
+    public CLEARCOAT_BUMPDIRECTUV = 0;
+
     public UNLIT = false;
     public UNLIT = false;
 
 
     /**
     /**
@@ -643,6 +650,11 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     private _unlit = false;
     private _unlit = false;
 
 
     /**
     /**
+     * Defines the clear coat layer parameters for the material.
+     */
+    public readonly clearCoat = new PBRClearCoatConfiguration(this._markAllSubMeshesAsTexturesDirty.bind(this));
+
+    /**
      * Instantiates a new PBRMaterial instance.
      * Instantiates a new PBRMaterial instance.
      *
      *
      * @param name The material name
      * @param name The material name
@@ -909,6 +921,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             }
             }
         }
         }
 
 
+        if (!this.clearCoat.isReadyForSubMesh(defines, scene, engine, this._disableBumpMap)) {
+            return false;
+        }
+
         if (defines._areImageProcessingDirty && this._imageProcessingConfiguration) {
         if (defines._areImageProcessingDirty && this._imageProcessingConfiguration) {
             if (!this._imageProcessingConfiguration.isReady()) {
             if (!this._imageProcessingConfiguration.isReady()) {
                 return false;
                 return false;
@@ -994,6 +1010,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             fallbacks.addFallback(fallbackRank++, "PARALLAXOCCLUSION");
             fallbacks.addFallback(fallbackRank++, "PARALLAXOCCLUSION");
         }
         }
 
 
+        fallbackRank = PBRClearCoatConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
+
         if (defines.ENVIRONMENTBRDF) {
         if (defines.ENVIRONMENTBRDF) {
             fallbacks.addFallback(fallbackRank++, "ENVIRONMENTBRDF");
             fallbacks.addFallback(fallbackRank++, "ENVIRONMENTBRDF");
         }
         }
@@ -1091,8 +1109,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             "refractionSampler", "refractionSamplerLow", "refractionSamplerHigh",
             "refractionSampler", "refractionSamplerLow", "refractionSamplerHigh",
             "reflectionSampler", "reflectionSamplerLow", "reflectionSamplerHigh",
             "reflectionSampler", "reflectionSamplerLow", "reflectionSamplerHigh",
             "microSurfaceSampler", "environmentBrdfSampler", "boneSampler"];
             "microSurfaceSampler", "environmentBrdfSampler", "boneSampler"];
+
         var uniformBuffers = ["Material", "Scene"];
         var uniformBuffers = ["Material", "Scene"];
 
 
+        PBRClearCoatConfiguration.AddUniforms(uniforms);
+        PBRClearCoatConfiguration.AddSamplers(samplers);
+
         if (ImageProcessingConfiguration) {
         if (ImageProcessingConfiguration) {
             ImageProcessingConfiguration.PrepareUniforms(uniforms, defines);
             ImageProcessingConfiguration.PrepareUniforms(uniforms, defines);
             ImageProcessingConfiguration.PrepareSamplers(samplers, defines);
             ImageProcessingConfiguration.PrepareSamplers(samplers, defines);
@@ -1369,6 +1391,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             defines.SPECULARAA = scene.getEngine().getCaps().standardDerivatives && this._enableSpecularAntiAliasing;
             defines.SPECULARAA = scene.getEngine().getCaps().standardDerivatives && this._enableSpecularAntiAliasing;
         }
         }
 
 
+        this.clearCoat.prepareDefines(defines, scene);
+
         if (defines._areImageProcessingDirty && this._imageProcessingConfiguration) {
         if (defines._areImageProcessingDirty && this._imageProcessingConfiguration) {
             this._imageProcessingConfiguration.prepareDefines(defines);
             this._imageProcessingConfiguration.prepareDefines(defines);
         }
         }
@@ -1456,6 +1480,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         this._uniformBuffer.addUniform("vEmissiveColor", 3);
         this._uniformBuffer.addUniform("vEmissiveColor", 3);
 
 
         this._uniformBuffer.addUniform("pointSize", 1);
         this._uniformBuffer.addUniform("pointSize", 1);
+
+        PBRClearCoatConfiguration.PrepareUniformBuffer(this._uniformBuffer);
+
         this._uniformBuffer.create();
         this._uniformBuffer.create();
     }
     }
 
 
@@ -1512,6 +1539,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
 
         let reflectionTexture: Nullable<BaseTexture> = null;
         let reflectionTexture: Nullable<BaseTexture> = null;
         if (mustRebind) {
         if (mustRebind) {
+            var engine = scene.getEngine();
             this._uniformBuffer.bindToEffect(effect, "Material");
             this._uniformBuffer.bindToEffect(effect, "Material");
 
 
             this.bindViewProjection(effect);
             this.bindViewProjection(effect);
@@ -1597,7 +1625,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         }
                         }
                     }
                     }
 
 
-                    if (this._bumpTexture && scene.getEngine().getCaps().standardDerivatives && MaterialFlags.BumpTextureEnabled && !this._disableBumpMap) {
+                    if (this._bumpTexture && engine.getCaps().standardDerivatives && MaterialFlags.BumpTextureEnabled && !this._disableBumpMap) {
                         this._uniformBuffer.updateFloat3("vBumpInfos", this._bumpTexture.coordinatesIndex, this._bumpTexture.level, this._parallaxScaleBias);
                         this._uniformBuffer.updateFloat3("vBumpInfos", this._bumpTexture.coordinatesIndex, this._bumpTexture.level, this._parallaxScaleBias);
                         MaterialHelper.BindTextureMatrix(this._bumpTexture, this._uniformBuffer, "bump");
                         MaterialHelper.BindTextureMatrix(this._bumpTexture, this._uniformBuffer, "bump");
 
 
@@ -1714,11 +1742,13 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                     }
                     }
                 }
                 }
 
 
-                if (this._bumpTexture && scene.getEngine().getCaps().standardDerivatives && MaterialFlags.BumpTextureEnabled && !this._disableBumpMap) {
+                if (this._bumpTexture && engine.getCaps().standardDerivatives && MaterialFlags.BumpTextureEnabled && !this._disableBumpMap) {
                     this._uniformBuffer.setTexture("bumpSampler", this._bumpTexture);
                     this._uniformBuffer.setTexture("bumpSampler", this._bumpTexture);
                 }
                 }
             }
             }
 
 
+            this.clearCoat.bindForSubMesh(this._uniformBuffer, scene, engine, this._disableBumpMap, this.isFrozen, this._invertNormalMapX, this._invertNormalMapY);
+
             // Clip plane
             // Clip plane
             MaterialHelper.BindClipPlane(this._activeEffect, scene);
             MaterialHelper.BindClipPlane(this._activeEffect, scene);
 
 
@@ -1812,6 +1842,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             results.push(this._refractionTexture);
             results.push(this._refractionTexture);
         }
         }
 
 
+        this.clearCoat.getAnimatables(results);
+
         return results;
         return results;
     }
     }
 
 
@@ -1845,6 +1877,111 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     }
     }
 
 
     /**
     /**
+     * Returns an array of the actively used textures.
+     * @returns - Array of BaseTextures
+     */
+    public getActiveTextures(): BaseTexture[] {
+        var activeTextures = super.getActiveTextures();
+
+        if (this._albedoTexture) {
+            activeTextures.push(this._albedoTexture);
+        }
+
+        if (this._ambientTexture) {
+            activeTextures.push(this._ambientTexture);
+        }
+
+        if (this._opacityTexture) {
+            activeTextures.push(this._opacityTexture);
+        }
+
+        if (this._reflectionTexture) {
+            activeTextures.push(this._reflectionTexture);
+        }
+
+        if (this._emissiveTexture) {
+            activeTextures.push(this._emissiveTexture);
+        }
+
+        if (this._reflectivityTexture) {
+            activeTextures.push(this._reflectivityTexture);
+        }
+
+        if (this._metallicTexture) {
+            activeTextures.push(this._metallicTexture);
+        }
+
+        if (this._microSurfaceTexture) {
+            activeTextures.push(this._microSurfaceTexture);
+        }
+
+        if (this._bumpTexture) {
+            activeTextures.push(this._bumpTexture);
+        }
+
+        if (this._lightmapTexture) {
+            activeTextures.push(this._lightmapTexture);
+        }
+
+        if (this._refractionTexture) {
+            activeTextures.push(this._refractionTexture);
+        }
+
+        this.clearCoat.getActiveTextures(activeTextures);
+
+        return activeTextures;
+    }
+
+    /**
+     * Checks to see if a texture is used in the material.
+     * @param texture - Base texture to use.
+     * @returns - Boolean specifying if a texture is used in the material.
+     */
+    public hasTexture(texture: BaseTexture): boolean {
+        if (super.hasTexture(texture)) {
+            return true;
+        }
+
+        if (this._albedoTexture === texture) {
+            return true;
+        }
+
+        if (this._ambientTexture === texture) {
+            return true;
+        }
+
+        if (this._opacityTexture === texture) {
+            return true;
+        }
+
+        if (this._reflectionTexture === texture) {
+            return true;
+        }
+
+        if (this._reflectivityTexture === texture) {
+            return true;
+        }
+
+        if (this._metallicTexture === texture) {
+            return true;
+        }
+
+        if (this._microSurfaceTexture === texture) {
+            return true;
+        }
+
+        if (this._bumpTexture === texture) {
+            return true;
+        }
+
+        if (this._lightmapTexture === texture) {
+            return true;
+        }
+
+        return this.clearCoat.hasTexture(texture);
+    }
+
+    /**
      * Disposes the resources of the material.
      * Disposes the resources of the material.
      * @param forceDisposeEffect - Forces the disposal of effects.
      * @param forceDisposeEffect - Forces the disposal of effects.
      * @param forceDisposeTextures - Forces the disposal of all textures.
      * @param forceDisposeTextures - Forces the disposal of all textures.
@@ -1896,6 +2033,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             }
             }
         }
         }
 
 
+        this.clearCoat.dispose(forceDisposeTextures);
+
         this._renderTargets.dispose();
         this._renderTargets.dispose();
 
 
         if (this._imageProcessingConfiguration && this._imageProcessingObserver) {
         if (this._imageProcessingConfiguration && this._imageProcessingObserver) {

+ 0 - 41
src/Materials/PBR/pbrBaseSimpleMaterial.ts

@@ -125,47 +125,6 @@ export abstract class PBRBaseSimpleMaterial extends PBRBaseMaterial {
     public useLightmapAsShadowmap = false;
     public useLightmapAsShadowmap = false;
 
 
     /**
     /**
-     * Return the active textures of the material.
-     */
-    public getActiveTextures(): BaseTexture[] {
-        var activeTextures = super.getActiveTextures();
-
-        if (this.environmentTexture) {
-            activeTextures.push(this.environmentTexture);
-        }
-
-        if (this.normalTexture) {
-            activeTextures.push(this.normalTexture);
-        }
-
-        if (this.emissiveTexture) {
-            activeTextures.push(this.emissiveTexture);
-        }
-
-        if (this.occlusionTexture) {
-            activeTextures.push(this.occlusionTexture);
-        }
-
-        if (this.lightmapTexture) {
-            activeTextures.push(this.lightmapTexture);
-        }
-
-        return activeTextures;
-    }
-
-    public hasTexture(texture: BaseTexture): boolean {
-        if (super.hasTexture(texture)) {
-            return true;
-        }
-
-        if (this.lightmapTexture === texture) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
      * Instantiates a new PBRMaterial instance.
      * Instantiates a new PBRMaterial instance.
      *
      *
      * @param name The material name
      * @param name The material name

+ 326 - 0
src/Materials/PBR/pbrClearCoatConfiguration.ts

@@ -0,0 +1,326 @@
+import { Nullable } from "../../types";
+import { IAnimatable } from "../../Misc/tools";
+import { SerializationHelper, serialize, serializeAsTexture, expandToProperty } from "../../Misc/decorators";
+import { BaseTexture } from "../../Materials/Textures/baseTexture";
+import { EffectFallbacks } from "../../Materials/effect";
+import { MaterialFlags } from "../materialFlags";
+import { UniformBuffer } from "../../Materials/uniformBuffer";
+import { MaterialHelper } from "../../Materials/materialHelper";
+
+declare type Engine = import("../../Engines/engine").Engine;
+declare type Scene = import("../../scene").Scene;
+
+/**
+ * @hidden
+ */
+export interface IMaterialClearCoatDefines {
+    CLEARCOAT: boolean;
+    CLEARCOAT_TEXTURE: boolean;
+    CLEARCOAT_TEXTUREDIRECTUV: number;
+    CLEARCOAT_BUMP: boolean;
+    CLEARCOAT_BUMPDIRECTUV: number;
+
+    /** @hidden */
+    _areTexturesDirty: boolean;
+}
+
+/**
+ * Define the code related to the clear coat parameters of the pbr material.
+ */
+export class PBRClearCoatConfiguration {
+
+    /**
+     * Defines if the clear coat is enabled in the material.
+     */
+    @serialize()
+    public enable = false;
+
+    /**
+     * Defines the clear coat layer strength (between 0 and 1) it defaults to 1.
+     */
+    @serialize()
+    public intensity: number = 1;
+
+    /**
+     * Defines the clear coat layer roughness.
+     */
+    @serialize()
+    public roughness: number = 0;
+
+    @serializeAsTexture()
+    private _texture: Nullable<BaseTexture> = null;
+    /**
+     * Stores the clear coat values in a texture.
+     */
+    @expandToProperty("markAllSubMeshesAsTexturesDirty")
+    public texture: Nullable<BaseTexture> = null;
+
+    @serializeAsTexture()
+    private _bumpTexture: Nullable<BaseTexture> = null;
+    /**
+     * Define the clear coat specific bump texture.
+     */
+    @expandToProperty("markAllSubMeshesAsTexturesDirty")
+    public bumpTexture: Nullable<BaseTexture> = null;
+
+    /** @hidden */
+    public _markAllSubMeshesAsTexturesDirty: () => void;
+
+    public markAllSubMeshesAsTexturesDirty(): void {
+        this._markAllSubMeshesAsTexturesDirty();
+    };
+
+    /**
+     * Instantiate a new istance of clear coat configuration.
+     * @param markAllSubMeshesAsTexturesDirty Callback to flag the material to dirty
+     */
+    constructor(markAllSubMeshesAsTexturesDirty: () => void) {
+        this._markAllSubMeshesAsTexturesDirty = markAllSubMeshesAsTexturesDirty;
+    }
+
+    /**
+     * Specifies that the submesh is ready to be used.
+     * @param defines defines the Base texture to use.
+     * @param scene defines the scene the material belongs to.
+     * @param engine defines the engine the material belongs to.
+     * @param disableBumpMap defines wether the material disables bump or not.
+     * @returns - boolean indicating that the submesh is ready or not.
+     */
+    public isReadyForSubMesh(defines: IMaterialClearCoatDefines, scene: Scene, engine: Engine, disableBumpMap: boolean): boolean {
+        if (defines._areTexturesDirty) {
+            if (scene.texturesEnabled) {
+                if (this._texture && MaterialFlags.ClearCoatTextureEnabled) {
+                    if (!this._texture.isReadyOrNotBlocking()) {
+                        return false;
+                    }
+                }
+
+                if (engine.getCaps().standardDerivatives && this._bumpTexture && MaterialFlags.ClearCoatBumpTextureEnabled && !disableBumpMap) {
+                    // Bump texture cannot be not blocking.
+                    if (!this._bumpTexture.isReady()) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks to see if a texture is used in the material.
+     * @param defines defines the Base texture to use.
+     * @param scene defines the scene to the material belongs to.
+     */
+    public prepareDefines(defines: IMaterialClearCoatDefines, scene: Scene): void {
+        if (this.enable) {
+            defines.CLEARCOAT = true;
+
+            if (defines._areTexturesDirty) {
+                if (scene.texturesEnabled) {
+                    if (this._texture && MaterialFlags.ClearCoatTextureEnabled) {
+                        MaterialHelper.PrepareDefinesForMergedUV(this._texture, defines, "CLEARCOAT_TEXTURE");
+                    } else {
+                        defines.CLEARCOAT_TEXTURE = false;
+                    }
+
+                    if (this._bumpTexture && MaterialFlags.ClearCoatBumpTextureEnabled) {
+                        MaterialHelper.PrepareDefinesForMergedUV(this._bumpTexture, defines, "CLEARCOAT_BUMP");
+                    } else {
+                        defines.CLEARCOAT_BUMP = false;
+                    }
+                }
+            }
+        }
+        else {
+            defines.CLEARCOAT = false;
+            defines.CLEARCOAT_TEXTURE = false;
+            defines.CLEARCOAT_BUMP = false;
+        }
+    }
+
+    /**
+     * Binds the material data.
+     * @param uniformBuffer defines the Uniform buffer to fill in.
+     * @param scene defines the scene the material belongs to.
+     * @param engine defines the engine the material belongs to.
+     * @param disableBumpMap defines wether the material disables bump or not.
+     * @param isFrozen defines wether the material is frozen or not.
+     * @param invertNormalMapX If sets to true, x component of normal map value will be inverted (x = 1.0 - x).
+     * @param invertNormalMapY If sets to true, y component of normal map value will be inverted (y = 1.0 - y).
+     */
+    public bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, engine: Engine, disableBumpMap: boolean, isFrozen: boolean, invertNormalMapX: boolean, invertNormalMapY: boolean): void {
+        if (!uniformBuffer.useUbo || !isFrozen || !uniformBuffer.isSync) {
+            if (this._texture && MaterialFlags.ClearCoatTextureEnabled) {
+                uniformBuffer.updateFloat2("vClearCoatInfos", this._texture.coordinatesIndex, this._texture.level);
+                MaterialHelper.BindTextureMatrix(this._texture, uniformBuffer, "clearCoat");
+            }
+
+            if (this._bumpTexture && engine.getCaps().standardDerivatives && MaterialFlags.ClearCoatTextureEnabled && !disableBumpMap) {
+                uniformBuffer.updateFloat2("vClearCoatBumpInfos", this._bumpTexture.coordinatesIndex, 1.0 / this._bumpTexture.level);
+                MaterialHelper.BindTextureMatrix(this._bumpTexture, uniformBuffer, "clearCoatBump");
+
+                if (scene._mirroredCameraPosition) {
+                    uniformBuffer.updateFloat2("vClearCoatTangentSpaceParams", invertNormalMapX ? 1.0 : -1.0, invertNormalMapY ? 1.0 : -1.0);
+                } else {
+                    uniformBuffer.updateFloat2("vClearCoatTangentSpaceParams", invertNormalMapX ? -1.0 : 1.0, invertNormalMapY ? -1.0 : 1.0);
+                }
+            }
+
+            // Clear Coat
+            uniformBuffer.updateFloat2("vClearCoatParams", this.intensity, this.roughness);
+        }
+
+        // Textures
+        if (scene.texturesEnabled) {
+            if (this._texture && MaterialFlags.ClearCoatTextureEnabled) {
+                uniformBuffer.setTexture("clearCoatSampler", this._texture);
+            }
+
+            if (this._bumpTexture && engine.getCaps().standardDerivatives && MaterialFlags.ClearCoatBumpTextureEnabled && !disableBumpMap) {
+                uniformBuffer.setTexture("clearCoatBumpSampler", this._bumpTexture);
+            }
+        }
+    }
+
+    /**
+     * Checks to see if a texture is used in the material.
+     * @param texture - Base texture to use.
+     * @returns - Boolean specifying if a texture is used in the material.
+     */
+    public hasTexture(texture: BaseTexture): boolean {
+        if (this._texture === texture) {
+            return true;
+        }
+
+        if (this._bumpTexture === texture) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns an array of the actively used textures.
+     * @param activeTextures Array of BaseTextures
+     */
+    public getActiveTextures(activeTextures: BaseTexture[]): void {
+        if (this._texture) {
+            activeTextures.push(this._texture);
+        }
+
+        if (this._bumpTexture) {
+            activeTextures.push(this._bumpTexture);
+        }
+    }
+
+    /**
+     * Returns the animatable textures.
+     * @param animatables Array of animatable textures.
+     */
+    public getAnimatables(animatables: IAnimatable[]): void {
+        if (this._texture && this._texture.animations && this._texture.animations.length > 0) {
+            animatables.push(this._texture);
+        }
+
+        if (this._bumpTexture && this._bumpTexture.animations && this._bumpTexture.animations.length > 0) {
+            animatables.push(this._bumpTexture);
+        }
+    }
+
+    /**
+     * Disposes the resources of the material.
+     * @param forceDisposeTextures - Forces the disposal of all textures.
+     */
+    public dispose(forceDisposeTextures?: boolean): void {
+        if (forceDisposeTextures) {
+            if (this._texture) {
+                this._texture.dispose();
+            }
+
+            if (this._bumpTexture) {
+                this._bumpTexture.dispose();
+            }
+        }
+    }
+
+    /**
+    * Get the current class name of the texture useful for serialization or dynamic coding.
+    * @returns "PBRClearCoatConfiguration"
+    */
+   public getClassName(): string {
+        return "PBRClearCoatConfiguration";
+    }
+
+    /**
+     * Makes a duplicate of the current configuration into another one.
+     * @param clearCoatconfiguration define the config where to copy the info
+     */
+    public copyTo(clearCoatconfiguration: PBRClearCoatConfiguration): void {
+        SerializationHelper.Clone(() => clearCoatconfiguration, this);
+    }
+
+    /**
+     * Serializes this clear coat configuration.
+     * @returns - An object with the serialized config.
+     */
+    public serialize(): any {
+        return SerializationHelper.Serialize(this);
+    }
+
+    /**
+     * Parses a Clear Coat Configuration from a serialized object.
+     * @param source - Serialized object.
+     */
+    public parse(source: any): void {
+        SerializationHelper.Parse(() => this, source, null);
+    }
+
+    /**
+     * Add fallbacks to the effect fallbacks list.
+     * @param defines defines the Base texture to use.
+     * @param fallbacks defines the current fallback list.
+     * @param currentRank defines the current fallback rank.
+     * @returns the new fallback rank.
+     */
+    public static AddFallbacks(defines: IMaterialClearCoatDefines, fallbacks: EffectFallbacks, currentRank: number): number {
+        if (defines.CLEARCOAT_BUMP) {
+            fallbacks.addFallback(currentRank++, "CLEARCOAT_BUMP");
+        }
+        if (defines.CLEARCOAT) {
+            fallbacks.addFallback(currentRank++, "CLEARCOAT");
+        }
+        return currentRank;
+    }
+
+    /**
+     * Add the required uniforms to the current list.
+     * @param uniforms defines the current uniform list.
+     */
+    public static AddUniforms(uniforms: string[]): void {
+        uniforms.push("vClearCoatTangentSpaceParams", "vClearCoatParams",
+            "clearCoatMatrix", "clearCoatBumpMatrix",
+            "vClearCoatInfos", "vClearCoatBumpInfos");
+    }
+
+    /**
+     * Add the required samplers to the current list.
+     * @param samplers defines the current sampler list.
+     */
+    public static AddSamplers(samplers: string[]): void {
+        samplers.push("clearCoatSampler", "clearCoatBumpSampler");
+    }
+
+    /**
+     * Add the required uniforms to the current buffer.
+     * @param uniformBuffer defines the current uniform buffer.
+     */
+    public static PrepareUniformBuffer(uniformBuffer: UniformBuffer): void {
+        uniformBuffer.addUniform("vClearCoatParams", 2);
+        uniformBuffer.addUniform("vClearCoatInfos", 2);
+        uniformBuffer.addUniform("clearCoatMatrix", 16);
+        uniformBuffer.addUniform("vClearCoatBumpInfos", 2);
+        uniformBuffer.addUniform("vClearCoatTangentSpaceParams", 2);
+        uniformBuffer.addUniform("clearCoatBumpMatrix", 16);
+    }
+}

+ 10 - 108
src/Materials/PBR/pbrMaterial.ts

@@ -683,113 +683,6 @@ export class PBRMaterial extends PBRBaseMaterial {
     }
     }
 
 
     /**
     /**
-     * Returns an array of the actively used textures.
-     * @returns - Array of BaseTextures
-     */
-    public getActiveTextures(): BaseTexture[] {
-        var activeTextures = super.getActiveTextures();
-
-        if (this._albedoTexture) {
-            activeTextures.push(this._albedoTexture);
-        }
-
-        if (this._ambientTexture) {
-            activeTextures.push(this._ambientTexture);
-        }
-
-        if (this._opacityTexture) {
-            activeTextures.push(this._opacityTexture);
-        }
-
-        if (this._reflectionTexture) {
-            activeTextures.push(this._reflectionTexture);
-        }
-
-        if (this._emissiveTexture) {
-            activeTextures.push(this._emissiveTexture);
-        }
-
-        if (this._reflectivityTexture) {
-            activeTextures.push(this._reflectivityTexture);
-        }
-
-        if (this._metallicTexture) {
-            activeTextures.push(this._metallicTexture);
-        }
-
-        if (this._microSurfaceTexture) {
-            activeTextures.push(this._microSurfaceTexture);
-        }
-
-        if (this._bumpTexture) {
-            activeTextures.push(this._bumpTexture);
-        }
-
-        if (this._lightmapTexture) {
-            activeTextures.push(this._lightmapTexture);
-        }
-
-        if (this._refractionTexture) {
-            activeTextures.push(this._refractionTexture);
-        }
-
-        return activeTextures;
-    }
-
-    /**
-     * Checks to see if a texture is used in the material.
-     * @param texture - Base texture to use.
-     * @returns - Boolean specifying if a texture is used in the material.
-     */
-    public hasTexture(texture: BaseTexture): boolean {
-        if (super.hasTexture(texture)) {
-            return true;
-        }
-
-        if (this._albedoTexture === texture) {
-            return true;
-        }
-
-        if (this._ambientTexture === texture) {
-            return true;
-        }
-
-        if (this._opacityTexture === texture) {
-            return true;
-        }
-
-        if (this._reflectionTexture === texture) {
-            return true;
-        }
-
-        if (this._reflectivityTexture === texture) {
-            return true;
-        }
-
-        if (this._metallicTexture === texture) {
-            return true;
-        }
-
-        if (this._microSurfaceTexture === texture) {
-            return true;
-        }
-
-        if (this._bumpTexture === texture) {
-            return true;
-        }
-
-        if (this._lightmapTexture === texture) {
-            return true;
-        }
-
-        if (this._refractionTexture === texture) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
      * Makes a duplicate of the current material.
      * Makes a duplicate of the current material.
      * @param name - name to use for the new material.
      * @param name - name to use for the new material.
      */
      */
@@ -799,6 +692,8 @@ export class PBRMaterial extends PBRBaseMaterial {
         clone.id = name;
         clone.id = name;
         clone.name = name;
         clone.name = name;
 
 
+        this.clearCoat.copyTo(clone.clearCoat);
+
         return clone;
         return clone;
     }
     }
 
 
@@ -809,6 +704,9 @@ export class PBRMaterial extends PBRBaseMaterial {
     public serialize(): any {
     public serialize(): any {
         var serializationObject = SerializationHelper.Serialize(this);
         var serializationObject = SerializationHelper.Serialize(this);
         serializationObject.customType = "BABYLON.PBRMaterial";
         serializationObject.customType = "BABYLON.PBRMaterial";
+
+        serializationObject.clearCoat = this.clearCoat.serialize();
+
         return serializationObject;
         return serializationObject;
     }
     }
 
 
@@ -821,7 +719,11 @@ export class PBRMaterial extends PBRBaseMaterial {
      * @returns - PBRMaterial
      * @returns - PBRMaterial
      */
      */
     public static Parse(source: any, scene: Scene, rootUrl: string): PBRMaterial {
     public static Parse(source: any, scene: Scene, rootUrl: string): PBRMaterial {
-        return SerializationHelper.Parse(() => new PBRMaterial(source.name, scene), source, scene, rootUrl);
+        const material = SerializationHelper.Parse(() => new PBRMaterial(source.name, scene), source, scene, rootUrl);
+        if (source.clearCoat) {
+            material.clearCoat.parse(source.clearCoat);
+        }
+        return material;
     }
     }
 }
 }
 
 

+ 10 - 39
src/Materials/PBR/pbrMetallicRoughnessMaterial.ts

@@ -80,44 +80,6 @@ export class PBRMetallicRoughnessMaterial extends PBRBaseSimpleMaterial {
     }
     }
 
 
     /**
     /**
-     * Return the active textures of the material.
-     */
-    public getActiveTextures(): BaseTexture[] {
-        var activeTextures = super.getActiveTextures();
-
-        if (this.baseTexture) {
-            activeTextures.push(this.baseTexture);
-        }
-
-        if (this.metallicRoughnessTexture) {
-            activeTextures.push(this.metallicRoughnessTexture);
-        }
-
-        return activeTextures;
-    }
-
-    /**
-     * Checks to see if a texture is used in the material.
-     * @param texture - Base texture to use.
-     * @returns - Boolean specifying if a texture is used in the material.
-     */
-    public hasTexture(texture: BaseTexture): boolean {
-        if (super.hasTexture(texture)) {
-            return true;
-        }
-
-        if (this.baseTexture === texture) {
-            return true;
-        }
-
-        if (this.metallicRoughnessTexture === texture) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
      * Makes a duplicate of the current material.
      * Makes a duplicate of the current material.
      * @param name - name to use for the new material.
      * @param name - name to use for the new material.
      */
      */
@@ -127,6 +89,8 @@ export class PBRMetallicRoughnessMaterial extends PBRBaseSimpleMaterial {
         clone.id = name;
         clone.id = name;
         clone.name = name;
         clone.name = name;
 
 
+        this.clearCoat.copyTo(clone.clearCoat);
+
         return clone;
         return clone;
     }
     }
 
 
@@ -136,6 +100,9 @@ export class PBRMetallicRoughnessMaterial extends PBRBaseSimpleMaterial {
     public serialize(): any {
     public serialize(): any {
         var serializationObject = SerializationHelper.Serialize(this);
         var serializationObject = SerializationHelper.Serialize(this);
         serializationObject.customType = "BABYLON.PBRMetallicRoughnessMaterial";
         serializationObject.customType = "BABYLON.PBRMetallicRoughnessMaterial";
+
+        serializationObject.clearCoat = this.clearCoat.serialize();
+
         return serializationObject;
         return serializationObject;
     }
     }
 
 
@@ -143,7 +110,11 @@ export class PBRMetallicRoughnessMaterial extends PBRBaseSimpleMaterial {
      * Parses a JSON object correponding to the serialize function.
      * Parses a JSON object correponding to the serialize function.
      */
      */
     public static Parse(source: any, scene: Scene, rootUrl: string): PBRMetallicRoughnessMaterial {
     public static Parse(source: any, scene: Scene, rootUrl: string): PBRMetallicRoughnessMaterial {
-        return SerializationHelper.Parse(() => new PBRMetallicRoughnessMaterial(source.name, scene), source, scene, rootUrl);
+        const material = SerializationHelper.Parse(() => new PBRMetallicRoughnessMaterial(source.name, scene), source, scene, rootUrl);
+        if (source.clearCoat) {
+            material.clearCoat.parse(source.clearCoat);
+        }
+        return material;
     }
     }
 }
 }
 
 

+ 10 - 39
src/Materials/PBR/pbrSpecularGlossinessMaterial.ts

@@ -70,44 +70,6 @@ export class PBRSpecularGlossinessMaterial extends PBRBaseSimpleMaterial {
     }
     }
 
 
     /**
     /**
-     * Return the active textures of the material.
-     */
-    public getActiveTextures(): BaseTexture[] {
-        var activeTextures = super.getActiveTextures();
-
-        if (this.diffuseTexture) {
-            activeTextures.push(this.diffuseTexture);
-        }
-
-        if (this.specularGlossinessTexture) {
-            activeTextures.push(this.specularGlossinessTexture);
-        }
-
-        return activeTextures;
-    }
-
-    /**
-     * Checks to see if a texture is used in the material.
-     * @param texture - Base texture to use.
-     * @returns - Boolean specifying if a texture is used in the material.
-     */
-    public hasTexture(texture: BaseTexture): boolean {
-        if (super.hasTexture(texture)) {
-            return true;
-        }
-
-        if (this.diffuseTexture === texture) {
-            return true;
-        }
-
-        if (this.specularGlossinessTexture === texture) {
-            return true;
-        }
-
-        return false;
-    }
-
-    /**
      * Makes a duplicate of the current material.
      * Makes a duplicate of the current material.
      * @param name - name to use for the new material.
      * @param name - name to use for the new material.
      */
      */
@@ -117,6 +79,8 @@ export class PBRSpecularGlossinessMaterial extends PBRBaseSimpleMaterial {
         clone.id = name;
         clone.id = name;
         clone.name = name;
         clone.name = name;
 
 
+        this.clearCoat.copyTo(clone.clearCoat);
+
         return clone;
         return clone;
     }
     }
 
 
@@ -126,6 +90,9 @@ export class PBRSpecularGlossinessMaterial extends PBRBaseSimpleMaterial {
     public serialize(): any {
     public serialize(): any {
         var serializationObject = SerializationHelper.Serialize(this);
         var serializationObject = SerializationHelper.Serialize(this);
         serializationObject.customType = "BABYLON.PBRSpecularGlossinessMaterial";
         serializationObject.customType = "BABYLON.PBRSpecularGlossinessMaterial";
+
+        serializationObject.clearCoat = this.clearCoat.serialize();
+
         return serializationObject;
         return serializationObject;
     }
     }
 
 
@@ -133,7 +100,11 @@ export class PBRSpecularGlossinessMaterial extends PBRBaseSimpleMaterial {
      * Parses a JSON object correponding to the serialize function.
      * Parses a JSON object correponding to the serialize function.
      */
      */
     public static Parse(source: any, scene: Scene, rootUrl: string): PBRSpecularGlossinessMaterial {
     public static Parse(source: any, scene: Scene, rootUrl: string): PBRSpecularGlossinessMaterial {
-        return SerializationHelper.Parse(() => new PBRSpecularGlossinessMaterial(source.name, scene), source, scene, rootUrl);
+        const material = SerializationHelper.Parse(() => new PBRSpecularGlossinessMaterial(source.name, scene), source, scene, rootUrl);
+        if (source.clearCoat) {
+            material.clearCoat.parse(source.clearCoat);
+        }
+        return material;
     }
     }
 }
 }
 
 

+ 32 - 0
src/Materials/materialFlags.ts

@@ -181,4 +181,36 @@ export class MaterialFlags {
         this._FresnelEnabled = value;
         this._FresnelEnabled = value;
         Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_FresnelDirtyFlag);
         Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_FresnelDirtyFlag);
     }
     }
+
+    private static _ClearCoatTextureEnabled = true;
+    /**
+     * Are clear coat textures enabled in the application.
+     */
+    public static get ClearCoatTextureEnabled(): boolean {
+        return this._ClearCoatTextureEnabled;
+    }
+    public static set ClearCoatTextureEnabled(value: boolean) {
+        if (this._ClearCoatTextureEnabled === value) {
+            return;
+        }
+
+        this._ClearCoatTextureEnabled = value;
+        Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+    }
+
+    private static _ClearCoatBumpTextureEnabled = true;
+    /**
+     * Are clear coat bump textures enabled in the application.
+     */
+    public static get ClearCoatBumpTextureEnabled(): boolean {
+        return this._ClearCoatBumpTextureEnabled;
+    }
+    public static set ClearCoatBumpTextureEnabled(value: boolean) {
+        if (this._ClearCoatBumpTextureEnabled === value) {
+            return;
+        }
+
+        this._ClearCoatBumpTextureEnabled = value;
+        Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+    }
 }
 }

+ 6 - 6
src/Shaders/ShadersInclude/bumpFragment.fx

@@ -25,10 +25,10 @@
 #endif
 #endif
 
 
 #ifdef BUMP
 #ifdef BUMP
-#ifdef OBJECTSPACE_NORMALMAP
-	normalW = normalize(texture2D(bumpSampler, vBumpUV).xyz  * 2.0 - 1.0);
-	normalW = normalize(mat3(normalMatrix) * normalW);	
-#else
-	normalW = perturbNormal(TBN, vBumpUV + uvOffset);
-#endif
+	#ifdef OBJECTSPACE_NORMALMAP
+		normalW = normalize(texture2D(bumpSampler, vBumpUV).xyz  * 2.0 - 1.0);
+		normalW = normalize(mat3(normalMatrix) * normalW);	
+	#else
+		normalW = perturbNormal(TBN, vBumpUV + uvOffset);
+	#endif
 #endif
 #endif

+ 87 - 73
src/Shaders/ShadersInclude/bumpFragmentFunctions.fx

@@ -1,22 +1,26 @@
-#ifdef BUMP
-	#if BUMPDIRECTUV == 1
-		#define vBumpUV vMainUV1
-	#elif BUMPDIRECTUV == 2
-		#define vBumpUV vMainUV2
-	#else
-		varying vec2 vBumpUV;
+#if defined(BUMP) || defined(CLEARCOAT_BUMP)
+	#if defined(TANGENT) && defined(NORMAL) 
+		varying mat3 vTBN;
 	#endif
 	#endif
-	uniform sampler2D bumpSampler;
-#if defined(TANGENT) && defined(NORMAL) 
-	varying mat3 vTBN;
-#endif
 
 
-#ifdef OBJECTSPACE_NORMALMAP
-uniform mat4 normalMatrix;
-#endif
+	#ifdef OBJECTSPACE_NORMALMAP
+		uniform mat4 normalMatrix;
+	#endif
+
+	vec3 perturbNormal(mat3 cotangentFrame, vec2 uv, sampler2D textureSampler, float scale)
+	{
+		vec3 map = texture2D(textureSampler, uv).xyz;
+		map = map * 2.0 - 1.0;
+
+		#ifdef NORMALXYSCALE
+			map = normalize(map * vec3(scale, scale, 1.0));
+		#endif
+
+		return normalize(cotangentFrame * map);
+	}
 
 
 	// Thanks to http://www.thetenthplanet.de/archives/1180
 	// Thanks to http://www.thetenthplanet.de/archives/1180
-	mat3 cotangent_frame(vec3 normal, vec3 p, vec2 uv)
+	mat3 cotangent_frame(vec3 normal, vec3 p, vec2 uv, vec2 tangentSpaceParams)
 	{
 	{
 		// flip the uv for the backface
 		// flip the uv for the backface
 		uv = gl_FrontFacing ? uv : -uv;
 		uv = gl_FrontFacing ? uv : -uv;
@@ -34,83 +38,93 @@ uniform mat4 normalMatrix;
 		vec3 bitangent = dp2perp * duv1.y + dp1perp * duv2.y;
 		vec3 bitangent = dp2perp * duv1.y + dp1perp * duv2.y;
 
 
 		// invert the tangent/bitangent if requested
 		// invert the tangent/bitangent if requested
-		tangent *= vTangentSpaceParams.x;
-		bitangent *= vTangentSpaceParams.y;
+		tangent *= tangentSpaceParams.x;
+		bitangent *= tangentSpaceParams.y;
 
 
 		// construct a scale-invariant frame
 		// construct a scale-invariant frame
 		float invmax = inversesqrt(max(dot(tangent, tangent), dot(bitangent, bitangent)));
 		float invmax = inversesqrt(max(dot(tangent, tangent), dot(bitangent, bitangent)));
 		return mat3(tangent * invmax, bitangent * invmax, normal);
 		return mat3(tangent * invmax, bitangent * invmax, normal);
 	}
 	}
+#endif
+
+#ifdef BUMP
+	#if BUMPDIRECTUV == 1
+		#define vBumpUV vMainUV1
+	#elif BUMPDIRECTUV == 2
+		#define vBumpUV vMainUV2
+	#else
+		varying vec2 vBumpUV;
+	#endif
+	uniform sampler2D bumpSampler;
 
 
 	vec3 perturbNormal(mat3 cotangentFrame, vec2 uv)
 	vec3 perturbNormal(mat3 cotangentFrame, vec2 uv)
 	{
 	{
-		vec3 map = texture2D(bumpSampler, uv).xyz;
-		map = map * 2.0 - 1.0;
-
-		#ifdef NORMALXYSCALE
-			map = normalize(map * vec3(vBumpInfos.y, vBumpInfos.y, 1.0));
-		#endif
+		return perturbNormal(cotangentFrame, uv, bumpSampler, vBumpInfos.y);
+	}
 
 
-		return normalize(cotangentFrame * map);
+	// Thanks to http://www.thetenthplanet.de/archives/1180
+	mat3 cotangent_frame(vec3 normal, vec3 p, vec2 uv)
+	{
+		return cotangent_frame(normal, p, uv, vTangentSpaceParams);
 	}
 	}
+#endif
+
+#if defined(BUMP) && defined(PARALLAX)
+	const float minSamples = 4.;
+	const float maxSamples = 15.;
+	const int iMaxSamples = 15;
 
 
-	#ifdef PARALLAX
-		const float minSamples = 4.;
-		const float maxSamples = 15.;
-		const int iMaxSamples = 15;
+	// http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/a-closer-look-at-parallax-occlusion-mapping-r3262
+	vec2 parallaxOcclusion(vec3 vViewDirCoT, vec3 vNormalCoT, vec2 texCoord, float parallaxScale) {
 
 
-		// http://www.gamedev.net/page/resources/_/technical/graphics-programming-and-theory/a-closer-look-at-parallax-occlusion-mapping-r3262
-		vec2 parallaxOcclusion(vec3 vViewDirCoT, vec3 vNormalCoT, vec2 texCoord, float parallaxScale) {
+		float parallaxLimit = length(vViewDirCoT.xy) / vViewDirCoT.z;
+		parallaxLimit *= parallaxScale;
+		vec2 vOffsetDir = normalize(vViewDirCoT.xy);
+		vec2 vMaxOffset = vOffsetDir * parallaxLimit;
+		float numSamples = maxSamples + (dot(vViewDirCoT, vNormalCoT) * (minSamples - maxSamples));
+		float stepSize = 1.0 / numSamples;
 
 
-			float parallaxLimit = length(vViewDirCoT.xy) / vViewDirCoT.z;
-			parallaxLimit *= parallaxScale;
-			vec2 vOffsetDir = normalize(vViewDirCoT.xy);
-			vec2 vMaxOffset = vOffsetDir * parallaxLimit;
-			float numSamples = maxSamples + (dot(vViewDirCoT, vNormalCoT) * (minSamples - maxSamples));
-			float stepSize = 1.0 / numSamples;
+		// Initialize the starting view ray height and the texture offsets.
+		float currRayHeight = 1.0;
+		vec2 vCurrOffset = vec2(0, 0);
+		vec2 vLastOffset = vec2(0, 0);
 
 
-			// Initialize the starting view ray height and the texture offsets.
-			float currRayHeight = 1.0;
-			vec2 vCurrOffset = vec2(0, 0);
-			vec2 vLastOffset = vec2(0, 0);
+		float lastSampledHeight = 1.0;
+		float currSampledHeight = 1.0;
 
 
-			float lastSampledHeight = 1.0;
-			float currSampledHeight = 1.0;
+		for (int i = 0; i < iMaxSamples; i++)
+		{
+			currSampledHeight = texture2D(bumpSampler, vBumpUV + vCurrOffset).w;
 
 
-			for (int i = 0; i < iMaxSamples; i++)
+			// Test if the view ray has intersected the surface.
+			if (currSampledHeight > currRayHeight)
 			{
 			{
-				currSampledHeight = texture2D(bumpSampler, vBumpUV + vCurrOffset).w;
-
-				// Test if the view ray has intersected the surface.
-				if (currSampledHeight > currRayHeight)
-				{
-					float delta1 = currSampledHeight - currRayHeight;
-					float delta2 = (currRayHeight + stepSize) - lastSampledHeight;
-					float ratio = delta1 / (delta1 + delta2);
-					vCurrOffset = (ratio)* vLastOffset + (1.0 - ratio) * vCurrOffset;
-
-					// Force the exit of the loop
-					break;
-				}
-				else
-				{
-					currRayHeight -= stepSize;
-					vLastOffset = vCurrOffset;
-					vCurrOffset += stepSize * vMaxOffset;
-
-					lastSampledHeight = currSampledHeight;
-				}
+				float delta1 = currSampledHeight - currRayHeight;
+				float delta2 = (currRayHeight + stepSize) - lastSampledHeight;
+				float ratio = delta1 / (delta1 + delta2);
+				vCurrOffset = (ratio)* vLastOffset + (1.0 - ratio) * vCurrOffset;
+
+				// Force the exit of the loop
+				break;
 			}
 			}
+			else
+			{
+				currRayHeight -= stepSize;
+				vLastOffset = vCurrOffset;
+				vCurrOffset += stepSize * vMaxOffset;
 
 
-			return vCurrOffset;
+				lastSampledHeight = currSampledHeight;
+			}
 		}
 		}
 
 
-		vec2 parallaxOffset(vec3 viewDir, float heightScale)
-		{
-			// calculate amount of offset for Parallax Mapping With Offset Limiting
-			float height = texture2D(bumpSampler, vBumpUV).w;
-			vec2 texCoordOffset = heightScale * viewDir.xy * height;
-			return -texCoordOffset;
-		}
-	#endif
+		return vCurrOffset;
+	}
+
+	vec2 parallaxOffset(vec3 viewDir, float heightScale)
+	{
+		// calculate amount of offset for Parallax Mapping With Offset Limiting
+		float height = texture2D(bumpSampler, vBumpUV).w;
+		vec2 texCoordOffset = heightScale * viewDir.xy * height;
+		return -texCoordOffset;
+	}
 #endif
 #endif

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

@@ -1,4 +1,4 @@
-#if defined(BUMP) || defined(PARALLAX)
+#if defined(BUMP) || defined(PARALLAX) || defined(CLEARCOAT_BUMP)
 	#if defined(TANGENT) && defined(NORMAL)
 	#if defined(TANGENT) && defined(NORMAL)
 		vec3 tbnNormal = normalize(normalUpdated);
 		vec3 tbnNormal = normalize(normalUpdated);
 		vec3 tbnTangent = normalize(tangentUpdated.xyz);
 		vec3 tbnTangent = normalize(tangentUpdated.xyz);

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

@@ -1,4 +1,4 @@
-#if defined(BUMP) || defined(PARALLAX)
+#if defined(BUMP) || defined(PARALLAX) || defined(CLEARCOAT_BUMP)
 	#if defined(TANGENT) && defined(NORMAL) 
 	#if defined(TANGENT) && defined(NORMAL) 
 		varying mat3 vTBN;
 		varying mat3 vTBN;
 	#endif
 	#endif

+ 75 - 23
src/Shaders/ShadersInclude/lightFragment.fx

@@ -3,42 +3,84 @@
         //No light calculation
         //No light calculation
     #else
     #else
         #ifdef PBR
         #ifdef PBR
+            // Compute Pre Lighting infos
             #ifdef SPOTLIGHT{X}
             #ifdef SPOTLIGHT{X}
-                spotInfo = computeSpotLightingInfo(light{X}.vLightData);
+                preInfo = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW);
+            #elif defined(POINTLIGHT{X})
+                preInfo = computePointAndSpotPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW);
+            #elif defined(HEMILIGHT{X})
+                preInfo = computeHemisphericPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW);
+            #elif defined(DIRLIGHT{X})
+                preInfo = computeDirectionalPreLightingInfo(light{X}.vLightData, viewDirectionW, normalW);
+            #endif
+
+            preInfo.NdotV = NdotV;
 
 
+            // Compute Attenuation infos
+            #ifdef SPOTLIGHT{X}
                 #ifdef LIGHT_FALLOFF_GLTF{X}
                 #ifdef LIGHT_FALLOFF_GLTF{X}
-                    spotInfo.attenuation = computeDistanceLightFalloff_GLTF(spotInfo.lightDistanceSquared, light{X}.vLightFalloff.y);
-                    spotInfo.attenuation *= computeDirectionalLightFalloff_GLTF(light{X}.vLightDirection.xyz, spotInfo.directionToLightCenterW, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w);
+                    preInfo.attenuation = computeDistanceLightFalloff_GLTF(preInfo.lightDistanceSquared, light{X}.vLightFalloff.y);
+                    preInfo.attenuation *= computeDirectionalLightFalloff_GLTF(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w);
                 #elif defined(LIGHT_FALLOFF_PHYSICAL{X})
                 #elif defined(LIGHT_FALLOFF_PHYSICAL{X})
-                    spotInfo.attenuation = computeDistanceLightFalloff_Physical(spotInfo.lightDistanceSquared);
-                    spotInfo.attenuation *= computeDirectionalLightFalloff_Physical(light{X}.vLightDirection.xyz, spotInfo.directionToLightCenterW, light{X}.vLightDirection.w);
+                    preInfo.attenuation = computeDistanceLightFalloff_Physical(preInfo.lightDistanceSquared);
+                    preInfo.attenuation *= computeDirectionalLightFalloff_Physical(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w);
                 #elif defined(LIGHT_FALLOFF_STANDARD{X})
                 #elif defined(LIGHT_FALLOFF_STANDARD{X})
-                    spotInfo.attenuation = computeDistanceLightFalloff_Standard(spotInfo.lightOffset, light{X}.vLightFalloff.x);
-                    spotInfo.attenuation *= computeDirectionalLightFalloff_Standard(light{X}.vLightDirection.xyz, spotInfo.directionToLightCenterW, light{X}.vLightDirection.w, light{X}.vLightData.w);
+                    preInfo.attenuation = computeDistanceLightFalloff_Standard(preInfo.lightOffset, light{X}.vLightFalloff.x);
+                    preInfo.attenuation *= computeDirectionalLightFalloff_Standard(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w, light{X}.vLightData.w);
                 #else
                 #else
-                    spotInfo.attenuation = computeDistanceLightFalloff(spotInfo.lightOffset, spotInfo.lightDistanceSquared, light{X}.vLightFalloff.x, light{X}.vLightFalloff.y);
-                    spotInfo.attenuation *= computeDirectionalLightFalloff(light{X}.vLightDirection.xyz, spotInfo.directionToLightCenterW, light{X}.vLightDirection.w, light{X}.vLightData.w, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w);
+                    preInfo.attenuation = computeDistanceLightFalloff(preInfo.lightOffset, preInfo.lightDistanceSquared, light{X}.vLightFalloff.x, light{X}.vLightFalloff.y);
+                    preInfo.attenuation *= computeDirectionalLightFalloff(light{X}.vLightDirection.xyz, preInfo.L, light{X}.vLightDirection.w, light{X}.vLightData.w, light{X}.vLightFalloff.z, light{X}.vLightFalloff.w);
                 #endif
                 #endif
-
-                info = computeSpotLighting(spotInfo, viewDirectionW, normalW, light{X}.vLightDirection, light{X}.vLightDiffuse.rgb, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR0, specularEnvironmentR90, geometricRoughnessFactor, NdotL);
             #elif defined(POINTLIGHT{X})
             #elif defined(POINTLIGHT{X})
-                pointInfo = computePointLightingInfo(light{X}.vLightData);
-
                 #ifdef LIGHT_FALLOFF_GLTF{X}
                 #ifdef LIGHT_FALLOFF_GLTF{X}
-                    pointInfo.attenuation = computeDistanceLightFalloff_GLTF(pointInfo.lightDistanceSquared, light{X}.vLightFalloff.y);
+                    preInfo.attenuation = computeDistanceLightFalloff_GLTF(preInfo.lightDistanceSquared, light{X}.vLightFalloff.y);
                 #elif defined(LIGHT_FALLOFF_PHYSICAL{X})
                 #elif defined(LIGHT_FALLOFF_PHYSICAL{X})
-                    pointInfo.attenuation = computeDistanceLightFalloff_Physical(pointInfo.lightDistanceSquared);
+                    preInfo.attenuation = computeDistanceLightFalloff_Physical(preInfo.lightDistanceSquared);
                 #elif defined(LIGHT_FALLOFF_STANDARD{X})
                 #elif defined(LIGHT_FALLOFF_STANDARD{X})
-                    pointInfo.attenuation = computeDistanceLightFalloff_Standard(pointInfo.lightOffset, light{X}.vLightFalloff.x);
+                    preInfo.attenuation = computeDistanceLightFalloff_Standard(preInfo.lightOffset, light{X}.vLightFalloff.x);
                 #else
                 #else
-                    pointInfo.attenuation = computeDistanceLightFalloff(pointInfo.lightOffset, pointInfo.lightDistanceSquared, light{X}.vLightFalloff.x, light{X}.vLightFalloff.y);
+                    preInfo.attenuation = computeDistanceLightFalloff(preInfo.lightOffset, preInfo.lightDistanceSquared, light{X}.vLightFalloff.x, light{X}.vLightFalloff.y);
+                #endif
+            #else
+                preInfo.attenuation = 1.0;
+            #endif
+
+            // Simulates Light radius for diffuse and spec term
+            // clear coat is using a dedicated roughness
+            #ifdef HEMILIGHT{X}
+                preInfo.roughness = roughness;
+            #else
+                preInfo.roughness = adjustRoughnessFromLightProperties(roughness, light{X}.vLightDiffuse.a, preInfo.lightDistance);
+            #endif
+
+            // Diffuse contribution
+            #ifdef HEMILIGHT{X}
+                info.diffuse = computeHemisphericDiffuseLighting(preInfo, light{X}.vLightDiffuse.rgb, light{X}.vLightGround);
+            #else
+                info.diffuse = computeDiffuseLighting(preInfo, light{X}.vLightDiffuse.rgb);
+            #endif
+
+            // Specular contribution
+            #ifdef SPECULARTERM
+                info.specular = computeSpecularLighting(preInfo, normalW, specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb);
+            #endif
+
+            // Clear Coat contribution
+            #ifdef CLEARCOAT
+                // Simulates Light radius
+                #ifdef HEMILIGHT{X}
+                    preInfo.roughness = clearCoatRoughness;
+                #else
+                    preInfo.roughness = adjustRoughnessFromLightProperties(clearCoatRoughness, light{X}.vLightDiffuse.a, preInfo.lightDistance);
+                #endif
+
+                info.clearCoat = computeClearCoatLighting(preInfo, clearCoatNormalW, clearCoatAARoughnessFactors.x, clearCoatIntensity, light{X}.vLightDiffuse.rgb);
+
+                // Apply energy conservation on diffuse and specular term.
+                info.diffuse *= info.clearCoat.w;
+                #ifdef SPECULARTERM
+                    info.specular *= info.clearCoat.w * info.clearCoat.w;
                 #endif
                 #endif
-                
-                info = computePointLighting(pointInfo, viewDirectionW, normalW, light{X}.vLightDiffuse.rgb, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR0, specularEnvironmentR90, geometricRoughnessFactor, NdotL);
-            #elif defined(HEMILIGHT{X})
-                info = computeHemisphericLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightGround, roughness, NdotV, specularEnvironmentR0, specularEnvironmentR90, geometricRoughnessFactor, NdotL);
-            #elif defined(DIRLIGHT{X})
-                info = computeDirectionalLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, roughness, NdotV, specularEnvironmentR0, specularEnvironmentR90, geometricRoughnessFactor, NdotL);
             #endif
             #endif
         #else
         #else
             #ifdef SPOTLIGHT{X}
             #ifdef SPOTLIGHT{X}
@@ -49,10 +91,12 @@
                 info = computeLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, glossiness);
                 info = computeLighting(viewDirectionW, normalW, light{X}.vLightData, light{X}.vLightDiffuse.rgb, light{X}.vLightSpecular, light{X}.vLightDiffuse.a, glossiness);
             #endif
             #endif
         #endif
         #endif
+
         #ifdef PROJECTEDLIGHTTEXTURE{X}
         #ifdef PROJECTEDLIGHTTEXTURE{X}
             info.diffuse *= computeProjectionTextureDiffuseLighting(projectionLightSampler{X}, textureProjectionMatrix{X});
             info.diffuse *= computeProjectionTextureDiffuseLighting(projectionLightSampler{X}, textureProjectionMatrix{X});
         #endif
         #endif
     #endif
     #endif
+
     #ifdef SHADOW{X}
     #ifdef SHADOW{X}
         #ifdef SHADOWCLOSEESM{X}
         #ifdef SHADOWCLOSEESM{X}
             #if defined(SHADOWCUBE{X})
             #if defined(SHADOWCUBE{X})
@@ -120,11 +164,19 @@
                     specularBase += info.specular * shadow * lightmapColor;
                     specularBase += info.specular * shadow * lightmapColor;
                 #endif
                 #endif
             #endif
             #endif
+            #ifdef CLEARCOAT
+                #ifndef LIGHTMAPNOSPECULAR{X}
+                    clearCoatBase += info.clearCoat.rgb * shadow * lightmapColor;
+                #endif
+            #endif
         #else
         #else
             diffuseBase += info.diffuse * shadow;
             diffuseBase += info.diffuse * shadow;
             #ifdef SPECULARTERM
             #ifdef SPECULARTERM
                 specularBase += info.specular * shadow;
                 specularBase += info.specular * shadow;
             #endif
             #endif
+            #ifdef CLEARCOAT
+                clearCoatBase += info.clearCoat.rgb * shadow;
+            #endif
         #endif
         #endif
     #endif
     #endif
 #endif
 #endif

+ 90 - 0
src/Shaders/ShadersInclude/pbrFalloffLightingFunctions.fx

@@ -0,0 +1,90 @@
+float computeDistanceLightFalloff_Standard(vec3 lightOffset, float range)
+{
+    return max(0., 1.0 - length(lightOffset) / range);
+}
+
+float computeDistanceLightFalloff_Physical(float lightDistanceSquared)
+{
+    return 1.0 / ((lightDistanceSquared + 0.001));
+}
+
+float computeDistanceLightFalloff_GLTF(float lightDistanceSquared, float inverseSquaredRange)
+{
+    const float minDistanceSquared = 0.01*0.01;
+    float lightDistanceFalloff = 1.0 / (max(lightDistanceSquared, minDistanceSquared));
+
+    float factor = lightDistanceSquared * inverseSquaredRange;
+    float attenuation = clamp(1.0 - factor * factor, 0., 1.);
+    attenuation *= attenuation;
+
+    // Smooth attenuation of the falloff defined by the range.
+    lightDistanceFalloff *= attenuation;
+    
+    return lightDistanceFalloff;
+}
+
+float computeDistanceLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range, float inverseSquaredRange)
+{
+    #ifdef USEPHYSICALLIGHTFALLOFF
+        return computeDistanceLightFalloff_Physical(lightDistanceSquared);
+    #elif defined(USEGLTFLIGHTFALLOFF)
+        return computeDistanceLightFalloff_GLTF(lightDistanceSquared, inverseSquaredRange);
+    #else
+        return computeDistanceLightFalloff_Standard(lightOffset, range);
+    #endif
+}
+
+float computeDirectionalLightFalloff_Standard(vec3 lightDirection, vec3 directionToLightCenterW, float cosHalfAngle, float exponent)
+{
+    float falloff = 0.0;
+
+    float cosAngle = max(0.000000000000001, dot(-lightDirection, directionToLightCenterW));
+    if (cosAngle >= cosHalfAngle)
+    {
+        falloff = max(0., pow(cosAngle, exponent));
+    }
+    
+    return falloff;
+}
+
+float computeDirectionalLightFalloff_Physical(vec3 lightDirection, vec3 directionToLightCenterW, float cosHalfAngle)
+{
+    const float kMinusLog2ConeAngleIntensityRatio = 6.64385618977; // -log2(0.01)
+
+    // Calculate a Spherical Gaussian (von Mises-Fisher distribution, not angle-based Gaussian) such that the peak is in the light direction,
+    // and the value at the nominal cone angle is 1% of the peak. Because we want the distribution to decay from unity (100%)
+    // at the peak direction (dot product = 1) down to 1% at the nominal cone cutoff (dot product = cosAngle) 
+    // the falloff rate expressed in terms of the base-two dot product is therefore -log2(ConeAngleIntensityRatio) / (1.0 - cosAngle).
+    // Note that the distribution is unnormalised in that peak density is unity, rather than the total energy is unity.
+    float concentrationKappa = kMinusLog2ConeAngleIntensityRatio / (1.0 - cosHalfAngle);
+
+    // Evaluate spherical gaussian for light directional falloff for spot light type (note: spot directional falloff; 
+    // not directional light type)
+    vec4 lightDirectionSpreadSG = vec4(-lightDirection * concentrationKappa, -concentrationKappa);
+    float falloff = exp2(dot(vec4(directionToLightCenterW, 1.0), lightDirectionSpreadSG));
+    return falloff;
+}
+
+float computeDirectionalLightFalloff_GLTF(vec3 lightDirection, vec3 directionToLightCenterW, float lightAngleScale, float lightAngleOffset)
+{
+    // On the CPU
+    // float lightAngleScale = 1.0 f / max (0.001f, ( cosInner - cosOuter ));
+    // float lightAngleOffset = -cosOuter * angleScale;
+
+    float cd = dot(-lightDirection, directionToLightCenterW);
+    float falloff = clamp(cd * lightAngleScale + lightAngleOffset, 0., 1.);
+    // smooth the transition
+    falloff *= falloff;
+    return falloff;
+}
+
+float computeDirectionalLightFalloff(vec3 lightDirection, vec3 directionToLightCenterW, float cosHalfAngle, float exponent, float lightAngleScale, float lightAngleOffset)
+{
+    #ifdef USEPHYSICALLIGHTFALLOFF
+        return computeDirectionalLightFalloff_Physical(lightDirection, directionToLightCenterW, cosHalfAngle);
+    #elif defined(USEGLTFLIGHTFALLOFF)
+        return computeDirectionalLightFalloff_GLTF(lightDirection, directionToLightCenterW, lightAngleScale, lightAngleOffset);
+    #else
+        return computeDirectionalLightFalloff_Standard(lightDirection, directionToLightCenterW, cosHalfAngle, exponent);
+    #endif
+}

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

@@ -63,4 +63,20 @@ uniform mat4 view;
 	    uniform vec3 vReflectionPosition;
 	    uniform vec3 vReflectionPosition;
 	    uniform vec3 vReflectionSize; 
 	    uniform vec3 vReflectionSize; 
     #endif
     #endif
+#endif
+
+// Clear Coat
+#ifdef CLEARCOAT
+    uniform vec2 vClearCoatParams;
+
+    #ifdef CLEARCOAT_TEXTURE
+        uniform vec2 vClearCoatInfos;
+        uniform mat4 clearCoatMatrix;
+    #endif
+
+    #ifdef CLEARCOAT_BUMP
+        uniform vec2 vClearCoatBumpInfos;
+        uniform vec2 vClearCoatTangentSpaceParams;
+        uniform mat4 clearCoatBumpMatrix;
+    #endif
 #endif
 #endif

+ 100 - 26
src/Shaders/ShadersInclude/pbrFunctions.fx

@@ -2,10 +2,6 @@
 #define RECIPROCAL_PI2 0.15915494
 #define RECIPROCAL_PI2 0.15915494
 #define FRESNEL_MAXIMUM_ON_ROUGH 0.25
 #define FRESNEL_MAXIMUM_ON_ROUGH 0.25
 
 
-// PBR CUSTOM CONSTANTS
-const float kRougnhessToAlphaScale = 0.1;
-const float kRougnhessToAlphaOffset = 0.29248125;
-
 float convertRoughnessToAverageSlope(float roughness)
 float convertRoughnessToAverageSlope(float roughness)
 {
 {
     // Calculate AlphaG as square of roughness; add epsilon to avoid numerical issues
     // Calculate AlphaG as square of roughness; add epsilon to avoid numerical issues
@@ -14,16 +10,70 @@ float convertRoughnessToAverageSlope(float roughness)
     return alphaG;
     return alphaG;
 }
 }
 
 
+vec2 getAARoughnessFactors(vec3 normalVector) {
+    #ifdef SPECULARAA
+        vec3 nDfdx = dFdx(normalVector.xyz);
+        vec3 nDfdy = dFdy(normalVector.xyz);
+        float slopeSquare = max(dot(nDfdx, nDfdx), dot(nDfdy, nDfdy));
+
+        // Vive analytical lights roughness factor.
+        float geometricRoughnessFactor = pow(clamp(slopeSquare , 0., 1.), 0.333);
+
+        // BJS factor.
+        float geometricAlphaGFactor = sqrt(slopeSquare);
+        // Adapt linear roughness (alphaG) to geometric curvature of the current pixel.
+        // 75% accounts a bit for the bigger tail linked to Gaussian Filtering.
+        geometricAlphaGFactor *= 0.75;
+
+        return vec2(geometricRoughnessFactor, geometricAlphaGFactor);
+    #else
+        return vec2(0.);
+    #endif
+}
+
 // From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
 // From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
-float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
+// Kepp for references
+// float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
+// {
+//     float tanSquared = (1.0 - dot * dot) / (dot * dot);
+//     return 2.0 / (1.0 + sqrt(1.0 + alphaG * alphaG * tanSquared));
+// }
+
+// float smithVisibility_TrowbridgeReitzGGX_Walter(float NdotL, float NdotV, float alphaG)
+// {
+//     float visibility = smithVisibilityG1_TrowbridgeReitzGGX(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGX(NdotV, alphaG);
+//     visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator  integrated in visibility to avoid issues when visibility function changes.
+//     return visibility;
+// }
+
+// From smithVisibilityG1_TrowbridgeReitzGGX * dot / dot to cancel the cook
+// torrance denominator :-)
+float smithVisibilityG1_TrowbridgeReitzGGXFast(float dot, float alphaG)
 {
 {
-    float tanSquared = (1.0 - dot * dot) / (dot * dot);
-    return 2.0 / (1.0 + sqrt(1.0 + alphaG * alphaG * tanSquared));
+    float alphaSquared = alphaG * alphaG;
+    return 1.0 / (dot + sqrt(alphaSquared + (1.0 - alphaSquared) * dot * dot));
 }
 }
 
 
-float smithVisibilityG_TrowbridgeReitzGGX_Walter(float NdotL, float NdotV, float alphaG)
+// From smithVisibilityG1_TrowbridgeReitzGGXFast
+// Appply simplification as all squared root terms are below 1 and squared
+// Ready to be used
+// float smithVisibilityG1_TrowbridgeReitzGGXMobile(float dot, float alphaG)
+// {
+//     return 1.0 / (dot + alpha + (1.0 - alpha) * dot ));
+// }
+
+float smithVisibility_TrowbridgeReitzGGXFast(float NdotL, float NdotV, float alphaG)
 {
 {
-    return smithVisibilityG1_TrowbridgeReitzGGX(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGX(NdotV, alphaG);
+    float visibility = smithVisibilityG1_TrowbridgeReitzGGXFast(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGXFast(NdotV, alphaG);
+    // No Cook Torance Denominator as it is canceled out in the previous form
+    return visibility;
+}
+
+float kelemenVisibility(float VdotH) {
+    // Simplified form integration the cook torrance denminator.
+    // Expanded is nl * nv / vh2 which factor with 1 / (4 * nl * nv)
+    // giving 1 / (4 * vh2))
+    return 0.25 / (VdotH * VdotH); 
 }
 }
 
 
 // Trowbridge-Reitz (GGX)
 // Trowbridge-Reitz (GGX)
@@ -38,9 +88,14 @@ float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
     return a2 / (PI * d * d);
     return a2 / (PI * d * d);
 }
 }
 
 
-vec3 fresnelSchlickGGX(float VdotH, vec3 reflectance0, vec3 reflectance90)
+vec3 fresnelSchlickGGXVec3(float VdotH, vec3 reflectance0, vec3 reflectance90)
 {
 {
-    return reflectance0 + (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotH, 0., 1.), 5.0);
+    return reflectance0 + (reflectance90 - reflectance0) * pow(1.0 - VdotH, 5.0);
+}
+
+float fresnelSchlickGGXFloat(float VdotH, float reflectance0, float reflectance90)
+{
+    return reflectance0 + (reflectance90 - reflectance0) * pow(1.0 - VdotH, 5.0);
 }
 }
 
 
 vec3 fresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
 vec3 fresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
@@ -50,6 +105,20 @@ vec3 fresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectan
     return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
     return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
 }
 }
 
 
+float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
+{
+    // Diffuse fresnel falloff as per Disney principled BRDF, and in the spirit of
+    // of general coupled diffuse/specular models e.g. Ashikhmin Shirley.
+    float diffuseFresnelNV = pow(clamp(1.0 - NdotL, 0.000001, 1.), 5.0);
+    float diffuseFresnelNL = pow(clamp(1.0 - NdotV, 0.000001, 1.), 5.0);
+    float diffuseFresnel90 = 0.5 + 2.0 * VdotH * VdotH * roughness;
+    float fresnel =
+        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNL) *
+        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNV);
+
+    return fresnel / PI;
+}
+
 // Cook Torance Specular computation.
 // Cook Torance Specular computation.
 vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor)
 vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor)
 {
 {
@@ -57,26 +126,31 @@ vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, flo
     float alphaG = convertRoughnessToAverageSlope(roughness);
     float alphaG = convertRoughnessToAverageSlope(roughness);
 
 
     float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
     float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
-    float visibility = smithVisibilityG_TrowbridgeReitzGGX_Walter(NdotL, NdotV, alphaG);
-    visibility /= (4.0 * NdotL * NdotV); // Cook Torance Denominator  integated in viibility to avoid issues when visibility function changes.
-    float specTerm = max(0., visibility * distribution) * NdotL;
+    float visibility = smithVisibility_TrowbridgeReitzGGXFast(NdotL, NdotV, alphaG);
+    float specTerm = max(0., visibility * distribution);
 
 
-    vec3 fresnel = fresnelSchlickGGX(VdotH, reflectance0, reflectance90);
+    vec3 fresnel = fresnelSchlickGGXVec3(VdotH, reflectance0, reflectance90);
     return fresnel * specTerm;
     return fresnel * specTerm;
 }
 }
 
 
-float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
-{
-    // Diffuse fresnel falloff as per Disney principled BRDF, and in the spirit of
-    // of general coupled diffuse/specular models e.g. Ashikhmin Shirley.
-    float diffuseFresnelNV = pow(clamp(1.0 - NdotL, 0.000001, 1.), 5.0);
-    float diffuseFresnelNL = pow(clamp(1.0 - NdotV, 0.000001, 1.), 5.0);
-    float diffuseFresnel90 = 0.5 + 2.0 * VdotH * VdotH * roughness;
-    float fresnel =
-        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNL) *
-        (1.0 + (diffuseFresnel90 - 1.0) * diffuseFresnelNV);
+vec2 computeClearCoatTerm(float NdotH, float VdotH, float clearCoatRoughness, float geometricRoughnessFactor, float clearCoatIntensity) {
+    clearCoatRoughness = max(clearCoatRoughness, geometricRoughnessFactor);
+    float alphaG = convertRoughnessToAverageSlope(clearCoatRoughness);
 
 
-    return fresnel * NdotL / PI;
+    float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
+    float visibility = kelemenVisibility(VdotH);
+    float clearCoatTerm = max(0., visibility * distribution);
+
+    // fo = 4% based on the IOR of a air-polyurethane interface.
+    // the max reflectance is relying on our special trick to prevent weird values on highly diffuse materials.
+    // To let as a configuration if required
+    const float reflectance0 = 0.04;
+    const float reflectance90 = 1.;
+
+    float fresnel = fresnelSchlickGGXFloat(VdotH, reflectance0, reflectance90);
+    fresnel *= clearCoatIntensity;
+    
+    return vec2(fresnel * clearCoatTerm, 1.0 - fresnel);
 }
 }
 
 
 float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
 float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)

+ 0 - 249
src/Shaders/ShadersInclude/pbrLightFunctions.fx

@@ -1,249 +0,0 @@
-// Light Computing
-struct lightingInfo
-{
-    vec3 diffuse;
-    #ifdef SPECULARTERM
-        vec3 specular;
-    #endif
-};
-
-struct pointLightingInfo
-{
-    vec3 lightOffset;
-    float lightDistanceSquared;
-
-    float attenuation;
-};
-
-struct spotLightingInfo
-{
-    vec3 lightOffset;
-    float lightDistanceSquared;
-    vec3 directionToLightCenterW;
-
-    float attenuation;
-};
-
-float computeDistanceLightFalloff_Standard(vec3 lightOffset, float range)
-{
-    return max(0., 1.0 - length(lightOffset) / range);
-}
-
-float computeDistanceLightFalloff_Physical(float lightDistanceSquared)
-{
-    return 1.0 / ((lightDistanceSquared + 0.001));
-}
-
-float computeDistanceLightFalloff_GLTF(float lightDistanceSquared, float inverseSquaredRange)
-{
-    const float minDistanceSquared = 0.01*0.01;
-    float lightDistanceFalloff = 1.0 / (max(lightDistanceSquared, minDistanceSquared));
-
-    float factor = lightDistanceSquared * inverseSquaredRange;
-    float attenuation = clamp(1.0 - factor * factor, 0., 1.);
-    attenuation *= attenuation;
-
-    // Smooth attenuation of the falloff defined by the range.
-    lightDistanceFalloff *= attenuation;
-    
-    return lightDistanceFalloff;
-}
-
-float computeDistanceLightFalloff(vec3 lightOffset, float lightDistanceSquared, float range, float inverseSquaredRange)
-{   
-    #ifdef USEPHYSICALLIGHTFALLOFF
-        return computeDistanceLightFalloff_Physical(lightDistanceSquared);
-    #elif defined(USEGLTFLIGHTFALLOFF)
-        return computeDistanceLightFalloff_GLTF(lightDistanceSquared, inverseSquaredRange);
-    #else
-        return computeDistanceLightFalloff_Standard(lightOffset, range);
-    #endif
-}
-
-float computeDirectionalLightFalloff_Standard(vec3 lightDirection, vec3 directionToLightCenterW, float cosHalfAngle, float exponent)
-{
-    float falloff = 0.0;
-
-    float cosAngle = max(0.000000000000001, dot(-lightDirection, directionToLightCenterW));
-    if (cosAngle >= cosHalfAngle)
-    {
-        falloff = max(0., pow(cosAngle, exponent));
-    }
-    
-    return falloff;
-}
-
-float computeDirectionalLightFalloff_Physical(vec3 lightDirection, vec3 directionToLightCenterW, float cosHalfAngle)
-{
-    const float kMinusLog2ConeAngleIntensityRatio = 6.64385618977; // -log2(0.01)
-
-    // Calculate a Spherical Gaussian (von Mises-Fisher distribution, not angle-based Gaussian) such that the peak is in the light direction,
-    // and the value at the nominal cone angle is 1% of the peak. Because we want the distribution to decay from unity (100%)
-    // at the peak direction (dot product = 1) down to 1% at the nominal cone cutoff (dot product = cosAngle) 
-    // the falloff rate expressed in terms of the base-two dot product is therefore -log2(ConeAngleIntensityRatio) / (1.0 - cosAngle).
-    // Note that the distribution is unnormalised in that peak density is unity, rather than the total energy is unity.
-    float concentrationKappa = kMinusLog2ConeAngleIntensityRatio / (1.0 - cosHalfAngle);
-
-    // Evaluate spherical gaussian for light directional falloff for spot light type (note: spot directional falloff; 
-    // not directional light type)
-    vec4 lightDirectionSpreadSG = vec4(-lightDirection * concentrationKappa, -concentrationKappa);
-    float falloff = exp2(dot(vec4(directionToLightCenterW, 1.0), lightDirectionSpreadSG));
-    return falloff;
-}
-
-float computeDirectionalLightFalloff_GLTF(vec3 lightDirection, vec3 directionToLightCenterW, float lightAngleScale, float lightAngleOffset)
-{
-    // On the CPU
-    // float lightAngleScale = 1.0 f / max (0.001f, ( cosInner - cosOuter ));
-    // float lightAngleOffset = -cosOuter * angleScale;
-
-    float cd = dot(-lightDirection, directionToLightCenterW);
-    float falloff = clamp(cd * lightAngleScale + lightAngleOffset, 0., 1.);
-    // smooth the transition
-    falloff *= falloff;
-    return falloff;
-}
-
-float computeDirectionalLightFalloff(vec3 lightDirection, vec3 directionToLightCenterW, float cosHalfAngle, float exponent, float lightAngleScale, float lightAngleOffset)
-{
-    #ifdef USEPHYSICALLIGHTFALLOFF
-        return computeDirectionalLightFalloff_Physical(lightDirection, directionToLightCenterW, cosHalfAngle);
-    #elif defined(USEGLTFLIGHTFALLOFF)
-        return computeDirectionalLightFalloff_GLTF(lightDirection, directionToLightCenterW, lightAngleScale, lightAngleOffset);
-    #else
-        return computeDirectionalLightFalloff_Standard(lightDirection, directionToLightCenterW, cosHalfAngle, exponent);
-    #endif
-}
-
-pointLightingInfo computePointLightingInfo(vec4 lightData) {
-    pointLightingInfo result;
-
-    result.lightOffset = lightData.xyz - vPositionW;
-    result.lightDistanceSquared = dot(result.lightOffset, result.lightOffset);
-
-    return result;
-}
-
-spotLightingInfo computeSpotLightingInfo(vec4 lightData) {
-    spotLightingInfo result;
-
-    result.lightOffset = lightData.xyz - vPositionW;
-    result.directionToLightCenterW = normalize(result.lightOffset);
-    result.lightDistanceSquared = dot(result.lightOffset, result.lightOffset);
-
-    return result;
-}
-
-lightingInfo computePointLighting(pointLightingInfo info, vec3 viewDirectionW, vec3 vNormal, vec3 diffuseColor,  float lightRadius, float roughness, float NdotV, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, out float NdotL) {
-    lightingInfo result;
-
-    float lightDistance = sqrt(info.lightDistanceSquared);
-    vec3 lightDirection = normalize(info.lightOffset);
-    
-    // Roughness
-    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
-    
-    // diffuse
-    vec3 H = normalize(viewDirectionW + lightDirection);
-    NdotL = clamp(dot(vNormal, lightDirection), 0.00000000001, 1.0);
-    float VdotH = clamp(dot(viewDirectionW, H), 0.0, 1.0);
-
-    float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-    result.diffuse = diffuseTerm * diffuseColor * info.attenuation;
-
-    #ifdef SPECULARTERM
-        // Specular
-        float NdotH = clamp(dot(vNormal, H), 0.000000000001, 1.0);
-
-        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, reflectance0, reflectance90, geometricRoughnessFactor);
-        result.specular = specTerm * diffuseColor * info.attenuation;
-    #endif
-
-    return result;
-}
-
-lightingInfo computeSpotLighting(spotLightingInfo info, vec3 viewDirectionW, vec3 vNormal, vec4 lightDirection, vec3 diffuseColor, float lightRadius, float roughness, float NdotV, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, out float NdotL) {
-    lightingInfo result;
-
-    // Roughness.
-    float lightDistance = sqrt(info.lightDistanceSquared);
-    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
-    
-    // Diffuse
-    vec3 H = normalize(viewDirectionW + info.directionToLightCenterW);
-    NdotL = clamp(dot(vNormal, info.directionToLightCenterW), 0.000000000001, 1.0);
-    float VdotH = clamp(dot(viewDirectionW, H), 0.0, 1.0);
-
-    float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-    result.diffuse = diffuseTerm * diffuseColor * info.attenuation;
-
-    #ifdef SPECULARTERM
-        // Specular
-        float NdotH = clamp(dot(vNormal, H), 0.000000000001, 1.0);
-
-        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, reflectance0, reflectance90, geometricRoughnessFactor);
-        result.specular = specTerm * diffuseColor * info.attenuation;
-    #endif
-
-    return result;
-}
-
-lightingInfo computeDirectionalLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, float lightRadius, float roughness, float NdotV, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, out float NdotL) {
-    lightingInfo result;
-
-    float lightDistance = length(-lightData.xyz);
-    vec3 lightDirection = normalize(-lightData.xyz);
-
-    // Roughness
-    roughness = adjustRoughnessFromLightProperties(roughness, lightRadius, lightDistance);
-    
-    // diffuse
-    vec3 H = normalize(viewDirectionW + lightDirection);
-    NdotL = clamp(dot(vNormal, lightDirection), 0.00000000001, 1.0);
-    float VdotH = clamp(dot(viewDirectionW, H), 0.0, 1.0);
-
-    float diffuseTerm = computeDiffuseTerm(NdotL, NdotV, VdotH, roughness);
-    result.diffuse = diffuseTerm * diffuseColor;
-
-    #ifdef SPECULARTERM
-        // Specular
-        float NdotH = clamp(dot(vNormal, H), 0.000000000001, 1.0);
-
-        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, reflectance0, reflectance90, geometricRoughnessFactor);
-        result.specular = specTerm * diffuseColor;
-    #endif
-
-    return result;
-}
-
-lightingInfo computeHemisphericLighting(vec3 viewDirectionW, vec3 vNormal, vec4 lightData, vec3 diffuseColor, vec3 specularColor, vec3 groundColor, float roughness, float NdotV, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, out float NdotL) {
-    lightingInfo result;
-
-    // Roughness
-    // Do not touch roughness on hemispheric.
-
-    // Diffuse
-    NdotL = dot(vNormal, lightData.xyz) * 0.5 + 0.5;
-    result.diffuse = mix(groundColor, diffuseColor, NdotL);
-
-    #ifdef SPECULARTERM
-        // Specular
-        vec3 lightVectorW = normalize(lightData.xyz);
-        vec3 H = normalize(viewDirectionW + lightVectorW);
-        float NdotH = clamp(dot(vNormal, H), 0.000000000001, 1.0);
-        NdotL = clamp(NdotL, 0.000000000001, 1.0);
-        float VdotH = clamp(dot(viewDirectionW, H), 0.0, 1.0);
-
-        vec3 specTerm = computeSpecularTerm(NdotH, NdotL, NdotV, VdotH, roughness, reflectance0, reflectance90, geometricRoughnessFactor);
-        result.specular = specTerm * diffuseColor;
-    #endif
-
-    return result;
-}
-
-vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, mat4 textureProjectionMatrix){
-	vec4 strq = textureProjectionMatrix * vec4(vPositionW, 1.0);
-	strq /= strq.w;
-	vec3 textureColor = texture2D(projectionLightSampler, strq.xy).rgb;
-	return toLinearSpace(textureColor);
-}

+ 48 - 0
src/Shaders/ShadersInclude/pbrLightingFunctions.fx

@@ -0,0 +1,48 @@
+// Light Results
+struct lightingInfo
+{
+    vec3 diffuse;
+    #ifdef SPECULARTERM
+        vec3 specular;
+    #endif
+    #ifdef CLEARCOAT
+        // xyz contains the clearcoat color.
+        // w contains the 1 - clearcoat fresnel to ease the energy conservation computation.
+        vec4 clearCoat;
+    #endif
+};
+
+vec3 computeHemisphericDiffuseLighting(preLightingInfo info, vec3 lightColor, vec3 groundColor) {
+    return mix(groundColor, lightColor, info.NdotL);
+}
+
+vec3 computeDiffuseLighting(preLightingInfo info, vec3 lightColor) {
+    float diffuseTerm = computeDiffuseTerm(info.NdotL, info.NdotV, info.VdotH, info.roughness);
+    return diffuseTerm * info.attenuation * info.NdotL * lightColor;
+}
+
+vec3 computeSpecularLighting(preLightingInfo info, vec3 normal, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, vec3 lightColor) {
+    float NdotH = clamp(dot(normal, info.H), 0.000000000001, 1.0);
+
+    vec3 specTerm = computeSpecularTerm(NdotH, info.NdotL, info.NdotV, info.VdotH, info.roughness, reflectance0, reflectance90, geometricRoughnessFactor);
+    return specTerm * info.attenuation * info.NdotL * lightColor;
+}
+
+vec4 computeClearCoatLighting(preLightingInfo info, vec3 normalClearCoat, float geometricRoughnessFactor, float clearCoatIntensity, vec3 lightColor) {
+    float NccdotL = clamp(dot(normalClearCoat, info.L), 0.00000000001, 1.0);
+    float NccdotH = clamp(dot(normalClearCoat, info.H), 0.000000000001, 1.0);
+
+    vec2 clearCoatTerm = computeClearCoatTerm(NccdotH, info.VdotH, info.roughness, geometricRoughnessFactor, clearCoatIntensity);
+
+    vec4 result = vec4(0.);
+    result.rgb = clearCoatTerm.x * info.attenuation * NccdotL * lightColor;
+    result.a = clearCoatTerm.y;
+    return result;
+}
+
+vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, mat4 textureProjectionMatrix){
+	vec4 strq = textureProjectionMatrix * vec4(vPositionW, 1.0);
+	strq /= strq.w;
+	vec3 textureColor = texture2D(projectionLightSampler, strq.xy).rgb;
+	return toLinearSpace(textureColor);
+}

+ 69 - 0
src/Shaders/ShadersInclude/pbrPreLightingFunctions.fx

@@ -0,0 +1,69 @@
+// Pre Light Computing
+struct preLightingInfo
+{
+    // Pre Falloff Info
+    vec3 lightOffset;
+    float lightDistanceSquared;
+    float lightDistance;
+
+    // Falloff Info
+    float attenuation;
+
+    // Lighting Info
+    vec3 L;
+    vec3 H;
+    float NdotV;
+    float NdotL;
+    float VdotH;
+    float roughness;
+};
+
+preLightingInfo computePointAndSpotPreLightingInfo(vec4 lightData, vec3 V, vec3 N) {
+    preLightingInfo result;
+
+    // Attenuation data.
+    result.lightOffset = lightData.xyz - vPositionW;
+    result.lightDistanceSquared = dot(result.lightOffset, result.lightOffset);
+
+    // Roughness.
+    result.lightDistance = sqrt(result.lightDistanceSquared);
+
+    // Geometry Data.
+    result.L = normalize(result.lightOffset);
+    result.H = normalize(V + result.L);
+    result.NdotL = clamp(dot(N, result.L), 0.000000000001, 1.0);
+    result.VdotH = clamp(dot(V, result.H), 0.0, 1.0);
+
+    return result;
+}
+
+preLightingInfo computeDirectionalPreLightingInfo(vec4 lightData, vec3 V, vec3 N) {
+    preLightingInfo result;
+
+    // Roughness
+    result.lightDistance = length(-lightData.xyz);
+
+    // Geometry Data.
+    result.L = normalize(-lightData.xyz);
+    result.H = normalize(V + result.L);
+    result.NdotL = clamp(dot(N, result.L), 0.00000000001, 1.0);
+    result.VdotH = clamp(dot(V, result.H), 0.0, 1.0);
+
+    return result;
+}
+
+preLightingInfo computeHemisphericPreLightingInfo(vec4 lightData, vec3 V, vec3 N) {
+    preLightingInfo result;
+
+    // Geometry Data.
+    result.NdotL = dot(N, lightData.xyz) * 0.5 + 0.5;
+    result.NdotL = clamp(result.NdotL, 0.000000000001, 1.0);
+
+    #ifdef SPECULARTERM
+        result.L = normalize(lightData.xyz);
+        result.H = normalize(V + result.L);
+        result.VdotH = clamp(dot(V, result.H), 0.0, 1.0);
+    #endif
+
+    return result;
+}

+ 38 - 31
src/Shaders/ShadersInclude/pbrUboDeclaration.fx

@@ -2,44 +2,51 @@ layout(std140, column_major) uniform;
 
 
 uniform Material
 uniform Material
 {
 {
-	uniform vec2 vAlbedoInfos;
-	uniform vec4 vAmbientInfos;
-	uniform vec2 vOpacityInfos;
-	uniform vec2 vEmissiveInfos;
-	uniform vec2 vLightmapInfos;
-	uniform vec3 vReflectivityInfos;
-	uniform vec2 vMicroSurfaceSamplerInfos;
-	uniform vec4 vRefractionInfos;
-	uniform vec2 vReflectionInfos;
-	uniform vec3 vReflectionPosition;
-	uniform vec3 vReflectionSize;	
-	uniform vec3 vBumpInfos;
-	uniform mat4 albedoMatrix;
-	uniform mat4 ambientMatrix;
-	uniform mat4 opacityMatrix;
-	uniform mat4 emissiveMatrix;
-	uniform mat4 lightmapMatrix;
-	uniform mat4 reflectivityMatrix;
-	uniform mat4 microSurfaceSamplerMatrix;
-	uniform mat4 bumpMatrix;
-	uniform vec2 vTangentSpaceParams;
-	uniform mat4 refractionMatrix;
-	uniform mat4 reflectionMatrix;
+    uniform vec2 vAlbedoInfos;
+    uniform vec4 vAmbientInfos;
+    uniform vec2 vOpacityInfos;
+    uniform vec2 vEmissiveInfos;
+    uniform vec2 vLightmapInfos;
+    uniform vec3 vReflectivityInfos;
+    uniform vec2 vMicroSurfaceSamplerInfos;
+    uniform vec4 vRefractionInfos;
+    uniform vec2 vReflectionInfos;
+    uniform vec3 vReflectionPosition;
+    uniform vec3 vReflectionSize;	
+    uniform vec3 vBumpInfos;
+    uniform mat4 albedoMatrix;
+    uniform mat4 ambientMatrix;
+    uniform mat4 opacityMatrix;
+    uniform mat4 emissiveMatrix;
+    uniform mat4 lightmapMatrix;
+    uniform mat4 reflectivityMatrix;
+    uniform mat4 microSurfaceSamplerMatrix;
+    uniform mat4 bumpMatrix;
+    uniform vec2 vTangentSpaceParams;
+    uniform mat4 refractionMatrix;
+    uniform mat4 reflectionMatrix;
 
 
-	uniform vec3 vReflectionColor;
-	uniform vec4 vAlbedoColor;
-	uniform vec4 vLightingIntensity;
+    uniform vec3 vReflectionColor;
+    uniform vec4 vAlbedoColor;
+    uniform vec4 vLightingIntensity;
 
 
     uniform vec3 vRefractionMicrosurfaceInfos;
     uniform vec3 vRefractionMicrosurfaceInfos;
     uniform vec3 vReflectionMicrosurfaceInfos;
     uniform vec3 vReflectionMicrosurfaceInfos;
 
 
-	uniform vec4 vReflectivityColor;
-	uniform vec3 vEmissiveColor;
+    uniform vec4 vReflectivityColor;
+    uniform vec3 vEmissiveColor;
 
 
-	uniform float pointSize;
+    uniform float pointSize;
+
+    uniform vec2 vClearCoatParams;
+    uniform vec2 vClearCoatInfos;
+    uniform mat4 clearCoatMatrix;
+    uniform vec2 vClearCoatBumpInfos;
+    uniform vec2 vClearCoatTangentSpaceParams;
+    uniform mat4 clearCoatBumpMatrix;
 };
 };
 
 
 uniform Scene {
 uniform Scene {
-	mat4 viewProjection;
-	mat4 view;
+    mat4 viewProjection;
+    mat4 view;
 };
 };

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

@@ -58,3 +58,16 @@ uniform float pointSize;
     uniform mat4 reflectionMatrix;
     uniform mat4 reflectionMatrix;
     uniform vec3 vReflectionMicrosurfaceInfos;
     uniform vec3 vReflectionMicrosurfaceInfos;
 #endif
 #endif
+
+// Clear Coat
+#ifdef CLEARCOAT
+    #ifdef CLEARCOAT_TEXTURE
+        uniform vec2 vClearCoatInfos;
+        uniform mat4 clearCoatMatrix;
+    #endif
+
+    #ifdef CLEARCOAT_BUMP
+        uniform vec2 vClearCoatBumpInfos;
+        uniform mat4 clearCoatBumpMatrix;
+    #endif
+#endif

+ 270 - 37
src/Shaders/pbr.fragment.fx

@@ -1,4 +1,4 @@
-#if defined(BUMP) || !defined(NORMAL) || defined(FORCENORMALFORWARD) || defined(SPECULARAA)
+#if defined(BUMP) || !defined(NORMAL) || defined(FORCENORMALFORWARD) || defined(SPECULARAA) || defined(CLEARCOAT_BUMP)
 #extension GL_OES_standard_derivatives : enable
 #extension GL_OES_standard_derivatives : enable
 #endif
 #endif
 
 
@@ -121,6 +121,30 @@ varying vec4 vColor;
     uniform sampler2D microSurfaceSampler;
     uniform sampler2D microSurfaceSampler;
 #endif
 #endif
 
 
+#ifdef CLEARCOAT
+    #ifdef CLEARCOAT_TEXTURE
+        #if CLEARCOAT_TEXTUREDIRECTUV == 1
+            #define vClearCoatUV vMainUV1
+        #elif CLEARCOAT_TEXTUREDIRECTUV == 2
+            #define vClearCoatUV vMainUV2
+        #else
+            varying vec2 vClearCoatUV;
+        #endif
+        uniform sampler2D clearCoatSampler;
+    #endif
+
+    #ifdef CLEARCOAT_BUMP
+        #if CLEARCOAT_BUMPDIRECTUV == 1
+            #define vClearCoatBumpUV vMainUV1
+        #elif CLEARCOAT_BUMPDIRECTUV == 2
+            #define vClearCoatBumpUV vMainUV2
+        #else
+            varying vec2 vClearCoatBumpUV;
+        #endif
+        uniform sampler2D clearCoatBumpSampler;
+    #endif
+#endif
+
 // Refraction
 // Refraction
 #ifdef REFRACTION
 #ifdef REFRACTION
     #ifdef REFRACTIONMAP_3D
     #ifdef REFRACTIONMAP_3D
@@ -204,7 +228,9 @@ varying vec4 vColor;
 #include<shadowsFragmentFunctions>
 #include<shadowsFragmentFunctions>
 #include<pbrFunctions>
 #include<pbrFunctions>
 #include<harmonicsFunctions>
 #include<harmonicsFunctions>
-#include<pbrLightFunctions>
+#include<pbrPreLightingFunctions>
+#include<pbrFalloffLightingFunctions>
+#include<pbrLightingFunctions>
 
 
 #include<bumpFragmentFunctions>
 #include<bumpFragmentFunctions>
 #include<clipPlaneFragmentDeclaration>
 #include<clipPlaneFragmentDeclaration>
@@ -226,20 +252,13 @@ void main(void) {
     vec3 normalW = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;
     vec3 normalW = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;
 #endif
 #endif
 
 
-#include<bumpFragment>
-
-#ifdef SPECULARAA
-    vec3 nDfdx = dFdx(normalW.xyz);
-    vec3 nDfdy = dFdy(normalW.xyz);
-    float slopeSquare = max(dot(nDfdx, nDfdx), dot(nDfdy, nDfdy));
-    // Vive analytical lights roughness factor.
-    float geometricRoughnessFactor = pow(clamp(slopeSquare , 0., 1.), 0.333);
-    // BJS factor.
-    float geometricAlphaGFactor = sqrt(slopeSquare);
-#else
-    float geometricRoughnessFactor = 0.;
+#ifdef CLEARCOAT
+    // Needs to use the geometric normal before bump for this.
+    vec3 clearCoatNormalW = normalW;
 #endif
 #endif
 
 
+#include<bumpFragment>
+
 #if defined(FORCENORMALFORWARD) && defined(NORMAL)
 #if defined(FORCENORMALFORWARD) && defined(NORMAL)
     vec3 faceNormal = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;
     vec3 faceNormal = normalize(cross(dFdx(vPositionW), dFdy(vPositionW))) * vEyePosition.w;
     #if defined(TWOSIDEDLIGHTING)
     #if defined(TWOSIDEDLIGHTING)
@@ -270,6 +289,10 @@ void main(void) {
     surfaceAlbedo *= vAlbedoInfos.y;
     surfaceAlbedo *= vAlbedoInfos.y;
 #endif
 #endif
 
 
+#ifdef VERTEXCOLOR
+    surfaceAlbedo *= vColor.rgb;
+#endif
+
 // _____________________________ Alpha Information _______________________________
 // _____________________________ Alpha Information _______________________________
 #ifdef OPACITY
 #ifdef OPACITY
     vec4 opacityMap = texture2D(opacitySampler, vOpacityUV + uvOffset);
     vec4 opacityMap = texture2D(opacitySampler, vOpacityUV + uvOffset);
@@ -301,10 +324,6 @@ void main(void) {
 
 
 #include<depthPrePass>
 #include<depthPrePass>
 
 
-#ifdef VERTEXCOLOR
-    surfaceAlbedo *= vColor.rgb;
-#endif
-
 // _____________________________ AO    Information _______________________________
 // _____________________________ AO    Information _______________________________
     vec3 ambientOcclusionColor = vec3(1., 1., 1.);
     vec3 ambientOcclusionColor = vec3(1., 1., 1.);
 
 
@@ -320,8 +339,8 @@ void main(void) {
     vec3 diffuseBase = vec3(1., 1., 1.);
     vec3 diffuseBase = vec3(1., 1., 1.);
 #else
 #else
     // _____________________________ Reflectivity Info _______________________________
     // _____________________________ Reflectivity Info _______________________________
-        float microSurface = vReflectivityColor.a;
-        vec3 surfaceReflectivityColor = vReflectivityColor.rgb;
+    float microSurface = vReflectivityColor.a;
+    vec3 surfaceReflectivityColor = vReflectivityColor.rgb;
 
 
     #ifdef METALLICWORKFLOW
     #ifdef METALLICWORKFLOW
         vec2 metallicRoughness = surfaceReflectivityColor.rg;
         vec2 metallicRoughness = surfaceReflectivityColor.rg;
@@ -429,16 +448,16 @@ void main(void) {
     #endif
     #endif
 
 
     // _____________________________ Compute LODs Fetch ____________________________________
     // _____________________________ Compute LODs Fetch ____________________________________
-        // Compute N dot V.
-        float NdotVUnclamped = dot(normalW, viewDirectionW);
-        float NdotV = clamp(NdotVUnclamped,0., 1.) + 0.00001;
-        float alphaG = convertRoughnessToAverageSlope(roughness);
-
-        #ifdef SPECULARAA
-            // Adapt linear roughness (alphaG) to geometric curvature of the current pixel.
-            // 75% accounts a bit for the bigger tail linked to Gaussian Filtering.
-            alphaG += (0.75 * geometricAlphaGFactor);
-        #endif
+    float NdotVUnclamped = dot(normalW, viewDirectionW);
+    float NdotV = clamp(NdotVUnclamped,0., 1.) + 0.00001;
+    float alphaG = convertRoughnessToAverageSlope(roughness);
+    vec2 AARoughnessFactors = getAARoughnessFactors(normalW.xyz);
+
+    #ifdef SPECULARAA
+        // Adapt linear roughness (alphaG) to geometric curvature of the current pixel.
+        // 75% accounts a bit for the bigger tail linked to Gaussian Filtering.
+        alphaG += AARoughnessFactors.y;
+    #endif
 
 
     // _____________________________ Refraction Info _______________________________________
     // _____________________________ Refraction Info _______________________________________
     #ifdef REFRACTION
     #ifdef REFRACTION
@@ -616,6 +635,132 @@ void main(void) {
         environmentIrradiance *= vReflectionColor.rgb;
         environmentIrradiance *= vReflectionColor.rgb;
     #endif
     #endif
 
 
+    // _____________________________ Clear Coat Information ____________________________
+    #ifdef CLEARCOAT
+        // Clear COAT parameters.
+        float clearCoatIntensity = vClearCoatParams.x;
+        float clearCoatRoughness = vClearCoatParams.y;
+
+        #ifdef CLEARCOAT_TEXTURE
+            vec2 clearCoatMapData = texture2D(clearCoatSampler, vClearCoatUV + uvOffset).rg * vClearCoatInfos.y;
+            clearCoatIntensity *= clearCoatMapData.x;
+            clearCoatRoughness *= clearCoatMapData.y;
+
+            // remapping and linearization of clear coat roughness
+            // Let s see how it ends up in gltf
+            // clearCoatRoughness = mix(0.089, 0.6, clearCoatRoughness);
+        #endif
+
+        #ifdef CLEARCOAT_BUMP
+            #ifdef NORMALXYSCALE
+                float clearCoatNormalScale = 1.0;
+            #else
+                float clearCoatNormalScale = vClearCoatBumpInfos.y;
+            #endif
+
+            #if defined(TANGENT) && defined(NORMAL)
+                mat3 TBNClearCoat = vTBN;
+            #else
+                mat3 TBNClearCoat = cotangent_frame(clearCoatNormalW * clearCoatNormalScale, vPositionW, vClearCoatBumpUV, vClearCoatTangentSpaceParams);
+            #endif
+
+            #ifdef OBJECTSPACE_NORMALMAP
+                clearCoatNormalW = normalize(texture2D(clearCoatBumpSampler, vClearCoatBumpUV + uvOffset).xyz  * 2.0 - 1.0);
+                clearCoatNormalW = normalize(mat3(normalMatrix) * clearCoatNormalW);
+            #else
+                clearCoatNormalW = perturbNormal(TBN, vClearCoatBumpUV + uvOffset, clearCoatBumpSampler, vClearCoatBumpInfos.y);
+            #endif
+        #endif
+
+        #if defined(FORCENORMALFORWARD) && defined(NORMAL)
+            clearCoatNormalW *= sign(dot(clearCoatNormalW, faceNormal));
+        #endif
+
+        #if defined(TWOSIDEDLIGHTING) && defined(NORMAL)
+            clearCoatNormalW = gl_FrontFacing ? clearCoatNormalW : -clearCoatNormalW;
+        #endif
+
+        // Clear Coat AA
+        vec2 clearCoatAARoughnessFactors = getAARoughnessFactors(clearCoatNormalW.xyz);
+
+        // Compute N dot V.
+        float clearCoatNdotVUnclamped = dot(clearCoatNormalW, viewDirectionW);
+        float clearCoatNdotV = clamp(clearCoatNdotVUnclamped,0., 1.) + 0.00001;
+
+        // Clear Coat Reflection
+        #if defined(REFLECTION)
+            float clearCoatAlphaG = convertRoughnessToAverageSlope(clearCoatRoughness);
+
+            #ifdef SPECULARAA
+                // Adapt linear roughness (alphaG) to geometric curvature of the current pixel.
+                // 75% accounts a bit for the bigger tail linked to Gaussian Filtering.
+                clearCoatAlphaG += clearCoatAARoughnessFactors.y;
+            #endif
+
+            vec4 environmentClearCoatRadiance = vec4(0., 0., 0., 0.);
+
+            vec3 clearCoatReflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), clearCoatNormalW);
+            #ifdef REFLECTIONMAP_OPPOSITEZ
+                clearCoatReflectionVector.z *= -1.0;
+            #endif
+
+            // _____________________________ 2D vs 3D Maps ________________________________
+            #ifdef REFLECTIONMAP_3D
+                vec3 clearCoatReflectionCoords = clearCoatReflectionVector;
+            #else
+                vec2 clearCoatReflectionCoords = clearCoatReflectionVector.xy;
+                #ifdef REFLECTIONMAP_PROJECTION
+                    clearCoatReflectionCoords /= clearCoatReflectionVector.z;
+                #endif
+                clearCoatReflectionCoords.y = 1.0 - clearCoatReflectionCoords.y;
+            #endif
+
+            #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX)
+                float clearCoatReflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, clearCoatAlphaG, clearCoatNdotVUnclamped);
+            #else
+                float clearCoatReflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, clearCoatAlphaG, 1.);
+            #endif
+
+            #ifdef LODBASEDMICROSFURACE
+                // Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
+                clearCoatReflectionLOD = clearCoatReflectionLOD * vReflectionMicrosurfaceInfos.y + vReflectionMicrosurfaceInfos.z;
+                float requestedClearCoatReflectionLOD = clearCoatReflectionLOD;
+
+                environmentClearCoatRadiance = sampleReflectionLod(reflectionSampler, clearCoatReflectionCoords, requestedClearCoatReflectionLOD);
+            #else
+                float lodClearCoatReflectionNormalized = clamp(clearCoatReflectionLOD / log2(vReflectionMicrosurfaceInfos.x), 0., 1.);
+                float lodClearCoatReflectionNormalizedDoubled = lodClearCoatReflectionNormalized * 2.0;
+
+                vec4 environmentClearCoatMid = sampleReflection(reflectionSampler, reflectionCoords);
+                if(lodClearCoatReflectionNormalizedDoubled < 1.0){
+                    environmentClearCoatRadiance = mix(
+                        sampleReflection(reflectionSamplerHigh, clearCoatReflectionCoords),
+                        environmentClearCoatMid,
+                        lodClearCoatReflectionNormalizedDoubled
+                    );
+                }else{
+                    environmentClearCoatRadiance = mix(
+                        environmentClearCoatMid,
+                        sampleReflection(reflectionSamplerLow, clearCoatReflectionCoords),
+                        lodClearCoatReflectionNormalizedDoubled - 1.0
+                    );
+                }
+            #endif
+
+            #ifdef RGBDREFLECTION
+                environmentClearCoatRadiance.rgb = fromRGBD(environmentClearCoatRadiance);
+            #endif
+
+            #ifdef GAMMAREFLECTION
+                environmentClearCoatRadiance.rgb = toLinearSpace(environmentClearCoatRadiance.rgb);
+            #endif
+
+            // _____________________________ Levels _____________________________________
+            environmentClearCoatRadiance.rgb *= vReflectionInfos.x;
+            environmentClearCoatRadiance.rgb *= vReflectionColor.rgb;
+        #endif
+    #endif
+
     // ____________________________________________________________________________________
     // ____________________________________________________________________________________
     // _____________________________ Direct Lighting Param ________________________________
     // _____________________________ Direct Lighting Param ________________________________
         // Compute reflectance.
         // Compute reflectance.
@@ -629,6 +774,9 @@ void main(void) {
     #ifdef SPECULARTERM
     #ifdef SPECULARTERM
         vec3 specularBase = vec3(0., 0., 0.);
         vec3 specularBase = vec3(0., 0., 0.);
     #endif
     #endif
+    #ifdef CLEARCOAT
+        vec3 clearCoatBase = vec3(0., 0., 0.);
+    #endif
 
 
     #ifdef LIGHTMAP
     #ifdef LIGHTMAP
         vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb;
         vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb;
@@ -638,14 +786,11 @@ void main(void) {
         lightmapColor *= vLightmapInfos.y;
         lightmapColor *= vLightmapInfos.y;
     #endif
     #endif
 
 
-        lightingInfo info;
+    preLightingInfo preInfo;
+    lightingInfo info;
 
 
-        pointLightingInfo pointInfo;
-
-        spotLightingInfo spotInfo;
-
-        float shadow = 1.; // 1 - shadowLevel
-        float NdotL = -1.;
+    // 1 - shadowLevel
+    float shadow = 1.;
 
 
     #include<lightFragment>[0..maxSimultaneousLights]
     #include<lightFragment>[0..maxSimultaneousLights]
 
 
@@ -683,6 +828,54 @@ void main(void) {
         vec3 specularEnvironmentReflectance = fresnelSchlickEnvironmentGGX(NdotV, specularEnvironmentR0, specularEnvironmentR90, sqrt(microSurface));
         vec3 specularEnvironmentReflectance = fresnelSchlickEnvironmentGGX(NdotV, specularEnvironmentR0, specularEnvironmentR90, sqrt(microSurface));
     #endif
     #endif
 
 
+    // _________________________ Clear Coat Environment Oclusion __________________________
+    #ifdef CLEARCOAT
+        #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX)
+            // Indexed on cos(theta) and roughness
+            vec2 brdfClearCoatSamplerUV = vec2(clearCoatNdotV, clearCoatRoughness);
+            
+            // We can find the scale and offset to apply to the specular value.
+            vec4 environmentClearCoatBrdf = texture2D(environmentBrdfSampler, brdfClearCoatSamplerUV);
+
+            vec3 clearCoatEnvironmentReflectance = vec3(0.04) * environmentClearCoatBrdf.x + environmentClearCoatBrdf.y;
+
+            #ifdef RADIANCEOCCLUSION
+                float clearCoatSeo = environmentRadianceOcclusion(ambientMonochrome, clearCoatNdotVUnclamped);
+                clearCoatEnvironmentReflectance *= clearCoatSeo;
+            #endif
+
+            #ifdef HORIZONOCCLUSION
+                #ifdef BUMP
+                    #ifdef REFLECTIONMAP_3D
+                        float clearCoatEho = environmentHorizonOcclusion(-viewDirectionW, clearCoatNormalW);
+                        clearCoatEnvironmentReflectance *= clearCoatEho;
+                    #endif
+                #endif
+            #endif
+        #else
+            // Jones implementation of a well balanced fast analytical solution.
+            vec3 clearCoatEnvironmentReflectance = fresnelSchlickEnvironmentGGX(clearCoatNdotV, vec3(1.), vec3(1.), sqrt(1. - clearCoatRoughness));
+        #endif
+
+        // clear coar energy conservation
+        // fo = 4% based on the IOR of a air-polyurethane interface.
+        // the max reflectance is relying on our special trick to prevent weird values on highly diffuse materials.
+        // To let as a configuration if required
+        const float IBLClearCoatReflectance0 = 0.04;
+        const float IBLClearCoatReflectance90 = 1.;
+
+        float fresnelIBLClearCoat = fresnelSchlickGGXFloat(clearCoatNdotV, IBLClearCoatReflectance0, IBLClearCoatReflectance90);
+        fresnelIBLClearCoat *= clearCoatIntensity;
+        
+        float conservationFactor = (1. - fresnelIBLClearCoat);
+
+        clearCoatEnvironmentReflectance *= clearCoatIntensity;
+        #ifdef REFLECTION
+            environmentIrradiance *= conservationFactor;
+        #endif
+        specularEnvironmentReflectance *= (conservationFactor * conservationFactor);
+    #endif
+
     // _____________________________ Refractance+Tint ________________________________
     // _____________________________ Refractance+Tint ________________________________
     #ifdef REFRACTION
     #ifdef REFRACTION
         vec3 refractance = vec3(0.0, 0.0, 0.0);
         vec3 refractance = vec3(0.0, 0.0, 0.0);
@@ -757,23 +950,53 @@ void main(void) {
         finalRefraction *= refractance;
         finalRefraction *= refractance;
     #endif
     #endif
 
 
+    // _____________________________ Clear Coat _______________________________________
+    #ifdef CLEARCOAT
+        vec3 finalClearCoat = clearCoatBase;
+        finalClearCoat = max(finalClearCoat, 0.0);
+
+        // Full value needed for alpha.
+        vec3 finalClearCoatScaled = finalClearCoat * vLightingIntensity.x * vLightingIntensity.w;
+
+    // ____________________________ Clear Coat Radiance _______________________________
+        #ifdef REFLECTION
+            vec3 finalClearCoatRadiance = environmentClearCoatRadiance.rgb;
+            finalClearCoatRadiance *= clearCoatEnvironmentReflectance;
+
+            // Full value needed for alpha. 
+            vec3 finalClearCoatRadianceScaled = finalClearCoatRadiance * vLightingIntensity.z;
+        #endif
+
+        #ifdef REFRACTION
+            finalRefraction *= (conservationFactor * conservationFactor);
+        #endif
+    #endif
+
     // _____________________________ Highlights on Alpha _____________________________
     // _____________________________ Highlights on Alpha _____________________________
     #ifdef ALPHABLEND
     #ifdef ALPHABLEND
         float luminanceOverAlpha = 0.0;
         float luminanceOverAlpha = 0.0;
         #if	defined(REFLECTION) && defined(RADIANCEOVERALPHA)
         #if	defined(REFLECTION) && defined(RADIANCEOVERALPHA)
             luminanceOverAlpha += getLuminance(finalRadianceScaled);
             luminanceOverAlpha += getLuminance(finalRadianceScaled);
+            #if defined(CLEARCOAT)
+                luminanceOverAlpha += getLuminance(finalClearCoatRadianceScaled);
+            #endif
         #endif
         #endif
 
 
         #if defined(SPECULARTERM) && defined(SPECULAROVERALPHA)
         #if defined(SPECULARTERM) && defined(SPECULAROVERALPHA)
             luminanceOverAlpha += getLuminance(finalSpecularScaled);
             luminanceOverAlpha += getLuminance(finalSpecularScaled);
         #endif
         #endif
 
 
+        #if defined(CLEARCOAT) && defined(CLEARCOATOVERALPHA)
+            luminanceOverAlpha += getLuminance(finalClearCoatScaled);
+        #endif
+
         #if defined(RADIANCEOVERALPHA) || defined(SPECULAROVERALPHA)
         #if defined(RADIANCEOVERALPHA) || defined(SPECULAROVERALPHA)
             alpha = clamp(alpha + luminanceOverAlpha * luminanceOverAlpha, 0., 1.);
             alpha = clamp(alpha + luminanceOverAlpha * luminanceOverAlpha, 0., 1.);
         #endif
         #endif
     #endif
     #endif
 #endif
 #endif
 
 
+// _______________ Not done before as it is unlit only __________________________
 // _____________________________ Diffuse ________________________________________
 // _____________________________ Diffuse ________________________________________
     vec3 finalDiffuse = diffuseBase;
     vec3 finalDiffuse = diffuseBase;
     finalDiffuse.rgb += vAmbientColor;
     finalDiffuse.rgb += vAmbientColor;
@@ -809,10 +1032,20 @@ void main(void) {
     //	finalSpecular			* vLightingIntensity.x * vLightingIntensity.w +
     //	finalSpecular			* vLightingIntensity.x * vLightingIntensity.w +
         finalSpecularScaled +
         finalSpecularScaled +
     #endif
     #endif
+    #ifdef CLEARCOAT
+    // Computed in the previous step to help with alpha luminance.
+    //	finalClearCoat			* vLightingIntensity.x * vLightingIntensity.w +
+        finalClearCoatScaled +
+    #endif
     #ifdef REFLECTION
     #ifdef REFLECTION
     // Comupted in the previous step to help with alpha luminance.
     // Comupted in the previous step to help with alpha luminance.
     //	finalRadiance			* vLightingIntensity.z +
     //	finalRadiance			* vLightingIntensity.z +
         finalRadianceScaled +
         finalRadianceScaled +
+        #ifdef CLEARCOAT
+        //  Comupted in the previous step to help with alpha luminance.
+        //  finalClearCoatRadiance * vLightingIntensity.z 
+            finalClearCoatRadianceScaled +
+        #endif
     #endif
     #endif
     #ifdef REFRACTION
     #ifdef REFRACTION
         finalRefraction			* vLightingIntensity.z +
         finalRefraction			* vLightingIntensity.z +

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

@@ -64,6 +64,16 @@ varying vec2 vMicroSurfaceSamplerUV;
 varying vec2 vBumpUV;
 varying vec2 vBumpUV;
 #endif
 #endif
 
 
+#ifdef CLEARCOAT
+    #if defined(CLEARCOAT_TEXTURE) && CLEARCOAT_TEXTUREDIRECTUV == 0 
+        varying vec2 vClearCoatUV;
+    #endif
+
+    #if defined(CLEARCOAT_BUMP) && CLEARCOAT_BUMPDIRECTUV == 0 
+        varying vec2 vClearCoatBumpUV;
+    #endif
+#endif
+
 // Output
 // Output
 varying vec3 vPositionW;
 varying vec3 vPositionW;
 #ifdef NORMAL
 #ifdef NORMAL
@@ -250,6 +260,30 @@ void main(void) {
     }
     }
 #endif
 #endif
 
 
+#ifdef CLEARCOAT
+    #if defined(CLEARCOAT_TEXTURE) && CLEARCOAT_TEXTUREDIRECTUV == 0 
+        if (vClearCoatInfos.x == 0.)
+        {
+            vClearCoatUV = vec2(clearCoatMatrix * vec4(uv, 1.0, 0.0));
+        }
+        else
+        {
+            vClearCoatUV = vec2(clearCoatMatrix * vec4(uv2, 1.0, 0.0));
+        }
+    #endif
+
+    #if defined(CLEARCOAT_BUMP) && CLEARCOAT_BUMPDIRECTUV == 0 
+        if (vClearCoatBumpInfos.x == 0.)
+        {
+            vClearCoatBumpUV = vec2(clearCoatBumpMatrix * vec4(uv, 1.0, 0.0));
+        }
+        else
+        {
+            vClearCoatBumpUV = vec2(clearCoatBumpMatrix * vec4(uv2, 1.0, 0.0));
+        }
+    #endif
+#endif
+
     // TBN
     // TBN
 #include<bumpVertex>
 #include<bumpVertex>
 
 

二進制
tests/validation/ReferenceImages/clearCoat.png


+ 5 - 0
tests/validation/config.json

@@ -2,6 +2,11 @@
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "tests": [
   "tests": [
     {
     {
+      "title": "Clear Coat",
+      "playgroundId": "#YACNQS#1",
+      "referenceImage": "clearCoat.png"
+    },
+    {
       "title": "GUI Transform StackPanel",
       "title": "GUI Transform StackPanel",
       "playgroundId": "#BS60AB#0",
       "playgroundId": "#BS60AB#0",
       "referenceImage": "TransformStackPanel.png"
       "referenceImage": "TransformStackPanel.png"