Преглед изворни кода

Merge pull request #5853 from sebavan/master

Add Clear Coat Tint
David Catuhe пре 6 година
родитељ
комит
e3e7ca73c1

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

@@ -62,15 +62,42 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                 <LineContainerComponent globalState={this.props.globalState} title="LIGHTING & COLORS">
                     <Color3LineComponent label="Albedo" target={material} propertyName="albedoColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Color3LineComponent label="Reflectivity" target={material} propertyName="reflectivityColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Micro-surface" target={material} propertyName="microSurface" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Color3LineComponent label="Emissive" target={material} propertyName="emissiveColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <Color3LineComponent label="Ambient" target={material} propertyName="ambientColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="METALLIC WORKFLOW">
+                    <SliderLineComponent label="Metallic" target={material} propertyName="metallic" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Roughness" target={material} propertyName="roughness" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="CLEAR COAT">
                     <CheckBoxLineComponent label="Enabled" target={material.clearCoat} propertyName="isEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Intensity" target={material.clearCoat} propertyName="intensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Roughness" target={material.clearCoat} propertyName="roughness" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="IOR" target={material.clearCoat} propertyName="indiceOfRefraction" minimum={1.0} maximum={3} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <TextureLinkLineComponent label="Texture" texture={material.clearCoat.texture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
                     <TextureLinkLineComponent label="Bump" texture={material.clearCoat.bumpTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                    {
+                        material.bumpTexture &&
+                        <SliderLineComponent label="Bump strength" target={material.clearCoat.bumpTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    <CheckBoxLineComponent label="Tint" target={material.clearCoat} propertyName="isTintEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    {
+                        material.clearCoat.isEnabled && material.clearCoat.isTintEnabled &&
+                        <Color3LineComponent label="Tint Color" target={material.clearCoat} propertyName="tintColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        material.clearCoat.isEnabled && material.clearCoat.isTintEnabled &&
+                        <SliderLineComponent label="At Distance" target={material.clearCoat} propertyName="tintColorAtDistance" minimum={0} maximum={20} step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        material.clearCoat.isEnabled && material.clearCoat.isTintEnabled &&
+                        <SliderLineComponent label="Tint Thickness" target={material.clearCoat} propertyName="tintThickness" minimum={0} maximum={20} step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        material.clearCoat.isEnabled && material.clearCoat.isTintEnabled &&
+                        <TextureLinkLineComponent label="Tint Texture" texture={material.clearCoat.tintTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                    }
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="ANISOTROPIC">
                     <CheckBoxLineComponent label="Enabled" target={material.anisotropy} propertyName="isEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
@@ -79,10 +106,9 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="LEVELS" closed={true}>
                     <SliderLineComponent label="Environment" target={material} propertyName="environmentIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <SliderLineComponent label="Metallic" target={material} propertyName="metallic" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <SliderLineComponent label="Roughness" target={material} propertyName="roughness" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <SliderLineComponent label="Micro-surface" target={material} propertyName="microSurface" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Specular" target={material} propertyName="specularIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Emissive" target={material} propertyName="emissiveIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Direct" target={material} propertyName="directIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     {
                         material.bumpTexture &&
                         <SliderLineComponent label="Bump strength" target={material.bumpTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 4 - 0
src/Materials/PBR/pbrBaseMaterial.ts

@@ -181,10 +181,14 @@ class PBRMaterialDefines extends MaterialDefines
     public SPECULARAA = false;
 
     public CLEARCOAT = false;
+    public CLEARCOAT_DEFAULTIOR = false;
     public CLEARCOAT_TEXTURE = false;
     public CLEARCOAT_TEXTUREDIRECTUV = 0;
     public CLEARCOAT_BUMP = false;
     public CLEARCOAT_BUMPDIRECTUV = 0;
+    public CLEARCOAT_TINT = false;
+    public CLEARCOAT_TINT_TEXTURE = false;
+    public CLEARCOAT_TINT_TEXTUREDIRECTUV = 0;
 
     public ANISOTROPIC = false;
 

+ 141 - 5
src/Materials/PBR/pbrClearCoatConfiguration.ts

@@ -1,6 +1,7 @@
 import { Nullable } from "../../types";
 import { IAnimatable } from "../../Misc/tools";
 import { SerializationHelper, serialize, serializeAsTexture, expandToProperty } from "../../Misc/decorators";
+import { Color3 } from "../../Maths/math";
 import { BaseTexture } from "../../Materials/Textures/baseTexture";
 import { EffectFallbacks } from "../../Materials/effect";
 import { MaterialFlags } from "../materialFlags";
@@ -15,11 +16,16 @@ declare type Scene = import("../../scene").Scene;
  */
 export interface IMaterialClearCoatDefines {
     CLEARCOAT: boolean;
+    CLEARCOAT_DEFAULTIOR: boolean;
     CLEARCOAT_TEXTURE: boolean;
     CLEARCOAT_TEXTUREDIRECTUV: number;
     CLEARCOAT_BUMP: boolean;
     CLEARCOAT_BUMPDIRECTUV: number;
 
+    CLEARCOAT_TINT: boolean;
+    CLEARCOAT_TINT_TEXTURE: boolean;
+    CLEARCOAT_TINT_TEXTUREDIRECTUV: number;
+
     /** @hidden */
     _areTexturesDirty: boolean;
 }
@@ -28,6 +34,11 @@ export interface IMaterialClearCoatDefines {
  * Define the code related to the clear coat parameters of the pbr material.
  */
 export class PBRClearCoatConfiguration {
+    /**
+     * This defaults to 1.5 corresponding to a 0.04 f0 or a 4% reflectance at normal incidence
+     * The default fits with a polyurethane material.
+     */
+    private static readonly _DefaultIndiceOfRefraction = 1.5;
 
     @serialize()
     private _isEnabled = false;
@@ -49,6 +60,17 @@ export class PBRClearCoatConfiguration {
     @serialize()
     public roughness: number = 0;
 
+    @serialize()
+    private _indiceOfRefraction = PBRClearCoatConfiguration._DefaultIndiceOfRefraction;
+    /**
+     * Defines the indice of refraction of the clear coat.
+     * This defaults to 1.5 corresponding to a 0.04 f0 or a 4% reflectance at normal incidence
+     * The default fits with a polyurethane material.
+     * Changing the default value is more performance intensive.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public indiceOfRefraction = PBRClearCoatConfiguration._DefaultIndiceOfRefraction;
+
     @serializeAsTexture()
     private _texture: Nullable<BaseTexture> = null;
     /**
@@ -65,6 +87,46 @@ export class PBRClearCoatConfiguration {
     @expandToProperty("_markAllSubMeshesAsTexturesDirty")
     public bumpTexture: Nullable<BaseTexture> = null;
 
+    @serialize()
+    private _isTintEnabled = false;
+    /**
+     * Defines if the clear coat tint is enabled in the material.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public isTintEnabled = false;
+
+    /**
+     * Defines if the clear coat tint is enabled in the material.
+     * This is only use if tint is enabled
+     */
+    @serialize()
+    public tintColor = Color3.White();
+
+    /**
+     * Defines if the distance at which the tint color should be found in the
+     * clear coat media.
+     * This is only use if tint is enabled
+     */
+    @serialize()
+    public tintColorAtDistance = 1;
+
+    /**
+     * Defines the clear coat layer thickness.
+     * This is only use if tint is enabled
+     */
+    @serialize()
+    public tintThickness: number = 1;
+
+    @serializeAsTexture()
+    private _tintTexture: Nullable<BaseTexture> = null;
+    /**
+     * Stores the clear tint values in a texture.
+     * rgb is tint
+     * a is a thickness factor
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public tintTexture: Nullable<BaseTexture> = null;
+
     /** @hidden */
     private _internalMarkAllSubMeshesAsTexturesDirty: () => void;
 
@@ -104,6 +166,12 @@ export class PBRClearCoatConfiguration {
                         return false;
                     }
                 }
+
+                if (this._isTintEnabled && this._tintTexture && MaterialFlags.ClearCoatTintTextureEnabled) {
+                    if (!this._tintTexture.isReadyOrNotBlocking()) {
+                        return false;
+                    }
+                }
             }
         }
 
@@ -132,6 +200,22 @@ export class PBRClearCoatConfiguration {
                     } else {
                         defines.CLEARCOAT_BUMP = false;
                     }
+
+                    defines.CLEARCOAT_DEFAULTIOR = this._indiceOfRefraction === PBRClearCoatConfiguration._DefaultIndiceOfRefraction;
+
+                    if (this._isTintEnabled) {
+                        defines.CLEARCOAT_TINT = true;
+                        if (this._tintTexture && MaterialFlags.ClearCoatTintTextureEnabled) {
+                            MaterialHelper.PrepareDefinesForMergedUV(this._tintTexture, defines, "CLEARCOAT_TINT_TEXTURE");
+                        }
+                        else {
+                            defines.CLEARCOAT_TINT_TEXTURE = false;
+                        }
+                    }
+                    else {
+                        defines.CLEARCOAT_TINT = false;
+                        defines.CLEARCOAT_TINT_TEXTURE = false;
+                    }
                 }
             }
         }
@@ -139,6 +223,8 @@ export class PBRClearCoatConfiguration {
             defines.CLEARCOAT = false;
             defines.CLEARCOAT_TEXTURE = false;
             defines.CLEARCOAT_BUMP = false;
+            defines.CLEARCOAT_TINT = false;
+            defines.CLEARCOAT_TINT_TEXTURE = false;
         }
     }
 
@@ -170,8 +256,29 @@ export class PBRClearCoatConfiguration {
                 }
             }
 
