Browse Source

adding diffusion profiles

Benjamin Guignabert 5 years ago
parent
commit
fbf3ad2126

+ 0 - 3
src/Materials/Background/backgroundMaterial.ts

@@ -817,9 +817,6 @@ export class BackgroundMaterial extends PushMaterial {
         // Values that need to be evaluated on every frame
         // Values that need to be evaluated on every frame
         MaterialHelper.PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances, null, subMesh.getRenderingMesh().hasThinInstances);
         MaterialHelper.PrepareDefinesForFrameBoundValues(scene, engine, defines, useInstances, null, subMesh.getRenderingMesh().hasThinInstances);
 
 
-        // Deferred
-        MaterialHelper.PrepareDefinesForDeferred(scene, defines);
-
         // Attribs
         // Attribs
         if (MaterialHelper.PrepareDefinesForAttributes(mesh, defines, false, true, false)) {
         if (MaterialHelper.PrepareDefinesForAttributes(mesh, defines, false, true, false)) {
             if (mesh) {
             if (mesh) {

+ 7 - 4
src/Materials/PBR/pbrBaseMaterial.ts

@@ -675,8 +675,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         this.markAsDirty(Constants.MATERIAL_TextureDirtyFlag);
         this.markAsDirty(Constants.MATERIAL_TextureDirtyFlag);
     }
     }
 
 
+    /**
+     * Should this material render to several textures at once
+     */
+    public forceRenderToMRT: boolean = false;
     public get shouldRenderToMRT() {
     public get shouldRenderToMRT() {
-        return true;
+        return this.subSurface.isScatteringEnabled || this.forceRenderToMRT;
     }
     }
 
 
     /**
     /**
@@ -1300,6 +1304,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             onError: onError,
             onError: onError,
             indexParameters: { maxSimultaneousLights: this._maxSimultaneousLights, maxSimultaneousMorphTargets: defines.NUM_MORPH_INFLUENCERS },
             indexParameters: { maxSimultaneousLights: this._maxSimultaneousLights, maxSimultaneousMorphTargets: defines.NUM_MORPH_INFLUENCERS },
             processFinalCode: csnrOptions.processFinalCode,
             processFinalCode: csnrOptions.processFinalCode,
+            multiTarget: this.shouldRenderToMRT
         }, engine);
         }, engine);
     }
     }
 
 
@@ -1315,9 +1320,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         MaterialHelper.PrepareDefinesForMultiview(scene, defines);
         MaterialHelper.PrepareDefinesForMultiview(scene, defines);
 
 
         // Deferred
         // Deferred
-        if (this.shouldRenderToMRT) {
-            MaterialHelper.PrepareDefinesForDeferred(scene, defines);
-        }
+        MaterialHelper.PrepareDefinesForDeferred(scene, defines, this.shouldRenderToMRT);
 
 
         // Textures
         // Textures
         defines.METALLICWORKFLOW = this.isMetallicWorkflow();
         defines.METALLICWORKFLOW = this.isMetallicWorkflow();

+ 18 - 1
src/Materials/PBR/pbrSubSurfaceConfiguration.ts

@@ -43,6 +43,12 @@ export interface IMaterialSubSurfaceDefines {
     _areTexturesDirty: boolean;
     _areTexturesDirty: boolean;
 }
 }
 
 
+export enum SubsurfaceDiffusionProfile {
+    NEUTRAL = 0,
+    SKIN = 1,
+    FOLIAGE = 2,
+};
+
 /**
 /**
  * Define the code related to the sub surface parameters of the pbr material.
  * Define the code related to the sub surface parameters of the pbr material.
  */
  */
@@ -72,6 +78,12 @@ export class PBRSubSurfaceConfiguration {
     public isScatteringEnabled = false;
     public isScatteringEnabled = false;
 
 
     /**
     /**
+     * Diffusion profile for subsurface scattering.
+     * Useful for better scattering in the skins or foliages.
+     */
+    public scatteringDiffusionProfile = SubsurfaceDiffusionProfile.NEUTRAL;
+
+    /**
      * Defines the refraction intensity of the material.
      * Defines the refraction intensity of the material.
      * The refraction when enabled replaces the Diffuse part of the material.
      * The refraction when enabled replaces the Diffuse part of the material.
      * The intensity helps transitionning between diffuse and refraction.
      * The intensity helps transitionning between diffuse and refraction.
@@ -369,6 +381,10 @@ export class PBRSubSurfaceConfiguration {
                 }
                 }
             }
             }
 
 
+
+            if (this.isScatteringEnabled) {
+                uniformBuffer.updateFloat("scatteringDiffusionProfile", this.scatteringDiffusionProfile);
+            }
             uniformBuffer.updateColor3("vDiffusionDistance", this.diffusionDistance);
             uniformBuffer.updateColor3("vDiffusionDistance", this.diffusionDistance);
 
 
             uniformBuffer.updateFloat4("vTintColor", this.tintColor.r,
             uniformBuffer.updateFloat4("vTintColor", this.tintColor.r,
@@ -554,7 +570,7 @@ export class PBRSubSurfaceConfiguration {
             "vDiffusionDistance", "vTintColor", "vSubSurfaceIntensity",
             "vDiffusionDistance", "vTintColor", "vSubSurfaceIntensity",
             "vRefractionMicrosurfaceInfos", "vRefractionFilteringInfo",
             "vRefractionMicrosurfaceInfos", "vRefractionFilteringInfo",
             "vRefractionInfos", "vThicknessInfos", "vThicknessParam",
             "vRefractionInfos", "vThicknessInfos", "vThicknessParam",
-            "refractionMatrix", "thicknessMatrix");
+            "refractionMatrix", "thicknessMatrix", "scatteringDiffusionProfile");
     }
     }
 
 
     /**
     /**
@@ -581,6 +597,7 @@ export class PBRSubSurfaceConfiguration {
         uniformBuffer.addUniform("vDiffusionDistance", 3);
         uniformBuffer.addUniform("vDiffusionDistance", 3);
         uniformBuffer.addUniform("vTintColor", 4);
         uniformBuffer.addUniform("vTintColor", 4);
         uniformBuffer.addUniform("vSubSurfaceIntensity", 3);
         uniformBuffer.addUniform("vSubSurfaceIntensity", 3);
+        uniformBuffer.addUniform("scatteringDiffusionProfile", 1);
     }
     }
 
 
     /**
     /**

+ 7 - 0
src/Materials/effect.ts

@@ -69,6 +69,10 @@ export interface IEffectCreationOptions {
      * If provided, will be called two times with the vertex and fragment code so that this code can be updated before it is compiled by the GPU
      * If provided, will be called two times with the vertex and fragment code so that this code can be updated before it is compiled by the GPU
      */
      */
     processFinalCode?: Nullable<(shaderType: string, code: string) => string>;
     processFinalCode?: Nullable<(shaderType: string, code: string) => string>;
+    /**
+     * Is this effect rendering to several color attachments ?
+     */
+    multiTarget?: boolean;
 }
 }
 
 
 /**
 /**
@@ -139,6 +143,8 @@ export class Effect implements IDisposable {
 
 
     /** @hidden */
     /** @hidden */
     public _bonesComputationForcedToCPU = false;
     public _bonesComputationForcedToCPU = false;
+    /** @hidden */
+    public _multiTarget: boolean = false;
 
 
     private static _uniqueIdSeed = 0;
     private static _uniqueIdSeed = 0;
     private _engine: Engine;
     private _engine: Engine;
@@ -208,6 +214,7 @@ export class Effect implements IDisposable {
             this._fallbacks = options.fallbacks;
             this._fallbacks = options.fallbacks;
             this._indexParameters = options.indexParameters;
             this._indexParameters = options.indexParameters;
             this._transformFeedbackVaryings = options.transformFeedbackVaryings || null;
             this._transformFeedbackVaryings = options.transformFeedbackVaryings || null;
+            this._multiTarget = !!options.multiTarget;
 
 
             if (options.uniformBuffersNames) {
             if (options.uniformBuffersNames) {
                 this._uniformBuffersNamesList = options.uniformBuffersNames.slice();
                 this._uniformBuffersNamesList = options.uniformBuffersNames.slice();

+ 2 - 2
src/Materials/materialHelper.ts

@@ -300,10 +300,10 @@ export class MaterialHelper {
      * @param scene The scene we are intending to draw
      * @param scene The scene we are intending to draw
      * @param defines The defines to update
      * @param defines The defines to update
      */
      */
-    public static PrepareDefinesForDeferred(scene: Scene, defines: any) {
+    public static PrepareDefinesForDeferred(scene: Scene, defines: any, shouldRenderToMRT: boolean) {
         var previousDeferred = defines.HIGH_DEFINITION_PIPELINE;
         var previousDeferred = defines.HIGH_DEFINITION_PIPELINE;
 
 
-        if (scene.highDefinitionPipeline) {
+        if (scene.highDefinitionPipeline && shouldRenderToMRT) {
             defines.HIGH_DEFINITION_PIPELINE = true;
             defines.HIGH_DEFINITION_PIPELINE = true;
             defines.SCENE_MRT_COUNT = scene.mrtCount;
             defines.SCENE_MRT_COUNT = scene.mrtCount;
         } else {
         } else {

+ 0 - 5
src/Materials/standardMaterial.ts

@@ -825,11 +825,6 @@ export class StandardMaterial extends PushMaterial {
         // Multiview
         // Multiview
         MaterialHelper.PrepareDefinesForMultiview(scene, defines);
         MaterialHelper.PrepareDefinesForMultiview(scene, defines);
 
 
-        // Deferred
-        if (this.shouldRenderToMRT) {
-            MaterialHelper.PrepareDefinesForDeferred(scene, defines);
-        }
-
         // Textures
         // Textures
         if (defines._areTexturesDirty) {
         if (defines._areTexturesDirty) {
             defines._needUVs = false;
             defines._needUVs = false;

+ 3 - 3
src/Meshes/mesh.ts

@@ -1798,9 +1798,6 @@ export class Mesh extends AbstractMesh implements IGetSetVerticesData {
             return this;
             return this;
         }
         }
 
 
-        // Render to MRT
-        scene.drawBuffers(material);
-
         // Material
         // Material
         if (!instanceDataStorage.isFrozen || !this._effectiveMaterial || this._effectiveMaterial !== material) {
         if (!instanceDataStorage.isFrozen || !this._effectiveMaterial || this._effectiveMaterial !== material) {
             if (material._storeEffectOnSubMeshes) {
             if (material._storeEffectOnSubMeshes) {
@@ -1834,6 +1831,9 @@ export class Mesh extends AbstractMesh implements IGetSetVerticesData {
             return this;
             return this;
         }
         }
 
 
+        // Render to MRT
+        scene.drawBuffers(effect);
+
         const effectiveMesh = effectiveMeshReplacement || this._effectiveMesh;
         const effectiveMesh = effectiveMeshReplacement || this._effectiveMesh;
 
 
         var sideOrientation: Nullable<number>;
         var sideOrientation: Nullable<number>;

+ 4 - 8
src/PostProcesses/SubSurfaceScatteringPostProcess.ts

@@ -20,11 +20,8 @@ export class SubSurfaceScatteringPostProcess extends PostProcess {
     /** @hidden */
     /** @hidden */
     public texelHeight: number;
     public texelHeight: number;
 
 
-    private _diffusionColor: Color3 = new Color3(0.7568628, 0.32156864, 0.20000002);
-    private _filterRadius: number;
-
     constructor(name: string, scene: Scene, options: number | PostProcessOptions, camera: Nullable<Camera> = null, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT) {
     constructor(name: string, scene: Scene, options: number | PostProcessOptions, camera: Nullable<Camera> = null, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT) {
-        super(name, "subSurfaceScattering", ["texelSize", "filterRadius", "viewportSize"], ["inputSampler", "irradianceSampler", "depthSampler", "albedoSampler"], options, camera, samplingMode || Texture.BILINEAR_SAMPLINGMODE, engine, reusable, null, textureType, "postprocess", undefined, true);
+        super(name, "subSurfaceScattering", ["texelSize", "viewportSize", "metersPerUnit"], ["inputSampler", "irradianceSampler", "depthSampler", "albedoSampler"], options, camera, samplingMode || Texture.BILINEAR_SAMPLINGMODE, engine, reusable, null, textureType, "postprocess", undefined, true);
         this._scene = scene;
         this._scene = scene;
 
 
         const defines = this._getDefines();
         const defines = this._getDefines();
@@ -32,18 +29,17 @@ export class SubSurfaceScatteringPostProcess extends PostProcess {
 
 
         this.onApplyObservable.add((effect: Effect) => {
         this.onApplyObservable.add((effect: Effect) => {
             var texelSize = this.texelSize;
             var texelSize = this.texelSize;
+            effect.setFloat("metersPerUnit", scene.metersPerUnit);
             effect.setFloat2("texelSize", texelSize.x, texelSize.y);
             effect.setFloat2("texelSize", texelSize.x, texelSize.y);
             effect.setTexture("inputSampler", scene.highDefinitionMRT.textures[4]);
             effect.setTexture("inputSampler", scene.highDefinitionMRT.textures[4]);
             effect.setTexture("irradianceSampler", scene.highDefinitionMRT.textures[1]);
             effect.setTexture("irradianceSampler", scene.highDefinitionMRT.textures[1]);
             effect.setTexture("depthSampler", scene.highDefinitionMRT.textures[2]);
             effect.setTexture("depthSampler", scene.highDefinitionMRT.textures[2]);
             effect.setTexture("albedoSampler", scene.highDefinitionMRT.textures[3]);
             effect.setTexture("albedoSampler", scene.highDefinitionMRT.textures[3]);
-            effect.setFloat("filterRadius", this._filterRadius);
             effect.setFloat2("viewportSize",
             effect.setFloat2("viewportSize",
                 Math.tan(scene.activeCamera!.fov / 2) * scene.getEngine().getAspectRatio(scene.activeCamera!, true),
                 Math.tan(scene.activeCamera!.fov / 2) * scene.getEngine().getAspectRatio(scene.activeCamera!, true),
                 Math.tan(scene.activeCamera!.fov / 2));
                 Math.tan(scene.activeCamera!.fov / 2));
         });
         });
 
 
-        this._filterRadius = this._getDiffusionProfileParameters();
     }
     }
 
 
     private _getDefines(): Nullable<string> {
     private _getDefines(): Nullable<string> {
@@ -62,7 +58,7 @@ export class SubSurfaceScatteringPostProcess extends PostProcess {
         return defines;
         return defines;
     }
     }
 
 
-    private _getDiffusionProfileParameters()
+    public getDiffusionProfileParameters(color: Color3)
     {
     {
         const cdf = 0.997;
         const cdf = 0.997;
         // Importance sample the normalized diffuse reflectance profile for the computed value of 's'.
         // Importance sample the normalized diffuse reflectance profile for the computed value of 's'.
@@ -72,7 +68,7 @@ export class SubSurfaceScatteringPostProcess extends PostProcess {
         // CDF[r, s]      = 1 - 1/4 * Exp[-r * s] - 3/4 * Exp[-r * s / 3]
         // CDF[r, s]      = 1 - 1/4 * Exp[-r * s] - 3/4 * Exp[-r * s / 3]
         // ------------------------------------------------------------------------------------
         // ------------------------------------------------------------------------------------
         // We importance sample the color channel with the widest scattering distance.
         // We importance sample the color channel with the widest scattering distance.
-        const maxScatteringDistance = Math.max(this._diffusionColor.r, this._diffusionColor.g, this._diffusionColor.b);
+        const maxScatteringDistance = Math.max(color.r, color.g, color.b);
 
 
         return this._sampleBurleyDiffusionProfile(cdf, maxScatteringDistance);
         return this._sampleBurleyDiffusionProfile(cdf, maxScatteringDistance);
     }
     }

+ 12 - 0
src/Shaders/ShadersInclude/diffusionProfile.fx

@@ -0,0 +1,12 @@
+struct DiffusionProfile
+{
+  vec3 S;
+  float d;
+  float filterRadius;
+};
+
+DiffusionProfile diffusionProfiles[3] = DiffusionProfile[3](
+	DiffusionProfile(vec3(1., 1., 1.), 1., 16.564398753373407), // neutral
+	DiffusionProfile(vec3(0.7568628, 0.32156864, 0.20000002), 0.7568628, 12.536977220794705), // skin
+	DiffusionProfile(vec3(0.7568628, 0.7019608, 0.24313727), 0.7568628, 12.536977220794705) // foliage
+);

+ 12 - 1
src/Shaders/ShadersInclude/helperFunctions.fx

@@ -1,11 +1,11 @@
 const float PI = 3.1415926535897932384626433832795;
 const float PI = 3.1415926535897932384626433832795;
+const float HALF_MIN = 5.96046448e-08; // Smallest positive half.
 
 
 const float LinearEncodePowerApprox = 2.2;
 const float LinearEncodePowerApprox = 2.2;
 const float GammaEncodePowerApprox = 1.0 / LinearEncodePowerApprox;
 const float GammaEncodePowerApprox = 1.0 / LinearEncodePowerApprox;
 const vec3 LuminanceEncodeApprox = vec3(0.2126, 0.7152, 0.0722);
 const vec3 LuminanceEncodeApprox = vec3(0.2126, 0.7152, 0.0722);
 
 
 const float Epsilon = 0.0000001;
 const float Epsilon = 0.0000001;
-
 #define saturate(x)         clamp(x, 0.0, 1.0)
 #define saturate(x)         clamp(x, 0.0, 1.0)
 
 
 #define absEps(x)           abs(x) + Epsilon
 #define absEps(x)           abs(x) + Epsilon
@@ -88,6 +88,17 @@ float getLuminance(vec3 color)
     return clamp(dot(color, LuminanceEncodeApprox), 0., 1.);
     return clamp(dot(color, LuminanceEncodeApprox), 0., 1.);
 }
 }
 
 
+vec3 tagLightingForSSS(vec3 color) {
+    color.b = max(color.b, HALF_MIN);
+
+    return color;
+}
+
+bool testLightingForSSS(vec3 color)
+{
+    return color.b > 0.;
+}
+
 // https://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl
 // https://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl
 float getRand(vec2 seed) {
 float getRand(vec2 seed) {
     return fract(sin(dot(seed.xy ,vec2(12.9898,78.233))) * 43758.5453);
     return fract(sin(dot(seed.xy ,vec2(12.9898,78.233))) * 43758.5453);

+ 4 - 0
src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx

@@ -134,4 +134,8 @@ uniform mat4 view;
     uniform vec3 vDiffusionDistance;
     uniform vec3 vDiffusionDistance;
     uniform vec4 vTintColor;
     uniform vec4 vTintColor;
     uniform vec3 vSubSurfaceIntensity;
     uniform vec3 vSubSurfaceIntensity;
+
+    #ifdef SS_SCATTERING
+        uniform float scatteringDiffusionProfile;
+    #endif
 #endif
 #endif

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

@@ -69,6 +69,7 @@ uniform Material
     uniform vec3 vDiffusionDistance;
     uniform vec3 vDiffusionDistance;
     uniform vec4 vTintColor;
     uniform vec4 vTintColor;
     uniform vec3 vSubSurfaceIntensity;
     uniform vec3 vSubSurfaceIntensity;
+    uniform float scatteringDiffusionProfile;
 
 
     uniform vec4 vDetailInfos;
     uniform vec4 vDetailInfos;
     uniform mat4 detailMatrix;
     uniform mat4 detailMatrix;

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

@@ -506,51 +506,16 @@ void main(void) {
         #endif
         #endif
     #endif
     #endif
 
 
-    // BJS
-    // vec3 finalDiffuse = diffuseBase; // shadows, lightmap and stuff
-    // finalDiffuse *= surfaceAlbedo.rgb;
-    // finalDiffuse = max(finalDiffuse, 0.0);
-    // finalDiffuse *= vLightingIntensity.x;
-    // finalDiffuse *= ambientOcclusionForDirectDiffuse;
-    
-    // #ifdef REFLECTION
-    //     vec3 finalIrradiance = reflectionOut.environmentIrradiance;
-
-    //     #if defined(CLEARCOAT)
-    //         finalIrradiance *= clearcoatOut.conservationFactor;
-    //         #if defined(CLEARCOAT_TINT)
-    //             finalIrradiance *= clearcoatOut.absorption;
-    //         #endif
-    //     #endif
-
-    //     #if defined(SS_REFRACTION)
-    //         finalIrradiance *= subSurfaceOut.refractionFactorForIrradiance;
-    //     #endif
-
-    //     #if defined(SS_TRANSLUCENCY)
-    //         finalIrradiance += subSurfaceOut.refractionIrradiance;
-    //     #endif
-
-    //     finalIrradiance *= surfaceAlbedo.rgb;
-    //     finalIrradiance *= vLightingIntensity.z;
-    //     finalIrradiance *= aoOut.ambientOcclusionColor;
-    // #endif 
-
-    // UNITY
-    // float3 modifiedDiffuseColor = GetModifiedDiffuseColorForSSS(bsdfData);
-    // diffuseLighting = modifiedDiffuseColor * lighting.direct.diffuse + builtinData.bakeDiffuseLighting + builtinData.emissiveColor;
-    // diffuseLighting = lerp(diffuseLighting, lighting.indirect.specularTransmitted, bsdfData.transmittanceMask * _EnableSSRefraction);
-
-    // finalDiffuse and finalIrradiance are already multiplied by surfaceAlbedo
-    // What about :
-    // Lightmaps ? (can we consider them as pure diffuse ?)
-    // AO and shadows, should they dim the diffuseLight ? (right now they are)
     vec3 sqAlbedo = sqrt(surfaceAlbedo); // for pre and post scatter
     vec3 sqAlbedo = sqrt(surfaceAlbedo); // for pre and post scatter
 
 
     // Irradiance is diffuse * surfaceAlbedo
     // Irradiance is diffuse * surfaceAlbedo
-    gl_FragData[0] = vec4(finalColor.rgb - irradiance, 1.0); // Lit without irradiance
+    gl_FragData[0] = vec4(finalColor.rgb - irradiance, finalColor.a); // Lit without irradiance
     irradiance /= sqAlbedo;
     irradiance /= sqAlbedo;
-    gl_FragData[1] = vec4(irradiance, 1.0); // irradiance, for pre and post scatter
+    #ifdef SS_SCATTERING
+    gl_FragData[1] = vec4(tagLightingForSSS(irradiance), scatteringDiffusionProfile / 255.); // Irradiance + SS diffusion profile
+    #else
+    gl_FragData[1] = vec4(irradiance, 1.0); // Irradiance
+    #endif
     gl_FragData[2] = vec4(vViewPos.z, 0.0, 0.0, 1.0); // Linear depth
     gl_FragData[2] = vec4(vViewPos.z, 0.0, 0.0, 1.0); // Linear depth
     gl_FragData[3] = vec4(sqAlbedo, 1.0); // albedo, for pre and post scatter
     gl_FragData[3] = vec4(sqAlbedo, 1.0); // albedo, for pre and post scatter
 #endif
 #endif

+ 11 - 36
src/Shaders/subSurfaceScattering.fragment.fx

@@ -1,6 +1,7 @@
 // Samplers
 // Samplers
 #include<fibonacci>
 #include<fibonacci>
 #include<helperFunctions>
 #include<helperFunctions>
+#include<diffusionProfile>
 
 
 varying vec2 vUV;
 varying vec2 vUV;
 uniform vec2 texelSize;
 uniform vec2 texelSize;
@@ -9,8 +10,8 @@ uniform sampler2D irradianceSampler;
 uniform sampler2D depthSampler;
 uniform sampler2D depthSampler;
 uniform sampler2D albedoSampler;
 uniform sampler2D albedoSampler;
 
 
-uniform float filterRadius;
 uniform vec2 viewportSize;
 uniform vec2 viewportSize;
+uniform float metersPerUnit;
 
 
 const float LOG2_E = 1.4426950408889634;
 const float LOG2_E = 1.4426950408889634;
 const float SSS_PIXELS_PER_SAMPLE = 4.;
 const float SSS_PIXELS_PER_SAMPLE = 4.;
@@ -82,11 +83,6 @@ vec3 ComputeBilateralWeight(float xy2, float z, float mmPerUnit, vec3 S, float r
     #endif
     #endif
 }
 }
 
 
-bool IsSSSMaterial(vec3 irradiance) {
-    return irradiance.b > 0.;
-}
-
-// TODO : inout vec3 totalIrradiance, inout vec3 totalWeight
 void EvaluateSample(int i, int n, vec3 S, float d, vec3 centerPosVS, float mmPerUnit, float pixelsPerMm,
 void EvaluateSample(int i, int n, vec3 S, float d, vec3 centerPosVS, float mmPerUnit, float pixelsPerMm,
                     float phase, inout vec3 totalIrradiance, inout vec3 totalWeight)
                     float phase, inout vec3 totalIrradiance, inout vec3 totalWeight)
 {
 {
@@ -117,12 +113,6 @@ void EvaluateSample(int i, int n, vec3 S, float d, vec3 centerPosVS, float mmPer
     vec2 position; 
     vec2 position; 
     float xy2;
     float xy2;
 
 
-    // TODO : TANGENT PLANE
-    // floor((vUV + 0.5) + vec * pixelsPerMm)
-    // position = vUV + floor(0.5 + vec * pixelsPerMm);
-    // position = vUV + round(vec * pixelsPerMm);
-    // Note that (int) truncates towards 0, while floor() truncates towards -Inf!
-
     position = vUV + round((pixelsPerMm * r) * vec2(cosPsi, sinPsi)) * texelSize;
     position = vUV + round((pixelsPerMm * r) * vec2(cosPsi, sinPsi)) * texelSize;
     xy2      = r * r;
     xy2      = r * r;
 
 
@@ -130,16 +120,12 @@ void EvaluateSample(int i, int n, vec3 S, float d, vec3 centerPosVS, float mmPer
     float viewZ = texture2D(depthSampler, position).r;
     float viewZ = texture2D(depthSampler, position).r;
     vec3 irradiance    = textureSample.rgb;
     vec3 irradiance    = textureSample.rgb;
 
 
-    // Check the results of the stencil test.
-    // TODO
-    if (IsSSSMaterial(irradiance))
+    if (testLightingForSSS(irradiance))
     {
     {
         // Apply bilateral weighting.
         // Apply bilateral weighting.
         float relZ = viewZ - centerPosVS.z;
         float relZ = viewZ - centerPosVS.z;
         vec3 weight = ComputeBilateralWeight(xy2, relZ, mmPerUnit, S, rcpPdf);
         vec3 weight = ComputeBilateralWeight(xy2, relZ, mmPerUnit, S, rcpPdf);
 
 
-        // Note: if the texture sample if off-screen, (z = 0) -> (viewZ = far) -> (weight ≈ 0).
-        // TODO : HANDLE OFFSCREN
         totalIrradiance += weight * irradiance;
         totalIrradiance += weight * irradiance;
         totalWeight     += weight;
         totalWeight     += weight;
     }
     }
@@ -156,10 +142,13 @@ void EvaluateSample(int i, int n, vec3 S, float d, vec3 centerPosVS, float mmPer
 
 
 void main(void) 
 void main(void) 
 {
 {
-	vec3 centerIrradiance  = texture2D(irradianceSampler, vUV).rgb;
+	vec4 irradianceAndDiffusionProfile  = texture2D(irradianceSampler, vUV);
+    vec3 centerIrradiance = irradianceAndDiffusionProfile.rgb;
+    int diffusionProfileIndex = int(irradianceAndDiffusionProfile.a * 255.);
+
 	float  centerDepth       = 0.;
 	float  centerDepth       = 0.;
     vec4 inputColor = texture2D(textureSampler, vUV);
     vec4 inputColor = texture2D(textureSampler, vUV);
-	bool passedStencilTest = IsSSSMaterial(centerIrradiance);
+	bool passedStencilTest = testLightingForSSS(centerIrradiance);
 
 
 	if (passedStencilTest)
 	if (passedStencilTest)
 	{
 	{
@@ -171,24 +160,10 @@ void main(void)
         return;
         return;
     }
     }
 
 
-
-    // SKIN DIFFUSION PROFILE
-    // profile:
-    //   name: Skin
-    //   scatteringDistance: {r: 0.7568628, g: 0.32156864, b: 0.20000002, a: 1}
-    //   transmissionTint: {r: 0.7568628, g: 0.32156864, b: 0.20000002, a: 1}
-    //   texturingMode: 0
-    //   transmissionMode: 0
-    //   thicknessRemap: {x: 0, y: 8.152544}
-    //   worldScale: 1
-    //   ior: 1.4
-    //   hash: 1075477546
-
-    // TODO : uniforms
 	float  distScale     = 1.; //sssData.subsurfaceMask;
 	float  distScale     = 1.; //sssData.subsurfaceMask;
-	vec3 S             = vec3(0.7568628, 0.32156864, 0.20000002); //_ShapeParamsAndMaxScatterDists[profileIndex].rgb diffusion color
-	float  d             = 0.7568628; //_ShapeParamsAndMaxScatterDists[profileIndex].a max scatter dist
-	float  metersPerUnit = 0.07; //_WorldScalesAndFilterRadiiAndThicknessRemaps[profileIndex].x;
+	vec3 S             = diffusionProfiles[diffusionProfileIndex].S;
+	float  d             = diffusionProfiles[diffusionProfileIndex].d;
+    float filterRadius = diffusionProfiles[diffusionProfileIndex].filterRadius;
 
 
 	// Reconstruct the view-space position corresponding to the central sample.
 	// Reconstruct the view-space position corresponding to the central sample.
 	vec2 centerPosNDC = vUV;
 	vec2 centerPosNDC = vUV;

+ 8 - 2
src/scene.ts

@@ -177,6 +177,12 @@ export class Scene extends AbstractScene implements IAnimatable {
     public ambientColor = new Color3(0, 0, 0);
     public ambientColor = new Color3(0, 0, 0);
 
 
     /**
     /**
+     * Defines the ratio real world => scene units.
+     * Used for subsurface scattering
+     */
+    public metersPerUnit: number = 1;
+
+    /**
      * This is use to store the default BRDF lookup for PBR materials in your scene.
      * This is use to store the default BRDF lookup for PBR materials in your scene.
      * It should only be one of the following (if not the default embedded one):
      * It should only be one of the following (if not the default embedded one):
      * * For uncorrelated BRDF (pbr.brdf.useEnergyConservation = false and pbr.brdf.useSmithVisibilityHeightCorrelated = false) : https://assets.babylonjs.com/environments/uncorrelatedBRDF.dds
      * * For uncorrelated BRDF (pbr.brdf.useEnergyConservation = false and pbr.brdf.useSmithVisibilityHeightCorrelated = false) : https://assets.babylonjs.com/environments/uncorrelatedBRDF.dds
@@ -3693,9 +3699,9 @@ export class Scene extends AbstractScene implements IAnimatable {
         this.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix(force));
         this.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix(force));
     }
     }
 
 
-    public drawBuffers(material: Material) {
+    public drawBuffers(effect: Effect) {
         if (this.highDefinitionPipeline) {
         if (this.highDefinitionPipeline) {
-            if (material.shouldRenderToMRT) {
+            if (effect._multiTarget) {
                 this._engine.renderToAttachments(this.multiRenderAttachments);
                 this._engine.renderToAttachments(this.multiRenderAttachments);
             } else {
             } else {
                 this._engine.renderToAttachments(this.defaultAttachments);      
                 this._engine.renderToAttachments(this.defaultAttachments);