Procházet zdrojové kódy

Merge pull request #5928 from sebavan/master

Sheen
David Catuhe před 6 roky
rodič
revize
ce6c3c9dd0

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

@@ -32,6 +32,7 @@
   - Added energy conservation through Multiscattering BRDF support to PBR ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)
   - Added clear coat support to PBR ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)
   - Added anisotropy support to PBR ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)
+  - Added sheen support to PBR ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)
 
 ## Updates
 

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

@@ -8,6 +8,7 @@ import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { Color3LineComponent } from "../../../lines/color3LineComponent";
 import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { Vector2LineComponent } from "../../../lines/vector2LineComponent";
 import { SliderLineComponent } from "../../../lines/sliderLineComponent";
 import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
 import { CommonMaterialPropertyGridComponent } from "./commonMaterialPropertyGridComponent";
@@ -69,6 +70,8 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
             { label: "ClearCoat Tangents", value: 9 },
             { label: "ClearCoat Bitangents", value: 10 },
             { label: "Anisotropic Normals", value: 11 },
+            { label: "Anisotropic Tangents", value: 12 },
+            { label: "Anisotropic Bitangents", value: 13 },
             // Maps
             { label: "Albdeo Map", value: 20 },
             { label: "Ambient Map", value: 21 },
@@ -79,6 +82,8 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
             { label: "Reflectivity Map", value: 26 },
             { label: "ClearCoat Map", value: 27 },
             { label: "ClearCoat Tint Map", value: 28 },
+            { label: "Sheen Map", value: 29 },
+            { label: "Anisotropic Map", value: 30 },
             // Env
             { label: "Env Refraction", value: 40 },
             { label: "Env Reflection", value: 41 },
@@ -87,7 +92,8 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
             { label: "Direct Diffuse", value: 50 },
             { label: "Direct Specular", value: 51 },
             { label: "Direct Clear Coat", value: 52 },
-            { label: "Env Irradiance", value: 53 },
+            { label: "Direct Sheen", value: 53 },
+            { label: "Env Irradiance", value: 54 },
             // Lighting Params
             { label: "Surface Albedo", value: 60 },
             { label: "Reflectance 0", value: 61 },
@@ -103,8 +109,9 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
             { label: "Energy Factor", value: 72 },
             { label: "Specular Reflectance", value: 73 },
             { label: "Clear Coat Reflectance", value: 74 },
-            { label: "Luminance Over Alpha", value: 75 },
-            { label: "Alpha", value: 76 },
+            { label: "Sheen Reflectance", value: 75 },
+            { label: "Luminance Over Alpha", value: 76 },
+            { label: "Alpha", value: 77 },
         ];
 
         return (
@@ -167,7 +174,22 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                         material.anisotropy.isEnabled &&
                         <div>
                             <SliderLineComponent label="Intensity" target={material.anisotropy} propertyName="intensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                            <CheckBoxLineComponent label="Follow tangents" target={material.anisotropy} propertyName="followTangents" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <Vector2LineComponent label="Direction" target={material.anisotropy} propertyName="direction" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <TextureLinkLineComponent label="Texture" texture={material.anisotropy.texture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={this._onDebugSelectionChangeObservable} />
+                        </div>
+                    }
+                </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="SHEEN">
+                    <CheckBoxLineComponent label="Enabled" target={material.sheen} propertyName="isEnabled"
+                        onValueChanged={() => this.forceUpdate()}
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    {
+                        material.sheen.isEnabled &&
+                        <div>
+                            <CheckBoxLineComponent label="Link to Albedo" target={material.sheen} propertyName="linkSheenWithAlbedo" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <SliderLineComponent label="Intensity" target={material.sheen} propertyName="intensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <Color3LineComponent label="Color" target={material.sheen} propertyName="color" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <TextureLinkLineComponent label="Texture" texture={material.sheen.texture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={this._onDebugSelectionChangeObservable} />
                         </div>
                     }
                 </LineContainerComponent>

+ 164 - 40
src/Materials/PBR/pbrAnisotropicConfiguration.ts

@@ -1,17 +1,26 @@
-import { SerializationHelper, serialize, expandToProperty } from "../../Misc/decorators";
+import { SerializationHelper, serialize, expandToProperty, serializeAsVector2, serializeAsTexture } from "../../Misc/decorators";
 import { EffectFallbacks } from "../../Materials/effect";
 import { UniformBuffer } from "../../Materials/uniformBuffer";
 import { AbstractMesh } from "../../Meshes/abstractMesh";
 import { VertexBuffer } from "../../Meshes/buffer";
+import { Vector2 } from "../../Maths/math";
+import { Scene } from "../../scene";
+import { MaterialFlags } from "../../Materials/materialFlags";
+import { MaterialHelper } from "../../Materials/materialHelper";
+import { BaseTexture } from "../../Materials/Textures/baseTexture";
+import { IAnimatable } from "../../Misc";
+import { Nullable } from "../../types";
 
 /**
  * @hidden
  */
 export interface IMaterialAnisotropicDefines {
     ANISOTROPIC: boolean;
+    ANISOTROPIC_TEXTURE: boolean;
+    ANISOTROPIC_TEXTUREDIRECTUV: number;
     MAINUV1: boolean;
 
-    _areMiscDirty: boolean;
+    _areTexturesDirty: boolean;
     _needUVs: boolean;
 }
 