-            // Clear Coat
+            if (this._tintTexture && MaterialFlags.ClearCoatTintTextureEnabled) {
+                uniformBuffer.updateFloat2("vClearCoatTintInfos", this._tintTexture.coordinatesIndex, this._tintTexture.level);
+                MaterialHelper.BindTextureMatrix(this._tintTexture, uniformBuffer, "clearCoatTint");
+            }
+
+            // Clear Coat General params
             uniformBuffer.updateFloat2("vClearCoatParams", this.intensity, this.roughness);
+
+            // Clear Coat Refraction params
+            const a = 1 - this._indiceOfRefraction;
+            const b = 1 + this._indiceOfRefraction;
+            const f0 = Math.pow((-a / b), 2); // Schlicks approx: (ior1 - ior2) / (ior1 + ior2) where ior2 for air is close to vacuum = 1.
+            const eta = 1 / this._indiceOfRefraction;
+            uniformBuffer.updateFloat4("vClearCoatRefractionParams", f0, eta, a,  b);
+
+            if (this._isTintEnabled) {
+                uniformBuffer.updateFloat4("vClearCoatTintParams",
+                    this.tintColor.r,
+                    this.tintColor.g,
+                    this.tintColor.b,
+                    Math.max(0.00001, this.tintThickness));
+                uniformBuffer.updateFloat("clearCoatColorAtDistance", Math.max(0.00001, this.tintColorAtDistance));
+            }
         }
 
         // Textures
