Browse Source

Environment First Pass

Sebastien Vandenberghe 8 years ago
parent
commit
95e0d4b09c

+ 0 - 1
Tools/Gulp/config.json

@@ -738,7 +738,6 @@
                 "../../src/Tools/HDR/babylon.cubemapToSphericalPolynomial.js",
                 "../../src/Tools/HDR/babylon.panoramaToCubemap.js",
                 "../../src/Tools/HDR/babylon.hdr.js",
-                "../../src/Tools/HDR/babylon.pmremgenerator.js",
                 "../../src/Materials/Textures/babylon.hdrCubeTexture.js"
             ],
             "dependUpon" : [

File diff suppressed because it is too large
+ 1 - 1
materialsLibrary/src/legacyPBR/babylon.legacyPbrMaterial.js.include.fx


+ 0 - 1
materialsLibrary/src/legacyPBR/legacyPbr.fragment.fx

@@ -124,7 +124,6 @@ varying vec3 vDirectionW;
 	#include<legacyColorCurves>
 #endif
 
-#include<harmonicsFunctions>
 #include<legacyPbrLightFunctions>
 
 mat3 transposeMat3(mat3 inMatrix) {

+ 32 - 0
materialsLibrary/src/legacyPBR/legacyPbrFunctions.fx

@@ -187,4 +187,36 @@ vec3 toGammaSpace(vec3 color)
 
         return color;
     }
+#endif
+
+#ifdef USESPHERICALFROMREFLECTIONMAP
+    uniform vec3 vSphericalX;
+    uniform vec3 vSphericalY;
+    uniform vec3 vSphericalZ;
+    uniform vec3 vSphericalXX;
+    uniform vec3 vSphericalYY;
+    uniform vec3 vSphericalZZ;
+    uniform vec3 vSphericalXY;
+    uniform vec3 vSphericalYZ;
+    uniform vec3 vSphericalZX;
+
+    vec3 EnvironmentIrradiance(vec3 normal)
+    {
+        // Note: 'normal' is assumed to be normalised (or near normalised)
+        // This isn't as critical as it is with other calculations (e.g. specular highlight), but the result will be incorrect nonetheless.
+
+        // TODO: switch to optimal implementation
+        vec3 result =
+            vSphericalX * normal.x +
+            vSphericalY * normal.y +
+            vSphericalZ * normal.z +
+            vSphericalXX * normal.x * normal.x +
+            vSphericalYY * normal.y * normal.y +
+            vSphericalZZ * normal.z * normal.z +
+            vSphericalYZ * normal.y * normal.z +
+            vSphericalZX * normal.z * normal.x +
+            vSphericalXY * normal.x * normal.y;
+
+        return result.rgb;
+    }
 #endif

+ 130 - 86
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -33,6 +33,7 @@
         public MICROSURFACEAUTOMATIC = false;
         public LIGHTMAP = false;
         public USELIGHTMAPASSHADOWMAP = false;
+        public LOGARITHMICDEPTH = false;
         public REFLECTIONMAP_3D = false;
         public REFLECTIONMAP_SPHERICAL = false;
         public REFLECTIONMAP_PLANAR = false;
@@ -44,17 +45,19 @@
         public REFLECTIONMAP_EQUIRECTANGULAR_FIXED = false;
         public REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED = false;
         public INVERTCUBICMAP = false;
-        public LOGARITHMICDEPTH = false;
         public USESPHERICALFROMREFLECTIONMAP = false;
+        public REFLECTIONMAP_OPPOSITEZ = false;
+        public LODINREFLECTIONALPHA = false;
+        public GAMMAREFLECTION = false;
         public REFRACTION = false;
         public REFRACTIONMAP_3D = false;
+        public REFRACTIONMAP_OPPOSITEZ = false;
+        public LODINREFRACTIONALPHA = false;
+        public GAMMAREFRACTION = false;
         public LINKREFRACTIONTOTRANSPARENCY = false;
-        public REFRACTIONMAPINLINEARSPACE = false;
         public LODBASEDMICROSFURACE = false;
         public USEPHYSICALLIGHTFALLOFF = false;
         public RADIANCEOVERALPHA = false;
-        public USEPMREMREFLECTION = false;
-        public USEPMREMREFRACTION = false;
         public INVERTNORMALMAPX = false;
         public INVERTNORMALMAPY = false;
         public TWOSIDEDLIGHTING = false;
@@ -163,7 +166,7 @@
 
         protected _reflectionTexture: BaseTexture;
 
-        private _microsurfaceTextureLods: Vector2 = new Vector2(0.0, 0.0);
+        protected _refractionTexture: BaseTexture;
 
         protected _emissiveTexture: BaseTexture;
         
@@ -199,8 +202,6 @@
 
         protected _lightmapTexture: BaseTexture;
 
-        protected _refractionTexture: BaseTexture;
-
         protected _ambientColor = new Color3(0, 0, 0);
 
         /**
@@ -539,13 +540,16 @@
                         }
                     }
 
-                    var reflectionTexture = this._reflectionTexture || scene.environmentTexture;
+                    var reflectionTexture = this._getReflectionTexture();
                     if (reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
                         if (!reflectionTexture.isReadyOrNotBlocking()) {
                             return false;
                         }
                         
                         defines.REFLECTION = true;
+                        defines.GAMMAREFLECTION = reflectionTexture.gammaSpace;
+                        defines.REFLECTIONMAP_OPPOSITEZ = reflectionTexture.invertZ;
+                        defines.LODINREFLECTIONALPHA = reflectionTexture.lodLevelInAlpha;
 
                         if (reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE) {
                             defines.INVERTCUBICMAP = true;
@@ -581,14 +585,13 @@
                                 break;
                             case Texture.FIXED_EQUIRECTANGULAR_MIRRORED_MODE:
                                 defines.REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED = true;
-                                break;                                
+                                break;
                         }
 
-                        if (reflectionTexture instanceof HDRCubeTexture && (<HDRCubeTexture>reflectionTexture)) {
-                            defines.USESPHERICALFROMREFLECTIONMAP = true;
-
-                            if ((<HDRCubeTexture>reflectionTexture).isPMREM) {
-                                defines.USEPMREMREFLECTION = true;
+                        if (reflectionTexture.coordinatesMode !== BABYLON.Texture.SKYBOX_MODE) {
+                            var polynomials = reflectionTexture.getSphericalPolynomial();
+                            if (polynomials) {
+                                defines.USESPHERICALFROMREFLECTIONMAP = true;
                             }
                         }
                     }
@@ -679,31 +682,27 @@
                         defines.USERIGHTHANDEDSYSTEM = scene.useRightHandedSystem;
                     }
 
-                    if (this._refractionTexture && StandardMaterial.RefractionTextureEnabled) {
-                        if (!this._refractionTexture.isReadyOrNotBlocking()) {
+                    var refractionTexture = this._getReractionTexture();
+                    if (refractionTexture && StandardMaterial.RefractionTextureEnabled) {
+                        if (!refractionTexture.isReadyOrNotBlocking()) {
                             return false;
                         }
                         
                         defines._needUVs = true;
                         defines.REFRACTION = true;
-                        defines.REFRACTIONMAP_3D = this._refractionTexture.isCube;
+                        defines.REFRACTIONMAP_3D = refractionTexture.isCube;
+                        defines.GAMMAREFRACTION = refractionTexture.gammaSpace;
+                        defines.REFRACTIONMAP_OPPOSITEZ = reflectionTexture.invertZ;
+                        defines.LODINREFRACTIONALPHA = reflectionTexture.lodLevelInAlpha;
 
                         if (this._linkRefractionWithTransparency) {
                             defines.LINKREFRACTIONTOTRANSPARENCY = true;
                         }
-                        if (this._refractionTexture instanceof HDRCubeTexture) {
-                            defines.REFRACTIONMAPINLINEARSPACE = true;
-
-                            if ((<HDRCubeTexture>this._refractionTexture).isPMREM) {
-                                defines.USEPMREMREFRACTION = true;
-                            }
-                        }
                     }
 
                     if (this._shouldUseAlphaFromAlbedoTexture()) {
                         defines.ALPHAFROMALBEDO = true;
                     }
-
                 }
 
                 if (this._useSpecularOverAlpha) {
@@ -848,10 +847,14 @@
                         "vSphericalX", "vSphericalY", "vSphericalZ",
                         "vSphericalXX", "vSphericalYY", "vSphericalZZ",
                         "vSphericalXY", "vSphericalYZ", "vSphericalZX",
-                        "vMicrosurfaceTextureLods"
+                        "vReflectionMicrosurfaceInfos", "vRefractionMicrosurfaceInfos"
                 ];
 
-                var samplers = ["albedoSampler", "ambientSampler", "opacitySampler", "reflectionCubeSampler", "reflection2DSampler", "emissiveSampler", "reflectivitySampler", "microSurfaceSampler", "bumpSampler", "lightmapSampler", "refractionCubeSampler", "refraction2DSampler"];
+                var samplers = ["albedoSampler", "reflectivitySampler", "ambientSampler", "emissiveSampler", 
+                    "bumpSampler", "lightmapSampler", "opacitySampler",
+                    "refractionSampler", "refractionSamplerLow", "refractionSamplerHigh",
+                    "reflectionSampler", "reflectionSamplerLow", "reflectionSamplerHigh",
+                    "microSurfaceSampler", "brdfSampler"];
                 var uniformBuffers = ["Material", "Scene"];
 
                 ImageProcessingConfiguration.PrepareUniforms(uniforms, defines);
@@ -927,7 +930,8 @@
             this._uniformBuffer.addUniform("vAlbedoColor", 4);
             this._uniformBuffer.addUniform("vLightingIntensity", 4);
 
-            this._uniformBuffer.addUniform("vMicrosurfaceTextureLods", 2);
+            this._uniformBuffer.addUniform("vRefractionMicrosurfaceInfos", 3);
+            this._uniformBuffer.addUniform("vReflectionMicrosurfaceInfos", 3);
             this._uniformBuffer.addUniform("vReflectivityColor", 4);
             this._uniformBuffer.addUniform("vEmissiveColor", 3);
 
@@ -938,11 +942,11 @@
 
         public unbind(): void {
             if (this._reflectionTexture && this._reflectionTexture.isRenderTarget) {
-                this._uniformBuffer.setTexture("reflection2DSampler", null);
+                this._uniformBuffer.setTexture("reflectionSampler", null);
             }
 
             if (this._refractionTexture && this._refractionTexture.isRenderTarget) {
-                this._uniformBuffer.setTexture("refraction2DSampler", null);
+                this._uniformBuffer.setTexture("refractionSampler", null);
             }
 
             super.unbind();
@@ -963,7 +967,7 @@
             var effect = subMesh.effect;
             this._activeEffect = effect;
 
-            // Matrices        
+            // Matrices
             this.bindOnlyWorldMatrix(world);
 
             // Bones
@@ -976,7 +980,7 @@
 
                 if (!this._uniformBuffer.useUbo || !this.isFrozen || !this._uniformBuffer.isSync) {
 
-                    // Texture uniforms      
+                    // Texture uniforms
                     if (scene.texturesEnabled) {
                         if (this._albedoTexture && StandardMaterial.DiffuseTextureEnabled) {
                             this._uniformBuffer.updateFloat2("vAlbedoInfos", this._albedoTexture.coordinatesIndex, this._albedoTexture.level);
@@ -993,41 +997,32 @@
                             this._uniformBuffer.updateMatrix("opacityMatrix", this._opacityTexture.getTextureMatrix());
                         }
 
-                        var reflectionTexture = this._reflectionTexture || scene.environmentTexture;;
+                        var reflectionTexture = this._getReflectionTexture();
                         if (reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
-                            this._microsurfaceTextureLods.x = Math.round(Math.log(reflectionTexture.getSize().width) * Math.LOG2E);
                             this._uniformBuffer.updateMatrix("reflectionMatrix", reflectionTexture.getReflectionTextureMatrix());
                             this._uniformBuffer.updateFloat2("vReflectionInfos", reflectionTexture.level, 0);
 
                             if (defines.USESPHERICALFROMREFLECTIONMAP) {
-                                this._activeEffect.setFloat3("vSphericalX", (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.x.x,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.x.y,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.x.z);
-                                this._activeEffect.setFloat3("vSphericalY", (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.y.x,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.y.y,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.y.z);
-                                this._activeEffect.setFloat3("vSphericalZ", (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.z.x,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.z.y,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.z.z);
-                                this._activeEffect.setFloat3("vSphericalXX", (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.xx.x,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.xx.y,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.xx.z);
-                                this._activeEffect.setFloat3("vSphericalYY", (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.yy.x,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.yy.y,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.yy.z);
-                                this._activeEffect.setFloat3("vSphericalZZ", (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.zz.x,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.zz.y,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.zz.z);
-                                this._activeEffect.setFloat3("vSphericalXY", (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.xy.x,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.xy.y,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.xy.z);
-                                this._activeEffect.setFloat3("vSphericalYZ", (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.yz.x,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.yz.y,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.yz.z);
-                                this._activeEffect.setFloat3("vSphericalZX", (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.zx.x,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.zx.y,
-                                    (<HDRCubeTexture>reflectionTexture).sphericalPolynomial.zx.z);
+                                var polynomials = reflectionTexture.getSphericalPolynomial();
+                                this._activeEffect.setFloat3("vSphericalX", polynomials.x.x, polynomials.x.y, polynomials.x.z);
+                                this._activeEffect.setFloat3("vSphericalY", polynomials.y.x, polynomials.y.y, polynomials.y.z);
+                                this._activeEffect.setFloat3("vSphericalZ", polynomials.z.x, polynomials.z.y, polynomials.z.z);
+                                this._activeEffect.setFloat3("vSphericalXX_ZZ", polynomials.xx.x - polynomials.zz.x,
+                                    polynomials.xx.y - polynomials.zz.y,
+                                    polynomials.xx.z - polynomials.zz.z);
+                                this._activeEffect.setFloat3("vSphericalYY_ZZ", polynomials.yy.x - polynomials.zz.x,
+                                    polynomials.yy.y - polynomials.zz.y,
+                                    polynomials.yy.z - polynomials.zz.z);
+                                this._activeEffect.setFloat3("vSphericalZZ", polynomials.zz.x, polynomials.zz.y, polynomials.zz.z);
+                                this._activeEffect.setFloat3("vSphericalXY", polynomials.xy.x, polynomials.xy.y, polynomials.xy.z);
+                                this._activeEffect.setFloat3("vSphericalYZ", polynomials.yz.x, polynomials.yz.y, polynomials.yz.z);
+                                this._activeEffect.setFloat3("vSphericalZX", polynomials.zx.x, polynomials.zx.y, polynomials.zx.z);
                             }
+
+                            this._uniformBuffer.updateFloat3("vReflectionMicrosurfaceInfos", 
+                                reflectionTexture.getSize().width, 
+                                reflectionTexture.lodGenerationScale,
+                                reflectionTexture.lodGenerationOffset);
                         }
 
                         if (this._emissiveTexture && StandardMaterial.EmissiveTextureEnabled) {
@@ -1061,22 +1056,21 @@
                             this._uniformBuffer.updateMatrix("bumpMatrix", this._bumpTexture.getTextureMatrix());
                         }
 
-                        if (this._refractionTexture && StandardMaterial.RefractionTextureEnabled) {
-                            this._microsurfaceTextureLods.y = Math.round(Math.log(this._refractionTexture.getSize().width) * Math.LOG2E);
-
+                        var refractionTexture = this._getReractionTexture();
+                        if (refractionTexture && StandardMaterial.RefractionTextureEnabled) {
                             var depth = 1.0;
-                            if (!this._refractionTexture.isCube) {
-                                this._uniformBuffer.updateMatrix("refractionMatrix", this._refractionTexture.getReflectionTextureMatrix());
+                            if (!refractionTexture.isCube) {
+                                this._uniformBuffer.updateMatrix("refractionMatrix", refractionTexture.getReflectionTextureMatrix());
 
-                                if ((<any>this._refractionTexture).depth) {
-                                    depth = (<any>this._refractionTexture).depth;
+                                if ((<any>refractionTexture).depth) {
+                                    depth = (<any>refractionTexture).depth;
                                 }
                             }
-                            this._uniformBuffer.updateFloat4("vRefractionInfos", this._refractionTexture.level, this._indexOfRefraction, depth, this._invertRefractionY ? -1 : 1);
-                        }
-
-                        if ((reflectionTexture || this._refractionTexture)) {
-                            this._uniformBuffer.updateFloat2("vMicrosurfaceTextureLods", this._microsurfaceTextureLods.x, this._microsurfaceTextureLods.y);
+                            this._uniformBuffer.updateFloat4("vRefractionInfos", refractionTexture.level, this._indexOfRefraction, depth, this._invertRefractionY ? -1 : 1);
+                            this._uniformBuffer.updateFloat3("vRefractionMicrosurfaceInfos", 
+                                refractionTexture.getSize().width, 
+                                refractionTexture.lodGenerationScale,
+                                refractionTexture.lodGenerationOffset);
                         }
                     }
 
@@ -1109,7 +1103,7 @@
                     this._uniformBuffer.updateVector4("vLightingIntensity", this._lightingInfos);
                 }
 
-                // Textures        
+                // Textures
                 if (scene.texturesEnabled) {
                     if (this._albedoTexture && StandardMaterial.DiffuseTextureEnabled) {
                         this._uniformBuffer.setTexture("albedoSampler", this._albedoTexture);
@@ -1124,10 +1118,48 @@
                     }
 
                     if (reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
-                        if (reflectionTexture.isCube) {
-                            this._uniformBuffer.setTexture("reflectionCubeSampler", reflectionTexture);
-                        } else {
-                            this._uniformBuffer.setTexture("reflection2DSampler", reflectionTexture);
+                        if (defines.LODBASEDMICROSFURACE) {
+                            this._uniformBuffer.setTexture("reflectionSampler", reflectionTexture);
+                        }
+                        else {
+                            this._uniformBuffer.setTexture("reflectionSampler", reflectionTexture);
+                            this._uniformBuffer.setTexture("reflectionSamplerLow", reflectionTexture);
+                            this._uniformBuffer.setTexture("reflectionSamplerHigh", reflectionTexture);
+
+                            const mipSlices = 3;
+                            const reflectionTextureWidth = reflectionTexture.getSize().width;
+                            for (let i = 0; i < mipSlices; i++) {
+                                //initialize lod texture if it doesn't already exist
+                                if (lod == null && reflectionTextureWidth) {
+                                    //compute LOD from even spacing in smoothness (matching shader calculation)
+                                    let smoothness = i / (mipSlices - 1);
+                                    let roughness = 1 - smoothness;
+                                    const kMinimumVariance = 0.0005;
+                                    let alphaG = roughness * roughness + kMinimumVariance;
+                                    let microsurfaceAverageSlopeTexels = alphaG * reflectionTextureWidth;
+
+                                    let environmentSpecularLOD = reflectionTexture.lodGenerationScale * ( MathTools.Log2(microsurfaceAverageSlopeTexels)) + reflectionTexture.lodGenerationOffset;
+
+                                    let maxLODIndex = MathTools.Log2(reflectionTextureWidth);
+                                    let mipmapIndex = Math.min(Math.max(Math.round(environmentSpecularLOD), 0), maxLODIndex);
+
+
+                                    lod = TextureUtils.GetBabylonCubeTexture(this._engineScene, new TextureCube(PixelFormat.RGBA, PixelType.UNSIGNED_BYTE, [environmentSpecular.source[mipmapIndex]]), Scene._EnvironmentSingleMipSampling, false);
+                                    (<any>environmentSpecular)[lodKey] = lod;
+                                }
+                                
+                            }
+                        }
+                    }
+
+                    if (refractionTexture && StandardMaterial.RefractionTextureEnabled) {
+                        if (defines.LODBASEDMICROSFURACE) {
+                            this._uniformBuffer.setTexture("refractionSampler", refractionTexture);
+                        }
+                        else {
+                            this._uniformBuffer.setTexture("refractionSampler", refractionTexture);
+                            this._uniformBuffer.setTexture("refractionSamplerLow", refractionTexture);
+                            this._uniformBuffer.setTexture("refractionSamplerHigh", refractionTexture);
                         }
                     }
 
@@ -1155,14 +1187,6 @@
                     if (this._bumpTexture && scene.getEngine().getCaps().standardDerivatives && StandardMaterial.BumpTextureEnabled && !this._disableBumpMap) {
                         this._uniformBuffer.setTexture("bumpSampler", this._bumpTexture);
                     }
-
-                    if (this._refractionTexture && StandardMaterial.RefractionTextureEnabled) {
-                        if (this._refractionTexture.isCube) {
-                            this._uniformBuffer.setTexture("refractionCubeSampler", this._refractionTexture);
-                        } else {
-                            this._uniformBuffer.setTexture("refraction2DSampler", this._refractionTexture);
-                        }
-                    }
                 }
 
                 // Clip plane
@@ -1253,6 +1277,26 @@
             return results;
         }
 
+        private _getReflectionTexture(): BaseTexture {
+            if (this._reflectionTexture) {
+                return this._reflectionTexture;
+            }
+
+            return this.getScene().environmentTexture;
+        }
+
+        private _getReractionTexture(): BaseTexture {
+            if (this._refractionTexture) {
+                return this._refractionTexture;
+            }
+
+            if (this._linkRefractionWithTransparency) {
+                return this.getScene().environmentTexture;
+            }
+
+            return null;
+        }
+
         public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
             if (forceDisposeTextures) {
                 if (this._albedoTexture) {

+ 28 - 0
src/Materials/Textures/babylon.baseTexture.ts

@@ -53,6 +53,21 @@
         public isCube = false;
 
         @serialize()
+        public gammaSpace = true;
+
+        @serialize()
+        public invertZ = false;
+
+        @serialize()
+        public lodLevelInAlpha = false;
+
+        @serialize()
+        public lodGenerationOffset = 1.0;
+
+        @serialize()
+        public lodGenerationScale = 0.8;
+
+        @serialize()
         public isRenderTarget = false;
 
         public get uid(): string {
@@ -220,6 +235,19 @@
             }
         }
 
+        public getSphericalPolynomial(): SphericalPolynomial {
+            if (!this._texture || !Internals.CubeMapToSphericalPolynomialTools || !this.isReady()) {
+                return null;
+            }
+
+            if (!this._texture._sphericalPolynomial) {
+                this._texture._sphericalPolynomial = 
+                    Internals.CubeMapToSphericalPolynomialTools.ConvertCubeMapTextureToSphericalPolynomial(this);
+            }
+
+            return this._texture._sphericalPolynomial;
+        }
+
         public dispose(): void {
             // Animations
             this.getScene().stopAnimation(this);

+ 4 - 0
src/Materials/Textures/babylon.cubeTexture.ts

@@ -82,6 +82,10 @@
             return this._textureMatrix;
         }
 
+        public setReflectionTextureMatrix(value: Matrix): void {
+            this._textureMatrix = value;
+        }
+
         public static Parse(parsedTexture: any, scene: Scene, rootUrl: string): CubeTexture {
             var texture = SerializationHelper.Parse(() => {
                 return new BABYLON.CubeTexture(rootUrl + parsedTexture.name, scene, parsedTexture.extensions);

+ 26 - 19
src/Materials/Textures/babylon.hdrCubeTexture.ts

@@ -88,6 +88,7 @@ module BABYLON {
             this._textureMatrix = Matrix.Identity();
             this._onLoad = onLoad;
             this._onError = onError;
+            this.gammaSpace = false;
 
             if (size) {
                 this._isBABYLONPreprocessed = false;
@@ -326,26 +327,28 @@ module BABYLON {
             }
 
             var mipmapGenerator = null;
-            if (!this._noMipmap &&
-                this._usePMREMGenerator) {
-                mipmapGenerator = (data: ArrayBufferView[]) => {
-                    // Custom setup of the generator matching with the PBR shader values.
-                    var generator = new BABYLON.Internals.PMREMGenerator(data,
-                        this._size,
-                        this._size,
-                        0,
-                        3,
-                        this.getScene().getEngine().getCaps().textureFloat,
-                        2048,
-                        0.25,
-                        false,
-                        true);
-
-                    return generator.filterCubeMap();
-                };
-            }
 
-            this._texture = (<any>this.getScene().getEngine()).createRawCubeTextureFromUrl(this.url, this.getScene(), this._size,
+            // TODO. Implement In code PMREM Generator following the LYS toolset generation.
+            // if (!this._noMipmap &&
+            //     this._usePMREMGenerator) {
+            //     mipmapGenerator = (data: ArrayBufferView[]) => {
+            //         // Custom setup of the generator matching with the PBR shader values.
+            //         var generator = new BABYLON.Internals.PMREMGenerator(data,
+            //             this._size,
+            //             this._size,
+            //             0,
+            //             3,
+            //             this.getScene().getEngine().getCaps().textureFloat,
+            //             2048,
+            //             0.25,
+            //             false,
+            //             true);
+
+            //         return generator.filterCubeMap();
+            //     };
+            // }
+
+            this._texture = this.getScene().getEngine().createRawCubeTextureFromUrl(this.url, this.getScene(), this._size,
                 Engine.TEXTUREFORMAT_RGB,
                 this.getScene().getEngine().getCaps().textureFloat ? BABYLON.Engine.TEXTURETYPE_FLOAT : BABYLON.Engine.TEXTURETYPE_UNSIGNED_INT,
                 this._noMipmap,
@@ -398,6 +401,10 @@ module BABYLON {
             return this._textureMatrix;
         }
 
+        public setReflectionTextureMatrix(value: Matrix): void {
+            this._textureMatrix = value;
+        }
+
         public static Parse(parsedTexture: any, scene: Scene, rootUrl: string): HDRCubeTexture {
             var texture = null;
             if (parsedTexture.name && !parsedTexture.isRenderTarget) {

+ 80 - 0
src/Math/babylon.math.ts

@@ -50,6 +50,13 @@
         public static Clamp(value: number, min = 0, max = 1): number {
             return Math.min(max, Math.max(min, value));
         }
+
+        /**
+         * Returns the log2 of value.
+         */
+        public static Log2(value: number): number {
+            return Math.log(value) * Math.LOG2E;
+        }
     }
 
 
