瀏覽代碼

Merge pull request #2450 from sebavan/Environment

PBR Update, Environment.
David Catuhe 8 年之前
父節點
當前提交
2b8dfe3b0d

+ 1 - 1
Tools/Gulp/config.json

@@ -735,10 +735,10 @@
         "hdr" : 
         {
             "files": [
+                "../../src/Math/babylon.sphericalPolynomial.js",
                 "../../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" : [

+ 14 - 27
materialsLibrary/src/legacyPBR/babylon.legacyPBRMaterial.ts

@@ -1331,33 +1331,20 @@ module BABYLON {
                             this._uniformBuffer.updateFloat2("vReflectionInfos", this.reflectionTexture.level, 0);
 
                             if (this._defines.USESPHERICALFROMREFLECTIONMAP) {
-                                this._effect.setFloat3("vSphericalX", (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.x.x,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.x.y,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.x.z);
-                                this._effect.setFloat3("vSphericalY", (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.y.x,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.y.y,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.y.z);
-                                this._effect.setFloat3("vSphericalZ", (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.z.x,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.z.y,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.z.z);
-                                this._effect.setFloat3("vSphericalXX", (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.xx.x,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.xx.y,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.xx.z);
-                                this._effect.setFloat3("vSphericalYY", (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.yy.x,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.yy.y,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.yy.z);
-                                this._effect.setFloat3("vSphericalZZ", (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.zz.x,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.zz.y,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.zz.z);
-                                this._effect.setFloat3("vSphericalXY", (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.xy.x,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.xy.y,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.xy.z);
-                                this._effect.setFloat3("vSphericalYZ", (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.yz.x,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.yz.y,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.yz.z);
-                                this._effect.setFloat3("vSphericalZX", (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.zx.x,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.zx.y,
-                                    (<HDRCubeTexture>this.reflectionTexture).sphericalPolynomial.zx.z);
+                                var polynomials = this.reflectionTexture.getSphericalPolynomial();
+                                this._effect.setFloat3("vSphericalX", polynomials.x.x, polynomials.x.y, polynomials.x.z);
+                                this._effect.setFloat3("vSphericalY", polynomials.y.x, polynomials.y.y, polynomials.y.z);
+                                this._effect.setFloat3("vSphericalZ", polynomials.z.x, polynomials.z.y, polynomials.z.z);
+                                this._effect.setFloat3("vSphericalXX_ZZ", polynomials.xx.x - polynomials.zz.x,
+                                    polynomials.xx.y - polynomials.zz.y,
+                                    polynomials.xx.z - polynomials.zz.z);
+                                this._effect.setFloat3("vSphericalYY_ZZ", polynomials.yy.x - polynomials.zz.x,
+                                    polynomials.yy.y - polynomials.zz.y,
+                                    polynomials.yy.z - polynomials.zz.z);
+                                this._effect.setFloat3("vSphericalZZ", polynomials.zz.x, polynomials.zz.y, polynomials.zz.z);
+                                this._effect.setFloat3("vSphericalXY", polynomials.xy.x, polynomials.xy.y, polynomials.xy.z);
+                                this._effect.setFloat3("vSphericalYZ", polynomials.yz.x, polynomials.yz.y, polynomials.yz.z);
+                                this._effect.setFloat3("vSphericalZX", polynomials.zx.x, polynomials.zx.y, polynomials.zx.z);
                             }
                         }
 

文件差異過大導致無法顯示
+ 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

+ 134 - 85
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;
@@ -69,6 +72,7 @@
         public METALLNESSSTOREINMETALMAPBLUE = false;
         public AOSTOREINMETALMAPRED = false;
         public MICROSURFACEMAP = false;
+        public ENVIRONMENTBRDF = false;
 
         public MORPHTARGETS = false;
         public MORPHTARGETS_NORMAL = false;
@@ -163,7 +167,7 @@
 
         protected _reflectionTexture: BaseTexture;
 
-        private _microsurfaceTextureLods: Vector2 = new Vector2(0.0, 0.0);
+        protected _refractionTexture: BaseTexture;
 
         protected _emissiveTexture: BaseTexture;
         
@@ -199,8 +203,6 @@
 
         protected _lightmapTexture: BaseTexture;
 
-        protected _refractionTexture: BaseTexture;
-
         protected _ambientColor = new Color3(0, 0, 0);
 
         /**
@@ -213,7 +215,7 @@
          */
         protected _reflectivityColor = new Color3(1, 1, 1);
 
-        protected _reflectionColor = new Color3(0.0, 0.0, 0.0);
+        protected _reflectionColor = new Color3(1, 1, 1);
 
         protected _emissiveColor = new Color3(0, 0, 0);
         
@@ -363,6 +365,13 @@
         protected _useAlphaFresnel = false;
 
         /**
+         * Specifies the environment BRDF texture used to comput the scale and offset roughness values
+         * from cos thetav and roughness: 
+         * http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
+         */
+        protected _environmentBRDFTexture: BaseTexture = null;
+
+        /**
          * Default configuration related to image processing available in the PBR Material.
          */
         @serializeAsImageProcessingConfiguration()
@@ -433,6 +442,8 @@
 
                 return this._renderTargets;
             }
+
+            this._environmentBRDFTexture = TextureTools.GetEnvironmentBRDFTexture(scene);
         }
 
         public abstract getClassName(): string;
@@ -539,13 +550,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 +595,12 @@
                                 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) {
+                            if (reflectionTexture.sphericalPolynomial) {
+                                defines.USESPHERICALFROMREFLECTIONMAP = true;
                             }
                         }
                     }
@@ -679,31 +691,35 @@
                         defines.USERIGHTHANDEDSYSTEM = scene.useRightHandedSystem;
                     }
 
-                    if (this._refractionTexture && StandardMaterial.RefractionTextureEnabled) {
-                        if (!this._refractionTexture.isReadyOrNotBlocking()) {
+                    var refractionTexture = this._getRefractionTexture();
+                    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._environmentBRDFTexture && StandardMaterial.ReflectionTextureEnabled) {
+                        // This is blocking.
+                        if (!this._environmentBRDFTexture.isReady()) {
+                            return false;
                         }
+                        defines.ENVIRONMENTBRDF = true;
                     }
 
                     if (this._shouldUseAlphaFromAlbedoTexture()) {
                         defines.ALPHAFROMALBEDO = true;
                     }
-
                 }
 
                 if (this._useSpecularOverAlpha) {
@@ -763,6 +779,10 @@
 
                 // Fallbacks
                 var fallbacks = new EffectFallbacks();
+                if (defines.ENVIRONMENTBRDF) {
+                    fallbacks.addFallback(0, "ENVIRONMENTBRDF");
+                }
+
                 if (defines.REFLECTION) {
                     fallbacks.addFallback(0, "REFLECTION");
                 }
@@ -850,10 +870,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", "environmentBrdfSampler"];
                 var uniformBuffers = ["Material", "Scene"];
 
                 ImageProcessingConfiguration.PrepareUniforms(uniforms, defines);
@@ -929,7 +953,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);
 
@@ -940,11 +965,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();
@@ -965,7 +990,7 @@
             var effect = subMesh.effect;
             this._activeEffect = effect;
 
-            // Matrices        
+            // Matrices
             this.bindOnlyWorldMatrix(world);
 
             // Bones
@@ -978,7 +1003,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);
@@ -995,41 +1020,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.sphericalPolynomial;
+                                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) {
@@ -1063,22 +1079,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._getRefractionTexture();
+                        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);
                         }
                     }
 
@@ -1111,7 +1126,7 @@
                     this._uniformBuffer.updateVector4("vLightingIntensity", this._lightingInfos);
                 }
 
-                // Textures        
+                // Textures
                 if (scene.texturesEnabled) {
                     if (this._albedoTexture && StandardMaterial.DiffuseTextureEnabled) {
                         this._uniformBuffer.setTexture("albedoSampler", this._albedoTexture);
@@ -1126,10 +1141,28 @@
                     }
 
                     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._lodTextureMid || reflectionTexture);
+                            this._uniformBuffer.setTexture("reflectionSamplerLow", reflectionTexture._lodTextureLow || reflectionTexture);
+                            this._uniformBuffer.setTexture("reflectionSamplerHigh", reflectionTexture._lodTextureHigh || reflectionTexture);
+                        }
+                    }
+
+                    if (defines.ENVIRONMENTBRDF) {
+                        this._uniformBuffer.setTexture("environmentBrdfSampler", this._environmentBRDFTexture);
+                    }
+
+                    if (refractionTexture && StandardMaterial.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);
                         }
                     }
 