@@ -183,6 +290,10 @@ export class PBRClearCoatConfiguration {
             if (this._bumpTexture && engine.getCaps().standardDerivatives && MaterialFlags.ClearCoatBumpTextureEnabled && !disableBumpMap) {
                 uniformBuffer.setTexture("clearCoatBumpSampler", this._bumpTexture);
             }
+
+            if (this._isTintEnabled && this._tintTexture && MaterialFlags.ClearCoatTintTextureEnabled) {
+                uniformBuffer.setTexture("clearCoatTintSampler", this._tintTexture);
+            }
         }
     }
 
@@ -200,6 +311,10 @@ export class PBRClearCoatConfiguration {
             return true;
         }
 
+        if (this._tintTexture === texture) {
+            return true;
+        }
+
         return false;
     }
 
@@ -215,6 +330,10 @@ export class PBRClearCoatConfiguration {
         if (this._bumpTexture) {
             activeTextures.push(this._bumpTexture);
         }
+
+        if (this._tintTexture) {
+            activeTextures.push(this._tintTexture);
+        }
     }
 
     /**
@@ -229,6 +348,10 @@ export class PBRClearCoatConfiguration {
         if (this._bumpTexture && this._bumpTexture.animations && this._bumpTexture.animations.length > 0) {
             animatables.push(this._bumpTexture);
         }
+
+        if (this._tintTexture && this._tintTexture.animations && this._tintTexture.animations.length > 0) {
+            animatables.push(this._tintTexture);
+        }
     }
 
     /**
@@ -244,6 +367,10 @@ export class PBRClearCoatConfiguration {
             if (this._bumpTexture) {
                 this._bumpTexture.dispose();
             }
+
+            if (this._tintTexture) {
+                this._tintTexture.dispose();
+            }
         }
     }
 
@@ -290,6 +417,9 @@ export class PBRClearCoatConfiguration {
         if (defines.CLEARCOAT_BUMP) {
             fallbacks.addFallback(currentRank++, "CLEARCOAT_BUMP");
         }
+        if (defines.CLEARCOAT_TINT) {
+            fallbacks.addFallback(currentRank++, "CLEARCOAT_TINT");
+        }
         if (defines.CLEARCOAT) {
             fallbacks.addFallback(currentRank++, "CLEARCOAT");
         }
@@ -301,9 +431,10 @@ export class PBRClearCoatConfiguration {
      * @param uniforms defines the current uniform list.
      */
     public static AddUniforms(uniforms: string[]): void {
-        uniforms.push("vClearCoatTangentSpaceParams", "vClearCoatParams",
-            "clearCoatMatrix", "clearCoatBumpMatrix",
-            "vClearCoatInfos", "vClearCoatBumpInfos");
+        uniforms.push("vClearCoatTangentSpaceParams", "vClearCoatParams", "vClearCoatRefractionParams",
+            "vClearCoatTintParams", "clearCoatColorAtDistance",
+            "clearCoatMatrix", "clearCoatBumpMatrix", "clearCoatTintMatrix",
+            "vClearCoatInfos", "vClearCoatBumpInfos", "vClearCoatTintInfos");
     }
 
     /**
@@ -311,7 +442,7 @@ export class PBRClearCoatConfiguration {
      * @param samplers defines the current sampler list.
      */
     public static AddSamplers(samplers: string[]): void {
-        samplers.push("clearCoatSampler", "clearCoatBumpSampler");
+        samplers.push("clearCoatSampler", "clearCoatBumpSampler", "clearCoatTintSampler");
     }
 
     /**
@@ -320,10 +451,15 @@ export class PBRClearCoatConfiguration {
      */
     public static PrepareUniformBuffer(uniformBuffer: UniformBuffer): void {
         uniformBuffer.addUniform("vClearCoatParams", 2);
+        uniformBuffer.addUniform("vClearCoatRefractionParams", 4);
         uniformBuffer.addUniform("vClearCoatInfos", 2);
         uniformBuffer.addUniform("clearCoatMatrix", 16);
         uniformBuffer.addUniform("vClearCoatBumpInfos", 2);
         uniformBuffer.addUniform("vClearCoatTangentSpaceParams", 2);
         uniformBuffer.addUniform("clearCoatBumpMatrix", 16);
+        uniformBuffer.addUniform("vClearCoatTintParams", 4);
+        uniformBuffer.addUniform("clearCoatColorAtDistance", 1);
+        uniformBuffer.addUniform("vClearCoatTintInfos", 2);
+        uniformBuffer.addUniform("clearCoatTintMatrix", 16);
     }
 }