@@ -5103,6 +5110,64 @@
             this.L21 = this.L21.scale(scale);
             this.L22 = this.L22.scale(scale);
         }
+
+        public convertIncidentRadianceToIrradiance(): void
+        {
+            // Convert from incident radiance (Li) to irradiance (E) by applying convolution with the cosine-weighted hemisphere.
+            //
+            //      E_lm = A_l * L_lm
+            // 
+            // In spherical harmonics this convolution amounts to scaling factors for each frequency band.
+            // This corresponds to equation 5 in "An Efficient Representation for Irradiance Environment Maps", where
+            // the scaling factors are given in equation 9.
+
+            // Constant (Band 0)
+            this.L00 = this.L00.scale(3.141593);
+
+            // Linear (Band 1)
+            this.L1_1 = this.L1_1.scale(2.094395);
+            this.L10 = this.L10.scale(2.094395);
+            this.L11 = this.L11.scale(2.094395);
+
+            // Quadratic (Band 2)
+            this.L2_2 = this.L2_2.scale(0.785398);
+            this.L2_1 = this.L2_1.scale(0.785398);
+            this.L20 = this.L20.scale(0.785398);
+            this.L21 = this.L21.scale(0.785398);
+            this.L22 = this.L22.scale(0.785398);
+        }
+
+        public convertIrradianceToLambertianRadiance(): void
+        {
+            // Convert from irradiance to outgoing radiance for Lambertian BDRF, suitable for efficient shader evaluation.
+            //      L = (1/pi) * E * rho
+            // 
+            // This is done by an additional scale by 1/pi, so is a fairly trivial operation but important conceptually.
+
+            this.scale(1.0 / Math.PI);
+
+            // The resultant SH now represents outgoing radiance, so includes the Lambert 1/pi normalisation factor but without albedo (rho) applied
+            // (The pixel shader must apply albedo after texture fetches, etc).
+        }
+
+        public static getsphericalHarmonicsFromPolynomial(polynomial: SphericalPolynomial): SphericalHarmonics
+        {
+            var result = new SphericalHarmonics();
+
+            result.L00 = polynomial.xx.scale(0.376127).add(polynomial.yy.scale(0.376127)).add(polynomial.zz.scale(0.376126));
+            result.L1_1 = polynomial.y.scale(0.977204);
+            result.L10 = polynomial.z.scale(0.977204);
+            result.L11 = polynomial.x.scale(0.977204);
+            result.L2_2 = polynomial.xy.scale(1.16538);
+            result.L2_1 = polynomial.yz.scale(1.16538);
+            result.L20 = polynomial.zz.scale(1.34567).subtract(polynomial.xx.scale(0.672834)).subtract(polynomial.yy.scale(0.672834));
+            result.L21 = polynomial.zx.scale(1.16538);
+            result.L22 = polynomial.xx.scale(1.16538).subtract(polynomial.yy.scale(1.16538));
+
+            result.scale(Math.PI);
+
+            return result;
+        }
     }
 
     // SphericalPolynomial
