Bladeren bron

Adding SubSurface

sebavan 6 jaren geleden
bovenliggende
commit
3e9c74a690

+ 37 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx

@@ -49,6 +49,7 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                 <TextureLinkLineComponent label="Opacity" texture={material.opacityTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
                 <TextureLinkLineComponent label="Ambient" texture={material.ambientTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
                 <TextureLinkLineComponent label="Lightmap" texture={material.lightmapTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
+                <TextureLinkLineComponent label="Thickness" texture={material.thicknessTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={onDebugSelectionChangeObservable} />
             </LineContainerComponent>
         );
     }
@@ -84,6 +85,7 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
             { label: "ClearCoat Tint Map", value: 28 },
             { label: "Sheen Map", value: 29 },
             { label: "Anisotropic Map", value: 30 },
+            { label: "Thickness Map", value: 31 },
             // Env
             { label: "Env Refraction", value: 40 },
             { label: "Env Reflection", value: 41 },
@@ -103,6 +105,8 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
             { label: "ClearCoat Color", value: 65 },
             { label: "ClearCoat Roughness", value: 66 },
             { label: "ClearCoat NdotV", value: 67 },
+            { label: "Transmittance", value: 68 },
+            { label: "Refraction Transmittance", value: 69 },
             // Misc
             { label: "SEO", value: 70 },
             { label: "EHO", value: 71 },
@@ -193,6 +197,39 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                         </div>
                     }
                 </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="SUBSURFACE">
+                    <TextureLinkLineComponent label="Thickness" texture={material.subSurface.thicknessTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={this._onDebugSelectionChangeObservable} />
+                    <SliderLineComponent label="Min Thickness" target={material.subSurface} propertyName="minimumThickness" minimum={0} maximum={10} step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Max Thickness" target={material.subSurface} propertyName="maximumThickness" minimum={0} maximum={10} step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Mask From Thickness" target={material.subSurface} propertyName="useMaskFromThicknessTexture"
+                        onValueChanged={() => this.forceUpdate()}
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <Color3LineComponent label="Tint Color" target={material.subSurface} propertyName="tintColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+
+                    <CheckBoxLineComponent label="Refraction Enabled" target={material.subSurface} propertyName="isRefractionEnabled"
+                        onValueChanged={() => this.forceUpdate()}
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    {
+                        material.subSurface.isRefractionEnabled &&
+                        <div className="fragment">
+                            <SliderLineComponent label="Intensity" target={material.subSurface} propertyName="refractionIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <SliderLineComponent label="Index of Refraction" target={material.subSurface} propertyName="indexOfRefraction" minimum={1} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <SliderLineComponent label="Tint at Distance" target={material.subSurface} propertyName="tintColorAtDistance" minimum={0} maximum={10} step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <CheckBoxLineComponent label="Link refraction with transparency" target={material.subSurface} propertyName="linkRefractionWithTransparency" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        </div>
+                    }
+
+                    <CheckBoxLineComponent label="Transluency Enabled" target={material.subSurface} propertyName="isTranslucencyEnabled"
+                        onValueChanged={() => this.forceUpdate()}
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    {
+                        material.subSurface.isTranslucencyEnabled &&
+                        <div className="fragment">
+                            <SliderLineComponent label="Intensity" target={material.subSurface} propertyName="translucencyIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <Color3LineComponent label="Diffusion Distance" target={material.subSurface} propertyName="diffusionDistance" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        </div>
+                    }
+                </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="LEVELS" closed={true}>
                     <SliderLineComponent label="Environment" target={material} propertyName="environmentIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Specular" target={material} propertyName="specularIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
@@ -215,7 +252,6 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                     <CheckBoxLineComponent label="Alpha from albedo" target={material} propertyName="useAlphaFromAlbedoTexture" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Ambient in grayscale" target={material} propertyName="useAmbientInGrayScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Radiance over alpha" target={material} propertyName="useRadianceOverAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <CheckBoxLineComponent label="Link refraction with transparency" target={material} propertyName="linkRefractionWithTransparency" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Micro-surface from ref. map alpha" target={material} propertyName="useMicroSurfaceFromReflectivityMapAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Specular over alpha" target={material} propertyName="useSpecularOverAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Specular anti-aliasing" target={material} propertyName="enableSpecularAntiAliasing" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 64 - 144
src/Materials/PBR/pbrBaseMaterial.ts

@@ -18,6 +18,7 @@ import { IMaterialClearCoatDefines, PBRClearCoatConfiguration } from "./pbrClear
 import { IMaterialAnisotropicDefines, PBRAnisotropicConfiguration } from "./pbrAnisotropicConfiguration";
 import { IMaterialBRDFDefines, PBRBRDFConfiguration } from "./pbrBRDFConfiguration";
 import { IMaterialSheenDefines, PBRSheenConfiguration } from "./pbrSheenConfiguration";
+import { IMaterialSubSurfaceDefines, PBRSubSurfaceConfiguration } from "./pbrSubSurfaceConfiguration";
 
 import { ImageProcessingConfiguration, IImageProcessingConfigurationDefines } from "../../Materials/imageProcessingConfiguration";
 import { Effect, EffectFallbacks, EffectCreationOptions } from "../../Materials/effect";
@@ -46,7 +47,8 @@ export class PBRMaterialDefines extends MaterialDefines
     IMaterialClearCoatDefines,
     IMaterialAnisotropicDefines,
     IMaterialBRDFDefines,
-    IMaterialSheenDefines {
+    IMaterialSheenDefines,
+    IMaterialSubSurfaceDefines {
     public PBR = true;
 
     public MAINUV1 = false;
@@ -135,14 +137,6 @@ export class PBRMaterialDefines extends MaterialDefines
     public RADIANCEOCCLUSION = false;
     public HORIZONOCCLUSION = false;
 
-    public REFRACTION = false;
-    public REFRACTIONMAP_3D = false;
-    public REFRACTIONMAP_OPPOSITEZ = false;
-    public LODINREFRACTIONALPHA = false;
-    public GAMMAREFRACTION = false;
-    public RGBDREFRACTION = false;
-    public LINKREFRACTIONTOTRANSPARENCY = false;
-
     public INSTANCES = false;
 
     public NUM_BONE_INFLUENCERS = 0;
@@ -210,6 +204,24 @@ export class PBRMaterialDefines extends MaterialDefines
     public SHEEN_TEXTUREDIRECTUV = 0;
     public SHEEN_LINKWITHALBEDO = false;
 
+    public SUBSURFACE = false;
+
+    public SS_REFRACTION = false;
+    public SS_TRANSLUCENCY = false;
+    public SS_SCATERRING = false;
+
+    public SS_THICKNESSANDMASK_TEXTURE = false;
+    public SS_THICKNESSANDMASK_TEXTUREDIRECTUV = 0;
+
+    public SS_REFRACTIONMAP_3D = false;
+    public SS_REFRACTIONMAP_OPPOSITEZ = false;
+    public SS_LODINREFRACTIONALPHA = false;
+    public SS_GAMMAREFRACTION = false;
+    public SS_RGBDREFRACTION = false;
+    public SS_LINKREFRACTIONTOTRANSPARENCY = false;
+
+    public SS_MASK_FROM_THICKNESS_TEXTURE = false;
+
     public UNLIT = false;
 
     public DEBUGMODE = 0;
@@ -351,11 +363,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     protected _reflectionTexture: BaseTexture;
 
     /**
-     * Stores the refraction values in a texture.
-     */
-    protected _refractionTexture: BaseTexture;
-
-    /**
      * Stores the emissive values in a texture.
      */
     protected _emissiveTexture: BaseTexture;
@@ -429,22 +436,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     protected _microSurface = 0.9;
 
     /**
-     * source material index of refraction (IOR)' / 'destination material IOR.
-     */
-    protected _indexOfRefraction = 0.66;
-
-    /**
-     * Controls if refraction needs to be inverted on Y. This could be useful for procedural texture.
-     */
-    protected _invertRefractionY = false;
-
-    /**
-     * 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.
-     */
-    protected _linkRefractionWithTransparency = false;
-
-    /**
      * Specifies that the material will use the light map as a show map.
      */
     protected _useLightmapAsShadowmap = false;
@@ -726,6 +717,11 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     public readonly sheen = new PBRSheenConfiguration(this._markAllSubMeshesAsTexturesDirty.bind(this));
 
     /**
+     * Defines the SubSurface parameters for the material.
+     */
+    public readonly subSurface = new PBRSubSurfaceConfiguration(this._markAllSubMeshesAsTexturesDirty.bind(this));
+
+    /**
      * Custom callback helping to override the default shader used in the material.
      */
     public customShaderNameResolve: (shaderName: string, uniforms: string[], uniformBuffers: string[], samplers: string[], defines: PBRMaterialDefines) => string;
@@ -749,9 +745,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                 this._renderTargets.push(<RenderTargetTexture>this._reflectionTexture);
             }
 
-            if (MaterialFlags.RefractionTextureEnabled && this._refractionTexture && this._refractionTexture.isRenderTarget) {
-                this._renderTargets.push(<RenderTargetTexture>this._refractionTexture);
-            }
+            this.subSurface.fillRenderTargetTextures(this._renderTargets);
 
             return this._renderTargets;
         };
@@ -767,11 +761,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             return true;
         }
 
-        if (MaterialFlags.RefractionTextureEnabled && this._refractionTexture && this._refractionTexture.isRenderTarget) {
-            return true;
-        }
-
-        return false;
+        return this.subSurface.hasRenderTargetTextures();
     }
 
     /**
@@ -831,7 +821,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
      * Returns true if alpha blending should be disabled.
      */
     private get _disableAlphaBlending(): boolean {
-        return (this._linkRefractionWithTransparency ||
+        return (this.subSurface.disableAlphaBlending ||
             this._transparencyMode === PBRBaseMaterial.PBRMATERIAL_OPAQUE ||
             this._transparencyMode === PBRBaseMaterial.PBRMATERIAL_ALPHATEST);
     }
@@ -867,7 +857,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             return true;
         }
 