+ 16 - 0
src/Materials/materialFlags.ts

@@ -213,4 +213,20 @@ export class MaterialFlags {
         this._ClearCoatBumpTextureEnabled = value;
         Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
     }
+
+    private static _ClearCoatTintTextureEnabled = true;
+    /**
+     * Are clear coat tint textures enabled in the application.
+     */
+    public static get ClearCoatTintTextureEnabled(): boolean {
+        return this._ClearCoatTintTextureEnabled;
+    }
+    public static set ClearCoatTintTextureEnabled(value: boolean) {
+        if (this._ClearCoatTintTextureEnabled === value) {
+            return;
+        }
+
+        this._ClearCoatTintTextureEnabled = value;
+        Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+    }
 }

+ 9 - 0
src/Shaders/ShadersInclude/lightFragment.fx

@@ -79,6 +79,15 @@
                 #endif
 
                 info.clearCoat = computeClearCoatLighting(preInfo, clearCoatNormalW, clearCoatAARoughnessFactors.x, clearCoatIntensity, light{X}.vLightDiffuse.rgb);
+                
+                #ifdef CLEARCOAT_TINT
+                    // Absorption
+                    absorption = computeClearCoatLightingAbsorption(clearCoatNdotVRefract, preInfo.L, clearCoatNormalW, clearCoatColor, clearCoatThickness, clearCoatIntensity);
+                    info.diffuse *= absorption;
+                    #ifdef SPECULARTERM
+                        info.specular *= absorption;
+                    #endif
+                #endif
 
                 // Apply energy conservation on diffuse and specular term.
                 info.diffuse *= info.clearCoat.w;

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