@@ -5139,8 +5204,23 @@
             result.zx = harmonics.L21.scale(0.858086);
             result.xy = harmonics.L2_2.scale(0.858086);
 
+            result.scale(1.0 / Math.PI);
+
             return result;
         }
+
+        public scale(scale: number)
+        {
+            this.x = this.x.scale(scale);
+            this.y = this.y.scale(scale);
+            this.z = this.z.scale(scale);
+            this.xx = this.xx.scale(scale);
+            this.yy = this.yy.scale(scale);
+            this.zz = this.zz.scale(scale);
+            this.yz = this.yz.scale(scale);
+            this.zx = this.zx.scale(scale);
+            this.xy = this.xy.scale(scale);
+        }
     }
 
     // Vertex formats

+ 42 - 17
src/Shaders/ShadersInclude/harmonicsFunctions.fx

@@ -2,30 +2,55 @@
     uniform vec3 vSphericalX;
     uniform vec3 vSphericalY;
     uniform vec3 vSphericalZ;
-    uniform vec3 vSphericalXX;
-    uniform vec3 vSphericalYY;
+    uniform vec3 vSphericalXX_ZZ;
+    uniform vec3 vSphericalYY_ZZ;
     uniform vec3 vSphericalZZ;
     uniform vec3 vSphericalXY;
     uniform vec3 vSphericalYZ;
     uniform vec3 vSphericalZX;
 
