Explorar el Código

Add the refraction part to the sub surface module

Popov72 hace 5 años
padre
commit
fc7ddab9e7

+ 3 - 0
nodeEditor/src/blockTools.ts

@@ -69,6 +69,7 @@ import { ReflectivityBlock } from 'babylonjs/Materials/Node/Blocks/PBR/reflectiv
 import { AnisotropyBlock } from 'babylonjs/Materials/Node/Blocks/PBR/anisotropyBlock';
 import { ReflectionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/reflectionBlock';
 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 {
@@ -454,6 +455,8 @@ export class BlockTools {
                 return new ReflectionBlock("Reflection");
             case "ClearCoatBlock":
                 return new ClearCoatBlock("ClearCoat");
+            case "RefractionBlock":
+                return new RefractionBlock("Refraction");
             case "SubSurfaceBlock":
                 return new SubSurfaceBlock("SubSurface");
         }

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

@@ -124,6 +124,7 @@ export class NodeListComponent extends React.Component<INodeListComponentProps,
         "AnisotropyBlock": "PBR Anisotropy block",
         "ReflectionBlock": "PBR Reflection block",
         "ClearCoatBlock": "PBR ClearCoat block",
+        "RefractionBlock": "PBR Refraction block",
         "SubSurfaceBlock": "PBR SubSurface block",
     };
 
@@ -153,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"],
             Noises: ["RandomNumberBlock", "SimplexPerlin3DBlock", "WorleyNoise3DBlock"],
             Output_Nodes: ["VertexOutputBlock", "FragmentOutputBlock", "DiscardBlock"],
-            PBR: ["PBRMetallicRoughnessBlock", "AmbientOcclusionBlock", "AnisotropyBlock", "ClearCoatBlock", "ReflectionBlock", "ReflectivityBlock", "SheenBlock", "SubSurfaceBlock"],
+            PBR: ["PBRMetallicRoughnessBlock", "AmbientOcclusionBlock", "AnisotropyBlock", "ClearCoatBlock", "ReflectionBlock", "ReflectivityBlock", "RefractionBlock", "SheenBlock", "SubSurfaceBlock"],
             Range: ["ClampBlock", "RemapBlock", "NormalizeBlock"],
             Round: ["RoundBlock", "CeilingBlock", "FloorBlock"],
             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 { TextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/textureBlock';
 import { ReflectionTextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/reflectionTextureBlock';
+import { RefractionBlock } from 'babylonjs/Materials/Node/Blocks/PBR/refractioneBlock';
 import { TextureLineComponent } from '../../sharedComponents/textureLineComponent';
 
 export class TextureDisplayManager implements IDisplayManager {
@@ -25,7 +26,7 @@ export class TextureDisplayManager implements IDisplayManager {
     }
 
     public updatePreviewContent(block: NodeMaterialBlock, contentArea: HTMLDivElement): void {       
-        const textureBlock = block as TextureBlock | ReflectionTextureBlock;
+        const textureBlock = block as TextureBlock | ReflectionTextureBlock | RefractionBlock;
 
         if (!this._previewCanvas) {
             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["ReflectionTextureBlock"] = TextureDisplayManager;
 DisplayLedger.RegisteredControls["ReflectionBlock"] = TextureDisplayManager;
+DisplayLedger.RegisteredControls["RefractionBlock"] = TextureDisplayManager;
 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 { ReflectionTextureBlock } from 'babylonjs/Materials/Node/Blocks/Dual/reflectionTextureBlock';
 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 { GeneralPropertyTabComponent, GenericPropertyTabComponent } from './genericNodePropertyComponent';
 
-type ReflectionTexture = ReflectionTextureBlock | ReflectionBlock;
+type ReflectionTexture = ReflectionTextureBlock | ReflectionBlock | RefractionBlock;
 
 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 (!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.coordinatesMode = Texture.EQUIRECTANGULAR_MODE;
             } else {
@@ -122,7 +123,7 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
         this._prepareTexture();
 
         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;
             if (url.toLowerCase().indexOf(".dds") > 0) {
                 extension = ".dds";
@@ -146,7 +147,7 @@ export class TexturePropertyTabComponent extends React.Component<IPropertyCompon
 
         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}[] = [
             {

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

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

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

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

+ 6 - 0
src/Materials/Node/Blocks/PBR/pbrMetallicRoughnessBlock.ts

@@ -27,6 +27,7 @@ import { AnisotropyBlock } from './anisotropyBlock';
 import { ReflectionBlock } from './reflectionBlock';
 import { ClearCoatBlock } from './clearCoatBlock';
 import { SubSurfaceBlock } from './subSurfaceBlock';
+import { RefractionBlock } from './refractionBlock';
 
 const mapOutputToVariable: { [name: string] : [string, string] } = {
     "ambient":      ["finalAmbient", ""],
@@ -994,6 +995,7 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
 
         // ___________________________________ SubSurface ______________________________________
         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);
 
@@ -1002,6 +1004,10 @@ export class PBRMetallicRoughnessBlock extends NodeMaterialBlock {
                 { 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" },
             ]
         });
 

+ 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;

+ 29 - 20
src/Materials/Node/Blocks/PBR/subSurfaceBlock.ts

@@ -33,8 +33,8 @@ export class SubSurfaceBlock extends NodeMaterialBlock {
         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.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"));
@@ -137,7 +137,7 @@ export class SubSurfaceBlock extends NodeMaterialBlock {
 
         const translucencyEnabled = this.translucencyDiffusionDistance.isConnected || this.translucencyIntensity.isConnected;
 
-        defines.setValue("SUBSURFACE", true);
+        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);
@@ -161,15 +161,15 @@ export class SubSurfaceBlock extends NodeMaterialBlock {
         const translucencyIntensity = ssBlock?.translucencyIntensity.isConnected ? ssBlock?.translucencyIntensity.associatedVariableName : "1.";
         const translucencyDiffusionDistance = ssBlock?.translucencyDiffusionDistance.isConnected ? ssBlock?.translucencyDiffusionDistance.associatedVariableName : "vec3(1.)";
 
-        const refractionTintAtDistance = "1.";
-        const refractionIntensity = "1.";
+        const refractionBlock: Nullable<RefractionBlock> = (ssBlock?.refraction.isConnected ? ssBlock?.refraction.connectedPoint?.ownerBlock : null) as Nullable<RefractionBlock>;
 
-        if (ssBlock) {
-            state._emitUniformFromString("vClearCoatRefractionParams", "vec4");
-            state._emitUniformFromString("vClearCoatTangentSpaceParams", "vec2");
-        }
+        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;
+        code += `subSurfaceOutParams subSurfaceOut;
 
         #ifdef SUBSURFACE
             vec2 vThicknessParam = vec2(${minThickness}, ${maxThickness} - ${minThickness});
@@ -199,29 +199,38 @@ export class SubSurfaceBlock extends NodeMaterialBlock {
                 #endif
             #endif
             #ifdef SS_REFRACTION
-                ${worldPosVarName},
+                ${worldPosVarName}.xyz,
                 viewDirectionW,
-                view,
+                ${refractionView},
                 surfaceAlbedo,
-                vRefractionInfos,
-                refractionMatrix,
-                vRefractionMicrosurfaceInfos,
+                ${refractionBlock?._vRefractionInfosName ?? ""},
+                ${refractionBlock?._refractionMatrixName ?? ""},
+                ${refractionBlock?._vRefractionMicrosurfaceInfosName ?? ""},
                 vLightingIntensity,
                 #ifdef SS_LINKREFRACTIONTOTRANSPARENCY
                     alpha,
                 #endif
-                #ifdef SS_LODINREFRACTIONALPHA
+                #ifdef ${refractionBlock?._defineLODRefractionAlpha ?? "IGNORE"}
                     NdotVUnclamped,
                 #endif
-                #ifdef SS_LINEARSPECULARREFRACTION
+                #ifdef ${refractionBlock?._defineLinearSpecularRefraction ?? "IGNORE"}
                     roughness,
                 #else
                     alphaG,
                 #endif
-                refractionSampler,
+                #ifdef ${refractionBlock?._define3DName ?? "IGNORE"}
+                    ${refractionBlock?._cubeSamplerName ?? ""},
+                #else
+                    ${refractionBlock?._2DSamplerName ?? ""},
+                #endif
                 #ifndef LODBASEDMICROSFURACE
-                    refractionSamplerLow,
-                    refractionSamplerHigh,
+                    #ifdef ${refractionBlock?._define3DName ?? "IGNORE"}
+                        ${refractionBlock?._cubeSamplerName ?? ""},
+                        ${refractionBlock?._cubeSamplerName ?? ""},
+                    #else
+                        ${refractionBlock?._2DSamplerName ?? ""},
+                        ${refractionBlock?._2DSamplerName ?? ""},
+                    #endif
                 #endif
                 #ifdef ANISOTROPIC
                     anisotropicOut,

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

@@ -27,6 +27,7 @@ import { _TypeStore } from '../../Misc/typeStore';
 import { SerializationHelper } from '../../Misc/decorators';
 import { TextureBlock } from './Blocks/Dual/textureBlock';
 import { ReflectionTextureBaseBlock } from './Blocks/Dual/reflectionTextureBaseBlock';
+import { RefractionBlock } from './Blocks/PBR/refractionBlock';
 import { EffectFallbacks } from '../effectFallbacks';
 import { WebRequest } from '../../Misc/webRequest';
 import { Effect } from '../effect';
@@ -949,7 +950,7 @@ export class NodeMaterial extends PushMaterial {
      * Gets the list of texture blocks
      * @returns an array of texture blocks
      */
-    public getTextureBlocks(): (TextureBlock | ReflectionTextureBaseBlock)[] {
+    public getTextureBlocks(): (TextureBlock | ReflectionTextureBaseBlock | RefractionBlock)[] {
         if (!this._sharedData) {
             return [];
         }

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

@@ -3,6 +3,7 @@ import { NodeMaterialBlock } from './nodeMaterialBlock';
 import { InputBlock } from './Blocks/Input/inputBlock';
 import { TextureBlock } from './Blocks/Dual/textureBlock';
 import { ReflectionTextureBaseBlock } from './Blocks/Dual/reflectionTextureBaseBlock';
+import { RefractionBlock } from './Blocks/PBR/refractionBlock';
 import { Scene } from '../../scene';
 
 /**
@@ -32,7 +33,7 @@ export class NodeMaterialBuildStateSharedData {
     /**
      * 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)