@@ -68,6 +68,7 @@ uniform mat4 view;
 // Clear Coat
 #ifdef CLEARCOAT
     uniform vec2 vClearCoatParams;
+    uniform vec4 vClearCoatRefractionParams;
 
     #ifdef CLEARCOAT_TEXTURE
         uniform vec2 vClearCoatInfos;
@@ -79,6 +80,16 @@ uniform mat4 view;
         uniform vec2 vClearCoatTangentSpaceParams;
         uniform mat4 clearCoatBumpMatrix;
     #endif
+
+    #ifdef CLEARCOAT_TINT
+        uniform vec4 vClearCoatTintParams;
+        uniform float clearCoatColorAtDistance;
+
+        #ifdef CLEARCOAT_TINT_TEXTURE
+            uniform vec2 vClearCoatTintInfos;
+            uniform mat4 clearCoatTintMatrix;
+        #endif
+    #endif
 #endif
 
 // Anisotropy

+ 51 - 21
src/Shaders/ShadersInclude/pbrFunctions.fx

@@ -1,11 +1,10 @@
 // Constants
 #define RECIPROCAL_PI2 0.15915494
 #define FRESNEL_MAXIMUM_ON_ROUGH 0.25
+
 // AlphaG epsilon to avoid numerical issues
 #define MINIMUMVARIANCE 0.0005
 
-// f0 = 4% based on the IOR of a air-polyurethane interface.
-#define CLEARCOATREFLECTANCE0 0.04
 #define CLEARCOATREFLECTANCE90 1.0
 
 float convertRoughnessToAverageSlope(float roughness)