-    vec3 EnvironmentIrradiance(vec3 normal)
+    vec3 quaternionVectorRotation_ScaledSqrtTwo(vec4 Q, vec3 V){
+        vec3 T = cross(Q.xyz, V);
+        T += Q.www * V;
+        return cross(Q.xyz, T) + V;
+    }
+
+    vec3 environmentIrradianceJones(vec3 normal)
     {
-        // Note: 'normal' is assumed to be normalised (or near normalised)
-        // This isn't as critical as it is with other calculations (e.g. specular highlight), but the result will be incorrect nonetheless.
+        // Fast method for evaluating a fixed spherical harmonics function on the sphere (e.g. irradiance or radiance).
+        // Cost: 24 scalar operations on modern GPU "scalar" shader core, or 8 multiply-adds of 3D vectors:
+        // "Function Cost 24	24x mad"
+
+        // Note: the lower operation count compared to other methods (e.g. Sloan) is by further
+        // taking advantage of the input 'normal' being normalised, which affords some further algebraic simplification.
+        // Namely, the SH coefficients are first converted to spherical polynomial (SP) basis, then 
+        // a substitution is performed using Z^2 = (1 - X^2 - Y^2).
+
+        // As with other methods for evaluation spherical harmonic, the input 'normal' is assumed to be normalised (or near normalised).
+        // This isn't as critical as it is with other calculations (e.g. specular highlight), but the result will be slightly incorrect nonetheless.
+        float Nx = normal.x;
+        float Ny = normal.y;
+        float Nz = normal.z;
+
+        vec3 C1 = vSphericalZZ.rgb;
+        vec3 Cx = vSphericalX.rgb;
+        vec3 Cy = vSphericalY.rgb;
+        vec3 Cz = vSphericalZ.rgb;
+        vec3 Cxx_zz = vSphericalXX_ZZ.rgb;
+        vec3 Cyy_zz = vSphericalYY_ZZ.rgb;
+        vec3 Cxy = vSphericalXY.rgb;
+        vec3 Cyz = vSphericalYZ.rgb;
+        vec3 Czx = vSphericalZX.rgb;
 
-        // TODO: switch to optimal implementation
-        vec3 result =
-            vSphericalX * normal.x +
-            vSphericalY * normal.y +
-            vSphericalZ * normal.z +
-            vSphericalXX * normal.x * normal.x +
-            vSphericalYY * normal.y * normal.y +
-            vSphericalZZ * normal.z * normal.z +
-            vSphericalYZ * normal.y * normal.z +
-            vSphericalZX * normal.z * normal.x +
-            vSphericalXY * normal.x * normal.y;
+        vec3 a1 = Cyy_zz * Ny + Cy;
+        vec3 a2 = Cyz * Nz + a1;
+        vec3 b1 = Czx * Nz + Cx;
+        vec3 b2 = Cxy * Ny + b1;
+        vec3 b3 = Cxx_zz * Nx + b2;
+        vec3 t1 = Cz  * Nz + C1;
+        vec3 t2 = a2  * Ny + t1;
+        vec3 t3 = b3  * Nx + t2;
 
-        return result.rgb;
+        return t3;
     }
 #endif

