ソースを参照

Merge pull request #9004 from Popov72/gltf-spec-sheen-clearcoat

Update PBR clear coat and sheen to latest spec
David Catuhe 4 年 前
コミット
6c22914668

+ 2 - 1
dist/preview release/glTF2Interface/babylon.glTF2Interface.d.ts

@@ -1028,8 +1028,9 @@ declare module BABYLON.GLTF2 {
     /** @hidden */
     interface IKHRMaterialsSheen {
         sheenColorFactor?: number[];
-        sheenTexture?: ITextureInfo;
+        sheenColorTexture?: ITextureInfo;
         sheenRoughnessFactor?: number;
+        sheenRoughnessTexture?: ITextureInfo;
     }
 
     /**

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

@@ -178,11 +178,13 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                             <SliderLineComponent label="IOR" target={material.clearCoat} propertyName="indexOfRefraction" minimum={1.0} maximum={3} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                             <CheckBoxLineComponent label="Remap F0" target={material.clearCoat} propertyName="remapF0OnInterfaceChange" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                             <TextureLinkLineComponent label="Clear coat" texture={material.clearCoat.texture} onTextureCreated={(texture) => material.clearCoat.texture = texture} onTextureRemoved={() => material.clearCoat.texture = null} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={this._onDebugSelectionChangeObservable} />
+                            <TextureLinkLineComponent label="Roughness" texture={material.clearCoat.textureRoughness} onTextureCreated={(texture) => material.clearCoat.textureRoughness = texture} onTextureRemoved={() => material.clearCoat.textureRoughness = null} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={this._onDebugSelectionChangeObservable} />
                             <TextureLinkLineComponent label="Bump" texture={material.clearCoat.bumpTexture} onTextureCreated={(texture) => material.clearCoat.bumpTexture = texture} onTextureRemoved={() => material.clearCoat.bumpTexture = null} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={this._onDebugSelectionChangeObservable} />
                             {
                                 material.clearCoat.bumpTexture &&
                                 <SliderLineComponent label="Bump strength" target={material.clearCoat.bumpTexture} propertyName="level" minimum={0} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                             }
+                            <CheckBoxLineComponent label="Use roughness from main texture" target={material.clearCoat} propertyName="useRoughnessFromMainTexture" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                             <CheckBoxLineComponent label="Tint" target={material.clearCoat} propertyName="isTintEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                             {
                                 material.clearCoat.isEnabled && material.clearCoat.isTintEnabled &&
@@ -227,10 +229,12 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                             <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} isLinear={true} />
                             <TextureLinkLineComponent label="Sheen" texture={material.sheen.texture} onTextureCreated={(texture) => material.sheen.texture = texture} onTextureRemoved={() => material.sheen.texture = null} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={this._onDebugSelectionChangeObservable} />
+                            <TextureLinkLineComponent label="Roughness" texture={material.sheen.textureRoughness} onTextureCreated={(texture) => material.sheen.textureRoughness = texture} onTextureRemoved={() => material.sheen.textureRoughness = null} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={this._onDebugSelectionChangeObservable} />
                             <CheckBoxLineComponent label="Use roughness" target={material.sheen} propertyName="_useRoughness" />
                             { (material.sheen as any)._useRoughness &&
                                 <SliderLineComponent label="Roughness" target={material.sheen} propertyName="roughness" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                             }
+                            <CheckBoxLineComponent label="Use roughness from main texture" target={material.sheen} propertyName="useRoughnessFromMainTexture" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                             <CheckBoxLineComponent label="Albedo scaling" target={material.sheen} propertyName="albedoScaling" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                         </div>
                     }

+ 2 - 1
loaders/src/glTF/2.0/Extensions/KHR_materials_clearcoat.ts

@@ -61,6 +61,7 @@ export class KHR_materials_clearcoat implements IGLTFLoaderExtension {
         const promises = new Array<Promise<any>>();
 
         babylonMaterial.clearCoat.isEnabled = true;
+        babylonMaterial.clearCoat.useRoughnessFromMainTexture = false;
         babylonMaterial.clearCoat.remapF0OnInterfaceChange = false;
 
         if (properties.clearcoatFactor != undefined) {
@@ -87,7 +88,7 @@ export class KHR_materials_clearcoat implements IGLTFLoaderExtension {
         if (properties.clearcoatRoughnessTexture) {
             promises.push(this._loader.loadTextureInfoAsync(`${context}/clearcoatRoughnessTexture`, properties.clearcoatRoughnessTexture, (texture) => {
                 texture.name = `${babylonMaterial.name} (ClearCoat Roughness)`;
-                babylonMaterial.clearCoat.texture = texture;
+                babylonMaterial.clearCoat.textureRoughness = texture;
             }));
         }
 

+ 10 - 2
loaders/src/glTF/2.0/Extensions/KHR_materials_sheen.ts

@@ -71,8 +71,8 @@ export class KHR_materials_sheen implements IGLTFLoaderExtension {
             babylonMaterial.sheen.color = Color3.Black();
         }
 
-        if (properties.sheenTexture) {
-            promises.push(this._loader.loadTextureInfoAsync(`${context}/sheenTexture`, properties.sheenTexture, (texture) => {
+        if (properties.sheenColorTexture) {
+            promises.push(this._loader.loadTextureInfoAsync(`${context}/sheenColorTexture`, properties.sheenColorTexture, (texture) => {
                 texture.name = `${babylonMaterial.name} (Sheen Color)`;
                 babylonMaterial.sheen.texture = texture;
             }));
@@ -84,7 +84,15 @@ export class KHR_materials_sheen implements IGLTFLoaderExtension {
             babylonMaterial.sheen.roughness = 0;
         }
 
+        if (properties.sheenRoughnessTexture) {
+            promises.push(this._loader.loadTextureInfoAsync(`${context}/sheenRoughnessTexture`, properties.sheenRoughnessTexture, (texture) => {
+                texture.name = `${babylonMaterial.name} (Sheen Roughness)`;
+                babylonMaterial.sheen.textureRoughness = texture;
+            }));
+        }
+
         babylonMaterial.sheen.albedoScaling = true;
+        babylonMaterial.sheen.useRoughnessFromMainTexture = false;
 
         return Promise.all(promises).then(() => { });
     }

+ 15 - 1
serializers/src/glTF/2.0/Extensions/KHR_materials_sheen.ts

@@ -93,7 +93,21 @@ export class KHR_materials_sheen implements IGLTFExporterExtensionV2 {
                     let textureIndex = this._getTextureIndex(babylonMaterial.sheen.texture);
 
                     if (textureIndex > -1) {
-                        sheenInfo.sheenTexture = this._textureInfos[textureIndex] ;
+                        sheenInfo.sheenColorTexture = this._textureInfos[textureIndex];
+                    }
+                }
+
+                if (babylonMaterial.sheen.textureRoughness && !babylonMaterial.sheen.useRoughnessFromMainTexture) {
+                    let textureIndex = this._getTextureIndex(babylonMaterial.sheen.textureRoughness);
+
+                    if (textureIndex > -1) {
+                        sheenInfo.sheenRoughnessTexture = this._textureInfos[textureIndex];
+                    }
+                } else if (babylonMaterial.sheen.texture && babylonMaterial.sheen.useRoughnessFromMainTexture) {
+                    let textureIndex = this._getTextureIndex(babylonMaterial.sheen.texture);
+
+                    if (textureIndex > -1) {
+                        sheenInfo.sheenRoughnessTexture = this._textureInfos[textureIndex];
                     }
                 }
 

+ 1 - 0
src/Materials/Node/Blocks/PBR/clearCoatBlock.ts

@@ -158,6 +158,7 @@ export class ClearCoatBlock extends NodeMaterialBlock {
 
         defines.setValue("CLEARCOAT", true);
         defines.setValue("CLEARCOAT_TEXTURE", this.texture.isConnected, true);
+        defines.setValue("CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE", true, true);
         defines.setValue("CLEARCOAT_TINT", this.tintColor.isConnected || this.tintThickness.isConnected || this.tintAtDistance.isConnected, true);
         defines.setValue("CLEARCOAT_BUMP", this.bumpTexture.isConnected, true);
         defines.setValue("CLEARCOAT_DEFAULTIOR", this.ior.isConnected ? this.ior.connectInputBlock!.value === 1.5 : false, true);

+ 1 - 0
src/Materials/Node/Blocks/PBR/sheenBlock.ts

@@ -99,6 +99,7 @@ export class SheenBlock extends NodeMaterialBlock {
         super.prepareDefines(mesh, nodeMaterial, defines);
 
         defines.setValue("SHEEN", true);
+        defines.setValue("SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE", true, true);
         defines.setValue("SHEEN_LINKWITHALBEDO", this.linkSheenWithAlbedo, true);
         defines.setValue("SHEEN_ROUGHNESS", this.roughness.isConnected, true);
         defines.setValue("SHEEN_ALBEDOSCALING", this.albedoScaling, true);

+ 10 - 2
src/Materials/PBR/pbrBaseMaterial.ts

@@ -229,7 +229,11 @@ export class PBRMaterialDefines extends MaterialDefines
     public CLEARCOAT = false;
     public CLEARCOAT_DEFAULTIOR = false;
     public CLEARCOAT_TEXTURE = false;
+    public CLEARCOAT_TEXTURE_ROUGHNESS = false;
     public CLEARCOAT_TEXTUREDIRECTUV = 0;
+    public CLEARCOAT_TEXTURE_ROUGHNESSDIRECTUV = 0;
+    public CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE = false;
+    public CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL = false;
     public CLEARCOAT_BUMP = false;
     public CLEARCOAT_BUMPDIRECTUV = 0;
     public CLEARCOAT_REMAP_F0 = true;
@@ -247,10 +251,14 @@ export class PBRMaterialDefines extends MaterialDefines
 
     public SHEEN = false;
     public SHEEN_TEXTURE = false;
+    public SHEEN_TEXTURE_ROUGHNESS = false;
     public SHEEN_TEXTUREDIRECTUV = 0;
+    public SHEEN_TEXTURE_ROUGHNESSDIRECTUV = 0;
     public SHEEN_LINKWITHALBEDO = false;
     public SHEEN_ROUGHNESS = false;
     public SHEEN_ALBEDOSCALING = false;
+    public SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE = false;
+    public SHEEN_TEXTURE_ROUGHNESS_IDENTICAL = false;
 
     public SUBSURFACE = false;
 
@@ -2020,9 +2028,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
             this.detailMap.bindForSubMesh(ubo, scene, this.isFrozen);
             this.subSurface.bindForSubMesh(ubo, scene, engine, this.isFrozen, defines.LODBASEDMICROSFURACE, this.realTimeFiltering);
-            this.clearCoat.bindForSubMesh(ubo, scene, engine, this._disableBumpMap, this.isFrozen, this._invertNormalMapX, this._invertNormalMapY);
+            this.clearCoat.bindForSubMesh(ubo, scene, engine, this._disableBumpMap, this.isFrozen, this._invertNormalMapX, this._invertNormalMapY, subMesh);
             this.anisotropy.bindForSubMesh(ubo, scene, this.isFrozen);
-            this.sheen.bindForSubMesh(ubo, scene, this.isFrozen);
+            this.sheen.bindForSubMesh(ubo, scene, this.isFrozen, subMesh);
 
             // Clip plane
             MaterialHelper.BindClipPlane(this._activeEffect, scene);

ファイルの差分が大きいため隠しています
+ 541 - 476
src/Materials/PBR/pbrClearCoatConfiguration.ts


+ 81 - 10
src/Materials/PBR/pbrSheenConfiguration.ts

@@ -8,6 +8,7 @@ import { BaseTexture } from "../../Materials/Textures/baseTexture";
 import { Nullable } from "../../types";
 import { IAnimatable } from '../../Animations/animatable.interface';
 import { EffectFallbacks } from '../effectFallbacks';
+import { SubMesh } from '../../Meshes/subMesh';
 
 /**
  * @hidden
@@ -15,10 +16,14 @@ import { EffectFallbacks } from '../effectFallbacks';
 export interface IMaterialSheenDefines {
     SHEEN: boolean;
     SHEEN_TEXTURE: boolean;
+    SHEEN_TEXTURE_ROUGHNESS: boolean;
     SHEEN_TEXTUREDIRECTUV: number;
+    SHEEN_TEXTURE_ROUGHNESSDIRECTUV: number;
     SHEEN_LINKWITHALBEDO: boolean;
     SHEEN_ROUGHNESS: boolean;
     SHEEN_ALBEDOSCALING: boolean;
+    SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE: boolean;
+    SHEEN_TEXTURE_ROUGHNESS_IDENTICAL: boolean;
 
     /** @hidden */
     _areTexturesDirty: boolean;
@@ -61,12 +66,22 @@ export class PBRSheenConfiguration {
     /**
      * Stores the sheen tint values in a texture.
      * rgb is tint
-     * a is a intensity or roughness if roughness has been defined
+     * a is a intensity or roughness if the roughness property has been defined and useRoughnessFromTexture is true (in that case, textureRoughness won't be used)
+     * If the roughness property has been defined and useRoughnessFromTexture is false then the alpha channel is not used to modulate roughness
      */
     @serializeAsTexture()
     @expandToProperty("_markAllSubMeshesAsTexturesDirty")
     public texture: Nullable<BaseTexture> = null;
 
+    private _useRoughnessFromMainTexture = true;
+    /**
+     * Indicates that the alpha channel of the texture property will be used for roughness.
+     * Has no effect if the roughness (and texture!) property is not defined
+     */
+    @serialize()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public useRoughnessFromMainTexture = true;
+
     private _roughness: Nullable<number> = null;
     /**
      * Defines the sheen roughness.
@@ -77,6 +92,15 @@ export class PBRSheenConfiguration {
     @expandToProperty("_markAllSubMeshesAsTexturesDirty")
     public roughness: Nullable<number> = null;
 
+    private _textureRoughness: Nullable<BaseTexture> = null;
+    /**
+     * Stores the sheen roughness in a texture.
+     * alpha channel is the roughness. This texture won't be used if the texture property is not empty and useRoughnessFromTexture is true
+     */
+    @serializeAsTexture()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public textureRoughness: Nullable<BaseTexture> = null;
+
     private _albedoScaling = false;
     /**
      * If true, the sheen effect is layered above the base BRDF with the albedo-scaling technique.
@@ -117,6 +141,12 @@ export class PBRSheenConfiguration {
                         return false;
                     }
                 }
+
+                if (this._textureRoughness && MaterialFlags.SheenTextureEnabled) {
+                    if (!this._textureRoughness.isReadyOrNotBlocking()) {
+                        return false;
+                    }
+                }
             }
         }
 
@@ -134,6 +164,8 @@ export class PBRSheenConfiguration {
             defines.SHEEN_LINKWITHALBEDO = this._linkSheenWithAlbedo;
             defines.SHEEN_ROUGHNESS = this._roughness !== null;
             defines.SHEEN_ALBEDOSCALING = this._albedoScaling;
+            defines.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE = this._useRoughnessFromMainTexture;
+            defines.SHEEN_TEXTURE_ROUGHNESS_IDENTICAL = this._texture !== null && this._texture._texture === this._textureRoughness?._texture && this._texture.checkTransformsAreIdentical(this._textureRoughness);
 
             if (defines._areTexturesDirty) {
                 if (scene.texturesEnabled) {
@@ -142,15 +174,24 @@ export class PBRSheenConfiguration {
                     } else {
                         defines.SHEEN_TEXTURE = false;
                     }
+
+                    if (this._textureRoughness && MaterialFlags.SheenTextureEnabled) {
+                        MaterialHelper.PrepareDefinesForMergedUV(this._textureRoughness, defines, "SHEEN_TEXTURE_ROUGHNESS");
+                    } else {
+                        defines.SHEEN_TEXTURE_ROUGHNESS = false;
+                    }
                 }
             }
         }
         else {
             defines.SHEEN = false;
             defines.SHEEN_TEXTURE = false;
+            defines.SHEEN_TEXTURE_ROUGHNESS = false;
             defines.SHEEN_LINKWITHALBEDO = false;
             defines.SHEEN_ROUGHNESS = false;
             defines.SHEEN_ALBEDOSCALING = false;
+            defines.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE = false;
+            defines.SHEEN_TEXTURE_ROUGHNESS_IDENTICAL = false;
         }
     }
 
@@ -159,12 +200,25 @@ export class PBRSheenConfiguration {
      * @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.
+     * @param subMesh the submesh to bind data for
      */
-    public bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, isFrozen: boolean): void {
+    public bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, isFrozen: boolean, subMesh?: SubMesh): void {
+        const defines = subMesh!._materialDefines as unknown as IMaterialSheenDefines;
+
+        const identicalTextures = defines.SHEEN_TEXTURE_ROUGHNESS_IDENTICAL;
+
         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");
+            if (identicalTextures && MaterialFlags.SheenTextureEnabled) {
+                uniformBuffer.updateFloat4("vSheenInfos", this._texture!.coordinatesIndex, this._texture!.level, -1, -1);
+                MaterialHelper.BindTextureMatrix(this._texture!, uniformBuffer, "sheen");
+            } else  if ((this._texture || this._textureRoughness) && MaterialFlags.SheenTextureEnabled) {
+                uniformBuffer.updateFloat4("vSheenInfos", this._texture?.coordinatesIndex ?? 0, this._texture?.level ?? 0, this._textureRoughness?.coordinatesIndex ?? 0, this._textureRoughness?.level ?? 0);
+                if (this._texture) {
+                    MaterialHelper.BindTextureMatrix(this._texture, uniformBuffer, "sheen");
+                }
+                if (this._textureRoughness && !identicalTextures && !defines.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE) {
+                    MaterialHelper.BindTextureMatrix(this._textureRoughness, uniformBuffer, "sheenRoughness");
+                }
             }
 
             // Sheen
@@ -184,6 +238,10 @@ export class PBRSheenConfiguration {
             if (this._texture && MaterialFlags.SheenTextureEnabled) {
                 uniformBuffer.setTexture("sheenSampler", this._texture);
             }
+
+            if (this._textureRoughness && !identicalTextures && !defines.SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE && MaterialFlags.SheenTextureEnabled) {
+                uniformBuffer.setTexture("sheenRoughnessSampler", this._textureRoughness);
+            }
         }
     }
 
@@ -197,6 +255,10 @@ export class PBRSheenConfiguration {
             return true;
         }
 
+        if (this._textureRoughness === texture) {
+            return true;
+        }
+
         return false;
     }
 
