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

Merge pull request #8062 from Popov72/nme-pbr

PBR NME: Support of SubSurface
David Catuhe пре 5 година
родитељ
комит
9c1d59a684

+ 6 - 0
nodeEditor/src/blockTools.ts

@@ -69,6 +69,8 @@ import { ReflectivityBlock } from 'babylonjs/Materials/Node/Blocks/PBR/reflectiv
 import { AnisotropyBlock } from 'babylonjs/Materials/Node/Blocks/PBR/anisotropyBlock';
 import { AnisotropyBlock } from 'babylonjs/Materials/Node/Blocks/PBR/anisotropyBlock';
 import { ReflectionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/reflectionBlock';
 import { ReflectionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/reflectionBlock';
 import { ClearCoatBlock } from 'babylonjs/Materials/Node/Blocks/PBR/clearCoatBlock';
 import { ClearCoatBlock } from 'babylonjs/Materials/Node/Blocks/PBR/clearCoatBlock';
+import { RefractionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/refractionBlock';
+import { SubSurfaceBlock } from 'babylonjs/Materials/Node/Blocks/PBR/subSurfaceBlock';
 
 
 export class BlockTools {
 export class BlockTools {
     public static GetBlockFromString(data: string, scene: Scene, nodeMaterial: NodeMaterial) {
     public static GetBlockFromString(data: string, scene: Scene, nodeMaterial: NodeMaterial) {
@@ -453,6 +455,10 @@ export class BlockTools {
                 return new ReflectionBlock("Reflection");
                 return new ReflectionBlock("Reflection");
             case "ClearCoatBlock":
             case "ClearCoatBlock":
                 return new ClearCoatBlock("ClearCoat");
                 return new ClearCoatBlock("ClearCoat");
+            case "RefractionBlock":
+                return new RefractionBlock("Refraction");
+            case "SubSurfaceBlock":
+                return new SubSurfaceBlock("SubSurface");
         }
         }
 
 
         return null;
         return null;

+ 3 - 1
nodeEditor/src/components/nodeList/nodeListComponent.tsx

@@ -124,6 +124,8 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
         "AnisotropyBlock": "PBR Anisotropy block",
         "AnisotropyBlock": "PBR Anisotropy block",
         "ReflectionBlock": "PBR Reflection block",
         "ReflectionBlock": "PBR Reflection block",
         "ClearCoatBlock": "PBR ClearCoat block",
         "ClearCoatBlock": "PBR ClearCoat block",
+        "RefractionBlock": "PBR Refraction block",
+        "SubSurfaceBlock": "PBR SubSurface block",
     };
     };
 
 
     constructor(props: INodeListComponentProps) {
     constructor(props: INodeListComponentProps) {
@@ -152,7 +154,7 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
             Mesh: ["InstancesBlock", "PositionBlock", "UVBlock", "ColorBlock", "NormalBlock", "PerturbNormalBlock", "NormalBlendBlock" , "TangentBlock", "MatrixIndicesBlock", "MatrixWeightsBlock", "WorldPositionBlock", "WorldNormalBlock", "WorldTangentBlock", "FrontFacingBlock"],
             Mesh: ["InstancesBlock", "PositionBlock", "UVBlock", "ColorBlock", "NormalBlock", "PerturbNormalBlock", "NormalBlendBlock" , "TangentBlock", "MatrixIndicesBlock", "MatrixWeightsBlock", "WorldPositionBlock", "WorldNormalBlock", "WorldTangentBlock", "FrontFacingBlock"],
             Noises: ["RandomNumberBlock", "SimplexPerlin3DBlock", "WorleyNoise3DBlock"],
             Noises: ["RandomNumberBlock", "SimplexPerlin3DBlock", "WorleyNoise3DBlock"],
             Output_Nodes: ["VertexOutputBlock", "FragmentOutputBlock", "DiscardBlock"],
             Output_Nodes: ["VertexOutputBlock", "FragmentOutputBlock", "DiscardBlock"],
-            PBR: ["PBRMetallicRoughnessBlock", "AmbientOcclusionBlock", "AnisotropyBlock", "ClearCoatBlock", "ReflectionBlock", "ReflectivityBlock", "SheenBlock"],
+            PBR: ["PBRMetallicRoughnessBlock", "AmbientOcclusionBlock", "AnisotropyBlock", "ClearCoatBlock", "ReflectionBlock", "ReflectivityBlock", "RefractionBlock", "SheenBlock", "SubSurfaceBlock"],
             Range: ["ClampBlock", "RemapBlock", "NormalizeBlock"],
             Range: ["ClampBlock", "RemapBlock", "NormalizeBlock"],
             Round: ["RoundBlock", "CeilingBlock", "FloorBlock"],
             Round: ["RoundBlock", "CeilingBlock", "FloorBlock"],
             Scene: ["FogBlock", "CameraPositionBlock", "FogColorBlock", "ImageProcessingBlock", "LightBlock", "LightInformationBlock", "ViewDirectionBlock"],
             Scene: ["FogBlock", "CameraPositionBlock", "FogColorBlock", "ImageProcessingBlock", "LightBlock", "LightInformationBlock", "ViewDirectionBlock"],

+ 2 - 1
nodeEditor/src/diagram/display/textureDisplayManager.ts

@@ -2,6 +2,7 @@ import { IDisplayManager } from './displayManager';
 import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
 import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
 import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/textureBlock';
 import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/textureBlock';
 import { ReflectionTextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/reflectionTextureBlock';
 import { ReflectionTextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/reflectionTextureBlock';
+import { RefractionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/refractioneBlock';
 import { TextureLineComponent } from '../../sharedComponents/textureLineComponent';
 import { TextureLineComponent } from '../../sharedComponents/textureLineComponent';
 
 
 export class TextureDisplayManager implements IDisplayManager {
 export class TextureDisplayManager implements IDisplayManager {
@@ -25,7 +26,7 @@ export class TextureDisplayManager implements IDisplayManager {
     }
     }
 
 
     public updatePreviewContent(block: NodeMaterialBlock, contentArea: HTMLDivElement): void {       
     public updatePreviewContent(block: NodeMaterialBlock, contentArea: HTMLDivElement): void {       
-        const textureBlock = block as TextureBlock | ReflectionTextureBlock;
+        const textureBlock = block as TextureBlock | ReflectionTextureBlock | RefractionBlock;
 
 
         if (!this._previewCanvas) {
         if (!this._previewCanvas) {
             contentArea.classList.add("texture-block");
             contentArea.classList.add("texture-block");

+ 1 - 0
nodeEditor/src/diagram/displayLedger.ts

@@ -21,4 +21,5 @@ DisplayLedger.RegisteredControls["TrigonometryBlock"] = TrigonometryDisplayManag
 DisplayLedger.RegisteredControls["TextureBlock"] = TextureDisplayManager;
 DisplayLedger.RegisteredControls["TextureBlock"] = TextureDisplayManager;
 DisplayLedger.RegisteredControls["ReflectionTextureBlock"] = TextureDisplayManager;
 DisplayLedger.RegisteredControls["ReflectionTextureBlock"] = TextureDisplayManager;
 DisplayLedger.RegisteredControls["ReflectionBlock"] = TextureDisplayManager;
 DisplayLedger.RegisteredControls["ReflectionBlock"] = TextureDisplayManager;
+DisplayLedger.RegisteredControls["RefractionBlock"] = TextureDisplayManager;
 DisplayLedger.RegisteredControls["DiscardBlock"] = DiscardDisplayManager;
 DisplayLedger.RegisteredControls["DiscardBlock"] = DiscardDisplayManager;

+ 5 - 4
nodeEditor/src/diagram/properties/texturePropertyTabComponent.tsx

@@ -15,10 +15,11 @@ import { OptionsLineComponent } from '../../sharedComponents/optionsLineComponen
 import { IPropertyComponentProps } from './propertyComponentProps';
 import { IPropertyComponentProps } from './propertyComponentProps';
 import { ReflectionTextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/reflectionTextureBlock';
 import { ReflectionTextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/reflectionTextureBlock';
 import { ReflectionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/reflectionBlock';
 import { ReflectionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/reflectionBlock';
+import { RefractionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/refractionBlock';
 import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/textureBlock';
 import { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/textureBlock';
 import { GeneralPropertyTabComponent, GenericPropertyTabComponent } from './genericNodePropertyComponent';
 import { GeneralPropertyTabComponent, GenericPropertyTabComponent } from './genericNodePropertyComponent';
 
 
-type ReflectionTexture = ReflectionTextureBlock | ReflectionBlock;
+type ReflectionTexture = ReflectionTextureBlock | ReflectionBlock | RefractionBlock;
 
 
 export class TexturePropertyTabComponent extends React.Component<IPropertyComponentProps, {isEmbedded: boolean, loadAsCubeTexture: boolean}> {
 export class TexturePropertyTabComponent extends React.Component<IPropertyComponentProps, {isEmbedded: boolean, loadAsCubeTexture: boolean}> {
 
 
@@ -79,7 +80,7 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
 
 
         if (!texture) {
         if (!texture) {
             if (!this.state.loadAsCubeTexture) {
             if (!this.state.loadAsCubeTexture) {
-                this.textureBlock.texture = new Texture(null, this.props.globalState.nodeMaterial.getScene(), false, this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock);
+                this.textureBlock.texture = new Texture(null, this.props.globalState.nodeMaterial.getScene(), false, this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock || this.textureBlock instanceof RefractionBlock);
                 texture = this.textureBlock.texture;
                 texture = this.textureBlock.texture;
                 texture.coordinatesMode = Texture.EQUIRECTANGULAR_MODE;
                 texture.coordinatesMode = Texture.EQUIRECTANGULAR_MODE;
             } else {
             } else {
@@ -122,7 +123,7 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
         this._prepareTexture();
         this._prepareTexture();
 
 
         let texture = this.textureBlock.texture as BaseTexture;       
         let texture = this.textureBlock.texture as BaseTexture;       
-        if (texture.isCube || this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock) {
+        if (texture.isCube || this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock || this.textureBlock instanceof RefractionBlock) {
             let extension: string | undefined = undefined;
             let extension: string | undefined = undefined;
             if (url.toLowerCase().indexOf(".dds") > 0) {
             if (url.toLowerCase().indexOf(".dds") > 0) {
                 extension = ".dds";
                 extension = ".dds";
@@ -146,7 +147,7 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
 
 
         url = url.replace(/\?nocache=\d+/, "");
         url = url.replace(/\?nocache=\d+/, "");
 
 
-        let isInReflectionMode = this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock;
+        let isInReflectionMode = this.textureBlock instanceof ReflectionTextureBlock || this.textureBlock instanceof ReflectionBlock || this.textureBlock instanceof RefractionBlock;
 
 
         var reflectionModeOptions: {label: string, value: number}[] = [
         var reflectionModeOptions: {label: string, value: number}[] = [
             {
             {

+ 1 - 0
nodeEditor/src/diagram/propertyLedger.ts

@@ -20,4 +20,5 @@ PropertyLedger.RegisteredControls["LightInformationBlock"] = LightInformationPro
 PropertyLedger.RegisteredControls["TextureBlock"] = TexturePropertyTabComponent;
 PropertyLedger.RegisteredControls["TextureBlock"] = TexturePropertyTabComponent;
 PropertyLedger.RegisteredControls["ReflectionTextureBlock"] = TexturePropertyTabComponent;
 PropertyLedger.RegisteredControls["ReflectionTextureBlock"] = TexturePropertyTabComponent;
 PropertyLedger.RegisteredControls["ReflectionBlock"] = TexturePropertyTabComponent;
 PropertyLedger.RegisteredControls["ReflectionBlock"] = TexturePropertyTabComponent;
+PropertyLedger.RegisteredControls["RefractionBlock"] = TexturePropertyTabComponent;
 PropertyLedger.RegisteredControls["TrigonometryBlock"] = TrigonometryPropertyTabComponent;
 PropertyLedger.RegisteredControls["TrigonometryBlock"] = TrigonometryPropertyTabComponent;

+ 13 - 12
src/Materials/Node/Blocks/Dual/reflectionTextureBaseBlock.ts

@@ -55,7 +55,8 @@ export abstract class ReflectionTextureBaseBlock extends NodeMaterialBlock {
     protected _reflectionVectorName: string;
     protected _reflectionVectorName: string;
     /** @hidden */
     /** @hidden */
     public _reflectionCoordsName: string;
     public _reflectionCoordsName: string;
-    protected _reflectionMatrixName: string;
+    /** @hidden */
+    public _reflectionMatrixName: string;
     protected _reflectionColorName: string;
     protected _reflectionColorName: string;
 
 
     /**
     /**
@@ -156,17 +157,17 @@ export abstract class ReflectionTextureBaseBlock extends NodeMaterialBlock {
             return;
             return;
         }
         }
 
 
-        defines.setValue(this._define3DName, texture.isCube);
-        defines.setValue(this._defineLocalCubicName, (<any>texture).boundingBoxSize ? true : false);
-        defines.setValue(this._defineExplicitName, texture.coordinatesMode === Constants.TEXTURE_EXPLICIT_MODE);
-        defines.setValue(this._defineSkyboxName, texture.coordinatesMode === Constants.TEXTURE_SKYBOX_MODE);
-        defines.setValue(this._defineCubicName, texture.coordinatesMode === Constants.TEXTURE_CUBIC_MODE);
-        defines.setValue(this._defineSphericalName, texture.coordinatesMode === Constants.TEXTURE_SPHERICAL_MODE);
-        defines.setValue(this._definePlanarName, texture.coordinatesMode === Constants.TEXTURE_PLANAR_MODE);
-        defines.setValue(this._defineProjectionName, texture.coordinatesMode === Constants.TEXTURE_PROJECTION_MODE);
-        defines.setValue(this._defineEquirectangularName, texture.coordinatesMode === Constants.TEXTURE_EQUIRECTANGULAR_MODE);
-        defines.setValue(this._defineEquirectangularFixedName, texture.coordinatesMode === Constants.TEXTURE_FIXED_EQUIRECTANGULAR_MODE);
-        defines.setValue(this._defineMirroredEquirectangularFixedName, texture.coordinatesMode === Constants.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE);
+        defines.setValue(this._define3DName, texture.isCube, true);
+        defines.setValue(this._defineLocalCubicName, (<any>texture).boundingBoxSize ? true : false, true);
+        defines.setValue(this._defineExplicitName, texture.coordinatesMode === Constants.TEXTURE_EXPLICIT_MODE, true);
+        defines.setValue(this._defineSkyboxName, texture.coordinatesMode === Constants.TEXTURE_SKYBOX_MODE, true);
+        defines.setValue(this._defineCubicName, texture.coordinatesMode === Constants.TEXTURE_CUBIC_MODE, true);
+        defines.setValue(this._defineSphericalName, texture.coordinatesMode === Constants.TEXTURE_SPHERICAL_MODE, true);
+        defines.setValue(this._definePlanarName, texture.coordinatesMode === Constants.TEXTURE_PLANAR_MODE, true);
+        defines.setValue(this._defineProjectionName, texture.coordinatesMode === Constants.TEXTURE_PROJECTION_MODE, true);
+        defines.setValue(this._defineEquirectangularName, texture.coordinatesMode === Constants.TEXTURE_EQUIRECTANGULAR_MODE, true);
+        defines.setValue(this._defineEquirectangularFixedName, texture.coordinatesMode === Constants.TEXTURE_FIXED_EQUIRECTANGULAR_MODE, true);
+        defines.setValue(this._defineMirroredEquirectangularFixedName, texture.coordinatesMode === Constants.TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE, true);
     }
     }
 
 
     public isReady() {
     public isReady() {

+ 2 - 2
src/Materials/Node/Blocks/PBR/ambientOcclusionBlock.ts

@@ -108,8 +108,8 @@ export class AmbientOcclusionBlock extends NodeMaterialBlock {
     public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
     public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
         super.prepareDefines(mesh, nodeMaterial, defines);
         super.prepareDefines(mesh, nodeMaterial, defines);
 
 
-        defines.setValue("AMBIENT", this.texture.isConnected);
-        defines.setValue("AMBIENTINGRAYSCALE", this.useAmbientInGrayScale);
+        defines.setValue("AMBIENT", this.texture.isConnected, true);
+        defines.setValue("AMBIENTINGRAYSCALE", this.useAmbientInGrayScale, true);
     }
     }
 
 
     protected _buildBlock(state: NodeMaterialBuildState) {
     protected _buildBlock(state: NodeMaterialBuildState) {

+ 1 - 1
src/Materials/Node/Blocks/PBR/anisotropyBlock.ts

@@ -181,7 +181,7 @@ export class AnisotropyBlock extends NodeMaterialBlock {
         super.prepareDefines(mesh, nodeMaterial, defines);
         super.prepareDefines(mesh, nodeMaterial, defines);
 
 
         defines.setValue("ANISOTROPIC", true);
         defines.setValue("ANISOTROPIC", true);
-        defines.setValue("ANISOTROPIC_TEXTURE", this.texture.isConnected);
+        defines.setValue("ANISOTROPIC_TEXTURE", this.texture.isConnected, true);
     }
     }
 
 
     protected _buildBlock(state: NodeMaterialBuildState) {
     protected _buildBlock(state: NodeMaterialBuildState) {

+ 50 - 9
src/Materials/Node/Blocks/PBR/clearCoatBlock.ts

@@ -43,6 +43,7 @@ export class ClearCoatBlock extends NodeMaterialBlock {
         this.registerInput("tintAtDistance", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
         this.registerInput("tintAtDistance", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
         this.registerInput("tintThickness", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
         this.registerInput("tintThickness", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
         this.registerInput("tintTexture", NodeMaterialBlockConnectionPointTypes.Color4, true, NodeMaterialBlockTargets.Fragment);
         this.registerInput("tintTexture", NodeMaterialBlockConnectionPointTypes.Color4, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("worldTangent", NodeMaterialBlockConnectionPointTypes.Vector4, true);
 
 
         this.registerOutput("clearcoat", NodeMaterialBlockConnectionPointTypes.Object, NodeMaterialBlockTargets.Fragment,
         this.registerOutput("clearcoat", NodeMaterialBlockConnectionPointTypes.Object, NodeMaterialBlockTargets.Fragment,
             new NodeMaterialConnectionPointCustomObject("clearcoat", this, NodeMaterialConnectionPointDirection.Output, ClearCoatBlock, "ClearCoatBlock"));
             new NodeMaterialConnectionPointCustomObject("clearcoat", this, NodeMaterialConnectionPointDirection.Output, ClearCoatBlock, "ClearCoatBlock"));
@@ -139,6 +140,13 @@ export class ClearCoatBlock extends NodeMaterialBlock {
     }
     }
 
 
     /**
     /**
+     * Gets the world tangent input component
+     */
+    public get worldTangent(): NodeMaterialConnectionPoint {
+        return this._inputs[10];
+    }
+
+    /**
      * Gets the clear coat object output component
      * Gets the clear coat object output component
      */
      */
     public get clearcoat(): NodeMaterialConnectionPoint {
     public get clearcoat(): NodeMaterialConnectionPoint {
@@ -157,12 +165,11 @@ export class ClearCoatBlock extends NodeMaterialBlock {
         super.prepareDefines(mesh, nodeMaterial, defines);
         super.prepareDefines(mesh, nodeMaterial, defines);
 
 
         defines.setValue("CLEARCOAT", true);
         defines.setValue("CLEARCOAT", true);
-        defines.setValue("CLEARCOAT_TEXTURE", this.texture.isConnected);
-        defines.setValue("CLEARCOAT_TINT", this.tintColor.isConnected || this.tintThickness.isConnected || this.tintAtDistance.isConnected || this.tintTexture.isConnected);
-        defines.setValue("CLEARCOAT_TINT_TEXTURE", this.tintTexture.isConnected);
-        defines.setValue("CLEARCOAT_BUMP", this.bumpTexture.isConnected);
-        defines.setValue("CLEARCOAT_DEFAULTIOR", this.ior.isConnected ? this.ior.connectInputBlock!.value === 1.5 : false);
-
+        defines.setValue("CLEARCOAT_TEXTURE", this.texture.isConnected, true);
+        defines.setValue("CLEARCOAT_TINT", this.tintColor.isConnected || this.tintThickness.isConnected || this.tintAtDistance.isConnected || this.tintTexture.isConnected, true);
+        defines.setValue("CLEARCOAT_TINT_TEXTURE", this.tintTexture.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);
     }
     }
 
 
     public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh, subMesh?: SubMesh) {
     public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh, subMesh?: SubMesh) {
@@ -189,15 +196,44 @@ export class ClearCoatBlock extends NodeMaterialBlock {
         }
         }
     }
     }
 
 
+    private _generateTBNSpace(state: NodeMaterialBuildState, worldPositionVarName: string, worldNormalVarName: string) {
+        let code = "";
+
+        let comments = `//${this.name}`;
+        let worldTangent = this.worldTangent;
+
+        state._emitExtension("derivatives", "#extension GL_OES_standard_derivatives : enable");
+
+        let tangentReplaceString = { search: /defined\(TANGENT\)/g, replace: worldTangent.isConnected ? "defined(TANGENT)" : "defined(IGNORE)" };
+
+        if (worldTangent.isConnected) {
+            code += `vec3 tbnNormal = normalize(${worldNormalVarName}.xyz);\r\n`;
+            code += `vec3 tbnTangent = normalize(${worldTangent.associatedVariableName}.xyz);\r\n`;
+            code += `vec3 tbnBitangent = cross(tbnNormal, tbnTangent);\r\n`;
+            code += `mat3 vTBN = mat3(tbnTangent, tbnBitangent, tbnNormal);\r\n`;
+        }
+
+        state._emitFunctionFromInclude("bumpFragmentMainFunctions", comments, {
+            replaceStrings: [
+                tangentReplaceString,
+            ]
+        });
+
+        return code;
+    }
+
     /**
     /**
      * Gets the main code of the block (fragment side)
      * Gets the main code of the block (fragment side)
      * @param state current state of the node material building
      * @param state current state of the node material building
      * @param ccBlock instance of a ClearCoatBlock or null if the code must be generated without an active clear coat module
      * @param ccBlock instance of a ClearCoatBlock or null if the code must be generated without an active clear coat module
      * @param reflectionBlock instance of a ReflectionBlock null if the code must be generated without an active reflection module
      * @param reflectionBlock instance of a ReflectionBlock null if the code must be generated without an active reflection module
      * @param worldPosVarName name of the variable holding the world position
      * @param worldPosVarName name of the variable holding the world position
+     * @param generateTBNSpace if true, the code needed to create the TBN coordinate space is generated
+     * @param vTBNAvailable indicate that the vTBN variable is already existing because it has already been generated by another block (PerturbNormal or Anisotropy)
+     * @param worldNormalVarName name of the variable holding the world normal
      * @returns the shader code
      * @returns the shader code
      */
      */
-    public static GetCode(state: NodeMaterialBuildState, ccBlock: Nullable<ClearCoatBlock>, reflectionBlock: Nullable<ReflectionBlock>, worldPosVarName: string): string {
+    public static GetCode(state: NodeMaterialBuildState, ccBlock: Nullable<ClearCoatBlock>, reflectionBlock: Nullable<ReflectionBlock>, worldPosVarName: string, generateTBNSpace: boolean, vTBNAvailable: boolean, worldNormalVarName: string): string {
         let code = "";
         let code = "";
 
 
         const intensity = ccBlock?.intensity.isConnected ? ccBlock.intensity.associatedVariableName : "1.";
         const intensity = ccBlock?.intensity.isConnected ? ccBlock.intensity.associatedVariableName : "1.";
@@ -216,7 +252,12 @@ export class ClearCoatBlock extends NodeMaterialBlock {
             state._emitUniformFromString("vClearCoatTangentSpaceParams", "vec2");
             state._emitUniformFromString("vClearCoatTangentSpaceParams", "vec2");
         }
         }
 
 
-        code = `clearcoatOutParams clearcoatOut;
+        if (generateTBNSpace && ccBlock) {
+            code += ccBlock._generateTBNSpace(state, worldPosVarName, worldNormalVarName);
+            vTBNAvailable = ccBlock.worldTangent.isConnected;
+        }
+
+        code += `clearcoatOutParams clearcoatOut;
 
 
         #ifdef CLEARCOAT
         #ifdef CLEARCOAT
             vec2 vClearCoatParams = vec2(${intensity}, ${roughness});
             vec2 vClearCoatParams = vec2(${intensity}, ${roughness});
@@ -243,7 +284,7 @@ export class ClearCoatBlock extends NodeMaterialBlock {
                 vec2(0., 1.),
                 vec2(0., 1.),
                 ${bumpTexture},
                 ${bumpTexture},
                 ${uv},
                 ${uv},
-                #if defined(TANGENT) && defined(NORMAL)
+                #if defined(${vTBNAvailable ? "TANGENT" : "IGNORE"}) && defined(NORMAL)
                     vTBN,
                     vTBN,
                 #else
                 #else
                     vClearCoatTangentSpaceParams,
                     vClearCoatTangentSpaceParams,

+ 2 - 0
src/Materials/Node/Blocks/PBR/index.ts

@@ -5,3 +5,5 @@ export * from "./reflectivityBlock";
 export * from "./anisotropyBlock";
 export * from "./anisotropyBlock";
 export * from "./reflectionBlock";
 export * from "./reflectionBlock";
 export * from "./clearCoatBlock";
 export * from "./clearCoatBlock";
+export * from "./refractionBlock";
+export * from "./subSurfaceBlock";

+ 75 - 29
src/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.ts

@@ -26,6 +26,9 @@ import { MaterialFlags } from '../../../materialFlags';
 import { AnisotropyBlock } from './anisotropyBlock';
 import { AnisotropyBlock } from './anisotropyBlock';
 import { ReflectionBlock } from './reflectionBlock';
 import { ReflectionBlock } from './reflectionBlock';
 import { ClearCoatBlock } from './clearCoatBlock';
 import { ClearCoatBlock } from './clearCoatBlock';
+import { SubSurfaceBlock } from './subSurfaceBlock';
+import { RefractionBlock } from './refractionBlock';
+import { PerturbNormalBlock } from '../Fragment/perturbNormalBlock';
 
 
 const mapOutputToVariable: { [name: string] : [string, string] } = {
 const mapOutputToVariable: { [name: string] : [string, string] } = {
     "ambient":      ["finalAmbient", ""],
     "ambient":      ["finalAmbient", ""],
@@ -84,9 +87,10 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
             new NodeMaterialConnectionPointCustomObject("reflection", this, NodeMaterialConnectionPointDirection.Input, ReflectionBlock, "ReflectionBlock"));
             new NodeMaterialConnectionPointCustomObject("reflection", this, NodeMaterialConnectionPointDirection.Input, ReflectionBlock, "ReflectionBlock"));
         this.registerInput("sheen", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment,
         this.registerInput("sheen", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment,
             new NodeMaterialConnectionPointCustomObject("sheen", this, NodeMaterialConnectionPointDirection.Input, SheenBlock, "SheenBlock"));
             new NodeMaterialConnectionPointCustomObject("sheen", this, NodeMaterialConnectionPointDirection.Input, SheenBlock, "SheenBlock"));
-        this.registerInput("clearCoat", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment,
+        this.registerInput("clearcoat", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment,
             new NodeMaterialConnectionPointCustomObject("clearcoat", this, NodeMaterialConnectionPointDirection.Input, ClearCoatBlock, "ClearCoatBlock"));
             new NodeMaterialConnectionPointCustomObject("clearcoat", this, NodeMaterialConnectionPointDirection.Input, ClearCoatBlock, "ClearCoatBlock"));
-        this.registerInput("subSurface", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("subsurface", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment,
+            new NodeMaterialConnectionPointCustomObject("subsurface", this, NodeMaterialConnectionPointDirection.Input, SubSurfaceBlock, "SubSurfaceBlock"));
         this.registerInput("anisotropy", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment,
         this.registerInput("anisotropy", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment,
             new NodeMaterialConnectionPointCustomObject("anisotropy", this, NodeMaterialConnectionPointDirection.Input, AnisotropyBlock, "AnisotropyBlock"));
             new NodeMaterialConnectionPointCustomObject("anisotropy", this, NodeMaterialConnectionPointDirection.Input, AnisotropyBlock, "AnisotropyBlock"));
 
 
@@ -106,6 +110,27 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
     }
     }
 
 
     /**
     /**
+     * Intensity of the direct lights e.g. the four lights available in your scene.
+     * This impacts both the direct diffuse and specular highlights.
+     */
+    @editableInPropertyPage("Direct lights", PropertyTypeForEdition.Float, "INTENSITY", { min: 0, max: 1, "notifiers": { "update": true }})
+    public directIntensity: number = 1.0;
+
+    /**
+     * Intensity of the environment e.g. how much the environment will light the object
+     * either through harmonics for rough material or through the refelction for shiny ones.
+     */
+    @editableInPropertyPage("Environment lights", PropertyTypeForEdition.Float, "INTENSITY", { min: 0, max: 1, "notifiers": { "update": true }})
+    public environmentIntensity: number = 1.0;
+
+    /**
+     * This is a special control allowing the reduction of the specular highlights coming from the
+     * four lights of the scene. Those highlights may not be needed in full environment lighting.
+     */
+    @editableInPropertyPage("Specular highlights", PropertyTypeForEdition.Float, "INTENSITY", { min: 0, max: 1, "notifiers": { "update": true }})
+    public specularIntensity: number = 1.0;
+
+    /**
      * Defines the  falloff type used in this material.
      * Defines the  falloff type used in this material.
      * It by default is Physical.
      * It by default is Physical.
      */
      */
@@ -431,7 +456,7 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
     /**
     /**
      * Gets the sub surface object parameters
      * Gets the sub surface object parameters
      */
      */
-    public get subSurface(): NodeMaterialConnectionPoint {
+    public get subsurface(): NodeMaterialConnectionPoint {
         return this._inputs[13];
         return this._inputs[13];
     }
     }
 
 
@@ -549,14 +574,14 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
         // General
         // General
         defines.setValue("PBR", true);
         defines.setValue("PBR", true);
         defines.setValue("METALLICWORKFLOW", true);
         defines.setValue("METALLICWORKFLOW", true);
-        defines.setValue("DEBUGMODE", this.debugMode);
+        defines.setValue("DEBUGMODE", this.debugMode, true);
         defines.setValue("NORMALXYSCALE", true);
         defines.setValue("NORMALXYSCALE", true);
-        defines.setValue("BUMP", this.perturbedNormal.isConnected);
+        defines.setValue("BUMP", this.perturbedNormal.isConnected, true);
         defines.setValue("LODBASEDMICROSFURACE", this._scene.getEngine().getCaps().textureLOD);
         defines.setValue("LODBASEDMICROSFURACE", this._scene.getEngine().getCaps().textureLOD);
 
 
         // Albedo & Opacity
         // Albedo & Opacity
-        defines.setValue("ALBEDO", this.baseTexture.isConnected);
-        defines.setValue("OPACITY", this.opacityTexture.isConnected);
+        defines.setValue("ALBEDO", this.baseTexture.isConnected, true);
+        defines.setValue("OPACITY", this.opacityTexture.isConnected, true);
 
 
         // Lighting & colors
         // Lighting & colors
         if (this.lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_STANDARD) {
         if (this.lightFalloff === PBRBaseMaterial.LIGHTFALLOFF_STANDARD) {
@@ -571,28 +596,28 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
         }
         }
 
 
         // Transparency
         // Transparency
-        defines.setValue("ALPHABLEND", this.useAlphaBlending);
-        defines.setValue("ALPHAFROMALBEDO", this.useAlphaFromAlbedoTexture);
-        defines.setValue("ALPHATEST", this.useAlphaTest);
-        defines.setValue("ALPHATESTVALUE", this.alphaTestCutoff);
-        defines.setValue("OPACITYRGB", this.opacityRGB);
+        defines.setValue("ALPHABLEND", this.useAlphaBlending, true);
+        defines.setValue("ALPHAFROMALBEDO", this.useAlphaFromAlbedoTexture, true);
+        defines.setValue("ALPHATEST", this.useAlphaTest, true);
+        defines.setValue("ALPHATESTVALUE", this.alphaTestCutoff, true);
+        defines.setValue("OPACITYRGB", this.opacityRGB, true);
 
 
         // Rendering
         // Rendering
-        defines.setValue("RADIANCEOVERALPHA", this.useRadianceOverAlpha);
-        defines.setValue("SPECULAROVERALPHA", this.useSpecularOverAlpha);
-        defines.setValue("SPECULARAA", this._scene.getEngine().getCaps().standardDerivatives && this.enableSpecularAntiAliasing);
+        defines.setValue("RADIANCEOVERALPHA", this.useRadianceOverAlpha, true);
+        defines.setValue("SPECULAROVERALPHA", this.useSpecularOverAlpha, true);
+        defines.setValue("SPECULARAA", this._scene.getEngine().getCaps().standardDerivatives && this.enableSpecularAntiAliasing, true);
 
 
         // Advanced
         // Advanced
         defines.setValue("BRDF_V_HEIGHT_CORRELATED", true);
         defines.setValue("BRDF_V_HEIGHT_CORRELATED", true);
-        defines.setValue("MS_BRDF_ENERGY_CONSERVATION", this.useEnergyConservation);
-        defines.setValue("RADIANCEOCCLUSION", this.useRadianceOcclusion);
-        defines.setValue("HORIZONOCCLUSION", this.useHorizonOcclusion);
-        defines.setValue("UNLIT", this.unlit);
-        defines.setValue("FORCENORMALFORWARD", this.forceNormalForward);
+        defines.setValue("MS_BRDF_ENERGY_CONSERVATION", this.useEnergyConservation, true);
+        defines.setValue("RADIANCEOCCLUSION", this.useRadianceOcclusion, true);
+        defines.setValue("HORIZONOCCLUSION", this.useHorizonOcclusion, true);
+        defines.setValue("UNLIT", this.unlit, true);
+        defines.setValue("FORCENORMALFORWARD", this.forceNormalForward, true);
 
 
         if (this._environmentBRDFTexture && MaterialFlags.ReflectionTextureEnabled) {
         if (this._environmentBRDFTexture && MaterialFlags.ReflectionTextureEnabled) {
             defines.setValue("ENVIRONMENTBRDF", true);
             defines.setValue("ENVIRONMENTBRDF", true);
-            defines.setValue("ENVIRONMENTBRDF_RGBD", this._environmentBRDFTexture.isRGBD);
+            defines.setValue("ENVIRONMENTBRDF_RGBD", this._environmentBRDFTexture.isRGBD, true);
         } else {
         } else {
             defines.setValue("ENVIRONMENTBRDF" , false);
             defines.setValue("ENVIRONMENTBRDF" , false);
             defines.setValue("ENVIRONMENTBRDF_RGBD", false);
             defines.setValue("ENVIRONMENTBRDF_RGBD", false);
@@ -664,6 +689,8 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
         const invertNormal = (scene.useRightHandedSystem === (scene._mirroredCameraPosition != null));
         const invertNormal = (scene.useRightHandedSystem === (scene._mirroredCameraPosition != null));
 
 
         effect.setFloat(this._invertNormalName, invertNormal ? -1 : 1);
         effect.setFloat(this._invertNormalName, invertNormal ? -1 : 1);
+
+        effect.setFloat4("vLightingIntensity", this.directIntensity, 1, this.environmentIntensity * this._scene.environmentIntensity, this.specularIntensity);
     }
     }
 
 
     private _injectVertexCode(state: NodeMaterialBuildState) {
     private _injectVertexCode(state: NodeMaterialBuildState) {
@@ -836,13 +863,12 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
         state._emitFunctionFromInclude("pbrBlockAmbientOcclusion", comments);
         state._emitFunctionFromInclude("pbrBlockAmbientOcclusion", comments);
         state._emitFunctionFromInclude("pbrBlockAlphaFresnel", comments);
         state._emitFunctionFromInclude("pbrBlockAlphaFresnel", comments);
         state._emitFunctionFromInclude("pbrBlockAnisotropic", comments);
         state._emitFunctionFromInclude("pbrBlockAnisotropic", comments);
-        state._emitFunctionFromInclude("pbrBlockSubSurface", comments);
 
 
         //
         //
         // code
         // code
         //
         //
 
 
-        state.compilationString += `vec4 vLightingIntensity = vec4(1.);\r\n`;
+        state._emitUniformFromString("vLightingIntensity", "vec4");
 
 
         // _____________________________ Geometry Information ____________________________
         // _____________________________ Geometry Information ____________________________
         this._vNormalWName = state._getFreeVariableName("vNormalW");
         this._vNormalWName = state._getFreeVariableName("vNormalW");
@@ -945,8 +971,16 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
 
 
         // _____________________________ Clear Coat ____________________________
         // _____________________________ Clear Coat ____________________________
         const clearcoatBlock = this.clearcoat.isConnected ? this.clearcoat.connectedPoint?.ownerBlock as ClearCoatBlock : null;
         const clearcoatBlock = this.clearcoat.isConnected ? this.clearcoat.connectedPoint?.ownerBlock as ClearCoatBlock : null;
+        const generateTBNSpace = !this.perturbedNormal.isConnected && !this.anisotropy.isConnected;
+        const isTangentConnectedToPerturbNormal = this.perturbedNormal.isConnected && (this.perturbedNormal.connectedPoint?.ownerBlock as PerturbNormalBlock).worldTangent.isConnected;
+        const isTangentConnectedToAnisotropy = this.anisotropy.isConnected && (this.anisotropy.connectedPoint?.ownerBlock as AnisotropyBlock).worldTangent.isConnected;
+        let vTBNAvailable = isTangentConnectedToPerturbNormal || (!this.perturbedNormal.isConnected && isTangentConnectedToAnisotropy);
 
 
-        state.compilationString += ClearCoatBlock.GetCode(state, clearcoatBlock, reflectionBlock, worldPosVarName);
+        state.compilationString += ClearCoatBlock.GetCode(state, clearcoatBlock, reflectionBlock, worldPosVarName, generateTBNSpace, vTBNAvailable, this.worldNormal.associatedVariableName);
+
+        if (generateTBNSpace) {
+            vTBNAvailable = clearcoatBlock?.worldTangent.isConnected ?? false;
+        }
 
 
         state._emitFunctionFromInclude("pbrBlockClearcoat", comments, {
         state._emitFunctionFromInclude("pbrBlockClearcoat", comments, {
             replaceStrings: [
             replaceStrings: [
@@ -957,6 +991,7 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
                 { search: /REFLECTIONMAP_SKYBOX/g, replace: reflectionBlock?._defineSkyboxName ?? "REFLECTIONMAP_SKYBOX" },
                 { search: /REFLECTIONMAP_SKYBOX/g, replace: reflectionBlock?._defineSkyboxName ?? "REFLECTIONMAP_SKYBOX" },
                 { search: /LODINREFLECTIONALPHA/g, replace: reflectionBlock?._defineLODReflectionAlpha ?? "LODINREFLECTIONALPHA" },
                 { search: /LODINREFLECTIONALPHA/g, replace: reflectionBlock?._defineLODReflectionAlpha ?? "LODINREFLECTIONALPHA" },
                 { search: /LINEARSPECULARREFLECTION/g, replace: reflectionBlock?._defineLinearSpecularReflection ?? "LINEARSPECULARREFLECTION" },
                 { search: /LINEARSPECULARREFLECTION/g, replace: reflectionBlock?._defineLinearSpecularReflection ?? "LINEARSPECULARREFLECTION" },
+                { search: /defined\(TANGENT\)/g, replace: vTBNAvailable ? "defined(TANGENT)" : "defined(IGNORE)" },
             ]
             ]
         });
         });
 
 
@@ -969,11 +1004,22 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
         });
         });
 
 
         // ___________________________________ SubSurface ______________________________________
         // ___________________________________ SubSurface ______________________________________
-        state.compilationString += `subSurfaceOutParams subSurfaceOut;
-            #ifdef SUBSURFACE
-            #else
-                subSurfaceOut.specularEnvironmentReflectance = specularEnvironmentReflectance;
-            #endif\r\n`;
+        const subsurfaceBlock = this.subsurface.isConnected ? this.subsurface.connectedPoint?.ownerBlock as SubSurfaceBlock : null;
+        const refractionBlock = this.subsurface.isConnected ? (this.subsurface.connectedPoint?.ownerBlock as SubSurfaceBlock).refraction.connectedPoint?.ownerBlock as RefractionBlock : null;
+
+        state.compilationString += SubSurfaceBlock.GetCode(state, subsurfaceBlock, reflectionBlock, worldPosVarName);
+
+        state._emitFunctionFromInclude("pbrBlockSubSurface", comments, {
+            replaceStrings: [
+                { search: /REFLECTIONMAP_3D/g, replace: reflectionBlock?._define3DName ?? "REFLECTIONMAP_3D" },
+                { search: /REFLECTIONMAP_OPPOSITEZ/g, replace: reflectionBlock?._defineOppositeZ ?? "REFLECTIONMAP_OPPOSITEZ" },
+                { search: /REFLECTIONMAP_PROJECTION/g, replace: reflectionBlock?._defineProjectionName ?? "REFLECTIONMAP_PROJECTION" },
+                { search: /SS_REFRACTIONMAP_3D/g, replace: refractionBlock?._define3DName ?? "SS_REFRACTIONMAP_3D" },
+                { search: /SS_LODINREFRACTIONALPHA/g, replace: refractionBlock?._defineLODRefractionAlpha ?? "SS_LODINREFRACTIONALPHA" },
+                { search: /SS_LINEARSPECULARREFRACTION/g, replace: refractionBlock?._defineLinearSpecularRefraction ?? "SS_LINEARSPECULARREFRACTION" },
+                { search: /SS_REFRACTIONMAP_OPPOSITEZ/g, replace: refractionBlock?._defineOppositeZ ?? "SS_REFRACTIONMAP_OPPOSITEZ" },
+            ]
+        });
 
 
         // _____________________________ Direct Lighting Info __________________________________
         // _____________________________ Direct Lighting Info __________________________________
         state.compilationString += state._emitCodeFromInclude("pbrBlockDirectLighting", comments);
         state.compilationString += state._emitCodeFromInclude("pbrBlockDirectLighting", comments);

+ 5 - 5
src/Materials/Node/Blocks/PBR/reflectionBlock.ts

@@ -168,17 +168,17 @@ export class ReflectionBlock extends ReflectionTextureBaseBlock {
         const reflectionTexture = this._getTexture();
         const reflectionTexture = this._getTexture();
         const reflection = reflectionTexture && reflectionTexture.getTextureMatrix;
         const reflection = reflectionTexture && reflectionTexture.getTextureMatrix;
 
 
-        defines.setValue("REFLECTION", reflection);
+        defines.setValue("REFLECTION", reflection, true);
 
 
         if (!reflection) {
         if (!reflection) {
             return;
             return;
         }
         }
 
 
-        defines.setValue(this._defineLODReflectionAlpha, reflectionTexture!.lodLevelInAlpha);
-        defines.setValue(this._defineLinearSpecularReflection, reflectionTexture!.linearSpecularLOD);
-        defines.setValue(this._defineOppositeZ, this._scene.useRightHandedSystem ? !reflectionTexture!.invertZ : reflectionTexture!.invertZ);
+        defines.setValue(this._defineLODReflectionAlpha, reflectionTexture!.lodLevelInAlpha, true);
+        defines.setValue(this._defineLinearSpecularReflection, reflectionTexture!.linearSpecularLOD, true);
+        defines.setValue(this._defineOppositeZ, this._scene.useRightHandedSystem ? !reflectionTexture!.invertZ : reflectionTexture!.invertZ, true);
 
 
-        defines.setValue("SPHERICAL_HARMONICS", this.useSphericalHarmonics);
+        defines.setValue("SPHERICAL_HARMONICS", this.useSphericalHarmonics, true);
 
 
         if (reflectionTexture && reflectionTexture.coordinatesMode !== Texture.SKYBOX_MODE) {
         if (reflectionTexture && reflectionTexture.coordinatesMode !== Texture.SKYBOX_MODE) {
             if (reflectionTexture.isCube) {
             if (reflectionTexture.isCube) {

+ 6 - 6
src/Materials/Node/Blocks/PBR/reflectivityBlock.ts

@@ -155,12 +155,12 @@ export class ReflectivityBlock extends NodeMaterialBlock {
     public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
     public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
         super.prepareDefines(mesh, nodeMaterial, defines);
         super.prepareDefines(mesh, nodeMaterial, defines);
 
 
-        defines.setValue("REFLECTIVITY", this.texture.isConnected);
-        defines.setValue("AOSTOREINMETALMAPRED", this.useAmbientOcclusionFromMetallicTextureRed);
-        defines.setValue("METALLNESSSTOREINMETALMAPBLUE", this.useMetallnessFromMetallicTextureBlue);
-        defines.setValue("ROUGHNESSSTOREINMETALMAPALPHA", this.useRoughnessFromMetallicTextureAlpha);
-        defines.setValue("ROUGHNESSSTOREINMETALMAPGREEN",  !this.useRoughnessFromMetallicTextureAlpha && this.useRoughnessFromMetallicTextureGreen);
-        defines.setValue("METALLICF0FACTORFROMMETALLICMAP", this.useMetallicF0FactorFromMetallicTexture);
+        defines.setValue("REFLECTIVITY", this.texture.isConnected, true);
+        defines.setValue("AOSTOREINMETALMAPRED", this.useAmbientOcclusionFromMetallicTextureRed, true);
+        defines.setValue("METALLNESSSTOREINMETALMAPBLUE", this.useMetallnessFromMetallicTextureBlue, true);
+        defines.setValue("ROUGHNESSSTOREINMETALMAPALPHA", this.useRoughnessFromMetallicTextureAlpha, true);
+        defines.setValue("ROUGHNESSSTOREINMETALMAPGREEN",  !this.useRoughnessFromMetallicTextureAlpha && this.useRoughnessFromMetallicTextureGreen, true);
+        defines.setValue("METALLICF0FACTORFROMMETALLICMAP", this.useMetallicF0FactorFromMetallicTexture, true);
     }
     }
 
 
     protected _buildBlock(state: NodeMaterialBuildState) {
     protected _buildBlock(state: NodeMaterialBuildState) {

+ 338 - 0
src/Materials/Node/Blocks/PBR/refractionBlock.ts

@@ -0,0 +1,338 @@
+import { NodeMaterialBlockConnectionPointTypes } from '../../Enums/nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialConnectionPoint, NodeMaterialConnectionPointDirection } from '../../nodeMaterialBlockConnectionPoint';
+import { NodeMaterialBlockTargets } from '../../Enums/nodeMaterialBlockTargets';
+import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+import { _TypeStore } from '../../../../Misc/typeStore';
+import { InputBlock } from '../Input/inputBlock';
+import { NodeMaterialConnectionPointCustomObject } from "../../nodeMaterialConnectionPointCustomObject";
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { Nullable } from '../../../../types';
+import { BaseTexture } from '../../../Textures/baseTexture';
+import { Mesh } from '../../../../Meshes/mesh';
+import { SubMesh } from '../../../../Meshes/subMesh';
+import { Effect } from '../../../effect';
+import { editableInPropertyPage, PropertyTypeForEdition } from "../../nodeMaterialDecorator";
+import { Scene } from '../../../../scene';
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { CubeTexture } from '../../../Textures/cubeTexture';
+import { Texture } from '../../../Textures/texture';
+import { NodeMaterialSystemValues } from '../../Enums/nodeMaterialSystemValues';
+
+/**
+ * Block used to implement the refraction part of the sub surface module of the PBR material
+ */
+export class RefractionBlock extends NodeMaterialBlock {
+
+    /** @hidden */
+    public _define3DName: string;
+    /** @hidden */
+    public _refractionMatrixName: string;
+    /** @hidden */
+    public _defineLODRefractionAlpha: string;
+    /** @hidden */
+    public _defineLinearSpecularRefraction: string;
+    /** @hidden */
+    public _defineOppositeZ: string;
+    /** @hidden */
+    public _cubeSamplerName: string;
+    /** @hidden */
+    public _2DSamplerName: string;
+    /** @hidden */
+    public _vRefractionMicrosurfaceInfosName: string;
+    /** @hidden */
+    public _vRefractionInfosName: string;
+
+    private _scene: Scene;
+
+    /**
+     * This parameters will make the material used its opacity to control how much it is refracting aginst not.
+     * Materials half opaque for instance using refraction could benefit from this control.
+     */
+    @editableInPropertyPage("Link refraction to transparency", PropertyTypeForEdition.Boolean, "ADVANCED", { "notifiers": { "update": true }})
+    public linkRefractionWithTransparency: boolean = false;
+
+    /**
+     * Controls if refraction needs to be inverted on Y. This could be useful for procedural texture.
+     */
+    @editableInPropertyPage("Invert refraction Y", PropertyTypeForEdition.Boolean, "ADVANCED", { "notifiers": { "update": true }})
+    public invertRefractionY: boolean = false;
+
+    /**
+     * Gets or sets the texture associated with the node
+     */
+    public texture: Nullable<BaseTexture>;
+
+    /**
+     * Create a new RefractionBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment);
+
+        this._isUnique = true;
+
+        this.registerInput("intensity", NodeMaterialBlockConnectionPointTypes.Float, false, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("indexOfRefraction", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("tintAtDistance", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("view", NodeMaterialBlockConnectionPointTypes.Matrix, false, NodeMaterialBlockTargets.Fragment);
+
+        this.registerOutput("refraction", NodeMaterialBlockConnectionPointTypes.Object, NodeMaterialBlockTargets.Fragment,
+            new NodeMaterialConnectionPointCustomObject("refraction", this, NodeMaterialConnectionPointDirection.Output, RefractionBlock, "RefractionBlock"));
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "RefractionBlock";
+    }
+
+    /**
+     * Gets the intensity input component
+     */
+    public get intensity(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    /**
+     * Gets the index of refraction input component
+     */
+    public get indexOfRefraction(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    /**
+     * Gets the tint at distance input component
+     */
+    public get tintAtDistance(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }
+
+    /**
+     * Gets the view input component
+     */
+    public get view(): NodeMaterialConnectionPoint {
+        return this._inputs[3];
+    }
+
+    /**
+     * Gets the refraction object output component
+     */
+    public get refraction(): NodeMaterialConnectionPoint {
+        return this._outputs[0];
+    }
+
+    /**
+     * Returns true if the block has a texture
+     */
+    public get hasTexture(): boolean {
+        return !!this._getTexture();
+    }
+
+    protected _getTexture(): Nullable<BaseTexture> {
+        if (this.texture) {
+            return this.texture;
+        }
+
+        return this._scene.environmentTexture;
+    }
+
+    public autoConfigure(material: NodeMaterial) {
+        if (!this.intensity.isConnected) {
+            let intensityInput = new InputBlock("Refraction intensity", NodeMaterialBlockTargets.Fragment, NodeMaterialBlockConnectionPointTypes.Float);
+            intensityInput.value = 1;
+            intensityInput.output.connectTo(this.intensity);
+        }
+
+        if (!this.view.isConnected) {
+            let viewInput = material.getInputBlockByPredicate((b) => b.systemValue === NodeMaterialSystemValues.View);
+
+            if (!viewInput) {
+                viewInput = new InputBlock("view");
+                viewInput.setAsSystemValue(NodeMaterialSystemValues.View);
+            }
+            viewInput.output.connectTo(this.view);
+        }
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        super.prepareDefines(mesh, nodeMaterial, defines);
+
+        const refractionTexture = this._getTexture();
+        const refraction = refractionTexture && refractionTexture.getTextureMatrix;
+
+        defines.setValue("SS_REFRACTION", refraction, true);
+
+        if (!refraction) {
+            return;
+        }
+
+        defines.setValue(this._define3DName, refractionTexture!.isCube, true);
+        defines.setValue(this._defineLODRefractionAlpha, refractionTexture!.lodLevelInAlpha, true);
+        defines.setValue(this._defineLinearSpecularRefraction, refractionTexture!.linearSpecularLOD, true);
+        defines.setValue(this._defineOppositeZ, this._scene.useRightHandedSystem ? !refractionTexture!.invertZ : refractionTexture!.invertZ, true);
+
+        defines.setValue("SS_LINKREFRACTIONTOTRANSPARENCY", this.linkRefractionWithTransparency, true);
+    }
+
+    public isReady() {
+        const texture = this._getTexture();
+
+        if (texture && !texture.isReadyOrNotBlocking()) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public bind(effect: Effect, nodeMaterial: NodeMaterial, mesh?: Mesh, subMesh?: SubMesh) {
+        super.bind(effect, nodeMaterial, mesh);
+
+        const refractionTexture = this._getTexture();
+
+        if (!refractionTexture) {
+            return;
+        }
+
+        if (refractionTexture.isCube) {
+            effect.setTexture(this._cubeSamplerName, refractionTexture);
+        } else {
+            effect.setTexture(this._2DSamplerName, refractionTexture);
+        }
+
+        effect.setMatrix(this._refractionMatrixName, refractionTexture.getReflectionTextureMatrix());
+
+        let depth = 1.0;
+        if (!refractionTexture.isCube) {
+            if ((<any>refractionTexture).depth) {
+                depth = (<any>refractionTexture).depth;
+            }
+        }
+
+        const indexOfRefraction = this.indexOfRefraction.connectInputBlock?.value ?? 1.0;
+
+        effect.setFloat4(this._vRefractionInfosName, refractionTexture.level, 1 / indexOfRefraction, depth, this.invertRefractionY ? -1 : 1);
+
+        effect.setFloat3(this._vRefractionMicrosurfaceInfosName, refractionTexture.getSize().width, refractionTexture.lodGenerationScale, refractionTexture.lodGenerationOffset);
+    }
+
+    /**
+     * Gets the main code of the block (fragment side)
+     * @param state current state of the node material building
+     * @returns the shader code
+     */
+    public getCode(state: NodeMaterialBuildState): string {
+        let code = "";
+
+        state.sharedData.blockingBlocks.push(this);
+        state.sharedData.textureBlocks.push(this);
+
+        // Samplers
+        this._cubeSamplerName = state._getFreeVariableName(this.name + "CubeSampler");
+        state.samplers.push(this._cubeSamplerName);
+
+        this._2DSamplerName = state._getFreeVariableName(this.name + "2DSampler");
+        state.samplers.push(this._2DSamplerName);
+
+        this._define3DName = state._getFreeDefineName("SS_REFRACTIONMAP_3D");
+
+        state._samplerDeclaration += `#ifdef ${this._define3DName}\r\n`;
+        state._samplerDeclaration += `uniform samplerCube ${this._cubeSamplerName};\r\n`;
+        state._samplerDeclaration += `#else\r\n`;
+        state._samplerDeclaration += `uniform sampler2D ${this._2DSamplerName};\r\n`;
+        state._samplerDeclaration += `#endif\r\n`;
+
+        // Fragment
+        state.sharedData.blocksWithDefines.push(this);
+        state.sharedData.bindableBlocks.push(this);
+
+        this._defineLODRefractionAlpha = state._getFreeDefineName("SS_LODINREFRACTIONALPHA");
+        this._defineLinearSpecularRefraction = state._getFreeDefineName("SS_LINEARSPECULARREFRACTION");
+        this._defineOppositeZ = state._getFreeDefineName("SS_REFRACTIONMAP_OPPOSITEZ");
+
+        this._refractionMatrixName = state._getFreeVariableName("refractionMatrix");
+
+        state._emitUniformFromString(this._refractionMatrixName, "mat4");
+
+        state._emitFunction("sampleRefraction", `
+            #ifdef ${this._define3DName}
+                #define sampleRefraction(s, c) textureCube(s, c)
+            #else
+                #define sampleRefraction(s, c) texture2D(s, c)
+            #endif\r\n`, `//${this.name}`);
+
+        state._emitFunction("sampleRefractionLod", `
+            #ifdef ${this._define3DName}
+                #define sampleRefractionLod(s, c, l) textureCubeLodEXT(s, c, l)
+            #else
+                #define sampleRefractionLod(s, c, l) texture2DLodEXT(s, c, l)
+            #endif\r\n`, `//${this.name}`);
+
+        this._vRefractionMicrosurfaceInfosName = state._getFreeVariableName("vRefractionMicrosurfaceInfos");
+
+        state._emitUniformFromString(this._vRefractionMicrosurfaceInfosName, "vec3");
+
+        this._vRefractionInfosName = state._getFreeVariableName("vRefractionInfos");
+
+        state._emitUniformFromString(this._vRefractionInfosName, "vec4");
+
+        return code;
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        this._scene = state.sharedData.scene;
+
+        return this;
+    }
+
+    protected _dumpPropertiesCode() {
+        let codeString: string = super._dumpPropertiesCode();
+
+        if (this.texture) {
+            if (this.texture.isCube) {
+                codeString = `${this._codeVariableName}.texture = new BABYLON.CubeTexture("${this.texture.name}");\r\n`;
+            } else {
+                codeString = `${this._codeVariableName}.texture = new BABYLON.Texture("${this.texture.name}");\r\n`;
+            }
+            codeString += `${this._codeVariableName}.texture.coordinatesMode = ${this.texture.coordinatesMode};\r\n`;
+        }
+
+        codeString += `${this._codeVariableName}.linkRefractionWithTransparency = ${this.linkRefractionWithTransparency};\r\n`;
+        codeString += `${this._codeVariableName}.invertRefractionY = ${this.invertRefractionY};\r\n`;
+
+        return codeString;
+    }
+
+    public serialize(): any {
+        let serializationObject = super.serialize();
+
+        if (this.texture) {
+            serializationObject.texture = this.texture.serialize();
+        }
+
+        serializationObject.linkRefractionWithTransparency = this.linkRefractionWithTransparency;
+        serializationObject.invertRefractionY = this.invertRefractionY;
+
+        return serializationObject;
+    }
+
+    public _deserialize(serializationObject: any, scene: Scene, rootUrl: string) {
+        super._deserialize(serializationObject, scene, rootUrl);
+
+        if (serializationObject.texture) {
+            rootUrl = serializationObject.texture.url.indexOf("data:") === 0 ? "" : rootUrl;
+            if (serializationObject.texture.isCube) {
+                this.texture = CubeTexture.Parse(serializationObject.texture, scene, rootUrl);
+            } else {
+                this.texture = Texture.Parse(serializationObject.texture, scene, rootUrl);
+            }
+        }
+
+        this.linkRefractionWithTransparency = serializationObject.linkRefractionWithTransparency;
+        this.invertRefractionY = serializationObject.invertRefractionY;
+    }
+}
+
+_TypeStore.RegisteredTypes["BABYLON.RefractionBlock"] = RefractionBlock;

+ 4 - 4
src/Materials/Node/Blocks/PBR/sheenBlock.ts

@@ -107,10 +107,10 @@ export class SheenBlock extends NodeMaterialBlock {
         super.prepareDefines(mesh, nodeMaterial, defines);
         super.prepareDefines(mesh, nodeMaterial, defines);
 
 
         defines.setValue("SHEEN", true);
         defines.setValue("SHEEN", true);
-        defines.setValue("SHEEN_LINKWITHALBEDO", this.linkSheenWithAlbedo);
-        defines.setValue("SHEEN_ROUGHNESS", this.roughness.isConnected);
-        defines.setValue("SHEEN_ALBEDOSCALING", this.albedoScaling);
-        defines.setValue("SHEEN_TEXTURE", this.texture.isConnected);
+        defines.setValue("SHEEN_LINKWITHALBEDO", this.linkSheenWithAlbedo, true);
+        defines.setValue("SHEEN_ROUGHNESS", this.roughness.isConnected, true);
+        defines.setValue("SHEEN_ALBEDOSCALING", this.albedoScaling, true);
+        defines.setValue("SHEEN_TEXTURE", this.texture.isConnected, true);
     }
     }
 
 
     /**
     /**

+ 267 - 0
src/Materials/Node/Blocks/PBR/subSurfaceBlock.ts

@@ -0,0 +1,267 @@
+import { NodeMaterialBlock } from '../../nodeMaterialBlock';
+import { NodeMaterialBlockConnectionPointTypes } from '../../Enums/nodeMaterialBlockConnectionPointTypes';
+import { NodeMaterialBuildState } from '../../nodeMaterialBuildState';
+import { NodeMaterialConnectionPoint, NodeMaterialConnectionPointDirection } from '../../nodeMaterialBlockConnectionPoint';
+import { NodeMaterialBlockTargets } from '../../Enums/nodeMaterialBlockTargets';
+import { _TypeStore } from '../../../../Misc/typeStore';
+import { editableInPropertyPage, PropertyTypeForEdition } from "../../nodeMaterialDecorator";
+import { InputBlock } from '../Input/inputBlock';
+import { NodeMaterialConnectionPointCustomObject } from "../../nodeMaterialConnectionPointCustomObject";
+import { NodeMaterial, NodeMaterialDefines } from '../../nodeMaterial';
+import { AbstractMesh } from '../../../../Meshes/abstractMesh';
+import { ReflectionBlock } from './reflectionBlock';
+import { Nullable } from '../../../../types';
+import { RefractionBlock } from './refractionBlock';
+
+/**
+ * Block used to implement the sub surface module of the PBR material
+ */
+export class SubSurfaceBlock extends NodeMaterialBlock {
+
+    /**
+     * Create a new SubSurfaceBlock
+     * @param name defines the block name
+     */
+    public constructor(name: string) {
+        super(name, NodeMaterialBlockTargets.Fragment);
+
+        this._isUnique = true;
+
+        this.registerInput("minThickness", NodeMaterialBlockConnectionPointTypes.Float, false, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("maxThickness", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("thicknessTexture", NodeMaterialBlockConnectionPointTypes.Color4, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("tintColor", NodeMaterialBlockConnectionPointTypes.Color3, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("translucencyIntensity", NodeMaterialBlockConnectionPointTypes.Float, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("translucencyDiffusionDistance", NodeMaterialBlockConnectionPointTypes.Color3, true, NodeMaterialBlockTargets.Fragment);
+        this.registerInput("refraction", NodeMaterialBlockConnectionPointTypes.Object, true, NodeMaterialBlockTargets.Fragment,
+            new NodeMaterialConnectionPointCustomObject("refraction", this, NodeMaterialConnectionPointDirection.Input, RefractionBlock, "RefractionBlock"));
+
+        this.registerOutput("subsurface", NodeMaterialBlockConnectionPointTypes.Object, NodeMaterialBlockTargets.Fragment,
+            new NodeMaterialConnectionPointCustomObject("subsurface", this, NodeMaterialConnectionPointDirection.Output, SubSurfaceBlock, "SubSurfaceBlock"));
+    }
+
+    /**
+     * Stores the intensity of the different subsurface effects in the thickness texture.
+     * * the green channel is the translucency intensity.
+     * * the blue channel is the scattering intensity.
+     * * the alpha channel is the refraction intensity.
+     */
+    @editableInPropertyPage("Mask from thickness texture", PropertyTypeForEdition.Boolean, "PROPERTIES", { "notifiers": { "update": true }})
+    public useMaskFromThicknessTexture: boolean = false;
+
+    /**
+     * Initialize the block and prepare the context for build
+     * @param state defines the state that will be used for the build
+     */
+    public initialize(state: NodeMaterialBuildState) {
+        state._excludeVariableName("subSurfaceOut");
+        state._excludeVariableName("vThicknessParam");
+        state._excludeVariableName("vTintColor");
+        state._excludeVariableName("vSubSurfaceIntensity");
+    }
+
+    /**
+     * Gets the current class name
+     * @returns the class name
+     */
+    public getClassName() {
+        return "SubSurfaceBlock";
+    }
+
+    /**
+     * Gets the min thickness input component
+     */
+    public get minThickness(): NodeMaterialConnectionPoint {
+        return this._inputs[0];
+    }
+
+    /**
+     * Gets the max thickness input component
+     */
+    public get maxThickness(): NodeMaterialConnectionPoint {
+        return this._inputs[1];
+    }
+
+    /**
+     * Gets the thickness texture component
+     */
+    public get thicknessTexture(): NodeMaterialConnectionPoint {
+        return this._inputs[2];
+    }
+
+    /**
+     * Gets the tint color input component
+     */
+    public get tintColor(): NodeMaterialConnectionPoint {
+        return this._inputs[3];
+    }
+
+    /**
+     * Gets the translucency intensity input component
+     */
+    public get translucencyIntensity(): NodeMaterialConnectionPoint {
+        return this._inputs[4];
+    }
+
+    /**
+     * Gets the translucency diffusion distance input component
+     */
+    public get translucencyDiffusionDistance(): NodeMaterialConnectionPoint {
+        return this._inputs[5];
+    }
+
+    /**
+     * Gets the refraction object parameters
+     */
+    public get refraction(): NodeMaterialConnectionPoint {
+        return this._inputs[6];
+    }
+
+    /**
+     * Gets the sub surface object output component
+     */
+    public get subsurface(): NodeMaterialConnectionPoint {
+        return this._outputs[0];
+    }
+
+    public autoConfigure(material: NodeMaterial) {
+        if (!this.minThickness.isConnected) {
+            let minThicknessInput = new InputBlock("SubSurface min thickness", NodeMaterialBlockTargets.Fragment, NodeMaterialBlockConnectionPointTypes.Float);
+            minThicknessInput.value = 0;
+            minThicknessInput.output.connectTo(this.minThickness);
+        }
+    }
+
+    public prepareDefines(mesh: AbstractMesh, nodeMaterial: NodeMaterial, defines: NodeMaterialDefines) {
+        super.prepareDefines(mesh, nodeMaterial, defines);
+
+        const translucencyEnabled = this.translucencyDiffusionDistance.isConnected || this.translucencyIntensity.isConnected;
+
+        defines.setValue("SUBSURFACE", translucencyEnabled || this.refraction.isConnected, true);
+        defines.setValue("SS_TRANSLUCENCY", translucencyEnabled, true);
+        defines.setValue("SS_THICKNESSANDMASK_TEXTURE", this.thicknessTexture.isConnected, true);
+        defines.setValue("SS_MASK_FROM_THICKNESS_TEXTURE", this.useMaskFromThicknessTexture, true);
+    }
+
+    /**
+     * Gets the main code of the block (fragment side)
+     * @param state current state of the node material building
+     * @param ssBlock instance of a SubSurfaceBlock or null if the code must be generated without an active sub surface module
+     * @param reflectionBlock instance of a ReflectionBlock null if the code must be generated without an active reflection module
+     * @param worldPosVarName name of the variable holding the world position
+     * @returns the shader code
+     */
+    public static GetCode(state: NodeMaterialBuildState, ssBlock: Nullable<SubSurfaceBlock>, reflectionBlock: Nullable<ReflectionBlock>, worldPosVarName: string): string {
+        let code = "";
+
+        const minThickness = ssBlock?.minThickness.isConnected ? ssBlock.minThickness.associatedVariableName : "0.";
+        const maxThickness = ssBlock?.maxThickness.isConnected ? ssBlock.maxThickness.associatedVariableName : "1.";
+        const thicknessTexture = ssBlock?.thicknessTexture.isConnected ? ssBlock.thicknessTexture.associatedVariableName : "vec4(0.)";
+        const tintColor = ssBlock?.tintColor.isConnected ? ssBlock.tintColor.associatedVariableName : "vec3(1.)";
+        const translucencyIntensity = ssBlock?.translucencyIntensity.isConnected ? ssBlock?.translucencyIntensity.associatedVariableName : "1.";
+        const translucencyDiffusionDistance = ssBlock?.translucencyDiffusionDistance.isConnected ? ssBlock?.translucencyDiffusionDistance.associatedVariableName : "vec3(1.)";
+
+        const refractionBlock: Nullable<RefractionBlock> = (ssBlock?.refraction.isConnected ? ssBlock?.refraction.connectedPoint?.ownerBlock : null) as Nullable<RefractionBlock>;
+
+        const refractionTintAtDistance = refractionBlock?.tintAtDistance.isConnected ? refractionBlock.tintAtDistance.associatedVariableName : "1.";
+        const refractionIntensity = refractionBlock?.intensity.isConnected ? refractionBlock.intensity.associatedVariableName : "1.";
+        const refractionView = refractionBlock?.view.isConnected ? refractionBlock.view.associatedVariableName : "";
+
+        code += refractionBlock?.getCode(state) ?? "";
+
+        code += `subSurfaceOutParams subSurfaceOut;
+
+        #ifdef SUBSURFACE
+            vec2 vThicknessParam = vec2(${minThickness}, ${maxThickness} - ${minThickness});
+            vec4 vTintColor = vec4(${tintColor}, ${refractionTintAtDistance});
+            vec3 vSubSurfaceIntensity = vec3(${refractionIntensity}, ${translucencyIntensity}, 0.);
+
+            subSurfaceBlock(
+                vSubSurfaceIntensity,
+                vThicknessParam,
+                vTintColor,
+                normalW,
+                specularEnvironmentReflectance,
+            #ifdef SS_THICKNESSANDMASK_TEXTURE
+                ${thicknessTexture},
+            #endif
+            #ifdef REFLECTION
+                #ifdef SS_TRANSLUCENCY
+                    ${reflectionBlock?._reflectionMatrixName},
+                    #ifdef USESPHERICALFROMREFLECTIONMAP
+                        #if !defined(NORMAL) || !defined(USESPHERICALINVERTEX)
+                            reflectionOut.irradianceVector,
+                        #endif
+                    #endif
+                    #ifdef USEIRRADIANCEMAP
+                        irradianceSampler,
+                    #endif
+                #endif
+            #endif
+            #ifdef SS_REFRACTION
+                ${worldPosVarName}.xyz,
+                viewDirectionW,
+                ${refractionView},
+                surfaceAlbedo,
+                ${refractionBlock?._vRefractionInfosName ?? ""},
+                ${refractionBlock?._refractionMatrixName ?? ""},
+                ${refractionBlock?._vRefractionMicrosurfaceInfosName ?? ""},
+                vLightingIntensity,
+                #ifdef SS_LINKREFRACTIONTOTRANSPARENCY
+                    alpha,
+                #endif
+                #ifdef ${refractionBlock?._defineLODRefractionAlpha ?? "IGNORE"}
+                    NdotVUnclamped,
+                #endif
+                #ifdef ${refractionBlock?._defineLinearSpecularRefraction ?? "IGNORE"}
+                    roughness,
+                #else
+                    alphaG,
+                #endif
+                #ifdef ${refractionBlock?._define3DName ?? "IGNORE"}
+                    ${refractionBlock?._cubeSamplerName ?? ""},
+                #else
+                    ${refractionBlock?._2DSamplerName ?? ""},
+                #endif
+                #ifndef LODBASEDMICROSFURACE
+                    #ifdef ${refractionBlock?._define3DName ?? "IGNORE"}
+                        ${refractionBlock?._cubeSamplerName ?? ""},
+                        ${refractionBlock?._cubeSamplerName ?? ""},
+                    #else
+                        ${refractionBlock?._2DSamplerName ?? ""},
+                        ${refractionBlock?._2DSamplerName ?? ""},
+                    #endif
+                #endif
+                #ifdef ANISOTROPIC
+                    anisotropicOut,
+                #endif
+            #endif
+            #ifdef SS_TRANSLUCENCY
+                ${translucencyDiffusionDistance},
+            #endif
+                subSurfaceOut
+            );
+
+            #ifdef SS_REFRACTION
+                surfaceAlbedo = subSurfaceOut.surfaceAlbedo;
+                #ifdef SS_LINKREFRACTIONTOTRANSPARENCY
+                    alpha = subSurfaceOut.alpha;
+                #endif
+            #endif
+        #else
+            subSurfaceOut.specularEnvironmentReflectance = specularEnvironmentReflectance;
+        #endif\r\n`;
+
+        return code;
+    }
+
+    protected _buildBlock(state: NodeMaterialBuildState) {
+        if (state.target === NodeMaterialBlockTargets.Fragment) {
+            state.sharedData.blocksWithDefines.push(this);
+        }
+
+        return this;
+    }
+}
+
+_TypeStore.RegisteredTypes["BABYLON.SubSurfaceBlock"] = SubSurfaceBlock;

+ 7 - 2
src/Materials/Node/nodeMaterial.ts

@@ -27,6 +27,7 @@ import { _TypeStore } from '../../Misc/typeStore';
 import { SerializationHelper } from '../../Misc/decorators';
 import { SerializationHelper } from '../../Misc/decorators';
 import { TextureBlock } from './Blocks/Dual/textureBlock';
 import { TextureBlock } from './Blocks/Dual/textureBlock';
 import { ReflectionTextureBaseBlock } from './Blocks/Dual/reflectionTextureBaseBlock';
 import { ReflectionTextureBaseBlock } from './Blocks/Dual/reflectionTextureBaseBlock';
+import { RefractionBlock } from './Blocks/PBR/refractionBlock';
 import { EffectFallbacks } from '../effectFallbacks';
 import { EffectFallbacks } from '../effectFallbacks';
 import { WebRequest } from '../../Misc/webRequest';
 import { WebRequest } from '../../Misc/webRequest';
 import { Effect } from '../effect';
 import { Effect } from '../effect';
@@ -87,11 +88,15 @@ export class NodeMaterialDefines extends MaterialDefines implements IImageProces
         this.rebuild();
         this.rebuild();
     }
     }
 
 
-    public setValue(name: string, value: any) {
+    public setValue(name: string, value: any, markAsUnprocessedIfDirty = false) {
         if (this[name] === undefined) {
         if (this[name] === undefined) {
             this._keys.push(name);
             this._keys.push(name);
         }
         }
 
 
+        if (markAsUnprocessedIfDirty && this[name] !== value) {
+            this.markAsUnprocessed();
+        }
+
         this[name] = value;
         this[name] = value;
     }
     }
 }
 }
@@ -945,7 +950,7 @@ export class NodeMaterial extends PushMaterial {
      * Gets the list of texture blocks
      * Gets the list of texture blocks
      * @returns an array of texture blocks
      * @returns an array of texture blocks
      */
      */
-    public getTextureBlocks(): (TextureBlock | ReflectionTextureBaseBlock)[] {
+    public getTextureBlocks(): (TextureBlock | ReflectionTextureBaseBlock | RefractionBlock)[] {
         if (!this._sharedData) {
         if (!this._sharedData) {
             return [];
             return [];
         }
         }

+ 2 - 1
src/Materials/Node/nodeMaterialBuildStateSharedData.ts

@@ -3,6 +3,7 @@ import { NodeMaterialBlock } from './nodeMaterialBlock';
 import { InputBlock } from './Blocks/Input/inputBlock';
 import { InputBlock } from './Blocks/Input/inputBlock';
 import { TextureBlock } from './Blocks/Dual/textureBlock';
 import { TextureBlock } from './Blocks/Dual/textureBlock';
 import { ReflectionTextureBaseBlock } from './Blocks/Dual/reflectionTextureBaseBlock';
 import { ReflectionTextureBaseBlock } from './Blocks/Dual/reflectionTextureBaseBlock';
+import { RefractionBlock } from './Blocks/PBR/refractionBlock';
 import { Scene } from '../../scene';
 import { Scene } from '../../scene';
 
 
 /**
 /**
@@ -32,7 +33,7 @@ export class NodeMaterialBuildStateSharedData {
     /**
     /**
      * Input blocks
      * Input blocks
      */
      */
-    public textureBlocks = new Array<TextureBlock | ReflectionTextureBaseBlock>();
+    public textureBlocks = new Array<TextureBlock | ReflectionTextureBaseBlock | RefractionBlock>();
 
 
     /**
     /**
      * Bindable blocks (Blocks that need to set data to the effect)
      * Bindable blocks (Blocks that need to set data to the effect)

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

@@ -26,14 +26,13 @@ struct subSurfaceOutParams
 
 
 #ifdef SUBSURFACE
 #ifdef SUBSURFACE
     void subSurfaceBlock(
     void subSurfaceBlock(
+        const in vec3 vSubSurfaceIntensity,
         const in vec2 vThicknessParam,
         const in vec2 vThicknessParam,
         const in vec4 vTintColor,
         const in vec4 vTintColor,
         const in vec3 normalW,
         const in vec3 normalW,
         const in vec3 specularEnvironmentReflectance,
         const in vec3 specularEnvironmentReflectance,
     #ifdef SS_THICKNESSANDMASK_TEXTURE
     #ifdef SS_THICKNESSANDMASK_TEXTURE
-        const in vec2 vThicknessUV,
-        const in vec2 uvOffset,
-        const in sampler2D thicknessSampler,
+        const in vec4 thicknessMap,
     #endif
     #endif
     #ifdef REFLECTION
     #ifdef REFLECTION
         #ifdef SS_TRANSLUCENCY
         #ifdef SS_TRANSLUCENCY
@@ -116,7 +115,6 @@ struct subSurfaceOutParams
     #endif
     #endif
 
 
     #ifdef SS_THICKNESSANDMASK_TEXTURE
     #ifdef SS_THICKNESSANDMASK_TEXTURE
-        vec4 thicknessMap = texture2D(thicknessSampler, vThicknessUV + uvOffset);
         float thickness = thicknessMap.r * vThicknessParam.y + vThicknessParam.x;
         float thickness = thicknessMap.r * vThicknessParam.y + vThicknessParam.x;
 
 
         #if DEBUGMODE > 0
         #if DEBUGMODE > 0

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

@@ -385,15 +385,18 @@ void main(void) {
     subSurfaceOutParams subSurfaceOut;
     subSurfaceOutParams subSurfaceOut;
 
 
     #ifdef SUBSURFACE
     #ifdef SUBSURFACE
+        #ifdef SS_THICKNESSANDMASK_TEXTURE
+            vec4 thicknessMap = texture2D(thicknessSampler, vThicknessUV + uvOffset);
+        #endif
+
         subSurfaceBlock(
         subSurfaceBlock(
+            vSubSurfaceIntensity,
             vThicknessParam,
             vThicknessParam,
             vTintColor,
             vTintColor,
             normalW,
             normalW,
             specularEnvironmentReflectance,
             specularEnvironmentReflectance,
         #ifdef SS_THICKNESSANDMASK_TEXTURE
         #ifdef SS_THICKNESSANDMASK_TEXTURE
-            vThicknessUV,
-            uvOffset,
-            thicknessSampler,
+            thicknessMap,
         #endif
         #endif
         #ifdef REFLECTION
         #ifdef REFLECTION
             #ifdef SS_TRANSLUCENCY
             #ifdef SS_TRANSLUCENCY