+ 6 - 19
src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx

@@ -4,10 +4,6 @@ uniform vec4 vAlbedoColor;
 // CUSTOM CONTROLS
 uniform vec4 vLightingIntensity;
 
-#if defined(REFLECTION) || defined(REFRACTION)
-uniform vec2 vMicrosurfaceTextureLods;
-#endif
-
 uniform vec4 vReflectivityColor;
 uniform vec3 vEmissiveColor;
 
@@ -51,23 +47,14 @@ uniform mat4 view;
 
 // Refraction
 #ifdef REFRACTION
-uniform vec4 vRefractionInfos;
-
-#ifdef REFRACTIONMAP_3D
-#else
-uniform mat4 refractionMatrix;
-#endif
+    uniform vec4 vRefractionInfos;
+    uniform mat4 refractionMatrix;
+    uniform vec3 vRefractionMicrosurfaceInfos;
 #endif
 
 // Reflection
 #ifdef REFLECTION
-uniform vec2 vReflectionInfos;
-
-#ifdef REFLECTIONMAP_SKYBOX
-#else
-
-#if defined(REFLECTIONMAP_PLANAR) || defined(REFLECTIONMAP_CUBIC) || defined(REFLECTIONMAP_PROJECTION)
-uniform mat4 reflectionMatrix;
-#endif
-#endif
+    uniform vec2 vReflectionInfos;
+    uniform mat4 reflectionMatrix;
+    uniform vec3 vReflectionMicrosurfaceInfos;
 #endif

+ 20 - 24
src/Shaders/ShadersInclude/pbrFunctions.fx

@@ -14,30 +14,6 @@ float convertRoughnessToAverageSlope(float roughness)
     return alphaG;
 }
 