-        if (this._linkRefractionWithTransparency) {
+        if (this.subSurface.disableAlphaBlending) {
             return false;
         }
 
@@ -981,13 +971,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                     }
                 }
 
-                var refractionTexture = this._getRefractionTexture();
-                if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
-                    if (!refractionTexture.isReadyOrNotBlocking()) {
-                        return false;
-                    }
-                }
-
                 if (this._environmentBRDFTexture && MaterialFlags.ReflectionTextureEnabled) {
                     // This is blocking.
                     if (!this._environmentBRDFTexture.isReady()) {
@@ -997,15 +980,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             }
         }
 
-        if (!this.clearCoat.isReadyForSubMesh(defines, scene, engine, this._disableBumpMap)) {
-            return false;
-        }
-
-        if (!this.sheen.isReadyForSubMesh(defines, scene)) {
-            return false;
-        }
-
-        if (!this.anisotropy.isReadyForSubMesh(defines, scene)) {
+        if (!this.subSurface.isReadyForSubMesh(defines, scene) ||
+            !this.clearCoat.isReadyForSubMesh(defines, scene, engine, this._disableBumpMap) ||
+            !this.sheen.isReadyForSubMesh(defines, scene) ||
+            !this.anisotropy.isReadyForSubMesh(defines, scene)) {
             return false;
         }
 
@@ -1095,7 +1073,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         }
 
         fallbackRank = PBRAnisotropicConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
-        fallbackRank = PBRClearCoatConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
+        fallbackRank = PBRAnisotropicConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
+        fallbackRank = PBRSubSurfaceConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
         fallbackRank = PBRSheenConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
 
         if (defines.ENVIRONMENTBRDF) {
@@ -1184,27 +1163,29 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vAlbedoColor", "vReflectivityColor", "vEmissiveColor", "visibility", "vReflectionColor",
             "vFogInfos", "vFogColor", "pointSize",
             "vAlbedoInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vReflectionPosition", "vReflectionSize", "vEmissiveInfos", "vReflectivityInfos",
-            "vMicroSurfaceSamplerInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
+            "vMicroSurfaceSamplerInfos", "vBumpInfos", "vLightmapInfos",
             "mBones",
-            "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "normalMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
+            "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "normalMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix",
             "vLightingIntensity",
             "logarithmicDepthConstant",
             "vSphericalX", "vSphericalY", "vSphericalZ",
             "vSphericalXX", "vSphericalYY", "vSphericalZZ",
             "vSphericalXY", "vSphericalYZ", "vSphericalZX",
-            "vReflectionMicrosurfaceInfos", "vRefractionMicrosurfaceInfos",
+            "vReflectionMicrosurfaceInfos",
             "vTangentSpaceParams", "boneTextureWidth",
             "vDebugMode"
         ];
 
         var samplers = ["albedoSampler", "reflectivitySampler", "ambientSampler", "emissiveSampler",
             "bumpSampler", "lightmapSampler", "opacitySampler",
-            "refractionSampler", "refractionSamplerLow", "refractionSamplerHigh",
             "reflectionSampler", "reflectionSamplerLow", "reflectionSamplerHigh",
             "microSurfaceSampler", "environmentBrdfSampler", "boneSampler"];
 
         var uniformBuffers = ["Material", "Scene"];
 
+        PBRSubSurfaceConfiguration.AddUniforms(uniforms);
+        PBRSubSurfaceConfiguration.AddSamplers(samplers);
+
         PBRClearCoatConfiguration.AddUniforms(uniforms);
         PBRClearCoatConfiguration.AddSamplers(samplers);
 
@@ -1436,22 +1417,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                     defines.BUMP = false;
                 }
 
-                var refractionTexture = this._getRefractionTexture();
-                if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
-                    defines.REFRACTION = true;
-                    defines.REFRACTIONMAP_3D = refractionTexture.isCube;
-                    defines.GAMMAREFRACTION = refractionTexture.gammaSpace;
-                    defines.RGBDREFRACTION = refractionTexture.isRGBD;
-                    defines.REFRACTIONMAP_OPPOSITEZ = refractionTexture.invertZ;
-                    defines.LODINREFRACTIONALPHA = refractionTexture.lodLevelInAlpha;
-
-                    if (this._linkRefractionWithTransparency) {
-                        defines.LINKREFRACTIONTOTRANSPARENCY = true;
-                    }
-                } else {
-                    defines.REFRACTION = false;
-                }
-
                 if (this._environmentBRDFTexture && MaterialFlags.ReflectionTextureEnabled) {
                     defines.ENVIRONMENTBRDF = true;
                     // Not actual true RGBD, only the B chanel is encoded as RGBD for sheen.
@@ -1518,6 +1483,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         }
 
         // External config
+        this.subSurface.prepareDefines(defines, scene);
         this.clearCoat.prepareDefines(defines, scene);
         this.anisotropy.prepareDefines(defines, mesh, scene);
         this.brdf.prepareDefines(defines);
@@ -1567,7 +1533,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         this._uniformBuffer.addUniform("vLightmapInfos", 2);
         this._uniformBuffer.addUniform("vReflectivityInfos", 3);
         this._uniformBuffer.addUniform("vMicroSurfaceSamplerInfos", 2);
-        this._uniformBuffer.addUniform("vRefractionInfos", 4);
         this._uniformBuffer.addUniform("vReflectionInfos", 2);
         this._uniformBuffer.addUniform("vReflectionPosition", 3);
         this._uniformBuffer.addUniform("vReflectionSize", 3);
@@ -1581,14 +1546,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         this._uniformBuffer.addUniform("microSurfaceSamplerMatrix", 16);
         this._uniformBuffer.addUniform("bumpMatrix", 16);
         this._uniformBuffer.addUniform("vTangentSpaceParams", 2);
-        this._uniformBuffer.addUniform("refractionMatrix", 16);
         this._uniformBuffer.addUniform("reflectionMatrix", 16);
 
         this._uniformBuffer.addUniform("vReflectionColor", 3);
         this._uniformBuffer.addUniform("vAlbedoColor", 4);
         this._uniformBuffer.addUniform("vLightingIntensity", 4);
 
-        this._uniformBuffer.addUniform("vRefractionMicrosurfaceInfos", 3);
         this._uniformBuffer.addUniform("vReflectionMicrosurfaceInfos", 3);
         this._uniformBuffer.addUniform("pointSize", 1);
         this._uniformBuffer.addUniform("vReflectivityColor", 4);
@@ -1598,20 +1561,29 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         PBRClearCoatConfiguration.PrepareUniformBuffer(this._uniformBuffer);
         PBRAnisotropicConfiguration.PrepareUniformBuffer(this._uniformBuffer);
         PBRSheenConfiguration.PrepareUniformBuffer(this._uniformBuffer);
+        PBRSubSurfaceConfiguration.PrepareUniformBuffer(this._uniformBuffer);
 
         this._uniformBuffer.create();
     }
 
     /**
-     * Unbinds the textures.
+     * Unbinds the material from the mesh
      */
     public unbind(): void {
-        if (this._reflectionTexture && this._reflectionTexture.isRenderTarget) {
-            this._uniformBuffer.setTexture("reflectionSampler", null);
-        }
+        if (this._activeEffect) {
+            let needFlag = false;
+            if (this._reflectionTexture && this._reflectionTexture.isRenderTarget) {
+                this._activeEffect.setTexture("reflection2DSampler", null);
+                needFlag = true;
+            }
+
+            if (this.subSurface.unbind(this._activeEffect)) {
+                needFlag = true;
+            }
 
-        if (this._refractionTexture && this._refractionTexture.isRenderTarget) {
-            this._uniformBuffer.setTexture("refractionSampler", null);
+            if (needFlag) {
+                this._markAllSubMeshesAsTexturesDirty();
+            }
         }
 
         super.unbind();
@@ -1660,7 +1632,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
             this.bindViewProjection(effect);
             reflectionTexture = this._getReflectionTexture();
-            var refractionTexture = this._getRefractionTexture();
 
             if (!this._uniformBuffer.useUbo || !this.isFrozen || !this._uniformBuffer.isSync) {
 
@@ -1751,22 +1722,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                             this._uniformBuffer.updateFloat2("vTangentSpaceParams", this._invertNormalMapX ? -1.0 : 1.0, this._invertNormalMapY ? -1.0 : 1.0);
                         }
                     }
-
-                    if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
-                        this._uniformBuffer.updateMatrix("refractionMatrix", refractionTexture.getReflectionTextureMatrix());
-
-                        var depth = 1.0;
-                        if (!refractionTexture.isCube) {
-                            if ((<any>refractionTexture).depth) {
-                                depth = (<any>refractionTexture).depth;
-                            }
-                        }
-                        this._uniformBuffer.updateFloat4("vRefractionInfos", refractionTexture.level, this._indexOfRefraction, depth, this._invertRefractionY ? -1 : 1);
-                        this._uniformBuffer.updateFloat3("vRefractionMicrosurfaceInfos",
-                            refractionTexture.getSize().width,
-                            refractionTexture.lodGenerationScale,
-                            refractionTexture.lodGenerationOffset);
-                    }
                 }
 
                 // Point size