@@ -25,7 +34,7 @@ export class PBRAnisotropicConfiguration {
     /**
      * Defines if the anisotropy is enabled in the material.
      */
-    @expandToProperty("_markAllSubMeshesAsMiscDirty")
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
     public isEnabled = false;
 
     /**
@@ -35,83 +44,164 @@ export class PBRAnisotropicConfiguration {
     public intensity: number = 1;
 
     /**
-     * Defines if the effect is along the tangents or bitangents.
+     * Defines if the effect is along the tangents, bitangents or in between.
      * By default, the effect is "strectching" the highlights along the tangents.
      */
-    @serialize()
-    public followTangents = true;
+    @serializeAsVector2()
+    public direction = new Vector2(1, 0);
+
+    @serializeAsTexture()
+    private _texture: Nullable<BaseTexture> = null;
+    /**
+     * Stores the anisotropy values in a texture.
+     * rg is direction (like normal from -1 to 1)
+     * b is a intensity
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public texture: Nullable<BaseTexture> = null;
 
     /** @hidden */
-    private _internalMarkAllSubMeshesAsMiscDirty: () => void;
+    private _internalMarkAllSubMeshesAsTexturesDirty: () => void;
 
     /** @hidden */
-    public _markAllSubMeshesAsMiscDirty(): void {
-        this._internalMarkAllSubMeshesAsMiscDirty();
+    public _markAllSubMeshesAsTexturesDirty(): void {
+        this._internalMarkAllSubMeshesAsTexturesDirty();
     }
 
     /**
-     * Instantiate a new istance of clear coat configuration.
-     * @param markAllSubMeshesAsMiscDirty Callback to flag the material to dirty
+     * Instantiate a new istance of anisotropy configuration.
+     * @param markAllSubMeshesAsTexturesDirty Callback to flag the material to dirty
+     */
+    constructor(markAllSubMeshesAsTexturesDirty: () => void) {
+        this._internalMarkAllSubMeshesAsTexturesDirty = markAllSubMeshesAsTexturesDirty;
+    }
+
+    /**
+     * Specifies that the submesh is ready to be used.
+     * @param defines the list of "defines" to update.
+     * @param scene defines the scene the material belongs to.
+     * @returns - boolean indicating that the submesh is ready or not.
      */
-    constructor(markAllSubMeshesAsMiscDirty: () => void) {
-        this._internalMarkAllSubMeshesAsMiscDirty = markAllSubMeshesAsMiscDirty;
+    public isReadyForSubMesh(defines: IMaterialAnisotropicDefines, scene: Scene): boolean {
+        if (defines._areTexturesDirty) {
+            if (scene.texturesEnabled) {
+                if (this._texture && MaterialFlags.AnisotropicTextureEnabled) {
+                    if (!this._texture.isReadyOrNotBlocking()) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        return true;
     }
 
     /**
      * Checks to see if a texture is used in the material.
      * @param defines the list of "defines" to update.
      * @param mesh the mesh we are preparing the defines for.
+     * @param scene defines the scene the material belongs to.
      */
-    public prepareDefines(defines: IMaterialAnisotropicDefines, mesh: AbstractMesh): void {
-        defines.ANISOTROPIC = this._isEnabled;
-        if (this._isEnabled && !mesh.isVerticesDataPresent(VertexBuffer.TangentKind)) {
-            defines._needUVs = true;
-            defines.MAINUV1 = true;
+    public prepareDefines(defines: IMaterialAnisotropicDefines, mesh: AbstractMesh, scene: Scene): void {
+        if (this._isEnabled) {
+            defines.ANISOTROPIC = this._isEnabled;
+            if (this._isEnabled && !mesh.isVerticesDataPresent(VertexBuffer.TangentKind)) {
+                defines._needUVs = true;
+                defines.MAINUV1 = true;
+            }
+
+            if (defines._areTexturesDirty) {
+                if (scene.texturesEnabled) {
+                    if (this._texture && MaterialFlags.AnisotropicTextureEnabled) {
+                        MaterialHelper.PrepareDefinesForMergedUV(this._texture, defines, "ANISOTROPIC_TEXTURE");
+                    } else {
+                        defines.ANISOTROPIC_TEXTURE = false;
+                    }
+                }
+            }
+        }
+        else {
+            defines.ANISOTROPIC = false;
+            defines.ANISOTROPIC_TEXTURE = false;
         }
     }
 
     /**
      * Binds the material data.
      * @param uniformBuffer defines the Uniform buffer to fill in.
+     * @param scene defines the scene the material belongs to.
      * @param isFrozen defines wether the material is frozen or not.
      */
-    public bindForSubMesh(uniformBuffer: UniformBuffer, isFrozen: boolean): void {
+    public bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, isFrozen: boolean): void {
         if (!uniformBuffer.useUbo || !isFrozen || !uniformBuffer.isSync) {
-            // Clear Coat
-            uniformBuffer.updateFloat("anisotropy", this.followTangents ? this.intensity : -this.intensity);
+            if (this._texture && MaterialFlags.AnisotropicTextureEnabled) {
+                uniformBuffer.updateFloat2("vAnisotropyInfos", this._texture.coordinatesIndex, this._texture.level);
+                MaterialHelper.BindTextureMatrix(this._texture, uniformBuffer, "anisotropy");
+            }
+
+            // Anisotropy
+            uniformBuffer.updateFloat3("vAnisotropy", this.direction.x, this.direction.y, this.intensity);
+        }
+
+        // Textures
+        if (scene.texturesEnabled) {
+            if (this._texture && MaterialFlags.AnisotropicTextureEnabled) {
+                uniformBuffer.setTexture("anisotropySampler", this._texture);
+            }
         }
     }
 
     /**
-    * Get the current class name of the texture useful for serialization or dynamic coding.
-    * @returns "PBRAnisotropicConfiguration"
-    */
-    public getClassName(): string {
-        return "PBRAnisotropicConfiguration";
+     * 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;
+        }
+
+        return false;
     }
 
     /**
-     * Makes a duplicate of the current configuration into another one.
-     * @param anisotropicConfiguration define the config where to copy the info
+     * Returns an array of the actively used textures.
+     * @param activeTextures Array of BaseTextures
      */
-    public copyTo(anisotropicConfiguration: PBRAnisotropicConfiguration): void {
-        SerializationHelper.Clone(() => anisotropicConfiguration, this);
+    public getActiveTextures(activeTextures: BaseTexture[]): void {
+        if (this._texture) {
+            activeTextures.push(this._texture);
+        }
     }
 
     /**
-     * Serializes this clear coat configuration.
-     * @returns - An object with the serialized config.
+     * Returns the animatable textures.
+     * @param animatables Array of animatable textures.
      */
-    public serialize(): any {
-        return SerializationHelper.Serialize(this);
+    public getAnimatables(animatables: IAnimatable[]): void {
+        if (this._texture && this._texture.animations && this._texture.animations.length > 0) {
+            animatables.push(this._texture);
+        }
     }
 
     /**
-     * Parses a Clear Coat Configuration from a serialized object.
-     * @param source - Serialized object.
+     * Disposes the resources of the material.
+     * @param forceDisposeTextures - Forces the disposal of all textures.
      */
-    public parse(source: any): void {
-        SerializationHelper.Parse(() => this, source, null);
+    public dispose(forceDisposeTextures?: boolean): void {
+        if (forceDisposeTextures) {
+            if (this._texture) {
+                this._texture.dispose();
+            }
+        }
+    }
+
+    /**
+    * Get the current class name of the texture useful for serialization or dynamic coding.
+    * @returns "PBRAnisotropicConfiguration"
+    */
+    public getClassName(): string {
+        return "PBRAnisotropicConfiguration";
     }
 
     /**
@@ -133,7 +223,7 @@ export class PBRAnisotropicConfiguration {
      * @param uniforms defines the current uniform list.
      */
     public static AddUniforms(uniforms: string[]): void {
-        uniforms.push("anisotropy");
+        uniforms.push("vAnisotropy", "vAnisotropyInfos", "anisotropyMatrix");
     }
 
     /**
@@ -141,6 +231,40 @@ export class PBRAnisotropicConfiguration {
      * @param uniformBuffer defines the current uniform buffer.
      */
     public static PrepareUniformBuffer(uniformBuffer: UniformBuffer): void {
-        uniformBuffer.addUniform("anisotropy", 1);
+        uniformBuffer.addUniform("vAnisotropy", 3);
+        uniformBuffer.addUniform("vAnisotropyInfos", 2);
+        uniformBuffer.addUniform("anisotropyMatrix", 16);
+    }
+
+    /**
+     * Add the required samplers to the current list.
+     * @param samplers defines the current sampler list.
+     */
+    public static AddSamplers(samplers: string[]): void {
+        samplers.push("anisotropySampler");
+    }
+
+    /**
+     * Makes a duplicate of the current configuration into another one.
+     * @param anisotropicConfiguration define the config where to copy the info
+     */
+    public copyTo(anisotropicConfiguration: PBRAnisotropicConfiguration): void {
+        SerializationHelper.Clone(() => anisotropicConfiguration, this);
+    }
+
+    /**
+     * Serializes this anisotropy configuration.
+     * @returns - An object with the serialized config.
+     */
+    public serialize(): any {
+        return SerializationHelper.Serialize(this);
+    }
+
+    /**
+     * Parses a anisotropy Configuration from a serialized object.
+     * @param source - Serialized object.
+     */
+    public parse(source: any): void {
+        SerializationHelper.Parse(() => this, source, null);
     }
 }

+ 46 - 5
src/Materials/PBR/pbrBaseMaterial.ts

@@ -17,6 +17,7 @@ import { _DepthCullingState, _StencilState, _AlphaState } from "../../States/ind
 import { IMaterialClearCoatDefines, PBRClearCoatConfiguration } from "./pbrClearCoatConfiguration";
 import { IMaterialAnisotropicDefines, PBRAnisotropicConfiguration } from "./pbrAnisotropicConfiguration";
 import { IMaterialBRDFDefines, PBRBRDFConfiguration } from "./pbrBRDFConfiguration";
+import { IMaterialSheenDefines, PBRSheenConfiguration } from "./pbrSheenConfiguration";
 
 import { ImageProcessingConfiguration, IImageProcessingConfigurationDefines } from "../../Materials/imageProcessingConfiguration";
 import { Effect, EffectFallbacks, EffectCreationOptions } from "../../Materials/effect";
@@ -41,7 +42,11 @@ import "../../Shaders/pbr.vertex";
  * @hiddenChildren
  */
 class PBRMaterialDefines extends MaterialDefines
-    implements IImageProcessingConfigurationDefines, IMaterialClearCoatDefines, IMaterialAnisotropicDefines, IMaterialBRDFDefines {
+    implements IImageProcessingConfigurationDefines,
+    IMaterialClearCoatDefines,
+    IMaterialAnisotropicDefines,
+    IMaterialBRDFDefines,
+    IMaterialSheenDefines {
     public PBR = true;
 
     public MAINUV1 = false;
@@ -192,10 +197,17 @@ class PBRMaterialDefines extends MaterialDefines
     public CLEARCOAT_TINT_TEXTUREDIRECTUV = 0;
 
     public ANISOTROPIC = false;
+    public ANISOTROPIC_TEXTURE = false;
+    public ANISOTROPIC_TEXTUREDIRECTUV = 0;
 
     public BRDF_V_HEIGHT_CORRELATED = false;
     public MS_BRDF_ENERGY_CONSERVATION = false;
 
+    public SHEEN = false;
+    public SHEEN_TEXTURE = false;
+    public SHEEN_TEXTUREDIRECTUV = 0;
+    public SHEEN_LINKWITHALBEDO = false;
+
     public UNLIT = false;
 
     public DEBUGMODE = 0;
@@ -699,7 +711,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     /**
      * Defines the anisotropic parameters for the material.
      */
-    public readonly anisotropy = new PBRAnisotropicConfiguration(this._markAllSubMeshesAsMiscDirty.bind(this));
+    public readonly anisotropy = new PBRAnisotropicConfiguration(this._markAllSubMeshesAsTexturesDirty.bind(this));
 
     /**
      * Defines the BRDF parameters for the material.
@@ -707,6 +719,11 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     public readonly brdf = new PBRBRDFConfiguration(this._markAllSubMeshesAsMiscDirty.bind(this));
 
     /**
+     * Defines the Sheen parameters for the material.
+     */
+    public readonly sheen = new PBRSheenConfiguration(this._markAllSubMeshesAsTexturesDirty.bind(this));
+
+    /**
      * Instantiates a new PBRMaterial instance.
      *
      * @param name The material name
@@ -977,6 +994,14 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             return false;
         }
 
+        if (!this.sheen.isReadyForSubMesh(defines, scene)) {
+            return false;
+        }
+
+        if (!this.anisotropy.isReadyForSubMesh(defines, scene)) {
+            return false;
+        }
+
         if (defines._areImageProcessingDirty && this._imageProcessingConfiguration) {
             if (!this._imageProcessingConfiguration.isReady()) {
                 return false;
@@ -1064,6 +1089,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
         fallbackRank = PBRAnisotropicConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
         fallbackRank = PBRClearCoatConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
+        fallbackRank = PBRSheenConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
 
         if (defines.ENVIRONMENTBRDF) {
             fallbacks.addFallback(fallbackRank++, "ENVIRONMENTBRDF");
@@ -1170,6 +1196,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         PBRClearCoatConfiguration.AddSamplers(samplers);
 
         PBRAnisotropicConfiguration.AddUniforms(uniforms);
+        PBRAnisotropicConfiguration.AddSamplers(samplers);
+
+        PBRSheenConfiguration.AddUniforms(uniforms);
+        PBRSheenConfiguration.AddSamplers(samplers);
 
         if (ImageProcessingConfiguration) {
             ImageProcessingConfiguration.PrepareUniforms(uniforms, defines);
@@ -1466,8 +1496,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
         // External config
         this.clearCoat.prepareDefines(defines, scene);
-        this.anisotropy.prepareDefines(defines, mesh);
+        this.anisotropy.prepareDefines(defines, mesh, scene);
         this.brdf.prepareDefines(defines);
+        this.sheen.prepareDefines(defines, scene);
 
         // Values that need to be evaluated on every frame
         MaterialHelper.PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances ? true : false, useClipPlane);
@@ -1543,6 +1574,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
         PBRClearCoatConfiguration.PrepareUniformBuffer(this._uniformBuffer);
         PBRAnisotropicConfiguration.PrepareUniformBuffer(this._uniformBuffer);
+        PBRSheenConfiguration.PrepareUniformBuffer(this._uniformBuffer);
 
         this._uniformBuffer.create();
     }
@@ -1809,7 +1841,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             }
 
             this.clearCoat.bindForSubMesh(this._uniformBuffer, scene, engine, this._disableBumpMap, this.isFrozen, this._invertNormalMapX, this._invertNormalMapY);
-            this.anisotropy.bindForSubMesh(this._uniformBuffer, this.isFrozen);
+            this.anisotropy.bindForSubMesh(this._uniformBuffer, scene, this.isFrozen);
+            this.sheen.bindForSubMesh(this._uniformBuffer, scene, this.isFrozen);
 
             // Clip plane
             MaterialHelper.BindClipPlane(this._activeEffect, scene);
@@ -1907,6 +1940,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         }
 
         this.clearCoat.getAnimatables(results);
+        this.sheen.getAnimatables(results);
+        this.anisotropy.getAnimatables(results);
 
         return results;
     }
@@ -1992,6 +2027,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         }
 
         this.clearCoat.getActiveTextures(activeTextures);
+        this.sheen.getActiveTextures(activeTextures);
+        this.anisotropy.getActiveTextures(activeTextures);
 
         return activeTextures;
     }
@@ -2042,7 +2079,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             return true;
         }
 
-        return this.clearCoat.hasTexture(texture);
+        return this.clearCoat.hasTexture(texture) ||
+            this.sheen.hasTexture(texture) ||
+            this.anisotropy.hasTexture(texture);
     }
 
     /**
@@ -2098,6 +2137,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         }
 
         this.clearCoat.dispose(forceDisposeTextures);
+        this.sheen.dispose(forceDisposeTextures);
+        this.anisotropy.dispose(forceDisposeTextures);
 
         this._renderTargets.dispose();
 

+ 26 - 26
src/Materials/PBR/pbrClearCoatConfiguration.ts

@@ -1,6 +1,6 @@
 import { Nullable } from "../../types";
 import { IAnimatable } from "../../Misc/tools";
-import { SerializationHelper, serialize, serializeAsTexture, expandToProperty } from "../../Misc/decorators";
+import { SerializationHelper, serialize, serializeAsTexture, expandToProperty, serializeAsColor3 } from "../../Misc/decorators";
 import { Color3 } from "../../Maths/math";
 import { BaseTexture } from "../../Materials/Textures/baseTexture";
 import { EffectFallbacks } from "../../Materials/effect";
@@ -99,7 +99,7 @@ export class PBRClearCoatConfiguration {
      * Defines if the clear coat tint is enabled in the material.
      * This is only use if tint is enabled
      */
-    @serialize()
+    @serializeAsColor3()
     public tintColor = Color3.White();
 
     /**
@@ -383,30 +383,6 @@ export class 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.
@@ -462,4 +438,28 @@ export class PBRClearCoatConfiguration {
         uniformBuffer.addUniform("vClearCoatTintInfos", 2);
         uniformBuffer.addUniform("clearCoatTintMatrix", 16);
     }
+
+    /**
+     * 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);
+    }
 }

+ 5 - 0
src/Materials/PBR/pbrMaterial.ts

@@ -699,6 +699,7 @@ export class PBRMaterial extends PBRBaseMaterial {
         this.clearCoat.copyTo(clone.clearCoat);
         this.anisotropy.copyTo(clone.anisotropy);
         this.brdf.copyTo(clone.brdf);
+        this.sheen.copyTo(clone.sheen);
 
         return clone;
     }
@@ -714,6 +715,7 @@ export class PBRMaterial extends PBRBaseMaterial {
         serializationObject.clearCoat = this.clearCoat.serialize();
         serializationObject.anisotropy = this.anisotropy.serialize();
         serializationObject.brdf = this.brdf.serialize();
+        serializationObject.sheen = this.sheen.serialize();
 
         return serializationObject;
     }
@@ -737,6 +739,9 @@ export class PBRMaterial extends PBRBaseMaterial {
         if (source.brdf) {
             material.brdf.parse(source.brdf);
         }
+        if (source.sheen) {
+            material.sheen.parse(source.brdf);
+        }
         return material;
     }
 }

+ 5 - 0
src/Materials/PBR/pbrMetallicRoughnessMaterial.ts

@@ -92,6 +92,7 @@ export class PBRMetallicRoughnessMaterial extends PBRBaseSimpleMaterial {
         this.clearCoat.copyTo(clone.clearCoat);
         this.anisotropy.copyTo(clone.anisotropy);
         this.brdf.copyTo(clone.brdf);
+        this.sheen.copyTo(clone.sheen);
 
         return clone;
     }
@@ -106,6 +107,7 @@ export class PBRMetallicRoughnessMaterial extends PBRBaseSimpleMaterial {
         serializationObject.clearCoat = this.clearCoat.serialize();
         serializationObject.anisotropy = this.anisotropy.serialize();
         serializationObject.brdf = this.brdf.serialize();
+        serializationObject.sheen = this.sheen.serialize();
 
         return serializationObject;
     }
@@ -124,6 +126,9 @@ export class PBRMetallicRoughnessMaterial extends PBRBaseSimpleMaterial {
         if (source.brdf) {
             material.brdf.parse(source.brdf);
         }
+        if (source.sheen) {
+            material.sheen.parse(source.brdf);
+        }
         return material;
     }
 }

+ 276 - 0
src/Materials/PBR/pbrSheenConfiguration.ts

@@ -0,0 +1,276 @@
+import { SerializationHelper, serialize, expandToProperty, serializeAsColor3, serializeAsTexture } from "../../Misc/decorators";
+import { EffectFallbacks } from "../../Materials/effect";
+import { UniformBuffer } from "../../Materials/uniformBuffer";
+import { Color3 } from "../../Maths/math";
+import { Scene } from "../../scene";
+import { MaterialFlags } from "../../Materials/materialFlags";
+import { MaterialHelper } from "../../Materials/materialHelper";
+import { BaseTexture } from "../../Materials/Textures/baseTexture";
+import { IAnimatable } from "../../Misc";
+import { Nullable } from "../../types";
+
+/**
+ * @hidden
+ */
+export interface IMaterialSheenDefines {
+    SHEEN: boolean;
+    SHEEN_TEXTURE: boolean;
+    SHEEN_TEXTUREDIRECTUV: number;
+    SHEEN_LINKWITHALBEDO: boolean;
+
+    /** @hidden */
+    _areTexturesDirty: boolean;
+}
+
+/**
+ * Define the code related to the Sheen parameters of the pbr material.
+ */
+export class PBRSheenConfiguration {
+
+    @serialize()
+    private _isEnabled = false;
+    /**
+     * Defines if the material uses sheen.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public isEnabled = false;
+
+    @serialize()
+    private _linkSheenWithAlbedo = false;
+    /**
+     * Defines if the sheen is linked to the sheen color.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public linkSheenWithAlbedo = false;
+
+    /**
+     * Defines the sheen intensity.
+     */
+    @serialize()
+    public intensity = 1;
+
+    /**
+     * Defines the sheen color.
+     */
+    @serializeAsColor3()
+    public color = Color3.White();
+
+    @serializeAsTexture()
+    private _texture: Nullable<BaseTexture> = null;
+    /**
+     * Stores the sheen tint values in a texture.
+     * rgb is tint
+     * a is a intensity
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public texture: Nullable<BaseTexture> = null;
+
+    /** @hidden */
+    private _internalMarkAllSubMeshesAsTexturesDirty: () => void;
+
+    /** @hidden */
+    public _markAllSubMeshesAsTexturesDirty(): void {
+        this._internalMarkAllSubMeshesAsTexturesDirty();
+    }
+
+    /**
+     * Instantiate a new istance of clear coat configuration.
+     * @param markAllSubMeshesAsTexturesDirty Callback to flag the material to dirty
+     */
+    constructor(markAllSubMeshesAsTexturesDirty: () => void) {
+        this._internalMarkAllSubMeshesAsTexturesDirty = markAllSubMeshesAsTexturesDirty;
+    }
+
+    /**
+     * Specifies that the submesh is ready to be used.
+     * @param defines the list of "defines" to update.
+     * @param scene defines the scene the material belongs to.
+     * @returns - boolean indicating that the submesh is ready or not.
+     */
+    public isReadyForSubMesh(defines: IMaterialSheenDefines, scene: Scene): boolean {
+        if (defines._areTexturesDirty) {
+            if (scene.texturesEnabled) {
+                if (this._texture && MaterialFlags.SheenTextureEnabled) {
+                    if (!this._texture.isReadyOrNotBlocking()) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks to see if a texture is used in the material.
+     * @param defines the list of "defines" to update.
+     * @param scene defines the scene the material belongs to.
+     */
+    public prepareDefines(defines: IMaterialSheenDefines, scene: Scene): void {
+        if (this._isEnabled) {
+            defines.SHEEN = this._isEnabled;
+            defines.SHEEN_LINKWITHALBEDO = this._linkSheenWithAlbedo;
+
+            if (defines._areTexturesDirty) {
+                if (scene.texturesEnabled) {
+                    if (this._texture && MaterialFlags.SheenTextureEnabled) {
+                        MaterialHelper.PrepareDefinesForMergedUV(this._texture, defines, "SHEEN_TEXTURE");
+                    } else {
+                        defines.SHEEN_TEXTURE = false;
+                    }
+                }
+            }
+        }
+        else {
+            defines.SHEEN = false;
+            defines.SHEEN_TEXTURE = false;
+            defines.SHEEN_LINKWITHALBEDO = false;
+        }
+    }
+
+    /**
+     * Binds the material data.
+     * @param uniformBuffer defines the Uniform buffer to fill in.
+     * @param scene defines the scene the material belongs to.
+     * @param isFrozen defines wether the material is frozen or not.
+     */
+    public bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, isFrozen: boolean): void {
+        if (!uniformBuffer.useUbo || !isFrozen || !uniformBuffer.isSync) {
+            if (this._texture && MaterialFlags.SheenTextureEnabled) {
+                uniformBuffer.updateFloat2("vSheenInfos", this._texture.coordinatesIndex, this._texture.level);
+                MaterialHelper.BindTextureMatrix(this._texture, uniformBuffer, "sheen");
+            }
+
+            // Sheen
+            uniformBuffer.updateFloat4("vSheenColor",
+                this.color.r,
+                this.color.g,
+                this.color.b,
+                this.intensity);
+        }
+
+        // Textures
+        if (scene.texturesEnabled) {
+            if (this._texture && MaterialFlags.SheenTextureEnabled) {
+                uniformBuffer.setTexture("sheenSampler", this._texture);
+            }
+        }
+    }
+
+    /**
+     * 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;
+        }
+
+        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);
+        }
+    }
+
+    /**
+     * 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);
+        }
+    }
+
+    /**
+     * 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();
+            }
+        }
+    }
+
+    /**
+    * Get the current class name of the texture useful for serialization or dynamic coding.
+    * @returns "PBRSheenConfiguration"
+    */
+    public getClassName(): string {
+        return "PBRSheenConfiguration";
+    }
+
+    /**
+     * 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: IMaterialSheenDefines, fallbacks: EffectFallbacks, currentRank: number): number {
+        if (defines.SHEEN) {
+            fallbacks.addFallback(currentRank++, "SHEEN");
+        }
+        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("vSheenColor", "vSheenInfos", "sheenMatrix");
+    }
+
+    /**
+     * Add the required uniforms to the current buffer.
+     * @param uniformBuffer defines the current uniform buffer.
+     */
+    public static PrepareUniformBuffer(uniformBuffer: UniformBuffer): void {
+        uniformBuffer.addUniform("vSheenColor", 4);
+        uniformBuffer.addUniform("vSheenInfos", 2);
+        uniformBuffer.addUniform("sheenMatrix", 16);
+    }
+
+    /**
+     * Add the required samplers to the current list.
+     * @param samplers defines the current sampler list.
+     */
+    public static AddSamplers(samplers: string[]): void {
+        samplers.push("sheenSampler");
+    }
+
+    /**
+     * Makes a duplicate of the current configuration into another one.
+     * @param sheenConfiguration define the config where to copy the info
+     */
+    public copyTo(sheenConfiguration: PBRSheenConfiguration): void {
+        SerializationHelper.Clone(() => sheenConfiguration, this);
+    }
+
+    /**
+     * Serializes this BRDF configuration.
+     * @returns - An object with the serialized config.
+     */
+    public serialize(): any {
+        return SerializationHelper.Serialize(this);
+    }
+
+    /**
+     * Parses a Sheen Configuration from a serialized object.
+     * @param source - Serialized object.
+     */
+    public parse(source: any): void {
+        SerializationHelper.Parse(() => this, source, null);
+    }
+}

+ 5 - 0
src/Materials/PBR/pbrSpecularGlossinessMaterial.ts

@@ -82,6 +82,7 @@ export class PBRSpecularGlossinessMaterial extends PBRBaseSimpleMaterial {
         this.clearCoat.copyTo(clone.clearCoat);
         this.anisotropy.copyTo(clone.anisotropy);
         this.brdf.copyTo(clone.brdf);
+        this.sheen.copyTo(clone.sheen);
 
         return clone;
     }
@@ -96,6 +97,7 @@ export class PBRSpecularGlossinessMaterial extends PBRBaseSimpleMaterial {
         serializationObject.clearCoat = this.clearCoat.serialize();
         serializationObject.anisotropy = this.anisotropy.serialize();
         serializationObject.brdf = this.brdf.serialize();
+        serializationObject.sheen = this.sheen.serialize();
 
         return serializationObject;
     }
@@ -114,6 +116,9 @@ export class PBRSpecularGlossinessMaterial extends PBRBaseSimpleMaterial {
         if (source.brdf) {
             material.brdf.parse(source.brdf);
         }
+        if (source.sheen) {
+            material.sheen.parse(source.brdf);
+        }
         return material;
     }
 }

+ 32 - 0
src/Materials/materialFlags.ts

@@ -229,4 +229,36 @@ export class MaterialFlags {
         this._ClearCoatTintTextureEnabled = value;
         Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
     }
+
+    private static _SheenTextureEnabled = true;
+    /**
+     * Are sheen textures enabled in the application.
+     */
+    public static get SheenTextureEnabled(): boolean {
+        return this._SheenTextureEnabled;
+    }
+    public static set SheenTextureEnabled(value: boolean) {
+        if (this._SheenTextureEnabled === value) {
+            return;
+        }
+
+        this._SheenTextureEnabled = value;
+        Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+    }
+
+    private static _AnisotropicTextureEnabled = true;
+    /**
+     * Are anisotropic textures enabled in the application.
+     */
+    public static get AnisotropicTextureEnabled(): boolean {
+        return this._AnisotropicTextureEnabled;
+    }
+    public static set AnisotropicTextureEnabled(value: boolean) {
+        if (this._AnisotropicTextureEnabled === value) {
+            return;
+        }
+
+        this._AnisotropicTextureEnabled = value;
+        Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+    }
 }

+ 21 - 1
src/Shaders/ShadersInclude/lightFragment.fx

@@ -63,12 +63,21 @@
             // Specular contribution
             #ifdef SPECULARTERM
                 #ifdef ANISOTROPIC
-                    info.specular = computeAnisotropicSpecularLighting(preInfo, viewDirectionW, normalW, normalize(TBN[0]), normalize(TBN[1]), anisotropy, specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb);
+                    info.specular = computeAnisotropicSpecularLighting(preInfo, viewDirectionW, normalW, anisotropicTangent, anisotropicBitangent, anisotropy, specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb);
                 #else
                     info.specular = computeSpecularLighting(preInfo, normalW, specularEnvironmentR0, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb);
                 #endif
             #endif
 
+            // Sheen contribution
+            #ifdef SHEEN
+                #ifdef SHEEN_LINKWITHALBEDO
+                    // BE Carefull: Sheen intensity is replacing the roughness value.
+                    preInfo.roughness = sheenIntensity;
+                #endif
+                info.sheen = computeSheenLighting(preInfo, normalW, sheenColor, specularEnvironmentR90, AARoughnessFactors.x, light{X}.vLightDiffuse.rgb);
+            #endif
+
             // Clear Coat contribution
             #ifdef CLEARCOAT
                 // Simulates Light radius
@@ -94,6 +103,9 @@
                 #ifdef SPECULARTERM
                     info.specular *= info.clearCoat.w * info.clearCoat.w;
                 #endif
+                #ifdef SHEEN
+                    info.sheen *= info.clearCoat.w * info.clearCoat.w;
+                #endif
             #endif
         #else
             #ifdef SPOTLIGHT{X}
@@ -182,6 +194,11 @@
                     clearCoatBase += info.clearCoat.rgb * shadow * lightmapColor;
                 #endif
             #endif
+            #ifdef SHEEN
+                #ifndef LIGHTMAPNOSPECULAR{X}
+                    sheenBase += info.sheen.rgb * shadow;
+                #endif
+            #endif
         #else
             diffuseBase += info.diffuse * shadow;
             #ifdef SPECULARTERM
@@ -190,6 +207,9 @@
             #ifdef CLEARCOAT
                 clearCoatBase += info.clearCoat.rgb * shadow;
             #endif
+            #ifdef SHEEN
+                sheenBase += info.sheen.rgb * shadow;
+            #endif
         #endif
     #endif
 #endif

+ 19 - 3
src/Shaders/ShadersInclude/pbrDebug.fx

@@ -40,6 +40,12 @@
     #elif DEBUGMODE == 11 && defined(ANISOTROPIC)
         gl_FragColor.rgb = anisotropicNormal;
         #define DEBUGMODE_NORMALIZE
+    #elif DEBUGMODE == 12 && defined(ANISOTROPIC)
+        gl_FragColor.rgb = anisotropicTangent;
+        #define DEBUGMODE_NORMALIZE
+    #elif DEBUGMODE == 13 && defined(ANISOTROPIC)
+        gl_FragColor.rgb = anisotropicBitangent;
+        #define DEBUGMODE_NORMALIZE
 // Maps
     #elif DEBUGMODE == 20 && defined(ALBEDO)
         gl_FragColor.rgb = albedoTexture.rgb;
@@ -62,6 +68,10 @@
         gl_FragColor.rgb = clearCoatMapData.rgb;
     #elif DEBUGMODE == 28 && defined(CLEARCOAT) && defined(CLEARCOAT_TINT) && defined(CLEARCOAT_TINT_TEXTURE)
         gl_FragColor.rgb = clearCoatTintMapData.rgb;
+    #elif DEBUGMODE == 29 && defined(SHEEN) && defined(SHEEN_TEXTURE)
+        gl_FragColor.rgb = sheenMapData.rgb;
+    #elif DEBUGMODE == 30 && defined(ANISOTROPIC) && defined(ANISOTROPIC_TEXTURE)
+        gl_FragColor.rgb = anisotropyMapData.rgb;
 // Env
     #elif DEBUGMODE == 40 && defined(REFRACTION)
         // Base color.
@@ -83,7 +93,10 @@
     #elif DEBUGMODE == 52 && defined(CLEARCOAT)
         gl_FragColor.rgb = clearCoatBase.rgb;
         #define DEBUGMODE_GAMMA
-    #elif DEBUGMODE == 53 && defined(REFLECTION)
+    #elif DEBUGMODE == 53 && defined(SHEEN)
+        gl_FragColor.rgb = sheenBase.rgb;
+        #define DEBUGMODE_GAMMA
+    #elif DEBUGMODE == 54 && defined(REFLECTION)
         gl_FragColor.rgb = environmentIrradiance.rgb;
         #define DEBUGMODE_GAMMA
 // Lighting Params
@@ -119,9 +132,12 @@
     #elif DEBUGMODE == 74 && defined(CLEARCOAT) && defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX)
         gl_FragColor.rgb = clearCoatEnvironmentReflectance;
         #define DEBUGMODE_GAMMA
-    #elif DEBUGMODE == 75 && defined(ALPHABLEND)
+    #elif DEBUGMODE == 75 && defined(SHEEN)
+        gl_FragColor.rgb = sheenEnvironmentReflectance;
+        #define DEBUGMODE_GAMMA
+    #elif DEBUGMODE == 76 && defined(ALPHABLEND)
         gl_FragColor.rgb = vec3(luminanceOverAlpha);
-    #elif DEBUGMODE == 76
+    #elif DEBUGMODE == 77
         gl_FragColor.rgb = vec3(alpha);
     #endif
 

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

@@ -94,5 +94,20 @@ uniform mat4 view;
 
 // Anisotropy
 #ifdef ANISOTROPIC
-    uniform float anisotropy;
+    uniform vec3 vAnisotropy;
+
+    #ifdef ANISOTROPIC_TEXTURE
+        uniform vec2 vAnisotropyInfos;
+        uniform mat4 anisotropyMatrix;
+    #endif
+#endif
+
+// Sheen
+#ifdef SHEEN
+    uniform vec4 vSheenColor;
+
+    #ifdef SHEEN_TEXTURE
+        uniform vec2 vSheenInfos;
+        uniform mat4 sheenMatrix;
+    #endif
 #endif

+ 104 - 38
src/Shaders/ShadersInclude/pbrFunctions.fx

@@ -53,6 +53,32 @@ vec2 getBRDFLookup(float NdotV, float perceptualRoughness, sampler2D brdfSampler
     return brdfLookup;
 }
 
+/**
+ * Special thanks to @romainguy for all the support :-)
+ * Analytical approximation of the pre-filtered DFG terms for the cloth shading
+ * model. This approximation is based on the Estevez & Kulla distribution term
+ * ("Charlie" sheen) and the Neubelt visibility term. See brdf.fs for more
+ * details.
+ */
+vec2 getCharlieSheenAnalyticalBRDFLookup_RomainGuy(float NoV, float roughness) {
+    const vec3 c0 = vec3(0.95, 1250.0, 0.0095);
+    const vec4 c1 = vec4(0.04, 0.2, 0.3, 0.2);
+
+    float a = 1.0 - NoV;
+    float b = 1.0 - roughness;
+
+    float n = pow(c1.x + a, 64.0);
+    float e = b - c0.x;
+    float g = exp2(-(e * e) * c0.y);
+    float f = b + c1.y;
+    float a2 = a * a;
+    float a3 = a2 * a;
+    float c = n * g + c1.z * (a + c1.w) * roughness + f * f * a3 * a3 * a2;
+    float r = min(c, 18.0);
+
+    return vec2(r, r * c0.z);
+}
+
 vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0, vec2 environmentBrdf) {
     #ifdef BRDF_V_HEIGHT_CORRELATED
         vec3 reflectance = mix(environmentBrdf.xxx, environmentBrdf.yyy, specularEnvironmentR0);
@@ -62,6 +88,20 @@ vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0, vec2 environ
     return reflectance;
 }
 
+vec3 getReflectanceFromAnalyticalBRDFLookup_Jones(float VdotN, vec3 reflectance0, vec3 reflectance90, float smoothness)
+{
+    // Schlick fresnel approximation, extended with basic smoothness term so that rough surfaces do not approach reflectance90 at grazing angle
+    float weight = mix(FRESNEL_MAXIMUM_ON_ROUGH, 1.0, smoothness);
+    return reflectance0 + weight * (reflectance90 - reflectance0) * pow(clamp(1.0 - VdotN, 0., 1.), 5.0);
+}
+
+vec3 getSheenReflectanceFromBRDFLookup(const vec3 reflectance0, float NdotV, float sheenAlphaG) {
+    vec2 environmentSheenBrdf = getCharlieSheenAnalyticalBRDFLookup_RomainGuy(NdotV, sheenAlphaG);
+    vec3 reflectance = reflectance0 * environmentSheenBrdf.x + environmentSheenBrdf.y;
+
+    return reflectance;
+}
+
 // Schlick's approximation for R0 (Fresnel Reflectance Values)
 // Keep for references
 // vec3 getR0fromAirToSurfaceIOR(vec3 ior1) {
@@ -141,13 +181,29 @@ float smithVisibility_TrowbridgeReitzGGXFast(float NdotL, float NdotV, float alp
     return visibility;
 }
 
-float kelemenVisibility(float VdotH) {
+float visibility_Kelemen(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); 
 }
 
+// https://knarkowicz.wordpress.com/2018/01/04/cloth-shading/
+// https://blog.selfshadow.com/publications/s2017-shading-course/imageworks/s2017_pbs_imageworks_sheen.pdf
+// http://www.cs.utah.edu/~premoze/dbrdf/dBRDF.pdf
+float visibility_Ashikhmin(float NdotL, float NdotV)
+{
+    return 1. / (4. * (NdotL + NdotV - NdotL * NdotV));
+}
+
+float normalDistributionFunction_CharlieSheen(float NdotH, float alphaG)
+{
+    float invR = 1. / alphaG;
+    float cos2h = NdotH * NdotH;
+    float sin2h = 1. - cos2h;
+    return (2. + invR) * pow(sin2h, invR * .5) / (2. * PI);
+}
+
 // Trowbridge-Reitz (GGX)
 // Generalised Trowbridge-Reitz with gamma power=2.0
 float normalDistributionFunction_TrowbridgeReitzGGX(float NdotH, float alphaG)
@@ -170,8 +226,8 @@ vec2 getAnisotropicRoughness(float alphaG, float anisotropy) {
 
 // Aniso Bent Normals
 // Mc Alley https://www.gdcvault.com/play/1022235/Rendering-the-World-of-Far 
-vec3 getAnisotropicBentNormals(const mat3 TBN, const vec3 V, const vec3 N, float anisotropy) {
-    vec3 anisotropicFrameDirection = anisotropy >= 0.0 ? TBN[1] : TBN[0];
+vec3 getAnisotropicBentNormals(const vec3 T, const vec3 B, const vec3 N, const vec3 V, float anisotropy) {
+    vec3 anisotropicFrameDirection = anisotropy >= 0.0 ? B : T;
     vec3 anisotropicFrameTangent = cross(normalize(anisotropicFrameDirection), V);
     vec3 anisotropicFrameNormal = cross(anisotropicFrameTangent, anisotropicFrameDirection);
     vec3 anisotropicNormal = normalize(mix(N, anisotropicFrameNormal, abs(anisotropy)));
@@ -226,13 +282,6 @@ float fresnelSchlickGGX(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)
-{
-    // Schlick fresnel approximation, extended with basic smoothness term so that rough surfaces do not approach reflectance90 at grazing angle
-    float weight = mix(FRESNEL_MAXIMUM_ON_ROUGH, 1.0, smoothness);
-    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
@@ -280,40 +329,57 @@ vec3 computeSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, flo
     return fresnel * specTerm;
 }
 
-vec3 computeAnisotropicSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float TdotH, float BdotH, float TdotV, float BdotV, float TdotL, float BdotL, float roughness, float anisotropy, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor) {
-    float alphaG = convertRoughnessToAverageSlope(roughness);
-    vec2 alphaTB = getAnisotropicRoughness(alphaG, anisotropy);
-    alphaTB = max(alphaTB, geometricRoughnessFactor * geometricRoughnessFactor);
+#ifdef SHEEN
+    vec3 computeSheenTerm(float NdotH, float NdotL, float NdotV, float VdotH, float roughness, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor) {
+        roughness = max(roughness, geometricRoughnessFactor);
+        float alphaG = convertRoughnessToAverageSlope(roughness);
 
-    float distribution = normalDistributionFunction_BurleyGGX_Anisotropic(NdotH, TdotH, BdotH, alphaTB);
-    float visibility = smithVisibility_GGXCorrelated_Anisotropic(NdotV, NdotL, TdotV, BdotV, TdotL, BdotL, alphaTB);
-    float specTerm = max(0., visibility * distribution);
+        float distribution = normalDistributionFunction_CharlieSheen(NdotH, alphaG);
+        float visibility = visibility_Ashikhmin(NdotL, NdotV);
 
-    vec3 fresnel = fresnelSchlickGGX(VdotH, reflectance0, reflectance90);
-    return fresnel * specTerm;
-}
+        float specTerm = max(0., visibility * distribution);
 
-#ifdef CLEARCOAT
-vec2 computeClearCoatTerm(float NdotH, float VdotH, float clearCoatRoughness, float geometricRoughnessFactor, float clearCoatIntensity) {
-    clearCoatRoughness = max(clearCoatRoughness, geometricRoughnessFactor);
-    float alphaG = convertRoughnessToAverageSlope(clearCoatRoughness);
+        vec3 fresnel = fresnelSchlickGGX(VdotH, reflectance0, reflectance90);
+        return vec3(specTerm);
+    }
+#endif
 
-    float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
-    float visibility = kelemenVisibility(VdotH);
-    float clearCoatTerm = max(0., visibility * distribution);
+#ifdef ANISOTROPIC
+    vec3 computeAnisotropicSpecularTerm(float NdotH, float NdotL, float NdotV, float VdotH, float TdotH, float BdotH, float TdotV, float BdotV, float TdotL, float BdotL, float roughness, float anisotropy, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor) {
+        float alphaG = convertRoughnessToAverageSlope(roughness);
+        vec2 alphaTB = getAnisotropicRoughness(alphaG, anisotropy);
+        alphaTB = max(alphaTB, geometricRoughnessFactor * geometricRoughnessFactor);
 
-    float fresnel = fresnelSchlickGGX(VdotH, vClearCoatRefractionParams.x, CLEARCOATREFLECTANCE90);
-    fresnel *= clearCoatIntensity;
-    
-    return vec2(fresnel * clearCoatTerm, 1.0 - fresnel);
-}
+        float distribution = normalDistributionFunction_BurleyGGX_Anisotropic(NdotH, TdotH, BdotH, alphaTB);
+        float visibility = smithVisibility_GGXCorrelated_Anisotropic(NdotV, NdotL, TdotV, BdotV, TdotL, BdotL, alphaTB);
+        float specTerm = max(0., visibility * distribution);
 
-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;
-}
+        vec3 fresnel = fresnelSchlickGGX(VdotH, reflectance0, reflectance90);
+        return fresnel * specTerm;
+    }
+#endif
+
+#ifdef CLEARCOAT
+    vec2 computeClearCoatTerm(float NdotH, float VdotH, float clearCoatRoughness, float geometricRoughnessFactor, float clearCoatIntensity) {
+        clearCoatRoughness = max(clearCoatRoughness, geometricRoughnessFactor);
+        float alphaG = convertRoughnessToAverageSlope(clearCoatRoughness);
+
+        float distribution = normalDistributionFunction_TrowbridgeReitzGGX(NdotH, alphaG);
+        float visibility = visibility_Kelemen(VdotH);
+        float clearCoatTerm = max(0., visibility * distribution);
+
+        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;
+    }
 #endif
 
 float adjustRoughnessFromLightProperties(float roughness, float lightRadius, float lightDistance)

+ 40 - 26
src/Shaders/ShadersInclude/pbrLightingFunctions.fx

@@ -10,6 +10,9 @@ struct lightingInfo
         // w contains the 1 - clearcoat fresnel to ease the energy conservation computation.
         vec4 clearCoat;
     #endif
+    #ifdef SHEEN
+        vec3 sheen;
+    #endif
 };
 
 vec3 computeHemisphericDiffuseLighting(preLightingInfo info, vec3 lightColor, vec3 groundColor) {
@@ -28,41 +31,52 @@ vec3 computeSpecularLighting(preLightingInfo info, vec3 N, vec3 reflectance0, ve
     return specTerm * info.attenuation * info.NdotL * lightColor;
 }
 
-vec3 computeAnisotropicSpecularLighting(preLightingInfo info, vec3 V, vec3 N, vec3 T, vec3 B, float anisotropy, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, vec3 lightColor) {
-    float NdotH = clamp(dot(N, info.H), 0.000000000001, 1.0);
+#ifdef ANISOTROPIC
+    vec3 computeAnisotropicSpecularLighting(preLightingInfo info, vec3 V, vec3 N, vec3 T, vec3 B, float anisotropy, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, vec3 lightColor) {
+        float NdotH = clamp(dot(N, info.H), 0.000000000001, 1.0);
 
-    float TdotH = dot(T, info.H);
-    float BdotH = dot(B, info.H);
+        float TdotH = dot(T, info.H);
+        float BdotH = dot(B, info.H);
 
-    float TdotV = dot(T, V);
-    float BdotV = dot(B, V);
-    float TdotL = dot(T, info.L);
-    float BdotL = dot(B, info.L);
+        float TdotV = dot(T, V);
+        float BdotV = dot(B, V);
+        float TdotL = dot(T, info.L);
+        float BdotL = dot(B, info.L);
 
-    vec3 specTerm = computeAnisotropicSpecularTerm(NdotH, info.NdotL, info.NdotV, info.VdotH, TdotH, BdotH, TdotV, BdotV, TdotL, BdotL, info.roughness, anisotropy, reflectance0, reflectance90, geometricRoughnessFactor);
-    return specTerm * info.attenuation * info.NdotL * lightColor;
-}
+        vec3 specTerm = computeAnisotropicSpecularTerm(NdotH, info.NdotL, info.NdotV, info.VdotH, TdotH, BdotH, TdotV, BdotV, TdotL, BdotL, info.roughness, anisotropy, reflectance0, reflectance90, geometricRoughnessFactor);
+        return specTerm * info.attenuation * info.NdotL * lightColor;
+    }
+#endif
 
 #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);
+    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);
 
-    vec2 clearCoatTerm = computeClearCoatTerm(NccdotH, info.VdotH, info.roughness, geometricRoughnessFactor, clearCoatIntensity);
+        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;
-}
+        vec4 result = vec4(0.);
+        result.rgb = clearCoatTerm.x * info.attenuation * NccdotL * lightColor;
+        result.a = clearCoatTerm.y;
+        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 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;
-}
+        vec3 absorption = computeClearCoatAbsorption(NdotVRefract, NdotLRefract, clearCoatColor, clearCoatThickness, clearCoatIntensity);
+        return absorption;
+    }
+#endif
+
+#ifdef SHEEN
+    vec3 computeSheenLighting(preLightingInfo info, vec3 N, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, vec3 lightColor) {
+        float NdotH = clamp(dot(N, info.H), 0.000000000001, 1.0);
+
+        vec3 specTerm = computeSheenTerm(NdotH, info.NdotL, info.NdotV, info.VdotH, info.roughness, reflectance0, reflectance90, geometricRoughnessFactor);
+        return specTerm * info.attenuation * info.NdotL * lightColor;
+    }
 #endif
 
 vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, mat4 textureProjectionMatrix){

+ 7 - 1
src/Shaders/ShadersInclude/pbrUboDeclaration.fx

@@ -50,7 +50,13 @@ uniform Material
     uniform vec2 vClearCoatTintInfos;
     uniform mat4 clearCoatTintMatrix;
 
-    uniform float anisotropy;
+    uniform vec3 vAnisotropy;
+    uniform vec2 vAnisotropyInfos;
+    uniform mat4 anisotropyMatrix;
+
+    uniform vec4 vSheenColor;
+    uniform vec2 vSheenInfos;
+    uniform mat4 sheenMatrix;
 };
 
 uniform Scene {

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

@@ -76,3 +76,19 @@ uniform float pointSize;
         uniform mat4 clearCoatTintMatrix;
     #endif
 #endif
+
+// Anisotropy
+#ifdef ANISOTROPIC
+    #ifdef ANISOTROPIC_TEXTURE
+        uniform vec2 vAnisotropyInfos;
+        uniform mat4 anisotropyMatrix;
+    #endif
+#endif
+
+// Sheen
+#ifdef SHEEN
+    #ifdef SHEEN_TEXTURE
+        uniform vec2 vSheenInfos;
+        uniform mat4 sheenMatrix;
+    #endif
+#endif

+ 191 - 7
src/Shaders/pbr.fragment.fx

@@ -161,6 +161,32 @@ varying vec4 vColor;
     #endif
 #endif
 
+#ifdef SHEEN
+    #ifdef SHEEN_TEXTURE
+        #if SHEEN_TEXTUREDIRECTUV == 1
+            #define vSheenUV vMainUV1
+        #elif SHEEN_TEXTUREDIRECTUV == 2
+            #define vSheenUV vMainUV2
+        #else
+            varying vec2 vSheenUV;
+        #endif
+        uniform sampler2D sheenSampler;
+    #endif
+#endif
+
+#ifdef ANISOTROPIC
+    #ifdef ANISOTROPIC_TEXTURE
+        #if ANISOTROPIC_TEXTUREDIRECTUV == 1
+            #define vAnisotropyUV vMainUV1
+        #elif ANISOTROPIC_TEXTUREDIRECTUV == 2
+            #define vAnisotropyUV vMainUV2
+        #else
+            varying vec2 vAnisotropyUV;
+        #endif
+        uniform sampler2D anisotropySampler;
+    #endif
+#endif
+
 // Refraction
 #ifdef REFRACTION
     #ifdef REFRACTIONMAP_3D
@@ -464,7 +490,7 @@ void main(void) {
             vec3 normalForward = faceforward(normalW, -viewDirectionW, normalW);
 
             // Calculate the appropriate linear opacity for the current viewing angle (formally, this quantity is the "directional absorptance").
-            alpha = fresnelSchlickEnvironmentGGX(clamp(dot(viewDirectionW, normalForward), 0.0, 1.0), vec3(opacity0), vec3(opacity90), sqrt(microSurface)).x;
+            alpha = getReflectanceFromAnalyticalBRDFLookup_Jones(clamp(dot(viewDirectionW, normalForward), 0.0, 1.0), vec3(opacity0), vec3(opacity90), sqrt(microSurface)).x;
 
             #ifdef ALPHATEST
                 if (alpha < ALPHATESTVALUE)
@@ -490,7 +516,20 @@ void main(void) {
     #endif
 
     #ifdef ANISOTROPIC
-        vec3 anisotropicNormal = getAnisotropicBentNormals(TBN, viewDirectionW, normalW, anisotropy);
+        float anisotropy = vAnisotropy.b;
+        vec3 anisotropyDirection = vec3(vAnisotropy.xy, 0.);
+
+        #ifdef ANISOTROPIC_TEXTURE
+            vec3 anisotropyMapData = texture2D(anisotropySampler, vAnisotropyUV + uvOffset).rgb * vAnisotropyInfos.y;
+            anisotropy *= anisotropyMapData.b;
+            anisotropyDirection.rg *= anisotropyMapData.rg * 2.0 - 1.0;
+        #endif
+
+        mat3 anisoTBN = mat3(normalize(TBN[0]), normalize(TBN[1]), normalize(TBN[2]));
+        vec3 anisotropicTangent = normalize(anisoTBN * anisotropyDirection);
+        vec3 anisotropicBitangent = normalize(cross(anisoTBN[2], anisotropicTangent));
+        
+        vec3 anisotropicNormal = getAnisotropicBentNormals(anisotropicTangent, anisotropicBitangent, normalW, viewDirectionW, anisotropy);
     #endif
 
     // _____________________________ Refraction Info _______________________________________
@@ -691,6 +730,88 @@ void main(void) {
     vec3 specularEnvironmentR0 = surfaceReflectivityColor.rgb;
     vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
 
+    // ________________________________ Sheen Information ______________________________
+    #ifdef SHEEN
+        float sheenIntensity = vSheenColor.a;
+
+        #ifdef SHEEN_TEXTURE
+            vec4 sheenMapData = texture2D(sheenSampler, vSheenUV + uvOffset) * vSheenInfos.y;
+            sheenIntensity *= sheenMapData.a;
+        #endif
+
+        #ifdef SHEEN_LINKWITHALBEDO
+            float sheenFactor = pow(1.0-sheenIntensity, 5.0);
+            vec3 sheenColor = baseColor.rgb*(1.0-sheenFactor);
+            float sheenRoughness = sheenIntensity;
+            // remap albedo.
+            surfaceAlbedo.rgb *= sheenFactor;
+        #else
+            vec3 sheenColor = vSheenColor.rgb;
+            #ifdef SHEEN_TEXTURE
+                sheenColor.rgb *= toLinearSpace(sheenMapData.rgb);
+            #endif
+            float sheenRoughness = roughness;
+            // Remap F0 and sheen.
+            sheenColor *= sheenIntensity;
+            specularEnvironmentR0 *= (1.0-sheenIntensity);
+        #endif
+
+        // Sheen Reflection
+        #if defined(REFLECTION)
+            float sheenAlphaG = convertRoughnessToAverageSlope(sheenRoughness);
+
+            #ifdef SPECULARAA
+                // Adapt linear roughness (alphaG) to geometric curvature of the current pixel.
+                sheenAlphaG += AARoughnessFactors.y;
+            #endif
+
+            vec4 environmentSheenRadiance = vec4(0., 0., 0., 0.);
+
+            // _____________________________ 2D vs 3D Maps ________________________________
+            #if defined(LODINREFLECTIONALPHA) && !defined(REFLECTIONMAP_SKYBOX)
+                float sheenReflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, sheenAlphaG, NdotVUnclamped);
+            #else
+                float sheenReflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, sheenAlphaG, 1.);
+            #endif
+
+            #ifdef LODBASEDMICROSFURACE
+                // Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
+                sheenReflectionLOD = sheenReflectionLOD * vReflectionMicrosurfaceInfos.y + vReflectionMicrosurfaceInfos.z;
+                environmentSheenRadiance = sampleReflectionLod(reflectionSampler, reflectionCoords, sheenReflectionLOD);
+            #else
+                float lodSheenReflectionNormalized = clamp(sheenReflectionLOD / log2(vReflectionMicrosurfaceInfos.x), 0., 1.);
+                float lodSheenReflectionNormalizedDoubled = lodSheenReflectionNormalized * 2.0;
+
+                vec4 environmentSheenMid = sampleReflection(reflectionSampler, reflectionCoords);
+                if(lodSheenReflectionNormalizedDoubled < 1.0){
+                    environmentSheenRadiance = mix(
+                        sampleReflection(reflectionSamplerHigh, reflectionCoords),
+                        environmentSheenMid,
+                        lodSheenReflectionNormalizedDoubled
+                    );
+                }else{
+                    environmentSheenRadiance = mix(
+                        environmentSheenMid,
+                        sampleReflection(reflectionSamplerLow, reflectionCoords),
+                        lodSheenReflectionNormalizedDoubled - 1.0
+                    );
+                }
+            #endif
+
+            #ifdef RGBDREFLECTION
+                environmentSheenRadiance.rgb = fromRGBD(environmentSheenRadiance);
+            #endif
+
+            #ifdef GAMMAREFLECTION
+                environmentSheenRadiance.rgb = toLinearSpace(environmentSheenRadiance.rgb);
+            #endif
+
+            // _____________________________ Levels _____________________________________
+            environmentSheenRadiance.rgb *= vReflectionInfos.x;
+            environmentSheenRadiance.rgb *= vReflectionColor.rgb;
+        #endif
+    #endif
+
     // _____________________________ Clear Coat Information ____________________________
     #ifdef CLEARCOAT
         // Clear COAT parameters.
@@ -842,6 +963,7 @@ void main(void) {
         #endif
     #endif
 
+    // _____________________________ IBL BRDF + Energy Cons _________________________________
     #if defined(ENVIRONMENTBRDF)
         // BRDF Lookup
         vec2 environmentBrdf = getBRDFLookup(NdotV, roughness, environmentBrdfSampler);
@@ -860,6 +982,9 @@ void main(void) {
     #ifdef CLEARCOAT
         vec3 clearCoatBase = vec3(0., 0., 0.);
     #endif
+    #ifdef SHEEN
+        vec3 sheenBase = vec3(0., 0., 0.);
+    #endif
 
     #ifdef LIGHTMAP
         vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb;
@@ -901,7 +1026,24 @@ void main(void) {
         #endif
     #else
         // Jones implementation of a well balanced fast analytical solution.
-        vec3 specularEnvironmentReflectance = fresnelSchlickEnvironmentGGX(NdotV, specularEnvironmentR0, specularEnvironmentR90, sqrt(microSurface));
+        vec3 specularEnvironmentReflectance = getReflectanceFromAnalyticalBRDFLookup_Jones(NdotV, specularEnvironmentR0, specularEnvironmentR90, sqrt(microSurface));
+    #endif
+
+    // _____________________________ Sheen Environment Oclusion __________________________
+    #ifdef SHEEN
+        vec3 sheenEnvironmentReflectance = getSheenReflectanceFromBRDFLookup(sheenColor, NdotV, sheenAlphaG);
+
+        #ifdef RADIANCEOCCLUSION
+            sheenEnvironmentReflectance *= seo;
+        #endif
+
+        #ifdef HORIZONOCCLUSION
+            #ifdef BUMP
+                #ifdef REFLECTIONMAP_3D
+                    sheenEnvironmentReflectance *= eho;
+                #endif
+            #endif
+        #endif
     #endif
 
     // _________________________ Clear Coat Environment Oclusion __________________________
@@ -926,7 +1068,7 @@ void main(void) {
             #endif
         #else
             // Jones implementation of a well balanced fast analytical solution.
-            vec3 clearCoatEnvironmentReflectance = fresnelSchlickEnvironmentGGX(clearCoatNdotV, vec3(1.), vec3(1.), sqrt(1. - clearCoatRoughness));
+            vec3 clearCoatEnvironmentReflectance = getReflectanceFromAnalyticalBRDFLookup_Jones(clearCoatNdotV, vec3(1.), vec3(1.), sqrt(1. - clearCoatRoughness));
         #endif
 
         clearCoatEnvironmentReflectance *= clearCoatIntensity;
@@ -938,6 +1080,11 @@ void main(void) {
             #ifdef REFLECTION
                 environmentIrradiance *= absorption;
             #endif
+
+            #ifdef SHEEN
+                sheenEnvironmentReflectance *= absorption;
+            #endif
+
             specularEnvironmentReflectance *= absorption;
         #endif
 
@@ -950,6 +1097,11 @@ void main(void) {
         #ifdef REFLECTION
             environmentIrradiance *= conservationFactor;
         #endif
+
+        #ifdef SHEEN
+            sheenEnvironmentReflectance *= (conservationFactor * conservationFactor);
+        #endif
+
         specularEnvironmentReflectance *= (conservationFactor * conservationFactor);
     #endif
 
@@ -993,9 +1145,10 @@ void main(void) {
 
     // ______________________________________________________________________________
     // _____________________________ Energy Conservation  ___________________________
-    // Apply Energy Conservation taking in account the environment level only if 
-    // the environment is present.
-    surfaceAlbedo.rgb = (1. - reflectance) * surfaceAlbedo.rgb;
+    // Apply Energy Conservation.
+    #ifndef METALLICWORKFLOW
+        surfaceAlbedo.rgb = (1. - reflectance) * surfaceAlbedo.rgb;
+    #endif
 
     // _____________________________ Irradiance ______________________________________
     #ifdef REFLECTION
@@ -1061,6 +1214,27 @@ void main(void) {
         #endif
     #endif
 
+    // ________________________________ Sheen ________________________________________
+    #ifdef SHEEN
+        vec3 finalSheen = sheenBase * sheenColor;
+        finalSheen = max(finalSheen, 0.0);
+
+        vec3 finalSheenScaled = finalSheen * vLightingIntensity.x * vLightingIntensity.w;
+        #if defined(ENVIRONMENTBRDF) && defined(MS_BRDF_ENERGY_CONSERVATION)
+            // The sheen does not use the same BRDF so not energy conservation is possible
+            // Should be less a problem as it is usually not metallic
+            // finalSheenScaled *= energyConservationFactor;
+        #endif
+        
+        #ifdef REFLECTION
+            vec3 finalSheenRadiance = environmentSheenRadiance.rgb;
+            finalSheenRadiance *= sheenEnvironmentReflectance;
+
+            // Full value needed for alpha. 
+            vec3 finalSheenRadianceScaled = finalSheenRadiance * vLightingIntensity.z;
+        #endif
+    #endif
+
     // _____________________________ Highlights on Alpha _____________________________
     #ifdef ALPHABLEND
         float luminanceOverAlpha = 0.0;
@@ -1126,6 +1300,11 @@ void main(void) {
     //	finalClearCoat			* vLightingIntensity.x * vLightingIntensity.w +
         finalClearCoatScaled +
     #endif
+    #ifdef SHEEN
+    // Computed in the previous step to help with alpha luminance.
+    //	finalSheen  			* vLightingIntensity.x * vLightingIntensity.w +
+        finalSheenScaled +
+    #endif
     #ifdef REFLECTION
     // Comupted in the previous step to help with alpha luminance.
     //	finalRadiance			* vLightingIntensity.z +
@@ -1135,6 +1314,11 @@ void main(void) {
         //  finalClearCoatRadiance * vLightingIntensity.z 
             finalClearCoatRadianceScaled +
         #endif
+        #ifdef SHEEN
+        //  Comupted in the previous step to help with alpha luminance.
+        //  finalSheenRadiance * vLightingIntensity.z 
+            finalSheenRadianceScaled +
+        #endif
     #endif
     #ifdef REFRACTION
         finalRefraction			* vLightingIntensity.z +

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

@@ -78,6 +78,18 @@ varying vec2 vBumpUV;
     #endif
 #endif
 
+#ifdef SHEEN
+    #if defined(SHEEN_TEXTURE) && SHEEN_TEXTUREDIRECTUV == 0 
+        varying vec2 vSheenUV;
+    #endif
+#endif
+
+#ifdef ANISOTROPIC
+    #if defined(ANISOTROPIC_TEXTURE) && ANISOTROPIC_TEXTUREDIRECTUV == 0 
+        varying vec2 vAnisotropyUV;
+    #endif
+#endif
+
 // Output
 varying vec3 vPositionW;
 #if DEBUGMODE > 0
@@ -305,6 +317,32 @@ void main(void) {
     #endif
 #endif
 
+#ifdef SHEEN
+    #if defined(SHEEN_TEXTURE) && SHEEN_TEXTUREDIRECTUV == 0 
+        if (vSheenInfos.x == 0.)
+        {
+            vSheenUV = vec2(sheenMatrix * vec4(uv, 1.0, 0.0));
+        }
+        else
+        {
+            vSheenUV = vec2(sheenMatrix * vec4(uv2, 1.0, 0.0));
+        }
+    #endif
+#endif
+
+#ifdef ANISOTROPIC
+    #if defined(ANISOTROPIC_TEXTURE) && ANISOTROPIC_TEXTUREDIRECTUV == 0 
+        if (vAnisotropyInfos.x == 0.)
+        {
+            vAnisotropyUV = vec2(anisotropyMatrix * vec4(uv, 1.0, 0.0));
+        }
+        else
+        {
+            vAnisotropyUV = vec2(anisotropyMatrix * vec4(uv2, 1.0, 0.0));
+        }
+    #endif
+#endif
+
     // TBN
 #include<bumpVertex>