-// Based on Beckamm roughness to Blinn exponent + http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html 
-float getMipMapIndexFromAverageSlope(float maxMipLevel, float alpha)
-{
-    // do not take in account lower mips hence -1... and wait from proper preprocess.
-    // formula comes from approximation of the mathematical solution.
-    //float mip = maxMipLevel + kRougnhessToAlphaOffset + 0.5 * log2(alpha);
-    
-    // In the mean time 
-    // Always [0..1] goes from max mip to min mip in a log2 way.  
-    // Change 5 to nummip below.
-    // http://www.wolframalpha.com/input/?i=x+in+0..1+plot+(+5+%2B+0.3+%2B+0.1+*+5+*+log2(+(1+-+x)+*+(1+-+x)+%2B+0.0005))
-    float mip = kRougnhessToAlphaOffset + maxMipLevel + (maxMipLevel * kRougnhessToAlphaScale * log2(alpha));
-    
-    return clamp(mip, 0., maxMipLevel);
-}
-
-float getMipMapIndexFromAverageSlopeWithPMREM(float maxMipLevel, float alphaG)
-{
-    float specularPower = clamp(2. / alphaG - 2., 0.000001, 2048.);
-    
-    // Based on CubeMapGen for cosine power with 2048 spec default and 0.25 dropoff 
-    return clamp(- 0.5 * log2(specularPower) + 5.5, 0., maxMipLevel);
-}
-
 // From Microfacet Models for Refraction through Rough Surfaces, Walter et al. 2007
 float smithVisibilityG1_TrowbridgeReitzGGX(float dot, float alphaG)
 {
@@ -130,4 +106,24 @@ float computeDefaultMicroSurface(float microSurface, vec3 reflectivityColor)
 float fresnelGrazingReflectance(float reflectance0) {
 	float reflectance90 = clamp(reflectance0 * 25.0, 0.0, 1.0);
 	return reflectance90;
+}
+
+// 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
+
+float getLodFromAlphaG(float cubeMapDimensionPixels, float alphaG, float NdotV) {
+    float microsurfaceAverageSlope = alphaG;
+
+    // Compensate for solid angle change between half-vector measure (Blinn-Phong) and reflected-vector measure (Phong):
+    //  dWr = 4*cos(theta)*dWh,
+    // where dWr = solid angle (delta omega) in environment incident radiance (reflection-vector) measure;
+    // where dWh = solid angle (delta omega) in microfacet normal (half-vector) measure;
+    // so the relationship is proportional to cosine theta = NdotV.
+    // The constant factor of four is handled elsewhere as part of the scale/offset filter parameters.
+    microsurfaceAverageSlope *= sqrt(abs(NdotV));
+
+    float microsurfaceAverageSlopeTexels = microsurfaceAverageSlope * cubeMapDimensionPixels;
+    float lod = log2(microsurfaceAverageSlopeTexels);
+    return lod;
 }

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

@@ -27,7 +27,9 @@ uniform Material
 	uniform vec4 vAlbedoColor;
 	uniform vec4 vLightingIntensity;
 
-	uniform vec2 vMicrosurfaceTextureLods;
+    uniform vec3 vRefractionMicrosurfaceInfos;
+    uniform vec3 vReflectionMicrosurfaceInfos;
+
 	uniform vec4 vReflectivityColor;
 	uniform vec3 vEmissiveColor;
 

+ 169 - 112
src/Shaders/pbr.fragment.fx

@@ -22,7 +22,10 @@ uniform vec4 vCameraInfos;
 varying vec3 vPositionW;
 
 #ifdef NORMAL
-varying vec3 vNormalW;
+	varying vec3 vNormalW;
+	#ifdef USESPHERICALFROMREFLECTIONMAP
+		varying vec3 vEnvironmentIrradiance;
+	#endif
 #endif
 
 #ifdef VERTEXCOLOR
@@ -70,20 +73,55 @@ uniform sampler2D microSurfaceSampler;
 
 // Refraction
 #ifdef REFRACTION
+	#ifdef REFRACTIONMAP_3D
+		#define sampleRefraction textureCube
+		
+		uniform samplerCube refractionSampler;
 
-#ifdef REFRACTIONMAP_3D
-uniform samplerCube refractionCubeSampler;
-#else
-uniform sampler2D refraction2DSampler;
-#endif
+		#ifndef LODBASEDMICROSFURACE
+			#define sampleRefractionLod textureCubeLodEXT
+
+			uniform samplerCube refractionSamplerLow;
+			uniform samplerCube refractionSamplerHigh;
+		#endif		
+	#else
+		#define sampleRefraction texture2D
+		
+		uniform sampler2D refractionSampler;
+
+		#ifndef LODBASEDMICROSFURACE
+			#define sampleRefractionLod texture2DLodEXT
+
+			uniform samplerCube refractionSamplerLow;
+			uniform samplerCube refractionSamplerHigh;
+		#endif
+	#endif
 #endif
 
 // Reflection
 #ifdef REFLECTION
 	#ifdef REFLECTIONMAP_3D
-		uniform samplerCube reflectionCubeSampler;
+		#define sampleReflection textureCube
+
+		uniform samplerCube reflectionSampler;
+		
+		#ifndef LODBASEDMICROSFURACE
+			#define sampleReflectionLod textureCubeLodEXT
+
+			uniform samplerCube reflectionSamplerLow;
+			uniform samplerCube reflectionSamplerHigh;
+		#endif
 	#else
-		uniform sampler2D reflection2DSampler;
+		#define sampleReflection texture2D
+
+		uniform sampler2D reflectionSampler;
+
+		#ifndef LODBASEDMICROSFURACE
+			#define sampleReflectionLod texture2DLodEXT
+
+			uniform samplerCube reflectionSamplerLow;
+			uniform samplerCube reflectionSamplerHigh;
+		#endif
 	#endif
 
 	#ifdef REFLECTIONMAP_SKYBOX
@@ -92,7 +130,6 @@ uniform sampler2D refraction2DSampler;
 		#if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED)
 			varying vec3 vDirectionW;
 		#endif
-
 	#endif
 
 	#include<reflectionFunction>
@@ -305,145 +342,161 @@ void main(void) {
 #endif
 
 // _____________________________ Compute LODs Fetch ____________________________________
-#ifdef LODBASEDMICROSFURACE
+	// Compute N dot V.
+	float NdotV = clamp(dot(normalW, viewDirectionW),0., 1.) + 0.00001;
 	float alphaG = convertRoughnessToAverageSlope(roughness);
-#endif
 
 // _____________________________ Refraction Info _______________________________________
 #ifdef REFRACTION
-	vec3 surfaceRefractionColor = vec3(0., 0., 0.);
+	vec3 environmentRefraction = vec3(0., 0., 0.);
+	
 	vec3 refractionVector = refract(-viewDirectionW, normalW, vRefractionInfos.y);
-
-	#ifdef LODBASEDMICROSFURACE
-		#ifdef USEPMREMREFRACTION
-			float lodRefraction = getMipMapIndexFromAverageSlopeWithPMREM(vMicrosurfaceTextureLods.y, alphaG);
-		#else
-			float lodRefraction = getMipMapIndexFromAverageSlope(vMicrosurfaceTextureLods.y, alphaG);
-		#endif
-	#else
-		float biasRefraction = (vMicrosurfaceTextureLods.y + 2.) * (1.0 - microSurface);
+	#ifdef REFRACTIONMAP_OPPOSITEZ
+		refractionVector.z *= -1.0;
 	#endif
 
+	// _____________________________ 2D vs 3D Maps ________________________________
 	#ifdef REFRACTIONMAP_3D
 		refractionVector.y = refractionVector.y * vRefractionInfos.w;
-
-		if (dot(refractionVector, viewDirectionW) < 1.0)
-		{
-		#ifdef LODBASEDMICROSFURACE
-			#ifdef USEPMREMREFRACTION
-					// Empiric Threshold
-					if ((vMicrosurfaceTextureLods.y - lodRefraction) > 4.0)
-					{
-						// Bend to not reach edges.
-						float scaleRefraction = 1. - exp2(lodRefraction) / exp2(vMicrosurfaceTextureLods.y); // CubemapSize is the size of the base mipmap
-						float maxRefraction = max(max(abs(refractionVector.x), abs(refractionVector.y)), abs(refractionVector.z));
-						if (abs(refractionVector.x) != maxRefraction) refractionVector.x *= scaleRefraction;
-						if (abs(refractionVector.y) != maxRefraction) refractionVector.y *= scaleRefraction;
-						if (abs(refractionVector.z) != maxRefraction) refractionVector.z *= scaleRefraction;
-					}
-			#endif
-
-				surfaceRefractionColor = textureCubeLodEXT(refractionCubeSampler, refractionVector, lodRefraction).rgb * vRefractionInfos.x;
-		#else
-				surfaceRefractionColor = textureCube(refractionCubeSampler, refractionVector, biasRefraction).rgb * vRefractionInfos.x;
-		#endif
-		}
-
-		#ifndef REFRACTIONMAPINLINEARSPACE
-			surfaceRefractionColor = toLinearSpace(surfaceRefractionColor.rgb);
-		#endif
+		vec3 refractionCoords = refractionVector;
+		refractionCoords = vec3(refractionMatrix * vec4(refractionCoords, 0));
 	#else
 		vec3 vRefractionUVW = vec3(refractionMatrix * (view * vec4(vPositionW + refractionVector * vRefractionInfos.z, 1.0)));
-
 		vec2 refractionCoords = vRefractionUVW.xy / vRefractionUVW.z;
-
 		refractionCoords.y = 1.0 - refractionCoords.y;
-
-		#ifdef LODBASEDMICROSFURACE
-			surfaceRefractionColor = texture2DLodEXT(refraction2DSampler, refractionCoords, lodRefraction).rgb * vRefractionInfos.x;
-		#else
-			surfaceRefractionColor = texture2D(refraction2DSampler, refractionCoords, biasRefraction).rgb * vRefractionInfos.x;
-		#endif    
-
-		surfaceRefractionColor = toLinearSpace(surfaceRefractionColor.rgb);
 	#endif
-#endif
-
-// _____________________________ Reflection Info _______________________________________
-#ifdef REFLECTION
-	vec3 environmentRadiance = vReflectionColor.rgb;
-	vec3 environmentIrradiance = vReflectionColor.rgb;
-	vec3 vReflectionUVW = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
 
 	#ifdef LODBASEDMICROSFURACE
-		#ifdef USEPMREMREFLECTION
-			float lodReflection = getMipMapIndexFromAverageSlopeWithPMREM(vMicrosurfaceTextureLods.x, alphaG);
+		float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG, NdotV);
+		// Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
+		refractionLOD = refractionLOD * vRefractionMicrosurfaceInfos.y + vRefractionMicrosurfaceInfos.z;
+
+		#ifdef 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
+			// the hardware-selected LOD, and this is then used to constrain the final LOD selection
+			// so that excessive surface smoothness does not cause aliasing (e.g. on curved geometry 
+			// where the normal is varying rapidly).
+
+			// Note: Shader Model 4.1 or higher can provide this directly via CalculateLevelOfDetail(), and
+			// manual calculation via derivatives is also possible, but for simplicity we use the 
+			// hardware LOD calculation with the alpha channel containing the LOD for each mipmap.
+			float automaticRefractionLOD = UNPACK_LOD(sampleRefraction(refractionSampler, refractionCoords).a);
+			float requestedRefractionLOD = max(automaticRefractionLOD, refractionLOD);
 		#else
-			float lodReflection = getMipMapIndexFromAverageSlope(vMicrosurfaceTextureLods.x, alphaG);
+			float requestedRefractionLOD = refractionLOD;
 		#endif
+
+		environmentRefraction = sampleRefractionLod(refractionSampler, refractionCoords, requestedRefractionLOD).rgb;
 	#else
-		float biasReflection = (vMicrosurfaceTextureLods.x + 2.) * (1.0 - microSurface);
+		float u = microSurface * 2.0;
+		vec3 environmentSpecularMid = sampleRefraction(refractionSampler, reflectionCoords).rgb;
+		if(u < 1.0){
+			environmentRefraction = mix(
+				sampleRefraction(refractionSamplerLow, reflectionCoords).rgb,
+				environmentSpecularMid,
+				u
+			);
+		}else{
+			environmentRefraction = mix(
+				environmentSpecularMid,
+				sampleRefraction(refractionSamplerHigh, reflectionCoords).rgb,
+				u - 1.0
+			);
+		}
 	#endif
 
-	#ifdef REFLECTIONMAP_3D
+	#ifdef GAMMAREFRACTION
+		environmentRefraction = toLinearSpace(environmentRefraction.rgb);
+	#endif
 
-		#ifdef LODBASEDMICROSFURACE
-			#ifdef USEPMREMREFLECTION
-				// Empiric Threshold
-				if ((vMicrosurfaceTextureLods.y - lodReflection) > 4.0)
-				{
-					// Bend to not reach edges.
-					float scaleReflection = 1. - exp2(lodReflection) / exp2(vMicrosurfaceTextureLods.x); // CubemapSize is the size of the base mipmap
-					float maxReflection = max(max(abs(vReflectionUVW.x), abs(vReflectionUVW.y)), abs(vReflectionUVW.z));
-					if (abs(vReflectionUVW.x) != maxReflection) vReflectionUVW.x *= scaleReflection;
-					if (abs(vReflectionUVW.y) != maxReflection) vReflectionUVW.y *= scaleReflection;
-					if (abs(vReflectionUVW.z) != maxReflection) vReflectionUVW.z *= scaleReflection;
-				}
-			#endif
+	// _____________________________ Levels _____________________________________
+	environmentRefraction *= vRefractionInfos.x;
+#endif
 
-			environmentRadiance = textureCubeLodEXT(reflectionCubeSampler, vReflectionUVW, lodReflection).rgb * vReflectionInfos.x;
-		#else
-			environmentRadiance = textureCube(reflectionCubeSampler, vReflectionUVW, biasReflection).rgb * vReflectionInfos.x;
-		#endif
+// _____________________________ Reflection Info _______________________________________
+#ifdef REFLECTION
+	vec3 environmentRadiance = vec3(0., 0., 0.);
+	vec3 environmentIrradiance = vec3(0., 0., 0.);
 
-		#ifdef USESPHERICALFROMREFLECTIONMAP
-			#ifndef REFLECTIONMAP_SKYBOX
-				vec3 normalEnvironmentSpace = (reflectionMatrix * vec4(normalW, 1)).xyz;
-				environmentIrradiance = EnvironmentIrradiance(normalEnvironmentSpace);
-			#endif
-		#else
-			environmentRadiance = toLinearSpace(environmentRadiance.rgb);
+	vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
+	#ifdef REFLECTIONMAP_OPPOSITEZ
+		reflectionVector.z *= -1.0;
+	#endif
 
-			environmentIrradiance = textureCube(reflectionCubeSampler, normalW, 20.).rgb * vReflectionInfos.x;
-			environmentIrradiance = toLinearSpace(environmentIrradiance.rgb);
-			environmentIrradiance *= 0.2; // Hack in case of no hdr cube map use for environment.
-		#endif
+	// _____________________________ 2D vs 3D Maps ________________________________
+	#ifdef REFLECTIONMAP_3D
+		vec3 reflectionCoords = reflectionVector;
 	#else
-		vec2 coords = vReflectionUVW.xy;
-
+		vec2 reflectionCoords = reflectionVector.xy;
 		#ifdef REFLECTIONMAP_PROJECTION
-			coords /= vReflectionUVW.z;
+			reflectionCoords /= reflectionVector.z;
 		#endif
-
-		coords.y = 1.0 - coords.y;
-		#ifdef LODBASEDMICROSFURACE
-			environmentRadiance = texture2DLodEXT(reflection2DSampler, coords, lodReflection).rgb * vReflectionInfos.x;
+		reflectionCoords.y = 1.0 - reflectionCoords.y;
+	#endif
+	
+	#ifdef LODBASEDMICROSFURACE
+		float reflectionLOD = getLodFromAlphaG(vReflectionMicrosurfaceInfos.x, alphaG, NdotV);
+		// Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
+		reflectionLOD = reflectionLOD * vReflectionMicrosurfaceInfos.y + vReflectionMicrosurfaceInfos.z;
+
+		#ifdef LODINREFLECTIONALPHA
+			// 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
+			// the hardware-selected LOD, and this is then used to constrain the final LOD selection
+			// so that excessive surface smoothness does not cause aliasing (e.g. on curved geometry 
+			// where the normal is varying rapidly).
+
+			// Note: Shader Model 4.1 or higher can provide this directly via CalculateLevelOfDetail(), and
+			// manual calculation via derivatives is also possible, but for simplicity we use the 
+			// hardware LOD calculation with the alpha channel containing the LOD for each mipmap.
+			float automaticReflectionLOD = UNPACK_LOD(sampleReflection(reflectionSampler, reflectionCoords).a);
+			float requestedReflectionLOD = max(automaticReflectionLOD, reflectionLOD);
 		#else
-			environmentRadiance = texture2D(reflection2DSampler, coords, biasReflection).rgb * vReflectionInfos.x;
+			float requestedReflectionLOD = reflectionLOD;
 		#endif
 
+		environmentRadiance = sampleReflectionLod(reflectionSampler, reflectionCoords, requestedReflectionLOD).rgb;
+	#else
+		float u = microSurface * 2.0;
+		vec3 environmentSpecularMid = sampleReflection(reflectionSampler, reflectionCoords).rgb;
+		if(u < 1.0){
+			environmentRadiance = mix(
+				sampleReflection(reflectionSamplerLow, reflectionCoords).rgb,
+				environmentSpecularMid,
+				u
+			);
+		}else{
+			environmentRadiance = mix(
+				environmentSpecularMid,
+				sampleReflection(reflectionSamplerHigh, reflectionCoords).rgb,
+				u - 1.0
+			);
+		}
+	#endif
+
+	#ifdef GAMMAREFLECTION
 		environmentRadiance = toLinearSpace(environmentRadiance.rgb);
+	#endif
 
-		environmentIrradiance = texture2D(reflection2DSampler, coords, 20.).rgb * vReflectionInfos.x;
-		environmentIrradiance = toLinearSpace(environmentIrradiance.rgb);
+	// _____________________________ Irradiance ________________________________
+	#ifdef USESPHERICALFROMREFLECTIONMAP
+		#ifdef NORMAL
+			environmentIrradiance = vEnvironmentIrradiance;
+		#else
+			environmentIrradiance = environmentIrradianceJones(reflectionVector);
+		#endif
 	#endif
+
+	// _____________________________ Levels _____________________________________
+	environmentRadiance *= vReflectionColor.rgb * vReflectionInfos.x;
+	environmentIrradiance *= vReflectionColor.rgb;
 #endif
 
 // ____________________________________________________________________________________
 // _____________________________ Direct Lighting Param ________________________________
-	// Compute N dot V.
-	float NdotV = clamp(dot(normalW, viewDirectionW),0., 1.) + 0.00001;
-
 	// Compute reflectance.
 	float reflectance = max(max(surfaceReflectivityColor.r, surfaceReflectivityColor.g), surfaceReflectivityColor.b);
 	float reflectance90 = fresnelGrazingReflectance(reflectance);
@@ -490,7 +543,7 @@ void main(void) {
 		environmentIrradiance *= alpha;
 
 		// Tint reflectance
-		surfaceRefractionColor *= tint;
+		environmentRefraction *= tint;
 
 		// Put alpha back to 1;
 		alpha = 1.0;
@@ -540,7 +593,7 @@ void main(void) {
 
 // _____________________________ Refraction ______________________________________
 #ifdef REFRACTION
-	vec3 finalRefraction = surfaceRefractionColor;
+	vec3 finalRefraction = environmentRefraction;
 	finalRefraction *= refractance;
 #endif
 
@@ -641,6 +694,10 @@ void main(void) {
 	// Final Specular
 	// gl_FragColor = vec4(finalSpecular.rgb, 1.0);
 
+	// Irradiance
+	//gl_FragColor = vec4(environmentIrradiance.rgb, 1.0);
+	//gl_FragColor = vec4(environmentIrradiance.rgb / 3.0, 1.0);
+
 	// Specular color.
 	// gl_FragColor = vec4(surfaceReflectivityColor.rgb, 1.0);
 

+ 13 - 1
src/Shaders/pbr.vertex.fx

@@ -60,7 +60,10 @@ varying vec2 vBumpUV;
 // Output
 varying vec3 vPositionW;
 #ifdef NORMAL
-varying vec3 vNormalW;
+    varying vec3 vNormalW;
+    #ifdef USESPHERICALFROMREFLECTIONMAP
+        varying vec3 vEnvironmentIrradiance;
+    #endif
 #endif
 
 #ifdef VERTEXCOLOR
@@ -85,6 +88,8 @@ varying vec3 vDirectionW;
 
 #include<logDepthDeclaration>
 
+#include<harmonicsFunctions>
+
 void main(void) {
 	vec3 positionUpdated = position;
 #ifdef NORMAL
@@ -110,6 +115,13 @@ void main(void) {
 
 #ifdef NORMAL
     vNormalW = normalize(vec3(finalWorld * vec4(normalUpdated, 0.0)));
+    #ifdef USESPHERICALFROMREFLECTIONMAP
+        vec3 reflectionVector = vec3(reflectionMatrix * vec4(vNormalW, 0)).xyz;
+        #ifdef REFLECTIONMAP_OPPOSITEZ
+            reflectionVector.z *= -1.0;
+        #endif
+        vEnvironmentIrradiance = environmentIrradianceJones(reflectionVector);
+    #endif
 #endif
 
 #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED)

+ 48 - 5
src/Tools/HDR/babylon.cubemapToSphericalPolynomial.ts

@@ -30,6 +30,40 @@ module BABYLON.Internals {
         ];
 
         /**
+         * Converts a texture to the according Spherical Polynomial data. 
+         * This extracts the first 3 orders only as they are the only one used in the lighting.
+         * 
+         * @param texture The texture to extract the information from.
+         * @return The Spherical Polynomial data.
+         */
+        public static ConvertCubeMapTextureToSphericalPolynomial(texture: BaseTexture) {
+            if (!texture.isCube) {
+                // Only supports cube Textures currently.
+                return null;
+            }
+
+            var size = texture.getSize().width;
+            var right = texture.readPixels(0);
+            var left = texture.readPixels(1);
+            var up = texture.readPixels(2);
+            var down = texture.readPixels(3);
+            var front = texture.readPixels(4);
+            var back = texture.readPixels(5);
+
+            var cubeInfo: CubeMapInfo = {
+                size,
+                right,
+                left,
+                up,
+                down,
+                front,
+                back,
+            };
+
+            return this.ConvertCubeMapToSphericalPolynomial(cubeInfo);
+        }
+
+        /**
          * Converts a cubemap to the according Spherical Polynomial data. 
          * This extracts the first 3 orders only as they are the only one used in the lighting.
          * 
@@ -127,13 +161,22 @@ module BABYLON.Internals {
                 }
             }
 
-            var correctSolidAngle = 4.0 * Math.PI; // Solid angle for entire sphere is 4*pi
-            var correction = correctSolidAngle / totalSolidAngle;
+            // Solid angle for entire sphere is 4*pi
+            var sphereSolidAngle = 4.0 * Math.PI; 
+
+            // Adjust the solid angle to allow for how many faces we processed.
+            var facesProcessed = 6.0;
+            var expectedSolidAngle = sphereSolidAngle * facesProcessed / 6.0;
 
-            sphericalHarmonics.scale(correction);
+            // Adjust the harmonics so that the accumulated solid angle matches the expected solid angle. 
+            // This is needed because the numerical integration over the cube uses a 
+            // small angle approximation of solid angle for each texel (see deltaSolidAngle),
+            // and also to compensate for accumulative error due to float precision in the summation.
+            var correctionFactor = expectedSolidAngle / totalSolidAngle;
+            sphericalHarmonics.scale(correctionFactor);
 
-            // Additionally scale by pi -- audit needed
-            sphericalHarmonics.scale(1.0 / Math.PI);
+            sphericalHarmonics.convertIncidentRadianceToIrradiance();
+            sphericalHarmonics.convertIrradianceToLambertianRadiance();
 
             return SphericalPolynomial.getSphericalPolynomialFromHarmonics(sphericalHarmonics);
         }

File diff suppressed because it is too large
+ 0 - 1228
src/Tools/HDR/babylon.pmremgenerator.ts


+ 1 - 0
src/babylon.mixins.ts

@@ -121,6 +121,7 @@ interface WebGLTexture {
     _isDisabled: boolean;
     _generateStencilBuffer: boolean;
     _generateDepthBuffer: boolean;
+    _sphericalPolynomial: BABYLON.SphericalPolynomial;
 }
 
 interface WebGLBuffer {

+ 0 - 1
tests/Tools/Jasmine/chutzpah.json

@@ -198,7 +198,6 @@
         { "Path": "../../../src/Rendering/babylon.edgesRenderer.js" },
         { "Path": "../../../src/Tools/babylon.loadingScreen.js" },
         { "Path": "../../../src/Probes/babylon.reflectionProbe.js" },
-        { "Path": "../../../src/tools/hdr/babylon.tools.pmremGenerator.js" },
         { "Path": "../../../src/tools/hdr/babylon.tools.cubemapToSphericalPolynomial.js" },
         { "Path": "../../../src/tools/hdr/babylon.tools.panoramaToCubemap.js" },
         { "Path": "../../../src/tools/hdr/babylon.tools.hdr.js" },