@@ -1829,17 +1784,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                     this._uniformBuffer.setTexture("environmentBrdfSampler", this._environmentBRDFTexture);
                 }
 
-                if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
-                    if (defines.LODBASEDMICROSFURACE) {
-                        this._uniformBuffer.setTexture("refractionSampler", refractionTexture);
-                    }
-                    else {
-                        this._uniformBuffer.setTexture("refractionSampler", refractionTexture._lodTextureMid || refractionTexture);
-                        this._uniformBuffer.setTexture("refractionSamplerLow", refractionTexture._lodTextureLow || refractionTexture);
-                        this._uniformBuffer.setTexture("refractionSamplerHigh", refractionTexture._lodTextureHigh || refractionTexture);
-                    }
-                }
-
                 if (this._emissiveTexture && MaterialFlags.EmissiveTextureEnabled) {
                     this._uniformBuffer.setTexture("emissiveSampler", this._emissiveTexture);
                 }
@@ -1866,6 +1810,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                 }
             }
 
+            this.subSurface.bindForSubMesh(this._uniformBuffer, scene, engine, this.isFrozen, defines.LODBASEDMICROSFURACE);
             this.clearCoat.bindForSubMesh(this._uniformBuffer, scene, engine, this._disableBumpMap, this.isFrozen, this._invertNormalMapX, this._invertNormalMapY);
             this.anisotropy.bindForSubMesh(this._uniformBuffer, scene, this.isFrozen);
             this.sheen.bindForSubMesh(this._uniformBuffer, scene, this.isFrozen);
@@ -1961,10 +1906,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             results.push(this._lightmapTexture);
         }
 
-        if (this._refractionTexture && this._refractionTexture.animations && this._refractionTexture.animations.length > 0) {
-            results.push(this._refractionTexture);
-        }
-
+        this.subSurface.getAnimatables(results);
         this.clearCoat.getAnimatables(results);
         this.sheen.getAnimatables(results);
         this.anisotropy.getAnimatables(results);
@@ -1985,23 +1927,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     }
 
     /**
-     * Returns the texture used for refraction or null if none is used.
-     * @returns - Refection texture if present.  If no refraction texture and refraction
-     * is linked with transparency, returns environment texture.  Otherwise, returns null.
-     */
-    private _getRefractionTexture(): Nullable<BaseTexture> {
-        if (this._refractionTexture) {
-            return this._refractionTexture;
-        }
-
-        if (this._linkRefractionWithTransparency) {
-            return this.getScene().environmentTexture;
-        }
-
-        return null;
-    }
-
-    /**
      * Returns an array of the actively used textures.
      * @returns - Array of BaseTextures
      */
@@ -2048,10 +1973,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             activeTextures.push(this._lightmapTexture);
         }
 
-        if (this._refractionTexture) {
-            activeTextures.push(this._refractionTexture);
-        }
-
+        this.subSurface.getActiveTextures(activeTextures);
         this.clearCoat.getActiveTextures(activeTextures);
         this.sheen.getActiveTextures(activeTextures);
         this.anisotropy.getActiveTextures(activeTextures);
@@ -2105,7 +2027,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             return true;
         }
 
-        return this.clearCoat.hasTexture(texture) ||
+        return this.subSurface.hasTexture(texture) ||
+            this.clearCoat.hasTexture(texture) ||
             this.sheen.hasTexture(texture) ||
             this.anisotropy.hasTexture(texture);
     }
@@ -2156,12 +2079,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             if (this._lightmapTexture) {
                 this._lightmapTexture.dispose();
             }
-
-            if (this._refractionTexture) {
-                this._refractionTexture.dispose();
-            }
         }
 
+        this.subSurface.dispose(forceDisposeTextures);
         this.clearCoat.dispose(forceDisposeTextures);
         this.sheen.dispose(forceDisposeTextures);
         this.anisotropy.dispose(forceDisposeTextures);

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