@@ -208,6 +270,10 @@ export class PBRSheenConfiguration {
         if (this._texture) {
             activeTextures.push(this._texture);
         }
+
+        if (this._textureRoughness) {
+            activeTextures.push(this._textureRoughness);
+        }
     }
 
     /**
@@ -218,6 +284,10 @@ export class PBRSheenConfiguration {
         if (this._texture && this._texture.animations && this._texture.animations.length > 0) {
             animatables.push(this._texture);
         }
+
+        if (this._textureRoughness && this._textureRoughness.animations && this._textureRoughness.animations.length > 0) {
+            animatables.push(this._textureRoughness);
+        }
     }
 
     /**
@@ -226,9 +296,8 @@ export class PBRSheenConfiguration {
      */
     public dispose(forceDisposeTextures?: boolean): void {
         if (forceDisposeTextures) {
-            if (this._texture) {
-                this._texture.dispose();
-            }
+            this._texture?.dispose();
+            this._textureRoughness?.dispose();
         }
     }
 
@@ -259,7 +328,7 @@ export class PBRSheenConfiguration {
      * @param uniforms defines the current uniform list.
      */
     public static AddUniforms(uniforms: string[]): void {
-        uniforms.push("vSheenColor", "vSheenRoughness", "vSheenInfos", "sheenMatrix");
+        uniforms.push("vSheenColor", "vSheenRoughness", "vSheenInfos", "sheenMatrix", "sheenRoughnessMatrix");
     }
 
     /**
@@ -269,8 +338,9 @@ export class PBRSheenConfiguration {
     public static PrepareUniformBuffer(uniformBuffer: UniformBuffer): void {
         uniformBuffer.addUniform("vSheenColor", 4);
         uniformBuffer.addUniform("vSheenRoughness", 1);
-        uniformBuffer.addUniform("vSheenInfos", 2);
+        uniformBuffer.addUniform("vSheenInfos", 4);
         uniformBuffer.addUniform("sheenMatrix", 16);
+        uniformBuffer.addUniform("sheenRoughnessMatrix", 16);
     }
 
     /**
@@ -279,6 +349,7 @@ export class PBRSheenConfiguration {
      */
     public static AddSamplers(samplers: string[]): void {
         samplers.push("sheenSampler");
+        samplers.push("sheenRoughnessSampler");
     }
 
     /**

+ 9 - 0
src/Materials/Textures/baseTexture.ts

@@ -474,6 +474,15 @@ export class BaseTexture implements IAnimatable {
     }
 
     /**
+     * Checks if the texture has the same transform matrix than another texture
+     * @param texture texture to check against
+     * @returns true if the transforms are the same, else false
+     */
+    public checkTransformsAreIdentical(texture: Nullable<BaseTexture>): boolean {
+        return texture !== null;
+    }
+
+    /**
      * Get the texture transform matrix used to offset tile the texture for istance.
      * @returns the transformation matrix
      */

+ 16 - 0
src/Materials/Textures/texture.ts

@@ -455,6 +455,22 @@ export class Texture extends BaseTexture {
     }
 
     /**
+     * Checks if the texture has the same transform matrix than another texture
+     * @param texture texture to check against
+     * @returns true if the transforms are the same, else false
+     */
+    public checkTransformsAreIdentical(texture: Nullable<Texture>): boolean {
+        return texture !== null &&
+                this.uOffset === texture.uOffset &&
+                this.vOffset === texture.vOffset &&
+                this.uScale === texture.uScale &&
+                this.vScale === texture.vScale &&
+                this.uAng === texture.uAng &&
+                this.vAng === texture.vAng &&
+                this.wAng === texture.wAng;
+    }
+
+    /**
      * Get the current texture matrix which includes the requested offsetting, tiling and rotation components.
      * @returns the transform matrix of the texture.
      */

+ 15 - 1
src/Shaders/ShadersInclude/pbrBlockClearcoat.fx

@@ -36,6 +36,9 @@ struct clearcoatOutParams
         const in vec3 geometricNormalW,
         const in vec3 viewDirectionW,
         const in vec2 vClearCoatParams,
+    #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE)
+        const in vec4 clearCoatMapRoughnessData,
+    #endif
         const in vec3 specularEnvironmentR0,
     #ifdef CLEARCOAT_TEXTURE
         const in vec2 clearCoatMapData,
@@ -98,12 +101,23 @@ struct clearcoatOutParams
 
         #ifdef CLEARCOAT_TEXTURE
             clearCoatIntensity *= clearCoatMapData.x;
-            clearCoatRoughness *= clearCoatMapData.y;
+            #ifdef CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE
+                clearCoatRoughness *= clearCoatMapData.y;
+            #endif
             #if DEBUGMODE > 0
                 outParams.clearCoatMapData = clearCoatMapData;
             #endif
         #endif
 
+
+        #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE)
+            #ifdef CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL
+                clearCoatRoughness *= clearCoatMapData.y;
+            #else
+                clearCoatRoughness *= clearCoatMapRoughnessData.y;
+            #endif
+        #endif
+
         outParams.clearCoatIntensity = clearCoatIntensity;
         outParams.clearCoatRoughness = clearCoatRoughness;
 

+ 13 - 2
src/Shaders/ShadersInclude/pbrBlockSheen.fx

@@ -25,6 +25,9 @@
         const in vec4 vSheenColor,
     #ifdef SHEEN_ROUGHNESS
         const in float vSheenRoughness,
+        #if defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE)
+            const in vec4 sheenMapRoughnessData,
+        #endif
     #endif
         const in float roughness,
     #ifdef SHEEN_TEXTURE
@@ -97,8 +100,16 @@
             
             #ifdef SHEEN_ROUGHNESS
                 float sheenRoughness = vSheenRoughness;
-                #ifdef SHEEN_TEXTURE
-                    sheenRoughness *= sheenMapData.a;
+                #ifdef SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE
+                    #if defined(SHEEN_TEXTURE)
+                        sheenRoughness *= sheenMapData.a;
+                    #endif
+                #elif defined(SHEEN_TEXTURE_ROUGHNESS)
+                    #ifdef SHEEN_TEXTURE_ROUGHNESS_IDENTICAL
+                        sheenRoughness *= sheenMapData.a;
+                    #else
+                        sheenRoughness *= sheenMapRoughnessData.a;
+                    #endif
                 #endif
             #else
                 float sheenRoughness = roughness;

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

@@ -69,11 +69,18 @@ uniform mat4 view;
     uniform vec2 vClearCoatParams;
     uniform vec4 vClearCoatRefractionParams;
 
+    #if defined(CLEARCOAT_TEXTURE) || defined(CLEARCOAT_TEXTURE_ROUGHNESS)
+        uniform vec4 vClearCoatInfos;
+    #endif
+
     #ifdef CLEARCOAT_TEXTURE
-        uniform vec2 vClearCoatInfos;
         uniform mat4 clearCoatMatrix;
     #endif
 
+    #ifdef CLEARCOAT_TEXTURE_ROUGHNESS
+        uniform mat4 clearCoatRoughnessMatrix;
+    #endif
+
     #ifdef CLEARCOAT_BUMP
         uniform vec2 vClearCoatBumpInfos;
         uniform vec2 vClearCoatTangentSpaceParams;
@@ -108,10 +115,17 @@ uniform mat4 view;
         uniform float vSheenRoughness;
     #endif
 
+    #if defined(SHEEN_TEXTURE) || defined(SHEEN_TEXTURE_ROUGHNESS)
+        uniform vec4 vSheenInfos;
+    #endif
+
     #ifdef SHEEN_TEXTURE
-        uniform vec2 vSheenInfos;
         uniform mat4 sheenMatrix;
     #endif
+
+    #ifdef SHEEN_TEXTURE_ROUGHNESS
+        uniform mat4 sheenRoughnessMatrix;
+    #endif
 #endif
 
 // SubSurface

+ 35 - 1
src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx

@@ -87,7 +87,7 @@
 #endif
 
 #ifdef CLEARCOAT
-    #ifdef CLEARCOAT_TEXTURE
+    #if defined(CLEARCOAT_TEXTURE)
         #if CLEARCOAT_TEXTUREDIRECTUV == 1
             #define vClearCoatUV vMainUV1
         #elif CLEARCOAT_TEXTUREDIRECTUV == 2
@@ -95,9 +95,26 @@
         #else
             varying vec2 vClearCoatUV;
         #endif
+    #endif
+
+    #if defined(CLEARCOAT_TEXTURE_ROUGHNESS)
+        #if CLEARCOAT_TEXTURE_ROUGHNESSDIRECTUV == 1
+            #define vClearCoatRoughnessUV vMainUV1
+        #elif CLEARCOAT_TEXTURE_ROUGHNESSDIRECTUV == 2
+            #define vClearCoatRoughnessUV vMainUV2
+        #else
+            varying vec2 vClearCoatRoughnessUV;
+        #endif
+    #endif
+
+    #ifdef CLEARCOAT_TEXTURE
         uniform sampler2D clearCoatSampler;
     #endif
 
+    #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL)
+        uniform sampler2D clearCoatRoughnessSampler;
+    #endif
+
     #ifdef CLEARCOAT_BUMP
         #if CLEARCOAT_BUMPDIRECTUV == 1
             #define vClearCoatBumpUV vMainUV1
@@ -130,8 +147,25 @@
         #else
             varying vec2 vSheenUV;
         #endif
+    #endif
+
+    #ifdef SHEEN_TEXTURE_ROUGHNESS
+        #if SHEEN_TEXTURE_ROUGHNESSDIRECTUV == 1
+            #define vSheenRoughnessUV vMainUV1
+        #elif SHEEN_TEXTURE_ROUGHNESSDIRECTUV == 2
+            #define vSheenRoughnessUV vMainUV2
+        #else
+            varying vec2 vSheenRoughnessUV;
+        #endif
+    #endif
+
+    #ifdef SHEEN_TEXTURE
         uniform sampler2D sheenSampler;
     #endif
+
+    #if defined(SHEEN_ROUGHNESS) && defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL)
+        uniform sampler2D sheenRoughnessSampler;
+    #endif
 #endif
 
 #ifdef ANISOTROPIC

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

@@ -40,8 +40,9 @@ uniform Material
 
     uniform vec2 vClearCoatParams;
     uniform vec4 vClearCoatRefractionParams;
-    uniform vec2 vClearCoatInfos;
+    uniform vec4 vClearCoatInfos;
     uniform mat4 clearCoatMatrix;
+    uniform mat4 clearCoatRoughnessMatrix;
     uniform vec2 vClearCoatBumpInfos;
     uniform vec2 vClearCoatTangentSpaceParams;
     uniform mat4 clearCoatBumpMatrix;
@@ -56,8 +57,9 @@ uniform Material
 
     uniform vec4 vSheenColor;
     uniform float vSheenRoughness;
-    uniform vec2 vSheenInfos;
+    uniform vec4 vSheenInfos;
     uniform mat4 sheenMatrix;
+    uniform mat4 sheenRoughnessMatrix;
 
     uniform vec3 vRefractionMicrosurfaceInfos;
     uniform vec2 vRefractionFilteringInfo;

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

@@ -58,11 +58,18 @@ uniform float pointSize;
 
 // Clear Coat
 #ifdef CLEARCOAT
+    #if defined(CLEARCOAT_TEXTURE) || defined(CLEARCOAT_TEXTURE_ROUGHNESS)
+        uniform vec4 vClearCoatInfos;
+    #endif
+
     #ifdef CLEARCOAT_TEXTURE
-        uniform vec2 vClearCoatInfos;
         uniform mat4 clearCoatMatrix;
     #endif
 
+    #ifdef CLEARCOAT_TEXTURE_ROUGHNESS
+        uniform mat4 clearCoatRoughnessMatrix;
+    #endif
+
     #ifdef CLEARCOAT_BUMP
         uniform vec2 vClearCoatBumpInfos;
         uniform mat4 clearCoatBumpMatrix;
@@ -84,10 +91,17 @@ uniform float pointSize;
 
 // Sheen
 #ifdef SHEEN
+    #if defined(SHEEN_TEXTURE) || defined(SHEEN_TEXTURE_ROUGHNESS)
+        uniform vec4 vSheenInfos;
+    #endif
+
     #ifdef SHEEN_TEXTURE
-        uniform vec2 vSheenInfos;
         uniform mat4 sheenMatrix;
     #endif
+
+    #ifdef SHEEN_TEXTURE_ROUGHNESS
+        uniform mat4 sheenRoughnessMatrix;
+    #endif
 #endif
 
 // Sub Surface

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

@@ -287,11 +287,17 @@ void main(void) {
         #ifdef SHEEN_TEXTURE
             vec4 sheenMapData = toLinearSpace(texture2D(sheenSampler, vSheenUV + uvOffset)) * vSheenInfos.y;
         #endif
+        #if defined(SHEEN_ROUGHNESS) && defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE)
+            vec4 sheenMapRoughnessData = texture2D(sheenRoughnessSampler, vSheenRoughnessUV + uvOffset) * vSheenInfos.w;
+        #endif
 
         sheenBlock(
             vSheenColor,
         #ifdef SHEEN_ROUGHNESS
             vSheenRoughness,
+            #if defined(SHEEN_TEXTURE_ROUGHNESS) && !defined(SHEEN_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(SHEEN_USE_ROUGHNESS_FROM_MAINTEXTURE)
+                sheenMapRoughnessData,
+            #endif
         #endif
             roughness,
         #ifdef SHEEN_TEXTURE
@@ -342,6 +348,10 @@ void main(void) {
             vec2 clearCoatMapData = texture2D(clearCoatSampler, vClearCoatUV + uvOffset).rg * vClearCoatInfos.y;
         #endif
 
+        #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE)
+            vec4 clearCoatMapRoughnessData = texture2D(clearCoatRoughnessSampler, vClearCoatRoughnessUV + uvOffset) * vClearCoatInfos.w;
+        #endif
+
         #if defined(CLEARCOAT_TINT) && defined(CLEARCOAT_TINT_TEXTURE)
             vec4 clearCoatTintMapData = toLinearSpace(texture2D(clearCoatTintSampler, vClearCoatTintUV + uvOffset));
         #endif
@@ -355,6 +365,9 @@ void main(void) {
             geometricNormalW,
             viewDirectionW,
             vClearCoatParams,
+            #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && !defined(CLEARCOAT_TEXTURE_ROUGHNESS_IDENTICAL) && !defined(CLEARCOAT_USE_ROUGHNESS_FROM_MAINTEXTURE)
+                clearCoatMapRoughnessData,
+            #endif
             specularEnvironmentR0,
         #ifdef CLEARCOAT_TEXTURE
             clearCoatMapData,

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

@@ -80,6 +80,10 @@ varying vec2 vBumpUV;
         varying vec2 vClearCoatUV;
     #endif
 
+    #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && CLEARCOAT_TEXTURE_ROUGHNESSDIRECTUV == 0 
+        varying vec2 vClearCoatRoughnessUV;
+    #endif
+
     #if defined(CLEARCOAT_BUMP) && CLEARCOAT_BUMPDIRECTUV == 0 
         varying vec2 vClearCoatBumpUV;
     #endif
@@ -93,6 +97,10 @@ varying vec2 vBumpUV;
     #if defined(SHEEN_TEXTURE) && SHEEN_TEXTUREDIRECTUV == 0 
         varying vec2 vSheenUV;
     #endif
+
+    #if defined(SHEEN_TEXTURE_ROUGHNESS) && SHEEN_TEXTURE_ROUGHNESSDIRECTUV == 0 
+        varying vec2 vSheenRoughnessUV;
+    #endif
 #endif
 
 #ifdef ANISOTROPIC
@@ -365,6 +373,17 @@ void main(void) {
         }
     #endif
 
+    #if defined(CLEARCOAT_TEXTURE_ROUGHNESS) && CLEARCOAT_TEXTURE_ROUGHNESSDIRECTUV == 0 
+        if (vClearCoatInfos.z == 0.)
+        {
+            vClearCoatRoughnessUV = vec2(clearCoatRoughnessMatrix * vec4(uvUpdated, 1.0, 0.0));
+        }
+        else
+        {
+            vClearCoatRoughnessUV = vec2(clearCoatRoughnessMatrix * vec4(uv2, 1.0, 0.0));
+        }
+    #endif
+
     #if defined(CLEARCOAT_BUMP) && CLEARCOAT_BUMPDIRECTUV == 0 
         if (vClearCoatBumpInfos.x == 0.)
         {
@@ -399,6 +418,17 @@ void main(void) {
             vSheenUV = vec2(sheenMatrix * vec4(uv2, 1.0, 0.0));
         }
     #endif
+
+    #if defined(SHEEN_TEXTURE_ROUGHNESS) && SHEEN_TEXTURE_ROUGHNESSDIRECTUV == 0 
+        if (vSheenInfos.z == 0.)
+        {
+            vSheenRoughnessUV = vec2(sheenRoughnessMatrix * vec4(uvUpdated, 1.0, 0.0));
+        }
+        else
+        {
+            vSheenRoughnessUV = vec2(sheenRoughnessMatrix * vec4(uv2, 1.0, 0.0));
+        }
+    #endif
 #endif
 
 #ifdef ANISOTROPIC

BIN
tests/validation/ReferenceImages/GLTFClearCoat.png


BIN
tests/validation/ReferenceImages/glTFSheen.png


+ 12 - 0
tests/validation/config.json

@@ -599,6 +599,18 @@
             "referenceImage": "gltfTextureSampler1.png"
         },
         {
+            "title": "GLTF Sheen",
+            "playgroundId": "#YG3BBF#2",
+            "referenceImage": "glTFSheen.png"
+        },
+        {
+            "title": "GLTF ClearCoat",
+            "playgroundId": "#YG3BBF#4",
+            "referenceImage": "glTFClearCoat.png",
+            "renderCount": 50,
+            "excludeFromAutomaticTesting": true
+        },
+        {
             "title": "GLTF Serializer with Negative World Matrix",
             "playgroundId": "#KX53VK#35",
             "referenceImage": "glTFSerializerNegativeWorldMatrix.png",