@@ -1157,14 +1190,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
@@ -1255,6 +1280,26 @@
             return results;
         }
 
+        private _getReflectionTexture(): BaseTexture {
+            if (this._reflectionTexture) {
+                return this._reflectionTexture;
+            }
+
+            return this.getScene().environmentTexture;
+        }
+
+        private _getRefractionTexture(): 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) {
@@ -1273,6 +1318,10 @@
                     this._reflectionTexture.dispose();
                 }
 
+                if (this._environmentBRDFTexture) {
+                    this._environmentBRDFTexture.dispose();
+                }
+
                 if (this._emissiveTexture) {
                     this._emissiveTexture.dispose();
                 }

+ 11 - 1
src/Materials/PBR/babylon.pbrMaterial.ts

@@ -182,7 +182,7 @@
 
         @serializeAsColor3("reflection")
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-        public reflectionColor = new Color3(0.0, 0.0, 0.0);
+        public reflectionColor = new Color3(1.0, 1.0, 1.0);
 
         @serializeAsColor3("emissive")
         @expandToProperty("_markAllSubMeshesAsTexturesDirty")
@@ -376,6 +376,14 @@
         public useAlphaFresnel = false;
 
         /**
+         * A fresnel is applied to the alpha of the model to ensure grazing angles edges are not alpha tested.
+         * And/Or occlude the blended part.
+         */
+        @serializeAsTexture()
+        @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+        public environmentBRDFTexture: BaseTexture = null;
+
+        /**
          * Gets the image processing configuration used either in this material.
          */
         public get imageProcessingConfiguration(): ImageProcessingConfiguration {
@@ -504,6 +512,8 @@
          */
         constructor(name: string, scene: Scene) {
             super(name, scene);
+
+            this._environmentBRDFTexture = TextureTools.GetEnvironmentBRDFTexture(scene);
         }
 
         public getClassName(): string {

+ 66 - 3
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 = 0.0;
+
+        @serialize()
+        public lodGenerationScale = 0.8;
+
+        @serialize()
         public isRenderTarget = false;
 
         public get uid(): string {
@@ -206,7 +221,15 @@
             return (this._texture.type !== undefined) ? this._texture.type : Engine.TEXTURETYPE_UNSIGNED_INT;
         }
 
-        public readPixels(faceIndex = 0, lodIndex = 0): ArrayBufferView {
+        public get textureFormat(): number {
+            if (!this._texture) {
+                return Engine.TEXTUREFORMAT_RGBA;
+            }
+
+            return (this._texture.format !== undefined) ? this._texture.format : Engine.TEXTUREFORMAT_RGBA;
+        }
+
+        public readPixels(faceIndex = 0): ArrayBufferView {
             if (!this._texture) {
                 return null;
             }
@@ -215,10 +238,10 @@
             var engine = this.getScene().getEngine();
 
             if (this._texture.isCube) {
-                return engine._readTexturePixels(this._texture, size.width, size.height, faceIndex, lodIndex);
+                return engine._readTexturePixels(this._texture, size.width, size.height, faceIndex);
             }
 
-            return engine._readTexturePixels(this._texture, size.width, size.height, -1, lodIndex);
+            return engine._readTexturePixels(this._texture, size.width, size.height, -1);
         }
 
         public releaseInternalTexture(): void {
@@ -228,6 +251,46 @@
             }
         }
 
+        public get sphericalPolynomial(): 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 set sphericalPolynomial(value: SphericalPolynomial) {
+            if (this._texture) {
+                this._texture._sphericalPolynomial = value;
+            }
+        }
+
+        public get _lodTextureHigh(): BaseTexture {
+            if (this._texture) {
+                return this._texture._lodTextureHigh;
+            }
+            return null;
+        }
+
+        public get _lodTextureMid(): BaseTexture {
+            if (this._texture) {
+                return this._texture._lodTextureMid;
+            }
+            return null;
+        }
+
+        public get _lodTextureLow(): BaseTexture {
+            if (this._texture) {
+                return this._texture._lodTextureLow;
+            }
+            return null;
+        }
+
         public dispose(): void {
             // Animations
             this.getScene().stopAnimation(this);

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

@@ -8,12 +8,17 @@
         private _extensions: string[];
         private _textureMatrix: Matrix;
         private _format: number;
+        private _prefiltered: boolean;
 
         public static CreateFromImages(files: string[], scene: Scene, noMipmap?: boolean) {
             return new CubeTexture("", scene, null, noMipmap, files);
         }
 
-        constructor(rootUrl: string, scene: Scene, extensions?: string[], noMipmap?: boolean, files?: string[], onLoad: () => void = null, onError: () => void = null, format: number = Engine.TEXTUREFORMAT_RGBA) {
+        public static CreateFromPrefilteredData(url: string, scene: Scene) {
+            return new CubeTexture(url, scene, null, false, null, null, null, undefined, true);
+        }
+
+        constructor(rootUrl: string, scene: Scene, extensions?: string[], noMipmap?: boolean, files?: string[], onLoad: () => void = null, onError: () => void = null, format: number = Engine.TEXTUREFORMAT_RGBA, prefiltered = false) {
             super(scene);
 
             this.name = rootUrl;
@@ -21,6 +26,7 @@
             this._noMipmap = noMipmap;
             this.hasAlpha = false;
             this._format = format;
+            this._prefiltered = prefiltered;
 
             if (!rootUrl && !files) {
                 return;
@@ -47,7 +53,12 @@
 
             if (!this._texture) {
                 if (!scene.useDelayedTextureLoading) {
-                    this._texture = scene.getEngine().createCubeTexture(rootUrl, scene, files, noMipmap, onLoad, onError, this._format);
+                    if (prefiltered) {
+                        this._texture = scene.getEngine().createPrefilteredCubeTexture(rootUrl, scene, this.lodGenerationScale, this.lodGenerationOffset, onLoad, onError, format);
+                    }
+                    else {
+                        this._texture = scene.getEngine().createCubeTexture(rootUrl, scene, files, noMipmap, onLoad, onError, this._format);
+                    }
                 } else {
                     this.delayLoadState = Engine.DELAYLOADSTATE_NOTLOADED;
                 }
@@ -74,7 +85,12 @@
             this._texture = this._getFromCache(this.url, this._noMipmap);
 
             if (!this._texture) {
-                this._texture = this.getScene().getEngine().createCubeTexture(this.url, this.getScene(), this._files, this._noMipmap, undefined, undefined, this._format);
+                if (this._prefiltered) {
+                    this._texture = this.getScene().getEngine().createPrefilteredCubeTexture(this.url, this.getScene(), this.lodGenerationScale, this.lodGenerationOffset, undefined, undefined, this._format);
+                }
+                else {
+                    this._texture = this.getScene().getEngine().createCubeTexture(this.url, this.getScene(), this._files, this._noMipmap, undefined, undefined, this._format);
+                }
             }
         }
 
@@ -82,6 +98,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);
@@ -97,7 +117,7 @@
             }
 
             return texture;
-        }        
+        }
 
         public clone(): CubeTexture {
             return SerializationHelper.Clone(() => {

+ 50 - 125
src/Materials/Textures/babylon.hdrCubeTexture.ts

@@ -10,10 +10,10 @@ module BABYLON {
 
         private static _facesMapping = [
             "right",
-            "up",
-            "front",
             "left",
+            "up",
             "down",
+            "front",
             "back"
         ];
 
@@ -39,11 +39,6 @@ module BABYLON {
         public coordinatesMode = Texture.CUBIC_MODE;
 
         /**
-         * The spherical polynomial data extracted from the texture.
-         */
-        public sphericalPolynomial: SphericalPolynomial = null;
-
-        /**
          * Specifies wether the texture has been generated through the PMREMGenerator tool.
          * This is usefull at run time to apply the good shader.
          */
@@ -88,6 +83,7 @@ module BABYLON {
             this._textureMatrix = Matrix.Identity();
             this._onLoad = onLoad;
             this._onError = onError;
+            this.gammaSpace = false;
 
             if (size) {
                 this._isBABYLONPreprocessed = false;
@@ -160,16 +156,17 @@ module BABYLON {
                 this.getScene().getEngine().updateTextureSize(this._texture, this._size, this._size);
 
                 // Fill polynomial information.
-                this.sphericalPolynomial = new SphericalPolynomial();
-                this.sphericalPolynomial.x.copyFromFloats(floatArrayView[2], floatArrayView[3], floatArrayView[4]);
-                this.sphericalPolynomial.y.copyFromFloats(floatArrayView[5], floatArrayView[6], floatArrayView[7]);
-                this.sphericalPolynomial.z.copyFromFloats(floatArrayView[8], floatArrayView[9], floatArrayView[10]);
-                this.sphericalPolynomial.xx.copyFromFloats(floatArrayView[11], floatArrayView[12], floatArrayView[13]);
-                this.sphericalPolynomial.yy.copyFromFloats(floatArrayView[14], floatArrayView[15], floatArrayView[16]);
-                this.sphericalPolynomial.zz.copyFromFloats(floatArrayView[17], floatArrayView[18], floatArrayView[19]);
-                this.sphericalPolynomial.xy.copyFromFloats(floatArrayView[20], floatArrayView[21], floatArrayView[22]);
-                this.sphericalPolynomial.yz.copyFromFloats(floatArrayView[23], floatArrayView[24], floatArrayView[25]);
-                this.sphericalPolynomial.zx.copyFromFloats(floatArrayView[26], floatArrayView[27], floatArrayView[28]);
+                var sphericalPolynomial = new SphericalPolynomial();
+                sphericalPolynomial.x.copyFromFloats(floatArrayView[2], floatArrayView[3], floatArrayView[4]);
+                sphericalPolynomial.y.copyFromFloats(floatArrayView[5], floatArrayView[6], floatArrayView[7]);
+                sphericalPolynomial.z.copyFromFloats(floatArrayView[8], floatArrayView[9], floatArrayView[10]);
+                sphericalPolynomial.xx.copyFromFloats(floatArrayView[11], floatArrayView[12], floatArrayView[13]);
+                sphericalPolynomial.yy.copyFromFloats(floatArrayView[14], floatArrayView[15], floatArrayView[16]);
+                sphericalPolynomial.zz.copyFromFloats(floatArrayView[17], floatArrayView[18], floatArrayView[19]);
+                sphericalPolynomial.xy.copyFromFloats(floatArrayView[20], floatArrayView[21], floatArrayView[22]);
+                sphericalPolynomial.yz.copyFromFloats(floatArrayView[23], floatArrayView[24], floatArrayView[25]);
+                sphericalPolynomial.zx.copyFromFloats(floatArrayView[26], floatArrayView[27], floatArrayView[28]);
+                this.sphericalPolynomial = sphericalPolynomial;
 
                 // Fill pixel data.
                 mipLevels = intArrayView[29]; // Number of mip levels.
@@ -188,11 +185,14 @@ module BABYLON {
                 for (var k = 0; k < 6; k++) {
                     var dataFace = null;
 
-                    // If special cases.
-                    if (!mipmapGenerator) {
-                        var j = ([0, 2, 4, 1, 3, 5])[k]; // Transforms +X+Y+Z... to +X-X+Y-Y... if no mipmapgenerator...
+                    // To be deprecated.
+                    if (version === 1) {
+                        var j = ([0, 2, 4, 1, 3, 5])[k]; // Transforms +X+Y+Z... to +X-X+Y-Y...
                         dataFace = data[j];
+                    }
 
+                    // If special cases.
+                    if (!mipmapGenerator) {
                         if (!this.getScene().getEngine().getCaps().textureFloat) {
                             // 3 channels of 1 bytes per pixel in bytes.
                             var byteBuffer = new ArrayBuffer(faceSize);
@@ -230,9 +230,6 @@ module BABYLON {
                             }
                         }
                     }
-                    else {
-                        dataFace = data[k];
-                    }
 
                     // Fill the array accordingly.
                     if (byteArray) {
@@ -264,7 +261,8 @@ module BABYLON {
 
                 // Generate harmonics if needed.
                 if (this._generateHarmonics) {
-                    this.sphericalPolynomial = BABYLON.Internals.CubeMapToSphericalPolynomialTools.ConvertCubeMapToSphericalPolynomial(data);
+                    var sphericalPolynomial = BABYLON.Internals.CubeMapToSphericalPolynomialTools.ConvertCubeMapToSphericalPolynomial(data);
+                    this.sphericalPolynomial = sphericalPolynomial;
                 }
 
                 var results = [];
@@ -326,26 +324,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 +398,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) {
@@ -484,88 +488,9 @@ module BABYLON {
                 return null;
             }
 
-            var getDataCallback = (dataBuffer: ArrayBuffer) => {
-                // Extract the raw linear data.
-                var cubeData = BABYLON.Internals.HDRTools.GetCubeMapTextureData(dataBuffer, size);
-
-                // Generate harmonics if needed.
-                var sphericalPolynomial = BABYLON.Internals.CubeMapToSphericalPolynomialTools.ConvertCubeMapToSphericalPolynomial(cubeData);
-
-                // Generate seamless faces
-                var mipGeneratorArray: ArrayBufferView[] = [];
-                // Data are known to be in +X +Y +Z -X -Y -Z
-                // mipmmapGenerator data is expected to be order in +X -X +Y -Y +Z -Z
-                mipGeneratorArray.push(cubeData.right); // +X
-                mipGeneratorArray.push(cubeData.left); // -X
-                mipGeneratorArray.push(cubeData.up); // +Y
-                mipGeneratorArray.push(cubeData.down); // -Y
-                mipGeneratorArray.push(cubeData.front); // +Z
-                mipGeneratorArray.push(cubeData.back); // -Z
-
-                // Custom setup of the generator matching with the PBR shader values.
-                var generator = new BABYLON.Internals.PMREMGenerator(mipGeneratorArray,
-                    size,
-                    size,
-                    0,
-                    3,
-                    true,
-                    2048,
-                    0.25,
-                    false,
-                    true);
-                var mippedData = generator.filterCubeMap();
-
-                // Compute required byte length.
-                var byteLength = 1 * 4; // Raw Data Version int32.
-                byteLength += 4; // CubeMap max mip face size int32.
-                byteLength += (9 * 3 * 4); // Spherical polynomial byte length 9 Vector 3 of floats.
-                // Add data size.
-                byteLength += 4; // Number of mip levels int32.
-                for (var level = 0; level < mippedData.length; level++) {
-                    var mipSize = size >> level;
-                    byteLength += (6 * mipSize * mipSize * 3 * 4); // 6 faces of size squared rgb float pixels.
-                }
-
-                // Prepare binary structure.
-                var buffer = new ArrayBuffer(byteLength);
-                var intArrayView = new Int32Array(buffer);
-                var floatArrayView = new Float32Array(buffer);
-
-                // Fill header.
-                intArrayView[0] = 1; // Version 1.
-                intArrayView[1] = size; // CubeMap max mip face size.
-
-                // Fill polynomial information.
-                sphericalPolynomial.x.toArray(floatArrayView, 2);
-                sphericalPolynomial.y.toArray(floatArrayView, 5);
-                sphericalPolynomial.z.toArray(floatArrayView, 8);
-                sphericalPolynomial.xx.toArray(floatArrayView, 11);
-                sphericalPolynomial.yy.toArray(floatArrayView, 14);
-                sphericalPolynomial.zz.toArray(floatArrayView, 17);
-                sphericalPolynomial.xy.toArray(floatArrayView, 20);
-                sphericalPolynomial.yz.toArray(floatArrayView, 23);
-                sphericalPolynomial.zx.toArray(floatArrayView, 26);
-
-                // Fill pixel data.
-                intArrayView[29] = mippedData.length; // Number of mip levels.
-                var startIndex = 30;
-                for (var level = 0; level < mippedData.length; level++) {
-                    // Fill each pixel of the mip level.
-                    var faceSize = Math.pow(size >> level, 2) * 3;
-                    for (var faceIndex = 0; faceIndex < 6; faceIndex++) {
-                        floatArrayView.set(<any>mippedData[level][faceIndex], startIndex);
-                        startIndex += faceSize;
-                    }
-                }
-
-                // Callback.
-                callback(buffer);
-            }
-
-            // Download and process.
-            Tools.LoadFile(url, data => {
-                getDataCallback(data);
-            }, null, null, true, onError);
+            // Coming Back in 3.1.
+            Tools.Error("Generation of Babylon HDR is coming back in 3.1.");
+            return null;
         }
     }
 }

+ 7 - 82
src/Math/babylon.math.ts

@@ -6,7 +6,6 @@
     export const ToLinearSpace = 2.2;
     export const Epsilon = 0.001;
 
-
     export class MathTools {
         /**
          * Boolean : true if the absolute difference between a and b is lower than epsilon (default = 1.401298E-45)
@@ -50,6 +49,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;
+        }
     }
 
 
@@ -5067,87 +5073,6 @@
         }
     }
 
-    // SphericalHarmonics
-    export class SphericalHarmonics {
-        public L00: Vector3 = Vector3.Zero();
-        public L1_1: Vector3 = Vector3.Zero();
-        public L10: Vector3 = Vector3.Zero();
-        public L11: Vector3 = Vector3.Zero();
-        public L2_2: Vector3 = Vector3.Zero();
-        public L2_1: Vector3 = Vector3.Zero();
-        public L20: Vector3 = Vector3.Zero();
-        public L21: Vector3 = Vector3.Zero();
-        public L22: Vector3 = Vector3.Zero();
-
-        public addLight(direction: Vector3, color: Color3, deltaSolidAngle: number): void {
-            var colorVector = new Vector3(color.r, color.g, color.b);
-            var c = colorVector.scale(deltaSolidAngle);
-
-            this.L00 = this.L00.add(c.scale(0.282095));
-
-            this.L1_1 = this.L1_1.add(c.scale(0.488603 * direction.y));
-            this.L10 = this.L10.add(c.scale(0.488603 * direction.z));
-            this.L11 = this.L11.add(c.scale(0.488603 * direction.x));
-
-            this.L2_2 = this.L2_2.add(c.scale(1.092548 * direction.x * direction.y));
-            this.L2_1 = this.L2_1.add(c.scale(1.092548 * direction.y * direction.z));
-            this.L21 = this.L21.add(c.scale(1.092548 * direction.x * direction.z));
-
-            this.L20 = this.L20.add(c.scale(0.315392 * (3.0 * direction.z * direction.z - 1.0)));
-            this.L22 = this.L22.add(c.scale(0.546274 * (direction.x * direction.x - direction.y * direction.y)));
-        }
-
-        public scale(scale: number): void {
-            this.L00 = this.L00.scale(scale);
-            this.L1_1 = this.L1_1.scale(scale);
-            this.L10 = this.L10.scale(scale);
-            this.L11 = this.L11.scale(scale);
-            this.L2_2 = this.L2_2.scale(scale);
-            this.L2_1 = this.L2_1.scale(scale);
-            this.L20 = this.L20.scale(scale);
-            this.L21 = this.L21.scale(scale);
-            this.L22 = this.L22.scale(scale);
-        }
-    }
-
-    // SphericalPolynomial
-    export class SphericalPolynomial {
-        public x: Vector3 = Vector3.Zero();
-        public y: Vector3 = Vector3.Zero();
-        public z: Vector3 = Vector3.Zero();
-        public xx: Vector3 = Vector3.Zero();
-        public yy: Vector3 = Vector3.Zero();
-        public zz: Vector3 = Vector3.Zero();
-        public xy: Vector3 = Vector3.Zero();
-        public yz: Vector3 = Vector3.Zero();
-        public zx: Vector3 = Vector3.Zero();
-
-        public addAmbient(color: Color3): void {
-            var colorVector = new Vector3(color.r, color.g, color.b);
-            this.xx = this.xx.add(colorVector);
-            this.yy = this.yy.add(colorVector);
-            this.zz = this.zz.add(colorVector);
-        }
-
-        public static getSphericalPolynomialFromHarmonics(harmonics: SphericalHarmonics): SphericalPolynomial {
-            var result = new SphericalPolynomial();
-
-            result.x = harmonics.L11.scale(1.02333);
-            result.y = harmonics.L1_1.scale(1.02333);
-            result.z = harmonics.L10.scale(1.02333);
-
-            result.xx = harmonics.L00.scale(0.886277).subtract(harmonics.L20.scale(0.247708)).add(harmonics.L22.scale(0.429043));
-            result.yy = harmonics.L00.scale(0.886277).subtract(harmonics.L20.scale(0.247708)).subtract(harmonics.L22.scale(0.429043));
-            result.zz = harmonics.L00.scale(0.886277).add(harmonics.L20.scale(0.495417));
-
-            result.yz = harmonics.L2_1.scale(0.858086);
-            result.zx = harmonics.L21.scale(0.858086);
-            result.xy = harmonics.L2_2.scale(0.858086);
-
-            return result;
-        }
-    }
-
     // Vertex formats
     export class PositionNormalVertex {
         constructor(public position: Vector3 = Vector3.Zero(), public normal: Vector3 = Vector3.Up()) {

+ 153 - 0
src/Math/babylon.sphericalPolynomial.ts

@@ -0,0 +1,153 @@
+module BABYLON {
+    export class SphericalPolynomial {
+        public x: Vector3 = Vector3.Zero();
+        public y: Vector3 = Vector3.Zero();
+        public z: Vector3 = Vector3.Zero();
+        public xx: Vector3 = Vector3.Zero();
+        public yy: Vector3 = Vector3.Zero();
+        public zz: Vector3 = Vector3.Zero();
+        public xy: Vector3 = Vector3.Zero();
+        public yz: Vector3 = Vector3.Zero();
+        public zx: Vector3 = Vector3.Zero();
+
+        public addAmbient(color: Color3): void {
+            var colorVector = new Vector3(color.r, color.g, color.b);
+            this.xx = this.xx.add(colorVector);
+            this.yy = this.yy.add(colorVector);
+            this.zz = this.zz.add(colorVector);
+        }
+
+        public static getSphericalPolynomialFromHarmonics(harmonics: SphericalHarmonics): SphericalPolynomial {
+            var result = new SphericalPolynomial();
+
+            result.x = harmonics.L11.scale(1.02333);
+            result.y = harmonics.L1_1.scale(1.02333);
+            result.z = harmonics.L10.scale(1.02333);
+
+            result.xx = harmonics.L00.scale(0.886277).subtract(harmonics.L20.scale(0.247708)).add(harmonics.L22.scale(0.429043));
+            result.yy = harmonics.L00.scale(0.886277).subtract(harmonics.L20.scale(0.247708)).subtract(harmonics.L22.scale(0.429043));
+            result.zz = harmonics.L00.scale(0.886277).add(harmonics.L20.scale(0.495417));
+
+            result.yz = harmonics.L2_1.scale(0.858086);
+            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);
+        }
+    }
+
+    export class SphericalHarmonics {
+        public L00: Vector3 = Vector3.Zero();
+        public L1_1: Vector3 = Vector3.Zero();
+        public L10: Vector3 = Vector3.Zero();
+        public L11: Vector3 = Vector3.Zero();
+        public L2_2: Vector3 = Vector3.Zero();
+        public L2_1: Vector3 = Vector3.Zero();
+        public L20: Vector3 = Vector3.Zero();
+        public L21: Vector3 = Vector3.Zero();
+        public L22: Vector3 = Vector3.Zero();
+
+        public addLight(direction: Vector3, color: Color3, deltaSolidAngle: number): void {
+            var colorVector = new Vector3(color.r, color.g, color.b);
+            var c = colorVector.scale(deltaSolidAngle);
+
+            this.L00 = this.L00.add(c.scale(0.282095));
+
+            this.L1_1 = this.L1_1.add(c.scale(0.488603 * direction.y));
+            this.L10 = this.L10.add(c.scale(0.488603 * direction.z));
+            this.L11 = this.L11.add(c.scale(0.488603 * direction.x));
+
+            this.L2_2 = this.L2_2.add(c.scale(1.092548 * direction.x * direction.y));
+            this.L2_1 = this.L2_1.add(c.scale(1.092548 * direction.y * direction.z));
+            this.L21 = this.L21.add(c.scale(1.092548 * direction.x * direction.z));
+
+            this.L20 = this.L20.add(c.scale(0.315392 * (3.0 * direction.z * direction.z - 1.0)));
+            this.L22 = this.L22.add(c.scale(0.546274 * (direction.x * direction.x - direction.y * direction.y)));
+        }
+
+        public scale(scale: number): void {
+            this.L00 = this.L00.scale(scale);
+            this.L1_1 = this.L1_1.scale(scale);
+            this.L10 = this.L10.scale(scale);
+            this.L11 = this.L11.scale(scale);
+            this.L2_2 = this.L2_2.scale(scale);
+            this.L2_1 = this.L2_1.scale(scale);
+            this.L20 = this.L20.scale(scale);
+            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;
+        }
+    }
+}

+ 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

+ 33 - 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,37 @@ 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;
+}
+
+float environmentRadianceOcclusion(float ambientOcclusion, float NdotVUnclamped) {
+    // Best balanced (implementation time vs result vs perf) analytical environment specular occlusion found.
+    // http://research.tri-ace.com/Data/cedec2011_RealtimePBR_Implementation_e.pptx
+    float temp = NdotVUnclamped + ambientOcclusion;
+    return clamp(square(temp) - 1.0 + ambientOcclusion, 0.0, 1.0);
+}
+
+float environmentHorizonOcclusion(vec3 reflection, vec3 normal) {
+	// http://marmosetco.tumblr.com/post/81245981087
+    float temp = clamp( 1.0 + 1.1 * dot(reflection, normal), 0.0, 1.0);
+    return square(temp);
 }

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

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

@@ -44,3 +44,17 @@ uniform mat4 bumpMatrix;
 #ifdef POINTSIZE
 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

+ 221 - 115
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(s, c) textureCube(s, c)
+		
+		uniform samplerCube refractionSampler;
 
-#ifdef REFRACTIONMAP_3D
-uniform samplerCube refractionCubeSampler;
-#else
-uniform sampler2D refraction2DSampler;
-#endif
+		#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
-		uniform samplerCube reflectionCubeSampler;
+		#define sampleReflection(s, c) textureCube(s, c)
+
+		uniform samplerCube reflectionSampler;
+		
+		#ifdef LODBASEDMICROSFURACE
+			#define sampleReflectionLod(s, c, l) textureCubeLodEXT(s, c, l)
+		#else
+			uniform samplerCube reflectionSamplerLow;
+			uniform samplerCube reflectionSamplerHigh;
+		#endif
 	#else
-		uniform sampler2D reflection2DSampler;
+		#define sampleReflection(s, c) texture2D(s, c)
+
+		uniform sampler2D reflectionSampler;
+
+		#ifdef LODBASEDMICROSFURACE
+			#define sampleReflectionLod(s, c, l) texture2DLodEXT(s, c, l)
+		#else
+			uniform samplerCube reflectionSamplerLow;
+			uniform samplerCube reflectionSamplerHigh;
+		#endif
 	#endif
 
 	#ifdef REFLECTIONMAP_SKYBOX
@@ -92,12 +130,15 @@ uniform sampler2D refraction2DSampler;
 		#if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED)
 			varying vec3 vDirectionW;
 		#endif
-
 	#endif
 
 	#include<reflectionFunction>
 #endif
 
+#ifdef ENVIRONMENTBRDF
+	uniform sampler2D environmentBrdfSampler;
+#endif
+
 // Forces linear space for image processing
 #ifndef FROMLINEARSPACE
 	#define FROMLINEARSPACE;
@@ -135,10 +176,18 @@ void main(void) {
 	vec3 normalW = normalize(cross(dFdx(vPositionW), dFdy(vPositionW)));
 #endif
 
+#ifdef BUMP
+	vec3 originalNormalW = normalW;
+#endif
+
 #include<bumpFragment>
 
 #if defined(TWOSIDEDLIGHTING) && defined(NORMAL) 
 	normalW = gl_FrontFacing ? normalW : -normalW;
+
+	#ifdef BUMP
+		vec3 originalNormalW = gl_FrontFacing ? originalNormalW : -originalNormalW;;
+	#endif
 #endif
 
 // _____________________________ Albedo Information ______________________________
@@ -305,154 +354,169 @@ void main(void) {
 #endif
 
 // _____________________________ Compute LODs Fetch ____________________________________
-#ifdef LODBASEDMICROSFURACE
+	// Compute N dot V.
+	float NdotVUnclamped = dot(normalW, viewDirectionW);
+	float NdotV = clamp(NdotVUnclamped,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 *= vReflectionInfos.x;
+	environmentRadiance *= vReflectionColor.rgb;
+	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);
 	vec3 specularEnvironmentR0 = surfaceReflectivityColor.rgb;
 	vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
 
-	// Environment Reflectance
-	vec3 specularEnvironmentReflectance = fresnelSchlickEnvironmentGGX(clamp(NdotV, 0., 1.), specularEnvironmentR0, specularEnvironmentR90, sqrt(microSurface));
-
 // _____________________________ Direct Lighting Info __________________________________
 	vec3 diffuseBase = vec3(0., 0., 0.);
 #ifdef SPECULARTERM
@@ -469,6 +533,36 @@ void main(void) {
 
 #include<lightFragment>[0..maxSimultaneousLights]
 
+// _________________________ Specular Environment Oclusion __________________________
+#if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX)
+	// Indexed on cos(theta) and roughness
+	vec2 brdfSamplerUV = vec2(NdotV, roughness);
+	
+	// We can find the scale and offset to apply to the specular value.
+	vec4 environmentBrdf = texture2D(environmentBrdfSampler, brdfSamplerUV);
+
+	vec3 specularEnvironmentReflectance = specularEnvironmentR0 * environmentBrdf.x + environmentBrdf.y;
+
+	#ifdef AMBIENTINGRAYSCALE
+		float ambientMonochrome = ambientOcclusionColor.r;
+	#else
+		float ambientMonochrome = getLuminance(ambientOcclusionColor);
+	#endif
+
+	float seo = environmentRadianceOcclusion(ambientMonochrome, NdotVUnclamped);
+	specularEnvironmentReflectance *= seo;
+
+	#ifdef BUMP
+		#ifdef REFLECTIONMAP_3D
+			float eho = environmentHorizonOcclusion(reflectionCoords, normalW);
+			specularEnvironmentReflectance *= eho;
+		#endif
+	#endif
+#else
+	// Jones implementation of a well balanced fast analytical solution.
+	vec3 specularEnvironmentReflectance = fresnelSchlickEnvironmentGGX(NdotV, specularEnvironmentR0, specularEnvironmentR90, sqrt(microSurface));
+#endif
+
 // _____________________________ Refractance+Tint ________________________________
 #ifdef REFRACTION
 	vec3 refractance = vec3(0.0, 0.0, 0.0);
@@ -490,7 +584,7 @@ void main(void) {
 		environmentIrradiance *= alpha;
 
 		// Tint reflectance
-		surfaceRefractionColor *= tint;
+		environmentRefraction *= tint;
 
 		// Put alpha back to 1;
 		alpha = 1.0;
@@ -540,7 +634,7 @@ void main(void) {
 
 // _____________________________ Refraction ______________________________________
 #ifdef REFRACTION
-	vec3 finalRefraction = surfaceRefractionColor;
+	vec3 finalRefraction = environmentRefraction;
 	finalRefraction *= refractance;
 #endif
 
@@ -641,6 +735,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);
 
@@ -659,4 +757,12 @@ void main(void) {
 	//// Emissive Color
 	//vec2 test = vEmissiveUV * 0.5 + 0.5;
 	//gl_FragColor = vec4(test.x, test.y, 1.0, 1.0);
+
+	// Specular Environment Occlusion
+	//gl_FragColor = vec4(seo, seo, seo, 1.0);
+
+	//// Horizon Environment Occlusion
+	//gl_FragColor = vec4(eho, eho, eho, 1.0);
+
+	//gl_FragColor = vec4(seo * eho, seo * eho, seo * eho, 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)

+ 78 - 51
src/Tools/HDR/babylon.cubemapToSphericalPolynomial.ts

@@ -30,6 +30,51 @@ 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 gammaSpace = texture.gammaSpace;
+            // Always read as RGBA.
+            var format = Engine.TEXTUREFORMAT_RGBA;
+            var type = Engine.TEXTURETYPE_UNSIGNED_INT;
+            if (texture.textureType && texture.textureType !== Engine.TEXTURETYPE_UNSIGNED_INT) {
+                type = Engine.TEXTURETYPE_FLOAT;
+            }
+
+            var cubeInfo: CubeMapInfo = {
+                size,
+                right,
+                left,
+                up,
+                down,
+                front,
+                back,
+                format,
+                type,
+                gammaSpace,
+            };
+
+            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.
          * 
@@ -55,7 +100,7 @@ module BABYLON.Internals {
                 // TODO: we could perform the summation directly into a SphericalPolynomial (SP), which is more efficient than SphericalHarmonic (SH).
                 // This is possible because during the summation we do not need the SH-specific properties, e.g. orthogonality.
                 // Because SP is still linear, so summation is fine in that basis.
-
+                const stride = cubeInfo.format === Engine.TEXTUREFORMAT_RGBA ? 4 : 3;
                 for (var y = 0; y < cubeInfo.size; y++) {
                     var u = minUV;
 
@@ -69,55 +114,28 @@ module BABYLON.Internals {
 
                         var deltaSolidAngle = Math.pow(1.0 + u * u + v * v, -3.0 / 2.0);
 
-                        if (1) {
-                            var r = dataArray[(y * cubeInfo.size * 3) + (x * 3) + 0];
-                            var g = dataArray[(y * cubeInfo.size * 3) + (x * 3) + 1];
-                            var b = dataArray[(y * cubeInfo.size * 3) + (x * 3) + 2];
-
-                            var color = new Color3(r, g, b);
+                        var r = dataArray[(y * cubeInfo.size * stride) + (x * stride) + 0];
+                        var g = dataArray[(y * cubeInfo.size * stride) + (x * stride) + 1];
+                        var b = dataArray[(y * cubeInfo.size * stride) + (x * stride) + 2];
 
-                            sphericalHarmonics.addLight(worldDirection, color, deltaSolidAngle);
+                        // Handle Integer types.
+                        if (cubeInfo.type === Engine.TEXTURETYPE_UNSIGNED_INT) {
+                            r /= 255;
+                            g /= 255;
+                            b /= 255;
                         }
-                        else {
-
-                            if (faceIndex == 0) {
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 0] = 1;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 1] = 0;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 2] = 0;
-                            }
-                            else if (faceIndex == 1) {
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 0] = 0;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 1] = 1;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 2] = 0;
-                            }
-                            else if (faceIndex == 2) {
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 0] = 0;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 1] = 0;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 2] = 1;
-                            }
-                            else if (faceIndex == 3) {
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 0] = 1;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 1] = 1;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 2] = 0;
-                            }
-                            else if (faceIndex == 4) {
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 0] = 1;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 1] = 0;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 2] = 1;
-                            }
-                            else if (faceIndex == 5) {
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 0] = 0;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 1] = 1;
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 2] = 1;
-                            }
-
-                            var color = new Color3(dataArray[(y * cubeInfo.size * 3) + (x * 3) + 0],
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 1],
-                                dataArray[(y * cubeInfo.size * 3) + (x * 3) + 2]);
-
-                            sphericalHarmonics.addLight(worldDirection, color, deltaSolidAngle);
+
+                        // Handle Gamma space textures.
+                        if (cubeInfo.gammaSpace) {
+                            r = Math.pow(r, ToLinearSpace);
+                            g = Math.pow(g, ToLinearSpace);
+                            b = Math.pow(b, ToLinearSpace);
                         }
 
+                        var color = new Color3(r, g, b);
+
+                        sphericalHarmonics.addLight(worldDirection, color, deltaSolidAngle);
+
                         totalSolidAngle += deltaSolidAngle;
 
                         u += du;
@@ -127,13 +145,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);
         }

+ 35 - 13
src/Tools/HDR/babylon.panoramaToCubemap.ts

@@ -5,39 +5,39 @@ module BABYLON.Internals {
     export interface CubeMapInfo {
         /**
          * The pixel array for the front face.
-         * This is stored in RGB, left to right, up to down format.
+         * This is stored in format, left to right, up to down format.
          */
-        front: Float32Array;
+        front: ArrayBufferView;
         
         /**
          * The pixel array for the back face.
-         * This is stored in RGB, left to right, up to down format.
+         * This is stored in format, left to right, up to down format.
          */
-        back: Float32Array;
+        back: ArrayBufferView;
         
         /**
          * The pixel array for the left face.
-         * This is stored in RGB, left to right, up to down format.
+         * This is stored in format, left to right, up to down format.
          */
-        left: Float32Array;
+        left: ArrayBufferView;
         
         /**
          * The pixel array for the right face.
-         * This is stored in RGB, left to right, up to down format.
+         * This is stored in format, left to right, up to down format.
          */
-        right: Float32Array;
+        right: ArrayBufferView;
         
         /**
          * The pixel array for the up face.
-         * This is stored in RGB, left to right, up to down format.
+         * This is stored in format, left to right, up to down format.
          */
-        up: Float32Array;
+        up: ArrayBufferView;
         
         /**
          * The pixel array for the down face.
-         * This is stored in RGB, left to right, up to down format.
+         * This is stored in format, left to right, up to down format.
          */
-        down: Float32Array;
+        down: ArrayBufferView;
         
         /**
          * The size of the cubemap stored.
@@ -45,6 +45,25 @@ module BABYLON.Internals {
          * Each faces will be size * size pixels.
          */
         size: number;
+
+        /**
+         * The format of the texture.
+         * 
+         * RGBA, RGB.
+         */
+        format: number;
+
+        /**
+         * The type of the texture data.
+         * 
+         * UNSIGNED_INT, FLOAT.
+         */
+        type: number;
+
+        /**
+         * Specifies whether the texture is in gamma space.
+         */
+        gammaSpace: boolean;
     }
 
     /**
@@ -121,7 +140,10 @@ module BABYLON.Internals {
                 right: textureRight,
                 up: textureUp,
                 down: textureDown,
-                size: size
+                size: size,
+                type: Engine.TEXTURETYPE_FLOAT,
+                format: Engine.TEXTUREFORMAT_RGB,
+                gammaSpace: false,
             };
         }
 

文件差異過大導致無法顯示
+ 0 - 1228
src/Tools/HDR/babylon.pmremgenerator.ts


+ 43 - 38
src/Tools/babylon.dds.ts

@@ -315,14 +315,14 @@
             return byteArray;
         }
 
-        public static UploadDDSLevels(engine: Engine, arrayBuffer: any, info: DDSInfo, loadMipmaps: boolean, faces: number): void {
+        public static UploadDDSLevels(engine: Engine, arrayBuffer: any, info: DDSInfo, loadMipmaps: boolean, faces: number, lodIndex = -1): void {
             var gl = engine._gl;
             var ext = engine.getCaps().s3tc;
 
             var header = new Int32Array(arrayBuffer, 0, headerLengthInt),
                 fourCC, blockBytes, internalFormat, format,
                 width, height, dataLength, dataOffset,
-                byteArray, mipmapCount, i;
+                byteArray, mipmapCount, mip;
 
             if (header[off_magic] != DDS_MAGIC) {
                 Tools.Error("Invalid magic number in DDS header");
@@ -408,45 +408,50 @@
                 width = header[off_width];
                 height = header[off_height];
 
-                for (i = 0; i < mipmapCount; ++i) {
-                    if (!info.isCompressed && info.isFourCC) {
-                        dataLength = width * height * 4;
-                        var floatArray: ArrayBufferView;
-                        if (bpp === 128) {
-                            floatArray = DDSTools._GetFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
-                        } else if (bpp === 64 && !engine.getCaps().textureHalfFloat) { // Let's fallback to full float
-                            floatArray = DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
-
-                            info.textureType = Engine.TEXTURETYPE_FLOAT;
-                            format = engine._getWebGLTextureType(info.textureType);    
-                            internalFormat = engine._getRGBABufferInternalSizedFormat(info.textureType);                            
-                        } else { // 64
-                            floatArray = DDSTools._GetHalfFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
-                        }
+                for (mip = 0; mip < mipmapCount; ++mip) {
+                    if (lodIndex === -1 || lodIndex === mip) {
+                        // In case of fixed LOD, if the lod has just been uploaded, early exit.
+                        const i = (lodIndex === -1) ? mip : 0;
 
-                        engine._uploadDataToTexture(sampler, i, internalFormat, width, height, gl.RGBA, format, floatArray);
-                    } else if (info.isRGB) {
-                        if (bpp === 24) {
-                            dataLength = width * height * 3;
-                            byteArray = DDSTools._GetRGBArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
-                            engine._uploadDataToTexture(sampler, i, gl.RGB, width, height, gl.RGB, gl.UNSIGNED_BYTE, byteArray);
-                        } else { // 32
+                        if (!info.isCompressed && info.isFourCC) {
                             dataLength = width * height * 4;
-                            byteArray = DDSTools._GetRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
-                            engine._uploadDataToTexture(sampler, i, gl.RGBA, width, height, gl.RGBA, gl.UNSIGNED_BYTE, byteArray);
+                            var floatArray: ArrayBufferView;
+                            if (bpp === 128) {
+                                floatArray = DDSTools._GetFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
+                            } else if (bpp === 64 && !engine.getCaps().textureHalfFloat) { // Let's fallback to full float
+                                floatArray = DDSTools._GetHalfFloatAsFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
+
+                                info.textureType = Engine.TEXTURETYPE_FLOAT;
+                                format = engine._getWebGLTextureType(info.textureType);    
+                                internalFormat = engine._getRGBABufferInternalSizedFormat(info.textureType);                            
+                            } else { // 64
+                                floatArray = DDSTools._GetHalfFloatRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, i);
+                            }
+
+                            engine._uploadDataToTexture(sampler, i, internalFormat, width, height, gl.RGBA, format, floatArray);
+                        } else if (info.isRGB) {
+                            if (bpp === 24) {
+                                dataLength = width * height * 3;
+                                byteArray = DDSTools._GetRGBArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
+                                engine._uploadDataToTexture(sampler, i, gl.RGB, width, height, gl.RGB, gl.UNSIGNED_BYTE, byteArray);
+                            } else { // 32
+                                dataLength = width * height * 4;
+                                byteArray = DDSTools._GetRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
+                                engine._uploadDataToTexture(sampler, i, gl.RGBA, width, height, gl.RGBA, gl.UNSIGNED_BYTE, byteArray);
+                            }
+                        } else if (info.isLuminance) {
+                            var unpackAlignment = gl.getParameter(gl.UNPACK_ALIGNMENT);
+                            var unpaddedRowSize = width;
+                            var paddedRowSize = Math.floor((width + unpackAlignment - 1) / unpackAlignment) * unpackAlignment;
+                            dataLength = paddedRowSize * (height - 1) + unpaddedRowSize;
+
+                            byteArray = DDSTools._GetLuminanceArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
+                            engine._uploadDataToTexture(sampler, i, gl.LUMINANCE, width, height, gl.LUMINANCE, gl.UNSIGNED_BYTE, byteArray);
+                        } else {
+                            dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
+                            byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);
+                            engine._uploadCompressedDataToTexture(sampler, i, internalFormat, width, height, byteArray);
                         }
-                    } else if (info.isLuminance) {
-                        var unpackAlignment = gl.getParameter(gl.UNPACK_ALIGNMENT);
-                        var unpaddedRowSize = width;
-                        var paddedRowSize = Math.floor((width + unpackAlignment - 1) / unpackAlignment) * unpackAlignment;
-                        dataLength = paddedRowSize * (height - 1) + unpaddedRowSize;
-
-                        byteArray = DDSTools._GetLuminanceArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer);
-                        engine._uploadDataToTexture(sampler, i, gl.LUMINANCE, width, height, gl.LUMINANCE, gl.UNSIGNED_BYTE, byteArray);
-                    } else {
-                        dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
-                        byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);
-                        engine._uploadCompressedDataToTexture(sampler, i, internalFormat, width, height, byteArray);
                     }
                     dataOffset += width * height * (bpp / 8);
                     width *= 0.5;

文件差異過大導致無法顯示
+ 15 - 1
src/Tools/babylon.textureTools.ts


+ 119 - 56
src/babylon.engine.ts

@@ -741,7 +741,7 @@
             this._caps.drawBuffersExtension = this._webGLVersion > 1 || this._gl.getExtension('WEBGL_draw_buffers');
 
             // Checks if some of the format renders first to allow the use of webgl inspector.
-            this._caps.colorBufferFloat = this._webGLVersion > 1 && this._gl.getExtension('EXT_color_buffer_float')
+            this._caps.colorBufferFloat = this._webGLVersion > 1 && this._gl.getExtension('EXT_color_buffer_float');
 
             this._caps.textureFloat = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_float');
             this._caps.textureFloatLinearFiltering = this._caps.textureFloat && this._gl.getExtension('OES_texture_float_linear');
@@ -2401,9 +2401,6 @@
             var lastDot = url.lastIndexOf('.');
             var extension = (lastDot > 0) ? url.substring(lastDot).toLowerCase() : "";
             var isDDS = this.getCaps().s3tc && (extension === ".dds");
-            if (isDDS) {
-                BABYLON.Tools.Warn("DDS files deprecated since 3.0, use KTX files");
-            }
             var isTGA = (extension === ".tga");
             
             // determine if a ktx file should be substituted
@@ -2465,7 +2462,7 @@
 
                          var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap && ((info.width >> (info.mipmapCount - 1)) === 1);
                          prepareWebGLTexture(texture, this._gl, scene, info.width, info.height, invertY, !loadMipmap, info.isFourCC, () => {
-                        //     Internals.DDSTools.UploadDDSLevels(this, data, info, loadMipmap, 1);
+                             Internals.DDSTools.UploadDDSLevels(this, data, info, loadMipmap, 1);
                          }, samplingMode);
                     };
                 }
@@ -3117,7 +3114,82 @@
             return texture;
         }
 
-        public createCubeTexture(rootUrl: string, scene: Scene, files: string[], noMipmap?: boolean, onLoad: () => void = null, onError: () => void = null, format?: number): WebGLTexture {
+        public createPrefilteredCubeTexture(rootUrl: string, scene: Scene, scale: number, offset: number, onLoad: () => void, onError: () => void = null, format?: number): WebGLTexture {
+            var callback = (loadData) => {
+                if (this._caps.textureLOD || !loadData) {
+                    // Do not add extra process if texture lod is supported.
+                    if (onLoad) {
+                        onLoad();
+                    }
+                    return;
+                }
+
+                const mipSlices = 3;
+                
+                var gl = this._gl;
+                const width = loadData.width;
+                if (!width) {
+                    return;
+                }
+
+                const textures: BaseTexture[] = [];
+                for (let i = 0; i < mipSlices; i++) {
+                    //compute LOD from even spacing in smoothness (matching shader calculation)
+                    let smoothness = i / (mipSlices - 1);
+                    let roughness = 1 - smoothness;
+                    const minimumVariance = 0.0005;
+                    let alphaG = roughness * roughness + minimumVariance;
+                    let microsurfaceAverageSlopeTexels = alphaG * width;
+
+                    let environmentSpecularLOD = scale * (MathTools.Log2(microsurfaceAverageSlopeTexels)) + offset;
+
+                    let maxLODIndex = MathTools.Log2(width);
+                    let mipmapIndex = Math.min(Math.max(Math.round(environmentSpecularLOD), 0), maxLODIndex);
+
+                    var glTextureFromLod = gl.createTexture();
+                    glTextureFromLod.isCube = true;
+                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, glTextureFromLod);
+
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
+                    gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
+
+                    if (loadData.isDDS) {
+                        var info: Internals.DDSInfo = loadData.info;
+                        var data: any = loadData.data;
+                        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, info.isCompressed ? 1 : 0);
+
+                        Internals.DDSTools.UploadDDSLevels(this, data, info, true, 6, mipmapIndex);
+                    }
+                    else {
+                        Tools.Warn("DDS is the only prefiltered cube map support so far.")
+                    }
+
+                    this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
+
+                    // Wrap in a base texture for easy binding.
+                    const lodTexture = new BaseTexture(scene);
+                    lodTexture.isCube = true;
+                    lodTexture._texture = glTextureFromLod;
+                    
+                    glTextureFromLod.isReady = true;
+                    textures.push(lodTexture);
+                }
+
+                (loadData.texture as WebGLTexture)._lodTextureHigh = textures[2];
+                (loadData.texture as WebGLTexture)._lodTextureMid = textures[1];
+                (loadData.texture as WebGLTexture)._lodTextureLow = textures[0];
+
+                if (onLoad) {
+                    onLoad();
+                }
+            };
+
+            return this.createCubeTexture(rootUrl, scene, null, false, callback, onError, format);
+        }
+
+        public createCubeTexture(rootUrl: string, scene: Scene, files: string[], noMipmap?: boolean, onLoad: (data?: any) => void = null, onError: () => void = null, format?: number): WebGLTexture {
             var gl = this._gl;
 
             var texture = gl.createTexture();
@@ -3190,6 +3262,10 @@
                     texture._height = info.height;
                     texture.isReady = true;
                     texture.type = info.textureType;
+
+                    if (onLoad) {
+                        onLoad({ isDDS: true, width: info.width, info, data, texture });
+                    }
                 }, null, null, true, onError);
             } else {
                 cascadeLoad(rootUrl, scene, imgs => {
@@ -3273,22 +3349,17 @@
                 gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
             }
 
-            var facesIndex = [
-                gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
-                gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
-            ];
-
             // Data are known to be in +X +Y +Z -X -Y -Z
-            for (let index = 0; index < facesIndex.length; index++) {
-                let faceData = data[index];
+            for (let faceIndex = 0; faceIndex < 6; faceIndex++) {
+                let faceData = data[faceIndex];
 
                 if (compression) {
-                    gl.compressedTexImage2D(facesIndex[index], level, this.getCaps().s3tc[compression], texture._width, texture._height, 0, faceData);
+                    gl.compressedTexImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, this.getCaps().s3tc[compression], texture._width, texture._height, 0, faceData);
                 } else {
                     if (needConversion) {
                         faceData = this._convertRGBtoRGBATextureData(faceData, texture._width, texture._height, type);
                     }
-                    gl.texImage2D(facesIndex[index], level, internalSizedFomat, texture._width, texture._height, 0, internalFormat, textureType, faceData);
+                    gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, level, internalSizedFomat, texture._width, texture._height, 0, internalFormat, textureType, faceData);
                 }
             }
 
@@ -3307,6 +3378,9 @@
             var texture = gl.createTexture();
             texture.isCube = true;
             texture.references = 1;
+            texture.noMipmap = !generateMipMaps;
+            texture.format = format;
+            texture.type = type;
 
             var textureType = this._getWebGLTextureType(type);
             var internalFormat = this._getInternalFormat(format);
@@ -3361,8 +3435,6 @@
             gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
             this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null);
 
-            this._loadedTexturesCache.push(texture);
-
             return texture;
         }
 
@@ -3378,6 +3450,7 @@
             var texture = this.createRawCubeTexture(null, size, format, type, !noMipmap, invertY, samplingMode);
             scene._addPendingData(texture);
             texture.url = url;
+            this._loadedTexturesCache.push(texture);
 
             var onerror = () => {
                 scene._removePendingData(texture);
@@ -3387,19 +3460,11 @@
             };
             
             var internalCallback = (data) => {
-                var rgbeDataArrays = callback(data);
-
-                var facesIndex = [
-                    gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
-                    gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z
-                ];
-
                 var width = texture._width;
                 var height = texture._height;
-                if (mipmmapGenerator) {
+                var faceDataArrays = callback(data);
 
-                    // TODO Remove this once Proper CubeMap Blur... This has nothing to do in engine...
-                    // I ll remove ASAP.
+                if (mipmmapGenerator) {
                     var textureType = this._getWebGLTextureType(type);
                     var internalFormat = this._getInternalFormat(format);
                     var internalSizedFomat = this._getRGBABufferInternalSizedFormat(type);
@@ -3412,28 +3477,17 @@
 
                     this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
                     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 0);
-                    var arrayTemp: ArrayBufferView[] = [];
-                    // Data are known to be in +X +Y +Z -X -Y -Z
-                    // mipmmapGenerator data is expected to be order in +X -X +Y -Y +Z -Z
-                    arrayTemp.push(rgbeDataArrays[0]); // +X
-                    arrayTemp.push(rgbeDataArrays[3]); // -X
-                    arrayTemp.push(rgbeDataArrays[1]); // +Y
-                    arrayTemp.push(rgbeDataArrays[4]); // -Y
-                    arrayTemp.push(rgbeDataArrays[2]); // +Z
-                    arrayTemp.push(rgbeDataArrays[5]); // -Z
-
-                    var mipData = mipmmapGenerator(arrayTemp);
-                    // mipData is order in +X -X +Y -Y +Z -Z
-                    var mipFaces = [0, 2, 4, 1, 3, 5];
+                    
+                    var mipData = mipmmapGenerator(faceDataArrays);
                     for (var level = 0; level < mipData.length; level++) {
                         var mipSize = width >> level;
 
-                        for (let mipIndex in mipFaces) {
-                            let mipFaceData = mipData[level][mipFaces[mipIndex]];
+                        for (var faceIndex = 0; faceIndex < 6; faceIndex++) {
+                            let mipFaceData = mipData[level][faceIndex];
                             if (needConversion) {
                                 mipFaceData = this._convertRGBtoRGBATextureData(mipFaceData, mipSize, mipSize, type);
                             }
-                            gl.texImage2D(facesIndex[mipIndex], level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipFaceData);
+                            gl.texImage2D(faceIndex, level, internalSizedFomat, mipSize, mipSize, 0, internalFormat, textureType, mipFaceData);
                         }
                     }
 
@@ -3441,7 +3495,7 @@
                 }
                 else {
                     texture.generateMipMaps = !noMipmap;
-                    this.updateRawCubeTexture(texture, rgbeDataArrays, format, type, invertY);
+                    this.updateRawCubeTexture(texture, faceDataArrays, format, type, invertY);
                 }
 
                 texture.isReady = true;
@@ -3527,6 +3581,17 @@
             if (index !== -1) {
                 this._loadedTexturesCache.splice(index, 1);
             }
+
+            // Integrated fixed lod samplers.
+            if (texture._lodTextureHigh) {
+                texture._lodTextureHigh.dispose();
+            }
+            if (texture._lodTextureMid) {
+                texture._lodTextureMid.dispose();
+            }
+            if (texture._lodTextureLow) {
+                texture._lodTextureLow.dispose();
+            }
         }
 
         private setProgram(program: WebGLProgram): void {
@@ -3969,7 +4034,7 @@
             }
         }
 
-        public _readTexturePixels(texture: WebGLTexture, width: number, height: number, faceIndex = -1, lodIndex = 0): ArrayBufferView {
+        public _readTexturePixels(texture: WebGLTexture, width: number, height: number, faceIndex = -1): ArrayBufferView {
             let gl = this._gl;
             if (!this._dummyFramebuffer) {
                 this._dummyFramebuffer = gl.createFramebuffer();
@@ -3977,32 +4042,30 @@
             gl.bindFramebuffer(gl.FRAMEBUFFER, this._dummyFramebuffer);
 
             if (faceIndex > -1) {
-                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, texture, lodIndex);            
+                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, texture, 0);
             } else {
-                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, lodIndex);
+                gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
             }
 
-            let readFormat = (texture.type !== undefined) ? this._getRGBABufferInternalSizedFormat(texture.type) : gl.RGBA;
             let readType = (texture.type !== undefined) ? this._getWebGLTextureType(texture.type) : gl.UNSIGNED_BYTE;
             let buffer: ArrayBufferView;
 
             switch (readType) {
                 case gl.UNSIGNED_BYTE:
                     buffer = new Uint8Array(4 * width * height);
+                    readType = gl.UNSIGNED_BYTE;
                     break;
-                case gl.FLOAT:
+                default:
                     buffer = new Float32Array(4 * width * height);
-                    break;            
-                case gl.HALF_FLOAT_OES:
-                    buffer = new Uint16Array(4 * width * height);
-                    break;                          
+                    readType = gl.FLOAT;
+                    break;
             }
-            gl.readPixels(0, 0, width, height, readFormat, readType, buffer);
-            
+
+            gl.readPixels(0, 0, width, height, gl.RGBA, readType, buffer);
             gl.bindFramebuffer(gl.FRAMEBUFFER, null);
 
             return buffer;
-        }    
+        }
 
         private _canRenderToFloatFramebuffer(): boolean {
             if (this._webGLVersion > 1) {

+ 8 - 0
src/babylon.mixins.ts

@@ -107,6 +107,7 @@ interface WebGLTexture {
     generateMipMaps: boolean;
     samples: number;
     type: number;
+    format: number;
     onLoadedCallbacks: Array<Function>;
     _size: number;
     _baseWidth: number;
@@ -125,6 +126,13 @@ interface WebGLTexture {
     _isDisabled: boolean;
     _generateStencilBuffer: boolean;
     _generateDepthBuffer: boolean;
+    _sphericalPolynomial: BABYLON.SphericalPolynomial;
+    // The following three fields helps sharing generated fixed LODs for texture filtering
+    // In environment not supporting the textureLOD extension like EDGE. They are for internal use only.
+    // They are at the level of the gl texture to benefit from the cache.
+    _lodTextureHigh: BABYLON.BaseTexture;
+    _lodTextureMid: BABYLON.BaseTexture;
+    _lodTextureLow: BABYLON.BaseTexture;
 }
 
 interface WebGLBuffer {

+ 2 - 0
src/babylon.scene.ts

@@ -190,6 +190,8 @@
         public clearColor: Color4 = new Color4(0.2, 0.2, 0.3, 1.0);
         public ambientColor = new Color3(0, 0, 0);
 
+        public _environmentBRDFTexture: BaseTexture;
+
         protected _environmentTexture: BaseTexture;
         /**
          * Texture used in all pbr material as the reflection texture.

+ 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" },