@@ -96,14 +96,14 @@ export class PBRClearCoatConfiguration {
     public isTintEnabled = false;
 
     /**
-     * Defines if the clear coat tint is enabled in the material.
+     * Defines the clear coat tint of the material.
      * This is only use if tint is enabled
      */
     @serializeAsColor3()
     public tintColor = Color3.White();
 
     /**
-     * Defines if the distance at which the tint color should be found in the
+     * Defines the distance at which the tint color should be found in the
      * clear coat media.
      * This is only use if tint is enabled
      */
@@ -144,7 +144,7 @@ export class PBRClearCoatConfiguration {
     }
 
     /**
-     * Specifies that the submesh is ready to be used.
+     * Gets wehter the submesh is ready to be used or not.
      * @param defines the list of "defines" to update.
      * @param scene defines the scene the material belongs to.
      * @param engine defines the engine the material belongs to.

+ 24 - 12
src/Materials/PBR/pbrMaterial.ts

@@ -191,9 +191,12 @@ export class PBRMaterial extends PBRBaseMaterial {
     /**
      * Stores the refracted light information in a texture.
      */
-    @serializeAsTexture()
-    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public refractionTexture: BaseTexture;
+    public get refractionTexture(): Nullable<BaseTexture> {
+        return this.subSurface.refractionTexture;
+    }
+    public set refractionTexture(value: Nullable<BaseTexture>) {
+        this.subSurface.refractionTexture = value;
+    }
 
     /**
      * The color of a material in ambient lighting.
@@ -240,24 +243,33 @@ export class PBRMaterial extends PBRBaseMaterial {
     /**
      * source material index of refraction (IOR)' / 'destination material IOR.
      */
-    @serialize()
-    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public indexOfRefraction = 0.66;
+    public get indexOfRefraction(): number {
+        return  1 / this.subSurface.indexOfRefraction;
+    }
+    public set indexOfRefraction(value: number) {
+        this.subSurface.indexOfRefraction =  1 / value;
+    }
 
     /**
      * Controls if refraction needs to be inverted on Y. This could be useful for procedural texture.
      */
-    @serialize()
-    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public invertRefractionY = false;
+    public get invertRefractionY(): boolean {
+        return this.subSurface.invertRefractionY;
+    }
+    public set invertRefractionY(value: boolean) {
+        this.subSurface.invertRefractionY = value;
+    }
 
     /**
      * 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.
      */
-    @serialize()
-    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public linkRefractionWithTransparency = false;
+    public get linkRefractionWithTransparency(): boolean {
+        return this.subSurface.linkRefractionWithTransparency;
+    }
+    public set linkRefractionWithTransparency(value: boolean) {
+        this.subSurface.linkRefractionWithTransparency = value;
+    }
 
     /**
      * If true, the light map contains occlusion information instead of lighting info.

+ 550 - 0
src/Materials/PBR/pbrSubSurfaceConfiguration.ts

@@ -0,0 +1,550 @@
+import { Nullable } from "../../types";
+import { IAnimatable } from "../../Misc/tools";
+import { SerializationHelper, serialize, serializeAsTexture, expandToProperty, serializeAsColor3 } from "../../Misc/decorators";
+import { Color3 } from "../../Maths/math";
+import { SmartArray } from "../../Misc/smartArray";
+import { BaseTexture } from "../../Materials/Textures/baseTexture";
+import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture";
+import { Effect, EffectFallbacks } from "../../Materials/effect";
+import { MaterialFlags } from "../materialFlags";
+import { UniformBuffer } from "../../Materials/uniformBuffer";
+import { MaterialHelper } from "../../Materials/materialHelper";
+
+declare type Engine = import("../../Engines/engine").Engine;
+declare type Scene = import("../../scene").Scene;
+
+/**
+ * @hidden
+ */
+export interface IMaterialSubSurfaceDefines {
+    SUBSURFACE: boolean;
+
+    SS_REFRACTION: boolean;
+    SS_TRANSLUCENCY: boolean;
+    SS_SCATERRING: boolean;
+
+    SS_THICKNESSANDMASK_TEXTURE: boolean;
+    SS_THICKNESSANDMASK_TEXTUREDIRECTUV: number;
+
+    SS_REFRACTIONMAP_3D: boolean;
+    SS_REFRACTIONMAP_OPPOSITEZ: boolean;
+    SS_LODINREFRACTIONALPHA: boolean;
+    SS_GAMMAREFRACTION: boolean;
+    SS_RGBDREFRACTION: boolean;
+    SS_LINKREFRACTIONTOTRANSPARENCY: boolean;
+
+    SS_MASK_FROM_THICKNESS_TEXTURE: boolean;
+
+    /** @hidden */
+    _areTexturesDirty: boolean;
+}
+
+/**
+ * Define the code related to the sub surface parameters of the pbr material.
+ */
+export class PBRSubSurfaceConfiguration {
+    @serialize()
+    private _isRefractionEnabled = false;
+    /**
+     * Defines if the refraction is enabled in the material.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public isRefractionEnabled = false;
+
+    @serialize()
+    private _isTranslucencyEnabled = false;
+    /**
+     * Defines if the translucency is enabled in the material.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public isTranslucencyEnabled = false;
+
+    @serialize()
+    private _isScatteringEnabled = false;
+    // /**
+    //  * Defines if the sub surface scattering is enabled in the material.
+    //  */
+    // @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    // public isScatteringEnabled = false;
+
+    /**
+     * Defines the refraction intensity of the material.
+     * The refraction when enabled replaces the Diffuse part of the material.
+     * The intensity helps transitionning between diffuse and refraction.
+     */
+    @serialize()
+    public refractionIntensity: number = 1;
+
+    /**
+     * Defines the translucency intensity of the material.
+     * When translucency has been enabled, this defines how much of the "translucency"
+     * is addded to the diffuse part of the material.
+     */
+    @serialize()
+    public translucencyIntensity: number = 1;
+
+    /**
+     * Defines the scattering intensity of the material.
+     * When scattering has been enabled, this defines how much of the "scattered light"
+     * is addded to the diffuse part of the material.
+     */
+    @serialize()
+    public scatteringIntensity: number = 1;
+
+    @serializeAsTexture()
+    private _thicknessTexture: Nullable<BaseTexture> = null;
+    /**
+     * Stores the average thickness of a mesh in a texture (The texture is holding the values linearly).
+     * The red channel of the texture should contain the thickness remapped between 0 and 1.
+     * 0 would mean minimumThickness
+     * 1 would mean maximumThickness
+     * The other channels might be use as a mask to vary the different effects intensity.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public thicknessTexture: Nullable<BaseTexture> = null;
+
+    private _refractionTexture: Nullable<BaseTexture> = null;
+    /**
+     * Defines the texture to use for refraction.
+     */
+    @serializeAsTexture()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public refractionTexture: Nullable<BaseTexture> = null;
+
+    private _indexOfRefraction = 1;
+    /**
+     * Defines the indice of refraction used in the material.
+     * https://en.wikipedia.org/wiki/List_of_refractive_indices
+     */
+    @serialize()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public indexOfRefraction = 1;
+
+    private _invertRefractionY = false;
+    /**
+     * Controls if refraction needs to be inverted on Y. This could be useful for procedural texture.
+     */
+    @serialize()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public invertRefractionY = false;
+
+    private _linkRefractionWithTransparency = false;
+    /**
+     * 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.
+     */
+    @serialize()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public linkRefractionWithTransparency = false;
+
+    /**
+     * Defines the minimum thickness stored in the thickness map.
+     * If no thickness map is defined, this value will be used to simulate thickness.
+     */
+    @serialize()
+    public minimumThickness: number = 0;
+
+    /**
+     * Defines the maximum thickness stored in the thickness map.
+     */
+    @serialize()
+    public maximumThickness: number = 1;
+
+    /**
+     * Defines the volume tint of the material.
+     * This is used for both translucency and scattering.
+     */
+    @serializeAsColor3()
+    public tintColor = Color3.White();
+
+    /**
+     * Defines the distance at which the tint color should be found in the media.
+     * This is used for refraction only.
+     */
+    @serialize()
+    public tintColorAtDistance = 1;
+
+    /**
+     * Defines how far each channel transmit through the media.
+     * It is defined as a color to simplify it selection.
+     */
+    @serializeAsColor3()
+    public diffusionDistance = Color3.White();
+
+    @serialize()
+    private _useMaskFromThicknessTexture = false;
+    /**
+     * 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.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public useMaskFromThicknessTexture: boolean = false;
+
+    /** @hidden */
+    private _internalMarkAllSubMeshesAsTexturesDirty: () => void;
+
+    /** @hidden */
+    public _markAllSubMeshesAsTexturesDirty(): void {
+        this._internalMarkAllSubMeshesAsTexturesDirty();
+    }
+
+    /**
+     * Instantiate a new istance of sub surface configuration.
+     * @param markAllSubMeshesAsTexturesDirty Callback to flag the material to dirty
+     */
+    constructor(markAllSubMeshesAsTexturesDirty: () => void) {
+        this._internalMarkAllSubMeshesAsTexturesDirty = markAllSubMeshesAsTexturesDirty;
+    }
+
+    /**
+     * Gets wehter the submesh is ready to be used or not.
+     * @param defines the list of "defines" to update.
+     * @param scene defines the scene the material belongs to.
+     * @returns - boolean indicating that the submesh is ready or not.
+     */
+    public isReadyForSubMesh(defines: IMaterialSubSurfaceDefines, scene: Scene): boolean {
+        if (defines._areTexturesDirty) {
+            if (scene.texturesEnabled) {
+                if (this._thicknessTexture && MaterialFlags.ThicknessTextureEnabled) {
+                    if (!this._thicknessTexture.isReadyOrNotBlocking()) {
+                        return false;
+                    }
+                }
+
+                var refractionTexture = this._getRefractionTexture(scene);
+                if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
+                    if (!refractionTexture.isReadyOrNotBlocking()) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks to see if a texture is used in the material.
+     * @param defines the list of "defines" to update.
+     * @param scene defines the scene to the material belongs to.
+     */
+    public prepareDefines(defines: IMaterialSubSurfaceDefines, scene: Scene): void {
+        if (defines._areTexturesDirty) {
+            defines.SUBSURFACE = false;
+
+            defines.SS_TRANSLUCENCY = this._isTranslucencyEnabled;
+            defines.SS_SCATERRING = this._isScatteringEnabled;
+            defines.SS_THICKNESSANDMASK_TEXTURE = false;
+            defines.SS_MASK_FROM_THICKNESS_TEXTURE = false;
+            defines.SS_REFRACTION = false;
+            defines.SS_REFRACTIONMAP_3D = false;
+            defines.SS_GAMMAREFRACTION = false;
+            defines.SS_RGBDREFRACTION = false;
+            defines.SS_REFRACTIONMAP_OPPOSITEZ = false;
+            defines.SS_LODINREFRACTIONALPHA = false;
+            defines.SS_LINKREFRACTIONTOTRANSPARENCY = false;
+
+            if (this._isRefractionEnabled || this._isTranslucencyEnabled || this._isScatteringEnabled) {
+                defines.SUBSURFACE = true;
+
+                if (defines._areTexturesDirty) {
+                    if (scene.texturesEnabled) {
+                        if (this._thicknessTexture && MaterialFlags.ThicknessTextureEnabled) {
+                            MaterialHelper.PrepareDefinesForMergedUV(this._thicknessTexture, defines, "SS_THICKNESSANDMASK_TEXTURE");
+                        }
+                    }
+                }
+
+                defines.SS_MASK_FROM_THICKNESS_TEXTURE = this._useMaskFromThicknessTexture;
+            }
+
+            if (this._isRefractionEnabled) {
+                if (scene.texturesEnabled) {
+                    var refractionTexture = this._getRefractionTexture(scene);
+                    if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
+                        defines.SS_REFRACTION = true;
+                        defines.SS_REFRACTIONMAP_3D = refractionTexture.isCube;
+                        defines.SS_GAMMAREFRACTION = refractionTexture.gammaSpace;
+                        defines.SS_RGBDREFRACTION = refractionTexture.isRGBD;
+                        defines.SS_REFRACTIONMAP_OPPOSITEZ = refractionTexture.invertZ;
+                        defines.SS_LODINREFRACTIONALPHA = refractionTexture.lodLevelInAlpha;
+                        defines.SS_LINKREFRACTIONTOTRANSPARENCY = this._linkRefractionWithTransparency;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Binds the material data.
+     * @param uniformBuffer defines the Uniform buffer to fill in.
+     * @param scene defines the scene the material belongs to.
+     * @param engine defines the engine the material belongs to.
+     * @param isFrozen defines wether the material is frozen or not.
+     * @param lodBasedMicrosurface defines wether the material relies on lod based microsurface or not.
+     */
+    public bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, engine: Engine, isFrozen: boolean, lodBasedMicrosurface: boolean): void {
+        var refractionTexture = this._getRefractionTexture(scene);
+
+        if (!uniformBuffer.useUbo || !isFrozen || !uniformBuffer.isSync) {
+            if (this._thicknessTexture && MaterialFlags.ThicknessTextureEnabled) {
+                uniformBuffer.updateFloat2("vThicknessInfos", this._thicknessTexture.coordinatesIndex, this._thicknessTexture.level);
+                MaterialHelper.BindTextureMatrix(this._thicknessTexture, uniformBuffer, "thickness");
+            }
+
+            uniformBuffer.updateFloat2("vThicknessParam", this.minimumThickness, this.maximumThickness - this.minimumThickness);
+
+            if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
+                uniformBuffer.updateMatrix("refractionMatrix", refractionTexture.getReflectionTextureMatrix());
+
+                var depth = 1.0;
+                if (!refractionTexture.isCube) {
+                    if ((<any>refractionTexture).depth) {
+                        depth = (<any>refractionTexture).depth;
+                    }
+                }
+                uniformBuffer.updateFloat4("vRefractionInfos", refractionTexture.level, 1 / this._indexOfRefraction, depth, this._invertRefractionY ? -1 : 1);
+                uniformBuffer.updateFloat3("vRefractionMicrosurfaceInfos",
+                    refractionTexture.getSize().width,
+                    refractionTexture.lodGenerationScale,
+                    refractionTexture.lodGenerationOffset);
+            }
+
+            uniformBuffer.updateColor3("vDiffusionDistance", this.diffusionDistance);
+
+            uniformBuffer.updateFloat4("vTintColor", this.tintColor.r,
+                this.tintColor.g,
+                this.tintColor.b,
+                this.tintColorAtDistance);
+
+            uniformBuffer.updateFloat3("vSubSurfaceIntensity", this.refractionIntensity, this.translucencyIntensity, this.scatteringIntensity);
+        }
+
+        // Textures
+        if (scene.texturesEnabled) {
+            if (this._thicknessTexture && MaterialFlags.ThicknessTextureEnabled) {
+                uniformBuffer.setTexture("thicknessSampler", this._thicknessTexture);
+            }
+
+            if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
+                if (lodBasedMicrosurface) {
+                    uniformBuffer.setTexture("refractionSampler", refractionTexture);
+                }
+                else {
+                    uniformBuffer.setTexture("refractionSampler", refractionTexture._lodTextureMid || refractionTexture);
+                    uniformBuffer.setTexture("refractionSamplerLow", refractionTexture._lodTextureLow || refractionTexture);
+                    uniformBuffer.setTexture("refractionSamplerHigh", refractionTexture._lodTextureHigh || refractionTexture);
+                }
+            }
+        }
+    }
+
+    /**
+     * Unbinds the material from the mesh.
+     * @returns true if unbound, otherwise false
+     */
+    public unbind(activeEffect: Effect): boolean {
+        if (this._refractionTexture && this._refractionTexture.isRenderTarget) {
+            activeEffect.setTexture("refractionSampler", null);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the texture used for refraction or null if none is used.
+     * @param scene defines the scene the material belongs to.
+     * @returns - Refraction texture if present.  If no refraction texture and refraction
+     * is linked with transparency, returns environment texture.  Otherwise, returns null.
+     */
+    private _getRefractionTexture(scene: Scene): Nullable<BaseTexture> {
+        if (this._refractionTexture) {
+            return this._refractionTexture;
+        }
+
+        if (this._linkRefractionWithTransparency) {
+            return scene.environmentTexture;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns true if alpha blending should be disabled.
+     */
+    public get disableAlphaBlending(): boolean {
+        return this._linkRefractionWithTransparency;
+    }
+
+    /**
+     * Fills the list of render target textures.
+     * @param renderTargets the list of render targets to update
+     */
+    public fillRenderTargetTextures(renderTargets: SmartArray<RenderTargetTexture>): void {
+        if (MaterialFlags.RefractionTextureEnabled && this._refractionTexture && this._refractionTexture.isRenderTarget) {
+            renderTargets.push(<RenderTargetTexture>this._refractionTexture);
+        }
+    }
+
+    /**
+     * Checks to see if a texture is used in the material.
+     * @param texture - Base texture to use.
+     * @returns - Boolean specifying if a texture is used in the material.
+     */
+    public hasTexture(texture: BaseTexture): boolean {
+        if (this._thicknessTexture === texture) {
+            return true;
+        }
+
+        if (this._refractionTexture === texture) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets a boolean indicating that current material needs to register RTT
+     * @returns true if this uses a render target otherwise false.
+     */
+    public hasRenderTargetTextures(): boolean {
+        if (MaterialFlags.RefractionTextureEnabled && this._refractionTexture && this._refractionTexture.isRenderTarget) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns an array of the actively used textures.
+     * @param activeTextures Array of BaseTextures
+     */
+    public getActiveTextures(activeTextures: BaseTexture[]): void {
+        if (this._thicknessTexture) {
+            activeTextures.push(this._thicknessTexture);
+        }
+
+        if (this._refractionTexture) {
+            activeTextures.push(this._refractionTexture);
+        }
+    }
+
+    /**
+     * Returns the animatable textures.
+     * @param animatables Array of animatable textures.
+     */
+    public getAnimatables(animatables: IAnimatable[]): void {
+        if (this._thicknessTexture && this._thicknessTexture.animations && this._thicknessTexture.animations.length > 0) {
+            animatables.push(this._thicknessTexture);
+        }
+
+        if (this._refractionTexture && this._refractionTexture.animations && this._refractionTexture.animations.length > 0) {
+            animatables.push(this._refractionTexture);
+        }
+    }
+
+    /**
+     * Disposes the resources of the material.
+     * @param forceDisposeTextures - Forces the disposal of all textures.
+     */
+    public dispose(forceDisposeTextures?: boolean): void {
+        if (forceDisposeTextures) {
+            if (this._thicknessTexture) {
+                this._thicknessTexture.dispose();
+            }
+
+            if (this._refractionTexture) {
+                this._refractionTexture.dispose();
+            }
+        }
+    }
+
+    /**
+    * Get the current class name of the texture useful for serialization or dynamic coding.
+    * @returns "PBRSubSurfaceConfiguration"
+    */
+    public getClassName(): string {
+        return "PBRSubSurfaceConfiguration";
+    }
+
+    /**
+     * Add fallbacks to the effect fallbacks list.
+     * @param defines defines the Base texture to use.
+     * @param fallbacks defines the current fallback list.
+     * @param currentRank defines the current fallback rank.
+     * @returns the new fallback rank.
+     */
+    public static AddFallbacks(defines: IMaterialSubSurfaceDefines, fallbacks: EffectFallbacks, currentRank: number): number {
+        if (defines.SS_SCATERRING) {
+            fallbacks.addFallback(currentRank++, "SS_SCATERRING");
+        }
+        if (defines.SS_TRANSLUCENCY) {
+            fallbacks.addFallback(currentRank++, "SS_TRANSLUCENCY");
+        }
+        return currentRank;
+    }
+
+    /**
+     * Add the required uniforms to the current list.
+     * @param uniforms defines the current uniform list.
+     */
+    public static AddUniforms(uniforms: string[]): void {
+        uniforms.push(
+            "vDiffusionDistance", "vTintColor", "vSubSurfaceIntensity",
+            "vRefractionMicrosurfaceInfos",
+            "vRefractionInfos", "vThicknessInfos", "vThicknessParam",
+            "refractionMatrix", "thicknessMatrix");
+    }
+
+    /**
+     * Add the required samplers to the current list.
+     * @param samplers defines the current sampler list.
+     */
+    public static AddSamplers(samplers: string[]): void {
+        samplers.push("thicknessSampler",
+            "refractionSampler", "refractionSamplerLow", "refractionSamplerHigh");
+    }
+
+    /**
+     * Add the required uniforms to the current buffer.
+     * @param uniformBuffer defines the current uniform buffer.
+     */
+    public static PrepareUniformBuffer(uniformBuffer: UniformBuffer): void {
+        uniformBuffer.addUniform("vRefractionMicrosurfaceInfos", 3);
+        uniformBuffer.addUniform("vRefractionInfos", 4);
+        uniformBuffer.addUniform("refractionMatrix", 16);
+        uniformBuffer.addUniform("vThicknessInfos", 2);
+        uniformBuffer.addUniform("thicknessMatrix", 16);
+        uniformBuffer.addUniform("vThicknessParam", 2);
+        uniformBuffer.addUniform("vDiffusionDistance", 3);
+        uniformBuffer.addUniform("vTintColor", 4);
+        uniformBuffer.addUniform("vSubSurfaceIntensity", 3);
+    }
+
+    /**
+     * Makes a duplicate of the current configuration into another one.
+     * @param configuration define the config where to copy the info
+     */
+    public copyTo(configuration: PBRSubSurfaceConfiguration): void {
+        SerializationHelper.Clone(() => configuration, this);
+    }
+
+    /**
+     * Serializes this Sub Surface configuration.
+     * @returns - An object with the serialized config.
+     */
+    public serialize(): any {
+        return SerializationHelper.Serialize(this);
+    }
+
+    /**
+     * Parses a Sub Surface Configuration from a serialized object.
+     * @param source - Serialized object.
+     */
+    public parse(source: any): void {
+        SerializationHelper.Parse(() => this, source, null);
+    }
+}

+ 16 - 0
src/Materials/materialFlags.ts

@@ -261,4 +261,20 @@ export class MaterialFlags {
         this._AnisotropicTextureEnabled = value;
         Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
     }
+
+    private static _ThicknessTextureEnabled = true;
+    /**
+     * Are thickness textures enabled in the application.
+     */
+    public static get ThicknessTextureEnabled(): boolean {
+        return this._ThicknessTextureEnabled;
+    }
+    public static set ThicknessTextureEnabled(value: boolean) {
+        if (this._ThicknessTextureEnabled === value) {
+            return;
+        }
+
+        this._ThicknessTextureEnabled = value;
+        Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+    }
 }

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

@@ -56,6 +56,8 @@
             // Diffuse contribution
             #ifdef HEMILIGHT{X}
                 info.diffuse = computeHemisphericDiffuseLighting(preInfo, light{X}.vLightDiffuse.rgb, light{X}.vLightGround);
+            #elif defined(SS_TRANSLUCENCY)
+                info.diffuse = computeDiffuseAndTransmittedLighting(preInfo, light{X}.vLightDiffuse.rgb, transmittance);
             #else
                 info.diffuse = computeDiffuseLighting(preInfo, light{X}.vLightDiffuse.rgb);
             #endif

+ 18 - 0
src/Shaders/ShadersInclude/pbrBRDFFunctions.fx

@@ -267,3 +267,21 @@ float diffuseBRDF_Burley(float NdotL, float NdotV, float VdotH, float roughness)
 
     return fresnel / PI;
 }
+
+#ifdef SS_TRANSLUCENCY
+    // Pixar diffusion profile
+    // http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf
+    vec3 transmittanceBRDF_Burley(const vec3 tintColor, const vec3 diffusionDistance, float thickness) {
+        vec3 S = 1. / maxEps(diffusionDistance);
+        vec3 temp = exp((-0.333333333 * thickness) * S);
+        return tintColor.rgb * 0.25 * (temp * temp * temp + 3.0 * temp);
+    }
+
+    // Extends the dark area to prevent seams
+    // Keep it energy conserving by using McCauley solution: https://blog.selfshadow.com/2011/12/31/righting-wrap-part-1/
+    float computeWrappedDiffuseNdotL(float NdotL, float w) {
+        float t = 1.0 + w;
+        float invt2 = 1.0 / square(t);
+        return saturate((NdotL + w) * invt2);
+    }
+#endif

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

@@ -72,8 +72,10 @@
         gl_FragColor.rgb = sheenMapData.rgb;
     #elif DEBUGMODE == 30 && defined(ANISOTROPIC) && defined(ANISOTROPIC_TEXTURE)
         gl_FragColor.rgb = anisotropyMapData.rgb;
+    #elif DEBUGMODE == 31 && defined(SUBSURFACE) && defined(SS_THICKNESSANDMASK_TEXTURE)
+        gl_FragColor.rgb = thicknessMap.rgb;
 // Env
-    #elif DEBUGMODE == 40 && defined(REFRACTION)
+    #elif DEBUGMODE == 40 && defined(SS_REFRACTION)
         // Base color.
         gl_FragColor.rgb = environmentRefraction.rgb;
         #define DEBUGMODE_GAMMA
@@ -119,6 +121,10 @@
         gl_FragColor.rgb = vec3(clearCoatRoughness);
     #elif DEBUGMODE == 67 && defined(CLEARCOAT)
         gl_FragColor.rgb = vec3(clearCoatNdotV);
+    #elif DEBUGMODE == 68 && defined(SUBSURFACE) && defined(SS_TRANSLUCENCY)
+        gl_FragColor.rgb = transmittance;
+    #elif DEBUGMODE == 69 && defined(SUBSURFACE) && defined(SS_REFRACTION)
+        gl_FragColor.rgb = refractionTransmittance;
 // Misc
     #elif DEBUGMODE == 70 && defined(RADIANCEOCCLUSION)
         gl_FragColor.rgb = vec3(seo);

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

@@ -46,6 +46,22 @@ vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, m
     return toLinearSpace(textureColor);
 }
 
+#ifdef SS_TRANSLUCENCY
+    vec3 computeDiffuseAndTransmittedLighting(preLightingInfo info, vec3 lightColor, vec3 transmittance) {
+        float NdotL = absEps(info.NdotLUnclamped);
+
+        // Use wrap lighting to simulate SSS.
+        float wrapNdotL = computeWrappedDiffuseNdotL(NdotL, 0.02);
+
+        // Remap transmittance from tr to 1. if ndotl is negative.
+        float trAdapt = step(0., info.NdotLUnclamped);
+        vec3 transmittanceNdotL = mix(transmittance * wrapNdotL, vec3(wrapNdotL), trAdapt);
+
+        float diffuseTerm = diffuseBRDF_Burley(NdotL, info.NdotV, info.VdotH, info.roughness);
+        return diffuseTerm * transmittanceNdotL * info.attenuation * lightColor;
+    }
+#endif
+
 #ifdef SPECULARTERM
     vec3 computeSpecularLighting(preLightingInfo info, vec3 N, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, vec3 lightColor) {
         float NdotH = saturateEps(dot(N, info.H));

+ 9 - 2
src/Shaders/ShadersInclude/pbrDirectLightingSetupFunctions.fx

@@ -13,6 +13,7 @@ struct preLightingInfo
     vec3 L;
     vec3 H;
     float NdotV;
+    float NdotLUnclamped;
     float NdotL;
     float VdotH;
     float roughness;
@@ -31,9 +32,11 @@ preLightingInfo computePointAndSpotPreLightingInfo(vec4 lightData, vec3 V, vec3
     // Geometry Data.
     result.L = normalize(result.lightOffset);
     result.H = normalize(V + result.L);
-    result.NdotL = saturateEps(dot(N, result.L));
     result.VdotH = saturate(dot(V, result.H));
 
+    result.NdotLUnclamped = dot(N, result.L);
+    result.NdotL = saturateEps(result.NdotLUnclamped);
+
     return result;
 }
 
@@ -46,9 +49,11 @@ preLightingInfo computeDirectionalPreLightingInfo(vec4 lightData, vec3 V, vec3 N
     // Geometry Data.
     result.L = normalize(-lightData.xyz);
     result.H = normalize(V + result.L);
-    result.NdotL = saturateEps(dot(N, result.L));
     result.VdotH = saturate(dot(V, result.H));
 
+    result.NdotLUnclamped = dot(N, result.L);
+    result.NdotL = saturateEps(result.NdotLUnclamped);
+
     return result;
 }
 
@@ -56,8 +61,10 @@ preLightingInfo computeHemisphericPreLightingInfo(vec4 lightData, vec3 V, vec3 N
     preLightingInfo result;
 
     // Geometry Data.
+    // Half Lambert for Hemispherix lighting.
     result.NdotL = dot(N, lightData.xyz) * 0.5 + 0.5;
     result.NdotL = saturateEps(result.NdotL);
+    result.NdotLUnclamped = result.NdotL;
 
     #ifdef SPECULARTERM
         result.L = normalize(lightData.xyz);

+ 20 - 8
src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx

@@ -44,17 +44,10 @@ uniform vec2 vMicroSurfaceSamplerInfos;
 #endif
 
 // Refraction Reflection
-#if defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_PROJECTION) || defined(REFRACTION)
+#if defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_PROJECTION) || defined(SS_REFRACTION)
 uniform mat4 view;
 #endif
 
-// Refraction
-#ifdef REFRACTION
-    uniform vec4 vRefractionInfos;
-    uniform mat4 refractionMatrix;
-    uniform vec3 vRefractionMicrosurfaceInfos;
-#endif
-
 // Reflection
 #ifdef REFLECTION
     uniform vec2 vReflectionInfos;
@@ -112,4 +105,23 @@ uniform mat4 view;
         uniform vec2 vSheenInfos;
         uniform mat4 sheenMatrix;
     #endif
+#endif
+
+// SubSurface
+#ifdef SUBSURFACE
+    #ifdef SS_REFRACTION
+        uniform vec3 vRefractionMicrosurfaceInfos;
+        uniform vec4 vRefractionInfos;
+        uniform mat4 refractionMatrix;
+    #endif
+
+    #ifdef SS_THICKNESSANDMASK_TEXTURE
+        uniform vec2 vThicknessInfos;
+        uniform mat4 thicknessMatrix;;
+    #endif
+
+    uniform vec2 vThicknessParam;
+    uniform vec3 vDiffusionDistance;
+    uniform vec4 vTintColor;
+    uniform vec3 vSubSurfaceIntensity;
 #endif

+ 40 - 27
src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx

@@ -136,33 +136,6 @@
     #endif
 #endif
 
-// Refraction
-#ifdef REFRACTION
-    #ifdef REFRACTIONMAP_3D
-        #define sampleRefraction(s, c) textureCube(s, c)
-        
-        uniform samplerCube refractionSampler;
-
-        #ifdef LODBASEDMICROSFURACE
-            #define sampleRefractionLod(s, c, l) textureCubeLodEXT(s, c, l)
-        #else
-            uniform samplerCube refractionSamplerLow;
-            uniform samplerCube refractionSamplerHigh;
-        #endif
-    #else
-        #define sampleRefraction(s, c) texture2D(s, c)
-        
-        uniform sampler2D refractionSampler;
-
-        #ifdef LODBASEDMICROSFURACE
-            #define sampleRefractionLod(s, c, l) texture2DLodEXT(s, c, l)
-        #else
-            uniform samplerCube refractionSamplerLow;
-            uniform samplerCube refractionSamplerHigh;
-        #endif
-    #endif
-#endif
-
 // Reflection
 #ifdef REFLECTION
     #ifdef REFLECTIONMAP_3D
@@ -200,4 +173,44 @@
 
 #ifdef ENVIRONMENTBRDF
     uniform sampler2D environmentBrdfSampler;
+#endif
+
+// SUBSURFACE
+#ifdef SUBSURFACE
+    #ifdef SS_REFRACTION
+        #ifdef SS_REFRACTIONMAP_3D
+            #define sampleRefraction(s, c) textureCube(s, c)
+            
+            uniform samplerCube refractionSampler;
+
+            #ifdef LODBASEDMICROSFURACE
+                #define sampleRefractionLod(s, c, l) textureCubeLodEXT(s, c, l)
+            #else
+                uniform samplerCube refractionSamplerLow;
+                uniform samplerCube refractionSamplerHigh;
+            #endif
+        #else
+            #define sampleRefraction(s, c) texture2D(s, c)
+            
+            uniform sampler2D refractionSampler;
+
+            #ifdef LODBASEDMICROSFURACE
+                #define sampleRefractionLod(s, c, l) texture2DLodEXT(s, c, l)
+            #else
+                uniform samplerCube refractionSamplerLow;
+                uniform samplerCube refractionSamplerHigh;
+            #endif
+        #endif
+    #endif
+
+    #ifdef SS_THICKNESSANDMASK_TEXTURE
+        #if SS_THICKNESSANDMASK_TEXTUREDIRECTUV == 1
+            #define vThicknessUV vMainUV1
+        #elif SS_THICKNESSANDMASK_TEXTUREDIRECTUV == 2
+            #define vThicknessUV vMainUV2
+        #else
+            varying vec2 vThicknessUV;
+        #endif
+        uniform sampler2D thicknessSampler;
+    #endif
 #endif

+ 7 - 3
src/Shaders/ShadersInclude/pbrHelperFunctions.fx

@@ -60,16 +60,20 @@ vec2 getAARoughnessFactors(vec3 normalVector) {
     }
 #endif
 
-#ifdef CLEARCOAT
+#if defined(CLEARCOAT) || defined(SS_REFRACTION)
     // From beer lambert law I1/I0 = e −α′lc
     // c is considered included in alpha
     // https://blog.selfshadow.com/publications/s2017-shading-course/drobot/s2017_pbs_multilayered.pdf page 47
+    vec3 cocaLambert(vec3 alpha, float distance) {
+        return exp(-alpha * distance);
+    }
+
     // where L on a thin constant size layer can be (d * ((NdotLRefract + NdotVRefract) / (NdotLRefract * NdotVRefract))
     vec3 cocaLambert(float NdotVRefract, float NdotLRefract, vec3 alpha, float thickness) {
-        return exp(alpha * -(thickness * ((NdotLRefract + NdotVRefract) / (NdotLRefract * NdotVRefract))));
+        return cocaLambert(alpha, (thickness * ((NdotLRefract + NdotVRefract) / (NdotLRefract * NdotVRefract))));
     }
 
-    // From beerLambert Solves what alpha should be for a given resutlt at a known distance.
+    // From beerLambert Solves what alpha should be for a given result at a known distance.
     vec3 computeColorAtDistanceInMedia(vec3 color, float distance) {
         return -log(color) / distance;
     }

+ 2 - 2
src/Shaders/ShadersInclude/pbrIBLFunctions.fx

@@ -1,4 +1,4 @@
-#if defined(REFLECTION) || defined(REFRACTION)
+#if defined(REFLECTION) || defined(SS_REFRACTION)
     float getLodFromAlphaG(float cubeMapDimensionPixels, float microsurfaceAverageSlope) {
         float microsurfaceAverageSlopeTexels = microsurfaceAverageSlope * cubeMapDimensionPixels;
         float lod = log2(microsurfaceAverageSlopeTexels);
@@ -29,7 +29,7 @@
 // LEGACY
 // ___________________________________________________________________________________
 
-#if defined(LODINREFLECTIONALPHA) || defined(LODINREFRACTIONALPHA)
+#if defined(LODINREFLECTIONALPHA) || defined(SS_LODINREFRACTIONALPHA)
     // To enable 8 bit textures to be used we need to pack and unpack the LOD
     //inverse alpha is used to work around low-alpha bugs in Edge and Firefox
     #define UNPACK_LOD(x) (1.0 - x) * 255.0

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

@@ -9,7 +9,6 @@ uniform Material
     uniform vec2 vLightmapInfos;
     uniform vec3 vReflectivityInfos;
     uniform vec2 vMicroSurfaceSamplerInfos;
-    uniform vec4 vRefractionInfos;
     uniform vec2 vReflectionInfos;
     uniform vec3 vReflectionPosition;
     uniform vec3 vReflectionSize;
@@ -23,18 +22,12 @@ uniform Material
     uniform mat4 microSurfaceSamplerMatrix;
     uniform mat4 bumpMatrix;
     uniform vec2 vTangentSpaceParams;
-    uniform mat4 refractionMatrix;
     uniform mat4 reflectionMatrix;
-
     uniform vec3 vReflectionColor;
     uniform vec4 vAlbedoColor;
     uniform vec4 vLightingIntensity;
-
-    uniform vec3 vRefractionMicrosurfaceInfos;
     uniform vec3 vReflectionMicrosurfaceInfos;
-
     uniform float pointSize;
-
     uniform vec4 vReflectivityColor;
     uniform vec3 vEmissiveColor;
 
@@ -59,6 +52,16 @@ uniform Material
     uniform vec4 vSheenColor;
     uniform vec2 vSheenInfos;
     uniform mat4 sheenMatrix;
+
+    uniform vec3 vRefractionMicrosurfaceInfos;
+    uniform vec4 vRefractionInfos;
+    uniform mat4 refractionMatrix;
+    uniform vec2 vThicknessInfos;
+    uniform mat4 thicknessMatrix;
+    uniform vec2 vThicknessParam;
+    uniform vec3 vDiffusionDistance;
+    uniform vec4 vTintColor;
+    uniform vec3 vSubSurfaceIntensity;
 };
 
 uniform Scene {

+ 14 - 8
src/Shaders/ShadersInclude/pbrVertexDeclaration.fx

@@ -45,18 +45,10 @@ uniform mat4 bumpMatrix;
 uniform float pointSize;
 #endif
 
-// Refraction
-#ifdef REFRACTION
-    uniform vec4 vRefractionInfos;
-    uniform mat4 refractionMatrix;
-    uniform vec3 vRefractionMicrosurfaceInfos;
-#endif
-
 // Reflection
 #ifdef REFLECTION
     uniform vec2 vReflectionInfos;
     uniform mat4 reflectionMatrix;
-    uniform vec3 vReflectionMicrosurfaceInfos;
 #endif
 
 // Clear Coat
@@ -92,3 +84,17 @@ uniform float pointSize;
         uniform mat4 sheenMatrix;
     #endif
 #endif
+
+// Sub Surface
+#ifdef SUBSURFACE
+    #ifdef SS_REFRACTION
+        uniform vec4 vRefractionInfos;
+        uniform mat4 refractionMatrix;
+    #endif
+
+    #ifdef SS_THICKNESSANDMASK_TEXTURE
+        uniform vec2 vThicknessInfos;
+        uniform mat4 thicknessMatrix;;
+    #endif
+#endif
+

+ 97 - 37
src/Shaders/pbr.fragment.fx

@@ -124,7 +124,7 @@ void main(void) {
     alpha *= vColor.a;
 #endif
 
-#if !defined(LINKREFRACTIONTOTRANSPARENCY) && !defined(ALPHAFRESNEL)
+#if !defined(SS_LINKREFRACTIONTOTRANSPARENCY) && !defined(ALPHAFRESNEL)
     #ifdef ALPHATEST
         if (alpha < ALPHATESTVALUE)
             discard;
@@ -310,7 +310,7 @@ void main(void) {
     #endif
 
     // _____________________________ Refraction Info _______________________________________
-    #ifdef REFRACTION
+    #ifdef SS_REFRACTION
         vec4 environmentRefraction = vec4(0., 0., 0., 0.);
 
         #ifdef ANISOTROPIC
@@ -319,12 +319,12 @@ void main(void) {
             vec3 refractionVector = refract(-viewDirectionW, normalW, vRefractionInfos.y);
         #endif
 
-        #ifdef REFRACTIONMAP_OPPOSITEZ
+        #ifdef SS_REFRACTIONMAP_OPPOSITEZ
             refractionVector.z *= -1.0;
         #endif
 
         // _____________________________ 2D vs 3D Maps ________________________________
-        #ifdef REFRACTIONMAP_3D
+        #ifdef SS_REFRACTIONMAP_3D
             refractionVector.y = refractionVector.y * vRefractionInfos.w;
             vec3 refractionCoords = refractionVector;
             refractionCoords = vec3(refractionMatrix * vec4(refractionCoords, 0));
@@ -334,7 +334,7 @@ void main(void) {
             refractionCoords.y = 1.0 - refractionCoords.y;
         #endif
 
-        #ifdef LODINREFRACTIONALPHA
+        #ifdef SS_LODINREFRACTIONALPHA
             float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG, NdotVUnclamped);
         #else
             float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG);
@@ -344,7 +344,7 @@ void main(void) {
             // Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
             refractionLOD = refractionLOD * vRefractionMicrosurfaceInfos.y + vRefractionMicrosurfaceInfos.z;
 
-            #ifdef LODINREFRACTIONALPHA
+            #ifdef SS_LODINREFRACTIONALPHA
                 // Automatic LOD adjustment to ensure that the smoothness-based environment LOD selection
                 // is constrained to appropriate LOD levels in order to prevent aliasing.
                 // The environment map is first sampled without custom LOD selection to determine
@@ -382,11 +382,11 @@ void main(void) {
             }
         #endif
 
-        #ifdef RGBDREFRACTION
+        #ifdef SS_RGBDREFRACTION
             environmentRefraction.rgb = fromRGBD(environmentRefraction);
         #endif
 
-        #ifdef GAMMAREFRACTION
+        #ifdef SS_GAMMAREFRACTION
             environmentRefraction.rgb = toLinearSpace(environmentRefraction.rgb);
         #endif
 
@@ -743,7 +743,7 @@ void main(void) {
         #endif
     #endif
 
-    // _____________________________ IBL BRDF + Energy Cons _________________________________
+    // _____________________________ IBL BRDF + Energy Cons ________________________________
     #if defined(ENVIRONMENTBRDF)
         // BRDF Lookup
         vec3 environmentBrdf = getBRDFLookup(NdotV, roughness, environmentBrdfSampler);
@@ -753,6 +753,49 @@ void main(void) {
         #endif
     #endif
 
+    // ___________________________________ SubSurface ______________________________________
+    #ifdef SUBSURFACE
+        #ifdef SS_REFRACTION
+            float refractionIntensity = vSubSurfaceIntensity.x;
+            #ifdef SS_LINKREFRACTIONTOTRANSPARENCY
+                refractionIntensity *= (1.0 - alpha);
+                // Put alpha back to 1;
+                alpha = 1.0;
+            #endif
+        #endif
+        #ifdef SS_TRANSLUCENCY
+            float translucencyIntensity = vSubSurfaceIntensity.y;
+        #endif
+        #ifdef SS_SCATTERING
+            float scatteringIntensity = vSubSurfaceIntensity.z;
+        #endif
+
+        #ifdef SS_THICKNESSANDMASK_TEXTURE
+            vec4 thicknessMap = texture2D(thicknessSampler, vThicknessUV + uvOffset);
+            float thickness = thicknessMap.r * vThicknessParam.y + vThicknessParam.x;
+
+            #ifdef SS_MASK_FROM_THICKNESS_TEXTURE
+                #ifdef SS_REFRACTION
+                    refractionIntensity *= thicknessMap.g;
+                #endif
+                #ifdef SS_TRANSLUCENCY
+                    translucencyIntensity *= thicknessMap.b;
+                #endif
+                #ifdef SS_SCATTERING
+                    scatteringIntensity *= thicknessMap.a;
+                #endif
+            #endif
+        #else
+            float thickness = vThicknessParam.y;
+        #endif
+
+        #ifdef SS_TRANSLUCENCY
+            thickness = maxEps(thickness);
+            vec3 transmittance = transmittanceBRDF_Burley(vTintColor.rgb, vDiffusionDistance, thickness);
+            transmittance *= translucencyIntensity;
+        #endif
+    #endif
+
     // ____________________________________________________________________________________
     // _____________________________ Direct Lighting Info __________________________________
     vec3 diffuseBase = vec3(0., 0., 0.);
@@ -885,42 +928,56 @@ void main(void) {
         specularEnvironmentReflectance *= (conservationFactor * conservationFactor);
     #endif
 
-    // _____________________________ Refractance+Tint ________________________________
-    #ifdef REFRACTION
-        vec3 refractance = vec3(0.0, 0.0, 0.0);
-        vec3 transmission = vec3(1.0, 1.0, 1.0);
-        #ifdef LINKREFRACTIONTOTRANSPARENCY
-            // Transmission based on alpha.
-            transmission *= (1.0 - alpha);
+    // _____________________________ Transmittance + Tint ________________________________
+    #ifdef SS_REFRACTION
+        vec3 refractionTransmittance = vec3(refractionIntensity);
+        #ifdef SS_THICKNESSANDMASK_TEXTURE
+            vec3 volumeAlbedo = computeColorAtDistanceInMedia(vTintColor.rgb, vTintColor.w);
 
-            // Tint the material with albedo.
-            // TODO. PBR Tinting.
-            vec3 mixedAlbedo = surfaceAlbedo;
-            float maxChannel = max(max(mixedAlbedo.r, mixedAlbedo.g), mixedAlbedo.b);
-            vec3 tint = saturate(maxChannel * mixedAlbedo);
+            // // Simulate Flat Surface
+            // thickness /=  dot(refractionVector, -normalW);
 
-            // Decrease Albedo Contribution
-            surfaceAlbedo *= alpha;
+            // // Simulate Curved Surface
+            // float NdotRefract = dot(normalW, refractionVector);
+            // thickness *= -NdotRefract;
 
-            // Decrease irradiance Contribution
-            environmentIrradiance *= alpha;
+            refractionTransmittance *= cocaLambert(volumeAlbedo, thickness);
+        #elif defined(SS_LINKREFRACTIONTOTRANSPARENCY)
+            // Tint the material with albedo.
+            float maxChannel = max(max(surfaceAlbedo.r, surfaceAlbedo.g), surfaceAlbedo.b);
+            vec3 volumeAlbedo = saturate(maxChannel * surfaceAlbedo);
 
             // Tint reflectance
-            environmentRefraction.rgb *= tint;
-
-            // Put alpha back to 1;
-            alpha = 1.0;
+            environmentRefraction.rgb *= volumeAlbedo;
+        #else
+            // Nothing to change for refraction.
         #endif
 
+        // Decrease Albedo Contribution
+        surfaceAlbedo *= (1. - refractionIntensity);
+
+        // Decrease irradiance Contribution
+        environmentIrradiance *= (1. - refractionIntensity);
+
         // Add Multiple internal bounces.
         vec3 bounceSpecularEnvironmentReflectance = (2.0 * specularEnvironmentReflectance) / (1.0 + specularEnvironmentReflectance);
-        specularEnvironmentReflectance = mix(bounceSpecularEnvironmentReflectance, specularEnvironmentReflectance, alpha);
+        specularEnvironmentReflectance = mix(bounceSpecularEnvironmentReflectance, specularEnvironmentReflectance, refractionIntensity);
 
         // In theory T = 1 - R.
-        transmission *= 1.0 - specularEnvironmentReflectance;
+        refractionTransmittance *= 1.0 - specularEnvironmentReflectance;
+    #endif
 
-        // Should baked in diffuse.
-        refractance = transmission;
+    // _______________________________  IBL Translucency ________________________________
+    #if defined(REFLECTION) && defined(USESPHERICALFROMREFLECTIONMAP) && defined(SS_TRANSLUCENCY)
+        #if defined(USESPHERICALINVERTEX)
+            vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz;
+            #ifdef REFLECTIONMAP_OPPOSITEZ
+                irradianceVector.z *= -1.0;
+            #endif
+        #endif
+
+        vec3 refractionIrradiance = environmentIrradianceJones(-irradianceVector);
+        refractionIrradiance *= transmittance;
     #endif
 
     // ______________________________________________________________________________
@@ -933,6 +990,9 @@ void main(void) {
     // _____________________________ Irradiance ______________________________________
     #ifdef REFLECTION
         vec3 finalIrradiance = environmentIrradiance;
+        #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(SS_TRANSLUCENCY)
+            finalIrradiance += refractionIrradiance;
+        #endif
         finalIrradiance *= surfaceAlbedo.rgb;
     #endif
 
@@ -961,9 +1021,9 @@ void main(void) {
     #endif
 
     // _____________________________ Refraction ______________________________________
-    #ifdef REFRACTION
+    #ifdef SS_REFRACTION
         vec3 finalRefraction = environmentRefraction.rgb;
-        finalRefraction *= refractance;
+        finalRefraction *= refractionTransmittance;
     #endif
 
     // _____________________________ Clear Coat _______________________________________
@@ -986,7 +1046,7 @@ void main(void) {
             vec3 finalClearCoatRadianceScaled = finalClearCoatRadiance * vLightingIntensity.z;
         #endif
 
-        #ifdef REFRACTION
+        #ifdef SS_REFRACTION
             finalRefraction *= (conservationFactor * conservationFactor);
             #ifdef CLEARCOAT_TINT
                 finalRefraction *= absorption;
@@ -1100,7 +1160,7 @@ void main(void) {
             finalSheenRadianceScaled +
         #endif
     #endif
-    #ifdef REFRACTION
+    #ifdef SS_REFRACTION
         finalRefraction			* vLightingIntensity.z +
     #endif
 #endif

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

@@ -92,6 +92,12 @@ varying vec2 vBumpUV;
     #endif
 #endif
 
+#ifdef SUBSURFACE
+    #if defined(SS_THICKNESSANDMASK_TEXTURE) && SS_THICKNESSANDMASK_TEXTUREDIRECTUV == 0 
+        varying vec2 vThicknessUV;
+    #endif
+#endif
+
 // Output
 varying vec3 vPositionW;
 #if DEBUGMODE > 0
@@ -362,6 +368,19 @@ void main(void) {
     #endif
 #endif
 
+#ifdef SUBSURFACE
+    #if defined(SS_THICKNESSANDMASK_TEXTURE) && SS_THICKNESSANDMASK_TEXTUREDIRECTUV == 0 
+        if (vThicknessInfos.x == 0.)
+        {
+            vThicknessUV = vec2(thicknessMatrix * vec4(uv, 1.0, 0.0));
+        }
+        else
+        {
+            vThicknessUV = vec2(thicknessMatrix * vec4(uv2, 1.0, 0.0));
+        }
+    #endif
+#endif
+
     // TBN
 #include<bumpVertex>