@@ -50,17 +49,33 @@ vec2 getAARoughnessFactors(vec3 normalVector) {
 //     return (1.0 + s) / (1.0 - s);
 // }
 
-// // Clear coat Remapping
+// f0 Remapping due to layers
 // vec3 getR0RemappedForClearCoat(vec3 f0, vec3 clearCoatF0) {
 //     vec3 iorBase = getIORfromAirToSurfaceR0(f0);
 //     vec3 clearCoatIor = getIORfromAirToSurfaceR0(clearCoatF0);
 //     return getR0fromIOR(iorBase, clearCoatIor);
 // }
 
-vec3 getR0RemappedForPolyurethaneClearCoat(vec3 f0) {
-    vec3 s = sqrt(f0);
-    return (-1.0 + 5.0 * s) / (5.0 + s);
+#ifdef CLEARCOAT
+// Knowing ior clear coat is fix for the material
+// Solving iorbase = 1 + sqrt(fo) / (1 - sqrt(fo)) and f0base = square((iorbase - iorclearcoat) / (iorbase - iorclearcoat))
+// provide f0base = square(A + B * sqrt(fo)) / (B + A * sqrt(fo))
+// where A = 1 - iorclearcoat
+// and   B = 1 + iorclearcoat
+vec3 getR0RemappedForClearCoat(vec3 f0) {
+    #ifdef CLEARCOAT_DEFAULTIOR
+        #ifdef MOBILE
+            return clamp(f0 * (f0 * 0.526868 + 0.529324) - 0.0482256, 0., 1.);
+        #else
+            return clamp(f0 * (f0 * (0.941892 - 0.263008 * f0) + 0.346479) - 0.0285998, 0., 1.);
+        #endif
+    #else
+        vec3 s = sqrt(f0);
+        vec3 t = (vClearCoatRefractionParams.z + vClearCoatRefractionParams.w * s) / (vClearCoatRefractionParams.w + vClearCoatRefractionParams.z * s);
+        return t * t;
+    #endif
 }
+#endif
 
 // From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
 // Keep for references
@@ -81,18 +96,15 @@ vec3 getR0RemappedForPolyurethaneClearCoat(vec3 f0) {
 // torrance denominator :-)
 float smithVisibilityG1_TrowbridgeReitzGGXFast(float dot, float alphaG)
 {
-    float alphaSquared = alphaG * alphaG;
-    return 1.0 / (dot + sqrt(alphaSquared + (1.0 - alphaSquared) * dot * dot));
+    #ifdef MOBILE
+        // Appply simplification as all squared root terms are below 1 and squared
+        return 1.0 / (dot + alphaG + (1.0 - alphaG) * dot ));
+    #else
+        float alphaSquared = alphaG * alphaG;
+        return 1.0 / (dot + sqrt(alphaSquared + (1.0 - alphaSquared) * dot * dot));
+    #endif
 }
 
-// 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)
 {
     float visibility = smithVisibilityG1_TrowbridgeReitzGGXFast(NdotL, alphaG) * smithVisibilityG1_TrowbridgeReitzGGXFast(NdotV, alphaG);
@@ -163,11 +175,22 @@ vec3 fresnelSchlickEnvironmentGGX(float VdotN, vec3 reflectance0, vec3 reflectan
     return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
 }
 
+// From beer lambert law I1/I0 = e −α′lc
+// c is considered included in alpha
+// https://blog.selfshadow.com/publications/s2017-shading-course/drobot/s2017_pbs_multilayered.pdf page 47
+// where L on a thin constant size layer can be (d * ((NdotLRefract + NdotVRefract) / (NdotLRefract * NdotVRefract))
+vec3 cocaLambert(float NdotVRefract, float NdotLRefract, vec3 alpha, float thickness) {
+    return exp(alpha * -(thickness * ((NdotLRefract + NdotVRefract) / (NdotLRefract * NdotVRefract))));
+}
+// From beerLambert Solves what alpha should be for a given resutlt at a known distance.
+vec3 computeColorAtDistanceInMedia(vec3 color, float distance) {
+    return -log(color) / distance;
+}
+
 // Disney diffuse term
 // https://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf
 // Page 14
-float computeDiffuseTerm(float NdotL, float NdotV, float VdotH, float roughness)
-{
+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);
@@ -214,12 +237,19 @@ vec2 computeClearCoatTerm(float NdotH, float VdotH, float clearCoatRoughness, fl
     float visibility = kelemenVisibility(VdotH);
     float clearCoatTerm = max(0., visibility * distribution);
 
-    float fresnel = fresnelSchlickGGX(VdotH, CLEARCOATREFLECTANCE0, CLEARCOATREFLECTANCE90);
+    float fresnel = fresnelSchlickGGX(VdotH, vClearCoatRefractionParams.x, CLEARCOATREFLECTANCE90);
     fresnel *= clearCoatIntensity;
     
     return vec2(fresnel * clearCoatTerm, 1.0 - fresnel);
 }
 
+vec3 computeClearCoatAbsorption(float NdotVRefract, float NdotLRefract, vec3 clearCoatColor, float clearCoatThickness, float clearCoatIntensity) {
+    vec3 clearCoatAbsorption = mix(vec3(1.0),
+        cocaLambert(NdotVRefract, NdotLRefract, clearCoatColor, clearCoatThickness),
+        clearCoatIntensity);
+    return clearCoatAbsorption;
+}
+
 float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)
 {
     #if defined(USEPHYSICALLIGHTFALLOFF) || defined(USEGLTFLIGHTFALLOFF)
@@ -247,8 +277,8 @@ float computeDefaultMicroSurface(float microSurface, vec3 reflectivityColor)
 // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
 // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
 float fresnelGrazingReflectance(float reflectance0) {
-	float reflectance90 = clamp(reflectance0 * 25.0, 0.0, 1.0);
-	return reflectance90;
+    float reflectance90 = clamp(reflectance0 * 25.0, 0.0, 1.0);
+    return reflectance90;
 }
 
 // To enable 8 bit textures to be used we need to pack and unpack the LOD

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

@@ -43,6 +43,7 @@ vec3 computeAnisotropicSpecularLighting(preLightingInfo info, vec3 V, vec3 N, ve
     return specTerm * info.attenuation * info.NdotL * lightColor;
 }
 
+#ifdef CLEARCOAT
 vec4 computeClearCoatLighting(preLightingInfo info, vec3 Ncc, float geometricRoughnessFactor, float clearCoatIntensity, vec3 lightColor) {
     float NccdotL = clamp(dot(Ncc, info.L), 0.00000000001, 1.0);
     float NccdotH = clamp(dot(Ncc, info.H), 0.000000000001, 1.0);
@@ -55,6 +56,15 @@ vec4 computeClearCoatLighting(preLightingInfo info, vec3 Ncc, float geometricRou
     return result;
 }
 
+vec3 computeClearCoatLightingAbsorption(float NdotVRefract, vec3 L, vec3 Ncc, vec3 clearCoatColor, float clearCoatThickness, float clearCoatIntensity) {
+    vec3 LRefract = -refract(L, Ncc, vClearCoatRefractionParams.y);
+    float NdotLRefract = clamp(dot(Ncc, LRefract), 0.00000000001, 1.0);
+
+    vec3 absorption = computeClearCoatAbsorption(NdotVRefract, NdotLRefract, clearCoatColor, clearCoatThickness, clearCoatIntensity);
+    return absorption;
+}
+#endif
+
 vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, mat4 textureProjectionMatrix){
 	vec4 strq = textureProjectionMatrix * vec4(vPositionW, 1.0);
 	strq /= strq.w;

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

@@ -39,11 +39,16 @@ uniform Material
     uniform float pointSize;
 
     uniform vec2 vClearCoatParams;
+    uniform vec4 vClearCoatRefractionParams;
     uniform vec2 vClearCoatInfos;
     uniform mat4 clearCoatMatrix;
     uniform vec2 vClearCoatBumpInfos;
     uniform vec2 vClearCoatTangentSpaceParams;
     uniform mat4 clearCoatBumpMatrix;
+    uniform vec4 vClearCoatTintParams;
+    uniform float clearCoatColorAtDistance;
+    uniform vec2 vClearCoatTintInfos;
+    uniform mat4 clearCoatTintMatrix;
 
     uniform float anisotropy;
 };

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

@@ -70,4 +70,9 @@ uniform float pointSize;
         uniform vec2 vClearCoatBumpInfos;
         uniform mat4 clearCoatBumpMatrix;
     #endif
+
+    #ifdef CLEARCOAT_TINT_TEXTURE
+        uniform vec2 vClearCoatTintInfos;
+        uniform mat4 clearCoatTintMatrix;
+    #endif
 #endif

+ 49 - 3
src/Shaders/pbr.fragment.fx

@@ -143,6 +143,17 @@ varying vec4 vColor;
         #endif
         uniform sampler2D clearCoatBumpSampler;
     #endif
+
+    #ifdef CLEARCOAT_TINT_TEXTURE
+        #if CLEARCOAT_TINT_TEXTUREDIRECTUV == 1
+            #define vClearCoatTintUV vMainUV1
+        #elif CLEARCOAT_TINT_TEXTUREDIRECTUV == 2
+            #define vClearCoatTintUV vMainUV2
+        #else
+            varying vec2 vClearCoatTintUV;
+        #endif
+        uniform sampler2D clearCoatTintSampler;
+    #endif
 #endif
 
 // Refraction
@@ -690,12 +701,25 @@ void main(void) {
             clearCoatRoughness *= clearCoatMapData.y;
         #endif
 
+        #ifdef CLEARCOAT_TINT
+            vec3 clearCoatColor = vClearCoatTintParams.rgb;
+            float clearCoatThickness = vClearCoatTintParams.a;
+
+            #ifdef CLEARCOAT_TINT_TEXTURE
+                vec4 clearCoatTintMapData = texture2D(clearCoatTintSampler, vClearCoatTintUV + uvOffset);
+                clearCoatColor *= toLinearSpace(clearCoatTintMapData.rgb);
+                clearCoatThickness *= clearCoatTintMapData.a;
+            #endif
+
+            clearCoatColor = computeColorAtDistanceInMedia(clearCoatColor, clearCoatColorAtDistance);
+        #endif
+
         // remapping and linearization of clear coat roughness
         // Let s see how it ends up in gltf
         // clearCoatRoughness = mix(0.089, 0.6, clearCoatRoughness);
 
         // Remap F0 to account for the change of interface within the material.
-        vec3 specularEnvironmentR0Updated = getR0RemappedForPolyurethaneClearCoat(specularEnvironmentR0);
+        vec3 specularEnvironmentR0Updated = getR0RemappedForClearCoat(specularEnvironmentR0);
         specularEnvironmentR0 = mix(specularEnvironmentR0, specularEnvironmentR0Updated, clearCoatIntensity);
 
         #ifdef CLEARCOAT_BUMP
@@ -801,6 +825,14 @@ void main(void) {
                 environmentClearCoatRadiance.rgb = toLinearSpace(environmentClearCoatRadiance.rgb);
             #endif
 
+            #ifdef CLEARCOAT_TINT
+                vec3 clearCoatVRefract = -refract(vPositionW, clearCoatNormalW, vClearCoatRefractionParams.y);
+                float clearCoatNdotVRefract = clamp(dot(clearCoatNormalW, clearCoatVRefract), 0.00000000001, 1.0);
+
+                // Used later on in the light fragment and ibl.
+                vec3 absorption = vec3(0.);
+            #endif
+
             // _____________________________ Levels _____________________________________
             environmentClearCoatRadiance.rgb *= vReflectionInfos.x;
             environmentClearCoatRadiance.rgb *= vReflectionColor.rgb;
@@ -875,7 +907,7 @@ void main(void) {
             // We can find the scale and offset to apply to the specular value.
             vec4 environmentClearCoatBrdf = texture2D(environmentBrdfSampler, brdfClearCoatSamplerUV);
 
-            vec3 clearCoatEnvironmentReflectance = vec3(CLEARCOATREFLECTANCE0 * environmentClearCoatBrdf.x + environmentClearCoatBrdf.y);
+            vec3 clearCoatEnvironmentReflectance = vec3(vClearCoatRefractionParams.x * environmentClearCoatBrdf.x + environmentClearCoatBrdf.y);
 
             #ifdef RADIANCEOCCLUSION
                 float clearCoatSeo = environmentRadianceOcclusion(ambientMonochrome, clearCoatNdotVUnclamped);
@@ -897,11 +929,22 @@ void main(void) {
 
         clearCoatEnvironmentReflectance *= clearCoatIntensity;
 
+        #ifdef CLEARCOAT_TINT
+            // NdotL = NdotV in IBL
+            absorption = computeClearCoatAbsorption(clearCoatNdotVRefract, clearCoatNdotVRefract, clearCoatColor, clearCoatThickness, clearCoatIntensity);
+
+            #ifdef REFLECTION
+                environmentIrradiance *= absorption;
+            #endif
+            specularEnvironmentReflectance *= absorption;
+        #endif
+
         // clear coat energy conservation
-        float fresnelIBLClearCoat = fresnelSchlickGGX(clearCoatNdotV, CLEARCOATREFLECTANCE0, CLEARCOATREFLECTANCE90);
+        float fresnelIBLClearCoat = fresnelSchlickGGX(clearCoatNdotV, vClearCoatRefractionParams.x, CLEARCOATREFLECTANCE90);
         fresnelIBLClearCoat *= clearCoatIntensity;
 
         float conservationFactor = (1. - fresnelIBLClearCoat);
+
         #ifdef REFLECTION
             environmentIrradiance *= conservationFactor;
         #endif
@@ -1001,6 +1044,9 @@ void main(void) {
 
         #ifdef REFRACTION
             finalRefraction *= (conservationFactor * conservationFactor);
+            #ifdef CLEARCOAT_TINT
+                finalRefraction *= absorption;
+            #endif
         #endif
     #endif
 

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

@@ -72,6 +72,10 @@ varying vec2 vBumpUV;
     #if defined(CLEARCOAT_BUMP) && CLEARCOAT_BUMPDIRECTUV == 0 
         varying vec2 vClearCoatBumpUV;
     #endif
+
+    #if defined(CLEARCOAT_TINT_TEXTURE) && CLEARCOAT_TINT_TEXTUREDIRECTUV == 0 
+        varying vec2 vClearCoatTintUV;
+    #endif
 #endif
 
 // Output
@@ -282,6 +286,17 @@ void main(void) {
             vClearCoatBumpUV = vec2(clearCoatBumpMatrix * vec4(uv2, 1.0, 0.0));
         }
     #endif
+
+    #if defined(CLEARCOAT_TINT_TEXTURE) && CLEARCOAT_TINT_TEXTUREDIRECTUV == 0 
+        if (vClearCoatTintInfos.x == 0.)
+        {
+            vClearCoatTintUV = vec2(clearCoatTintMatrix * vec4(uv, 1.0, 0.0));
+        }
+        else
+        {
+            vClearCoatTintUV = vec2(clearCoatTintMatrix * vec4(uv2, 1.0, 0.0));
+        }
+    #endif
 #endif
 
     // TBN