瀏覽代碼

Read and Run

sebastien 7 年之前
父節點
當前提交
bb28fc3923

+ 43 - 1
src/Engine/babylon.engine.ts

@@ -5259,6 +5259,23 @@
             this._gl.compressedTexImage2D(target, lod, internalFormat, width, height, 0, <DataView>data);
         }
 
+        /** @hidden */
+        public _uploadImageToTexture(texture: InternalTexture, faceIndex: number, lod: number, image: HTMLImageElement) {
+            var gl = this._gl;
+
+            var textureType = this._getWebGLTextureType(texture.type);
+            var format = this._getInternalFormat(texture.format);
+            var internalFormat = this._getRGBABufferInternalSizedFormat(texture.type, format);
+
+            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
+            gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, texture.invertY ? 1 : 0);
+
+            var target = gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex;
+            gl.texImage2D(target, lod, internalFormat, format, textureType, image);
+
+            this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, null, true);
+        }
+
         /**
          * Creates a new render target cube texture
          * @param size defines the size of the texture
@@ -5474,6 +5491,7 @@
 
             var isKTX = false;
             var isDDS = false;
+            var isEnv = false;
             var lastDot = rootUrl.lastIndexOf('.');
             var extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
             if (this._textureFormatInUse) {
@@ -5482,6 +5500,7 @@
                 isKTX = true;
             } else {
                 isDDS = (extension === ".dds");
+                isEnv = (extension.indexOf(".env") === 0);
             }
 
             let onerror = (request?: XMLHttpRequest, exception?: any) => {
@@ -5507,7 +5526,30 @@
                     texture.height = ktx.pixelHeight;
                     texture.isReady = true;
                 }, undefined, undefined, true, onerror);
-            } else if (isDDS) {
+            }
+            else if (isEnv) {
+                texture._isRGBM = true;
+                this._loadFile(rootUrl, (data) => {
+                    data = data as ArrayBuffer;
+                    var info = EnvironmentTextureTools.GetEnvInfo(data);
+                    if (info) {
+                        texture.width = info.width;
+                        texture.height = info.width;
+
+                        EnvironmentTextureTools.UploadPolynomials(texture, data, info!);
+                        EnvironmentTextureTools.UploadLevelsAsync(texture, data, info!).then(() => {
+                            texture.isReady = true;
+                            if (onLoad) {
+                                onLoad();
+                            }
+                        });
+                    }
+                    else if (onError) {
+                        onError("Can not parse the environment file", null);
+                    }
+                }, undefined, undefined, true, onerror);
+            }
+            else if (isDDS) {
                 if (files && files.length === 6) {
                     this._cascadeLoadFiles(
                         scene,

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

@@ -106,6 +106,7 @@
         public REFLECTIONMAP_OPPOSITEZ = false;
         public LODINREFLECTIONALPHA = false;
         public GAMMAREFLECTION = false;
+        public RGBMREFLECTION = false;
         public EQUIRECTANGULAR_RELFECTION_FOV = false;
 
         // Default BJS.
@@ -655,6 +656,7 @@
 
                         defines.REFLECTION = true;
                         defines.GAMMAREFLECTION = reflectionTexture.gammaSpace;
+                        defines.RGBMREFLECTION = reflectionTexture.isRGBM;
                         defines.REFLECTIONBLUR = this._reflectionBlur > 0;
                         defines.REFLECTIONMAP_OPPOSITEZ = this.getScene().useRightHandedSystem ? !reflectionTexture.invertZ : reflectionTexture.invertZ;
                         defines.LODINREFLECTIONALPHA = reflectionTexture.lodLevelInAlpha;
@@ -730,6 +732,7 @@
                         defines.REFLECTIONMAP_OPPOSITEZ = false;
                         defines.LODINREFLECTIONALPHA = false;
                         defines.GAMMAREFLECTION = false;
+                        defines.RGBMREFLECTION = false;
                     }
                 }
 

+ 5 - 0
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -86,6 +86,7 @@
         public REFLECTIONMAP_OPPOSITEZ = false;
         public LODINREFLECTIONALPHA = false;
         public GAMMAREFLECTION = false;
+        public RGBMREFLECTION = false;
         public RADIANCEOCCLUSION = false;
         public HORIZONOCCLUSION = false;
 
@@ -94,6 +95,7 @@
         public REFRACTIONMAP_OPPOSITEZ = false;
         public LODINREFRACTIONALPHA = false;
         public GAMMAREFRACTION = false;
+        public RGBMREFRACTION = false;
         public LINKREFRACTIONTOTRANSPARENCY = false;
 
         public INSTANCES = false;
@@ -1039,6 +1041,7 @@
                     if (reflectionTexture && StandardMaterial.ReflectionTextureEnabled) {
                         defines.REFLECTION = true;
                         defines.GAMMAREFLECTION = reflectionTexture.gammaSpace;
+                        defines.RGBMREFLECTION = reflectionTexture.isRGBM;
                         defines.REFLECTIONMAP_OPPOSITEZ = this.getScene().useRightHandedSystem ? !reflectionTexture.invertZ : reflectionTexture.invertZ;
                         defines.LODINREFLECTIONALPHA = reflectionTexture.lodLevelInAlpha;
 
@@ -1111,6 +1114,7 @@
                         defines.REFLECTIONMAP_OPPOSITEZ = false;
                         defines.LODINREFLECTIONALPHA = false;
                         defines.GAMMAREFLECTION = false;
+                        defines.RGBMREFLECTION = false;
                     }
 
                     if (this._lightmapTexture && StandardMaterial.LightmapTextureEnabled) {
@@ -1174,6 +1178,7 @@
                         defines.REFRACTION = true;
                         defines.REFRACTIONMAP_3D = refractionTexture.isCube;
                         defines.GAMMAREFRACTION = refractionTexture.gammaSpace;
+                        defines.RGBMREFRACTION = refractionTexture.isRGBM;
                         defines.REFRACTIONMAP_OPPOSITEZ = refractionTexture.invertZ;
                         defines.LODINREFRACTIONALPHA = refractionTexture.lodLevelInAlpha;
 

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

@@ -103,6 +103,13 @@
         @serialize()
         public gammaSpace = true;
 
+        /**
+         * Gets whether or not the texture contains RGBM data.
+         */
+        public get isRGBM(): boolean {
+            return this._texture != null && this._texture._isRGBM;
+        }
+
         @serialize()
         public invertZ = false;
 

+ 7 - 1
src/Materials/Textures/babylon.cubeTexture.ts

@@ -119,9 +119,10 @@
             const lastDot = rootUrl.lastIndexOf(".");
             const extension = forcedExtension ? forcedExtension : (lastDot > -1 ? rootUrl.substring(lastDot).toLowerCase() : "");
             const isDDS = (extension === ".dds");
+            const isEnv = (extension === ".env");
 
             if (!files) {
-                if (!isDDS && !extensions) {
+                if (!isEnv && !isDDS && !extensions) {
                     extensions = ["_px.jpg", "_py.jpg", "_pz.jpg", "_nx.jpg", "_ny.jpg", "_nz.jpg"];
                 }
 
@@ -138,6 +139,11 @@
             this._files = files;
 
             if (!this._texture) {
+                // Prefiltered are only available in DDS. 
+                if (!isDDS) {
+                    prefiltered = false;
+                }
+
                 if (!scene.useDelayedTextureLoading) {
                     if (prefiltered) {
                         this._texture = scene.getEngine().createPrefilteredCubeTexture(rootUrl, scene, this.lodGenerationScale, this.lodGenerationOffset, onLoad, onError, format, forcedExtension, this._createPolynomials);

+ 11 - 0
src/Materials/Textures/babylon.internalTexture.ts

@@ -201,14 +201,25 @@ module BABYLON {
         public _lodTextureMid: BaseTexture;
         /** @hidden */
         public _lodTextureLow: BaseTexture;
+        /** @hidden */
+        public _isRGBM: boolean = false;
 
         /** @hidden */
         public _webGLTexture: Nullable<WebGLTexture>;
         /** @hidden */
         public _references: number = 1;
+
         private _engine: Engine;
 
         /**
+         * Gets the Engine the texture belongs to.
+         * @returns The babylon engine
+         */
+        public getEngine(): Engine {
+            return this._engine;
+        }
+
+        /**
          * Gets the data source type of the texture (can be one of the BABYLON.InternalTexture.DATASOURCE_XXXX)
          */
         public get dataSource(): number {

+ 15 - 0
src/Shaders/ShadersInclude/helperFunctions.fx

@@ -75,4 +75,19 @@ float dither(vec2 seed, float varianceAmount) {
 	float dither = mix(-varianceAmount/255.0, varianceAmount/255.0, rand);
 	
 	return dither;
+}
+
+const float rgbmMaxRange = 255.0;
+
+vec4 toRGBM(vec3 color) {
+	vec4 rgbm = vec4(0.);
+	color *= 1.0 / rgbmMaxRange;
+	rgbm.a = clamp( max( max( color.r, color.g ), max( color.b, 0.000001 ) ), 0., 1. );
+	rgbm.a = ceil( rgbm.a * 255.0 ) / 255.0;
+	rgbm.rgb = color / rgbm.a;
+	return rgbm;
+}
+
+vec3 fromRGBM(vec4 color) {
+	return rgbmMaxRange * rgbm.rgb * rgbm.a;
 }

+ 15 - 11
src/Shaders/background.fragment.fx

@@ -137,7 +137,7 @@ void main(void) {
 #endif
 
 // _____________________________ REFLECTION ______________________________________
-vec3 reflectionColor = vec3(1., 1., 1.);
+vec4 reflectionColor = vec4(1., 1., 1., 1.);
 #ifdef REFLECTION
     vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
     #ifdef REFLECTIONMAP_OPPOSITEZ
@@ -161,41 +161,45 @@ vec3 reflectionColor = vec3(1., 1., 1.);
         #ifdef TEXTURELODSUPPORT
             // Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
             reflectionLOD = reflectionLOD * log2(vReflectionMicrosurfaceInfos.x) * vReflectionMicrosurfaceInfos.y + vReflectionMicrosurfaceInfos.z;
-            reflectionColor = sampleReflectionLod(reflectionSampler, reflectionCoords, reflectionLOD).rgb;
+            reflectionColor = sampleReflectionLod(reflectionSampler, reflectionCoords, reflectionLOD);
         #else
             float lodReflectionNormalized = clamp(reflectionLOD, 0., 1.);
             float lodReflectionNormalizedDoubled = lodReflectionNormalized * 2.0;
 
-            vec3 reflectionSpecularMid = sampleReflection(reflectionSampler, reflectionCoords).rgb;
+            vec3 reflectionSpecularMid = sampleReflection(reflectionSampler, reflectionCoords);
             if(lodReflectionNormalizedDoubled < 1.0){
                 reflectionColor = mix(
-                    sampleReflection(reflectionSamplerHigh, reflectionCoords).rgb,
+                    sampleReflection(reflectionSamplerHigh, reflectionCoords),
                     reflectionSpecularMid,
                     lodReflectionNormalizedDoubled
                 );
             } else {
                 reflectionColor = mix(
                     reflectionSpecularMid,
-                    sampleReflection(reflectionSamplerLow, reflectionCoords).rgb,
+                    sampleReflection(reflectionSamplerLow, reflectionCoords),
                     lodReflectionNormalizedDoubled - 1.0
                 );
             }
         #endif
     #else
         vec4 reflectionSample = sampleReflection(reflectionSampler, reflectionCoords);
-        reflectionColor = reflectionSample.rgb;
+        reflectionColor = reflectionSample;
+    #endif
+
+    #ifdef RGBMREFLECTION
+        reflectionColor.rgb = fromRGBM(reflectionColor);
     #endif
 
     #ifdef GAMMAREFLECTION
-        reflectionColor = toLinearSpace(reflectionColor.rgb);
+        reflectionColor.rgb = toLinearSpace(reflectionColor.rgb);
     #endif
 
     #ifdef REFLECTIONBGR
-        reflectionColor = reflectionColor.bgr;
+        reflectionColor.rgb = reflectionColor.bgr;
     #endif
 
     // _____________________________ Levels _____________________________________
-    reflectionColor *= vReflectionInfos.x;
+    reflectionColor.rgb *= vReflectionInfos.x;
 #endif
 
 // _____________________________ Diffuse Information _______________________________
@@ -221,7 +225,7 @@ float finalAlpha = alpha;
 #ifdef REFLECTIONFRESNEL
     vec3 colorBase = diffuseColor;
 #else
-    vec3 colorBase = reflectionColor * diffuseColor;
+    vec3 colorBase = reflectionColor,rgb * diffuseColor;
 #endif
     colorBase = max(colorBase, 0.0);
 
@@ -254,7 +258,7 @@ float finalAlpha = alpha;
         reflectionAmount *= reflectionDistanceFalloff;
     #endif
 
-    finalColor = mix(finalColor, reflectionColor, clamp(reflectionAmount, 0., 1.));
+    finalColor = mix(finalColor, reflectionColor.rgb, clamp(reflectionAmount, 0., 1.));
 #endif
 
 #ifdef OPACITYFRESNEL

+ 25 - 17
src/Shaders/pbr.fragment.fx

@@ -442,7 +442,7 @@ void main(void) {
 
     // _____________________________ Refraction Info _______________________________________
     #ifdef REFRACTION
-        vec3 environmentRefraction = vec3(0., 0., 0.);
+        vec4 environmentRefraction = vec4(0., 0., 0., 0.);
 
         vec3 refractionVector = refract(-viewDirectionW, normalW, vRefractionInfos.y);
         #ifdef REFRACTIONMAP_OPPOSITEZ
@@ -487,38 +487,42 @@ void main(void) {
                 float requestedRefractionLOD = refractionLOD;
             #endif
 
-            environmentRefraction = sampleRefractionLod(refractionSampler, refractionCoords, requestedRefractionLOD).rgb;
+            environmentRefraction = sampleRefractionLod(refractionSampler, refractionCoords, requestedRefractionLOD);
         #else
             float lodRefractionNormalized = clamp(refractionLOD / log2(vRefractionMicrosurfaceInfos.x), 0., 1.);
             float lodRefractionNormalizedDoubled = lodRefractionNormalized * 2.0;
 
-            vec3 environmentRefractionMid = sampleRefraction(refractionSampler, refractionCoords).rgb;
+            vec4 environmentRefractionMid = sampleRefraction(refractionSampler, refractionCoords);
             if(lodRefractionNormalizedDoubled < 1.0){
                 environmentRefraction = mix(
-                    sampleRefraction(refractionSamplerHigh, refractionCoords).rgb,
+                    sampleRefraction(refractionSamplerHigh, refractionCoords),
                     environmentRefractionMid,
                     lodRefractionNormalizedDoubled
                 );
             }else{
                 environmentRefraction = mix(
                     environmentRefractionMid,
-                    sampleRefraction(refractionSamplerLow, refractionCoords).rgb,
+                    sampleRefraction(refractionSamplerLow, refractionCoords),
                     lodRefractionNormalizedDoubled - 1.0
                 );
             }
         #endif
 
         #ifdef GAMMAREFRACTION
-            environmentRefraction = toLinearSpace(environmentRefraction.rgb);
+            environmentRefraction.rgb = fromRGBM(environmentRefraction);
+        #endif
+
+        #ifdef RGBMREFRACTION
+            environmentRefraction.rgb = toLinearSpace(environmentRefraction.rgb);
         #endif
 
         // _____________________________ Levels _____________________________________
-        environmentRefraction *= vRefractionInfos.x;
+        environmentRefraction.rgb *= vRefractionInfos.x;
     #endif
 
     // _____________________________ Reflection Info _______________________________________
     #ifdef REFLECTION
-        vec3 environmentRadiance = vec3(0., 0., 0.);
+        vec4 environmentRadiance = vec4(0., 0., 0., 0.);
         vec3 environmentIrradiance = vec3(0., 0., 0.);
 
         vec3 reflectionVector = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
@@ -564,7 +568,7 @@ void main(void) {
                 float requestedReflectionLOD = reflectionLOD;
             #endif
 
-            environmentRadiance = sampleReflectionLod(reflectionSampler, reflectionCoords, requestedReflectionLOD).rgb;
+            environmentRadiance = sampleReflectionLod(reflectionSampler, reflectionCoords, requestedReflectionLOD);
         #else
             float lodReflectionNormalized = clamp(reflectionLOD / log2(vReflectionMicrosurfaceInfos.x), 0., 1.);
             float lodReflectionNormalizedDoubled = lodReflectionNormalized * 2.0;
@@ -572,21 +576,25 @@ void main(void) {
             vec3 environmentSpecularMid = sampleReflection(reflectionSampler, reflectionCoords).rgb;
             if(lodReflectionNormalizedDoubled < 1.0){
                 environmentRadiance = mix(
-                    sampleReflection(reflectionSamplerHigh, reflectionCoords).rgb,
+                    sampleReflection(reflectionSamplerHigh, reflectionCoords),
                     environmentSpecularMid,
                     lodReflectionNormalizedDoubled
                 );
             }else{
                 environmentRadiance = mix(
                     environmentSpecularMid,
-                    sampleReflection(reflectionSamplerLow, reflectionCoords).rgb,
+                    sampleReflection(reflectionSamplerLow, reflectionCoords),
                     lodReflectionNormalizedDoubled - 1.0
                 );
             }
         #endif
 
+        #ifdef RGBMREFLECTION
+            environmentRadiance.rgb = fromRGBM(environmentRadiance);
+        #endif
+
         #ifdef GAMMAREFLECTION
-            environmentRadiance = toLinearSpace(environmentRadiance.rgb);
+            environmentRadiance.rgb = toLinearSpace(environmentRadiance.rgb);
         #endif
 
         // _____________________________ Irradiance ________________________________
@@ -603,8 +611,8 @@ void main(void) {
         #endif
 
         // _____________________________ Levels _____________________________________
-        environmentRadiance *= vReflectionInfos.x;
-        environmentRadiance *= vReflectionColor.rgb;
+        environmentRadiance.rgb *= vReflectionInfos.x;
+        environmentRadiance.rgb *= vReflectionColor.rgb;
         environmentIrradiance *= vReflectionColor.rgb;
     #endif
 
@@ -691,7 +699,7 @@ void main(void) {
             environmentIrradiance *= alpha;
 
             // Tint reflectance
-            environmentRefraction *= tint;
+            environmentRefraction.rgb *= tint;
 
             // Put alpha back to 1;
             alpha = 1.0;
@@ -731,7 +739,7 @@ void main(void) {
 
     // _____________________________ Radiance ________________________________________
     #ifdef REFLECTION
-        vec3 finalRadiance = environmentRadiance;
+        vec3 finalRadiance = environmentRadiance.rgb;
         finalRadiance *= specularEnvironmentReflectance;
 
         // Full value needed for alpha. 
@@ -740,7 +748,7 @@ void main(void) {
 
     // _____________________________ Refraction ______________________________________
     #ifdef REFRACTION
-        vec3 finalRefraction = environmentRefraction;
+        vec3 finalRefraction = environmentRefraction.rgb;
         finalRefraction *= refractance;
     #endif
 

+ 279 - 0
src/Tools/babylon.environmentTextureTools.ts

@@ -0,0 +1,279 @@
+module BABYLON {
+    /**
+     * Raw texture data and descriptor sufficient for WebGL texture upload
+     */
+    export interface EnvironmentTextureInfo {
+        /**
+         * Version of the environment map
+         */
+        version: number;
+
+        /**
+         * Width of image
+         */
+        width: number;
+
+        /**
+         * Irradiance information stored in the file.
+         */
+        irradiance: any;
+
+        /**
+         * Radiance information stored in the file.
+         */
+        radiance: any;
+
+        /**
+         * Specular information stored in the file.
+         */
+        specular: any;
+    }
+
+    interface EnvironmentTextureIrradianceInfoV1 {
+        l00: Array<number>;
+
+        l1_1: Array<number>;
+        l10: Array<number>;
+        l11: Array<number>;
+
+        l2_2: Array<number>;
+        l2_1: Array<number>;
+        l20: Array<number>;
+        l21: Array<number>;
+        l22: Array<number>;
+    }
+
+    interface BufferImageData {
+        length: number;
+        position: number;
+    }
+
+    interface EnvironmentTextureSpecularInfoV1 {
+        mipmaps: Array<BufferImageData>
+    }
+
+    export class EnvironmentTextureTools {
+
+        public static GetEnvInfo(data: ArrayBuffer): Nullable<EnvironmentTextureInfo> {
+            // Close to network
+            let littleEndian = false;
+
+            let magicBytes = [0x86, 0x16, 0x87, 0x96, 0xf6, 0xd6, 0x96, 0x36];
+
+            let dataView = new DataView(data);
+            let pos = 0;
+
+            for (let i = 0; i < magicBytes.length; i++) {
+                if (dataView.getUint8(pos++) !== magicBytes[i]) {
+                    Tools.Error('Not a babylon environment map');
+                    return null;
+                }
+            }
+
+            let version = dataView.getUint16(pos,  littleEndian); pos += 2;
+            if (version !== 1) {
+                Tools.Warn('Unsupported babylon environment map version "' + version + '"');
+            }
+
+            // Read json manifest - collect characters up to null terminator
+            let manifestString = '';
+            let charCode = 0x00;
+            while ((charCode = dataView.getUint8(pos++))) {
+                manifestString += String.fromCharCode(charCode);
+            }
+
+            let manifest: EnvironmentTextureInfo = JSON.parse(manifestString);
+            return manifest;
+        }
+
+        public static UploadLevelsAsync(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): Promise<void[]> {
+            if (info.version !== 1) {
+                Tools.Warn('Unsupported babylon environment map version "' + info.version + '"');
+            }
+
+            var specularInfo = info.specular as EnvironmentTextureSpecularInfoV1;
+            if (!specularInfo) {
+                return Promise.resolve([]);
+            }
+
+            var mipmapsCount = Scalar.Log2(info.width);
+            if (specularInfo.mipmaps.length !== 6 * mipmapsCount) {
+                Tools.Warn('Unsupported specular mipmaps number "' + specularInfo.mipmaps.length + '"');
+            }
+
+            var engine = texture.getEngine();
+            var textureType = Engine.TEXTURETYPE_UNSIGNED_INT;
+            var targetTextureType = Engine.TEXTURETYPE_UNSIGNED_INT;
+            var expandTexture = false;
+            if (engine.getCaps().textureHalfFloatRender) {
+                targetTextureType = Engine.TEXTURETYPE_HALF_FLOAT;
+                expandTexture = true;
+            }
+            texture.type = textureType;
+            texture.format = Engine.TEXTUREFORMAT_RGBA;
+            texture.invertY = false;
+            texture._isRGBM = true;
+
+            var promises: Promise<void>[] = [];
+            // All mipmaps
+            for (let i = 0; i < mipmapsCount; i++) {
+                // All faces
+                for (let face = 0; face < 6; face++) {
+                    const imageData = specularInfo.mipmaps[i * 6 + face];
+                    let bytes = new Uint8Array(arrayBuffer, imageData.position, imageData.length);
+                    //construct image element from bytes
+                    let image = new Image();
+                    let src = URL.createObjectURL(new Blob([bytes], { type: 'image/png' }));
+                    image.src = src;
+
+                    // Enqueue promise to upload to the texture.
+                    var promise = new Promise<void>((resolve, reject) => {;
+                        image.onload = () => {
+                            engine._uploadImageToTexture(texture, face, i, image);
+                            resolve();
+                        };
+                        image.onerror = (error) => {
+                            reject(error);
+                        };
+                    });
+                    promises.push(promise);
+                }
+            }
+
+            // if (expandTexture) {
+            //     return Promise.all(promises).then(() => {
+            //         return this._expandTexture(texture, targetTextureType);
+            //     });
+            // }
+            // else {
+                return Promise.all(promises);
+            //}
+        }
+
+        public static UploadPolynomials(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): void {
+            if (info.version !== 1) {
+                Tools.Warn('Unsupported babylon environment map version "' + info.version + '"');
+            }
+
+            var irradianceInfo = info.irradiance as EnvironmentTextureIrradianceInfoV1;
+            if (!irradianceInfo) {
+                return;
+            }
+            
+            //irradiance
+            EnvironmentTextureTools._ConvertSHIrradianceToLambertianRadiance(irradianceInfo);
+
+            //harmonics now represent radiance
+            texture._sphericalPolynomial = new SphericalPolynomial();
+            EnvironmentTextureTools._ConvertSHToSP(irradianceInfo, texture._sphericalPolynomial);
+        }
+
+        /**
+         * 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.
+         * @param harmonics Spherical harmonic coefficients (9)
+         */
+        private static _ConvertSHIrradianceToLambertianRadiance(harmonics: any): void {
+            const scaleFactor = 1 / 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).
+            harmonics.l00[0] *= scaleFactor;
+            harmonics.l00[1] *= scaleFactor;
+            harmonics.l00[2] *= scaleFactor;
+            harmonics.l1_1[0] *= scaleFactor;
+            harmonics.l1_1[1] *= scaleFactor;
+            harmonics.l1_1[2] *= scaleFactor;
+            harmonics.l10[0] *= scaleFactor;
+            harmonics.l10[1] *= scaleFactor;
+            harmonics.l10[2] *= scaleFactor;
+            harmonics.l11[0] *= scaleFactor;
+            harmonics.l11[1] *= scaleFactor;
+            harmonics.l11[2] *= scaleFactor;
+            harmonics.l2_2[0] *= scaleFactor;
+            harmonics.l2_2[1] *= scaleFactor;
+            harmonics.l2_2[2] *= scaleFactor;
+            harmonics.l2_1[0] *= scaleFactor;
+            harmonics.l2_1[1] *= scaleFactor;
+            harmonics.l2_1[2] *= scaleFactor;
+            harmonics.l20[0] *= scaleFactor;
+            harmonics.l20[1] *= scaleFactor;
+            harmonics.l20[2] *= scaleFactor;
+            harmonics.l21[0] *= scaleFactor;
+            harmonics.l21[1] *= scaleFactor;
+            harmonics.l21[2] *= scaleFactor;
+            harmonics.l22[0] *= scaleFactor;
+            harmonics.l22[1] *= scaleFactor;
+            harmonics.l22[2] *= scaleFactor;
+        }
+
+        /**
+         * Convert spherical harmonics to spherical polynomial coefficients
+         * @param harmonics Spherical harmonic coefficients (9)
+         * @param outPolynomialCoefficents Polynomial coefficients (9) object to store result
+         */
+        private static _ConvertSHToSP(harmonics: any, outPolynomialCoefficents: SphericalPolynomial) {
+            const rPi = 1 / Math.PI;
+
+            //x
+            outPolynomialCoefficents.x.x = 1.02333 * harmonics.l11[0] * rPi;
+            outPolynomialCoefficents.x.y = 1.02333 * harmonics.l11[1] * rPi;
+            outPolynomialCoefficents.x.z = 1.02333 * harmonics.l11[2] * rPi;
+
+            outPolynomialCoefficents.y.x = 1.02333 * harmonics.l1_1[0] * rPi;
+            outPolynomialCoefficents.y.y = 1.02333 * harmonics.l1_1[1] * rPi;
+            outPolynomialCoefficents.y.z = 1.02333 * harmonics.l1_1[2] * rPi;
+
+            outPolynomialCoefficents.z.x = 1.02333 * harmonics.l10[0] * rPi;
+            outPolynomialCoefficents.z.y = 1.02333 * harmonics.l10[1] * rPi;
+            outPolynomialCoefficents.z.z = 1.02333 * harmonics.l10[2] * rPi;
+
+            //xx
+            outPolynomialCoefficents.xx.x = (0.886277 * harmonics.l00[0] - 0.247708 * harmonics.l20[0] + 0.429043 * harmonics.l22[0]) * rPi;
+            outPolynomialCoefficents.xx.y = (0.886277 * harmonics.l00[1] - 0.247708 * harmonics.l20[1] + 0.429043 * harmonics.l22[1]) * rPi;
+            outPolynomialCoefficents.xx.z = (0.886277 * harmonics.l00[2] - 0.247708 * harmonics.l20[2] + 0.429043 * harmonics.l22[2]) * rPi;
+
+            outPolynomialCoefficents.yy.x = (0.886277 * harmonics.l00[0] - 0.247708 * harmonics.l20[0] - 0.429043 * harmonics.l22[0]) * rPi;
+            outPolynomialCoefficents.yy.y = (0.886277 * harmonics.l00[1] - 0.247708 * harmonics.l20[1] - 0.429043 * harmonics.l22[1]) * rPi;
+            outPolynomialCoefficents.yy.z = (0.886277 * harmonics.l00[2] - 0.247708 * harmonics.l20[2] - 0.429043 * harmonics.l22[2]) * rPi;
+
+            outPolynomialCoefficents.zz.x = (0.886277 * harmonics.l00[0] + 0.495417 * harmonics.l20[0]) * rPi;
+            outPolynomialCoefficents.zz.y = (0.886277 * harmonics.l00[1] + 0.495417 * harmonics.l20[1]) * rPi;
+            outPolynomialCoefficents.zz.z = (0.886277 * harmonics.l00[2] + 0.495417 * harmonics.l20[2]) * rPi;
+
+            //yz
+            outPolynomialCoefficents.yz.x = 0.858086 * harmonics.l2_1[0] * rPi;
+            outPolynomialCoefficents.yz.y = 0.858086 * harmonics.l2_1[1] * rPi;
+            outPolynomialCoefficents.yz.z = 0.858086 * harmonics.l2_1[2] * rPi;
+
+            outPolynomialCoefficents.zx.x = 0.858086 * harmonics.l21[0] * rPi;
+            outPolynomialCoefficents.zx.y = 0.858086 * harmonics.l21[1] * rPi;
+            outPolynomialCoefficents.zx.z = 0.858086 * harmonics.l21[2] * rPi;
+
+            outPolynomialCoefficents.xy.x = 0.858086 * harmonics.l2_2[0] * rPi;
+            outPolynomialCoefficents.xy.y = 0.858086 * harmonics.l2_2[1] * rPi;
+            outPolynomialCoefficents.xy.z = 0.858086 * harmonics.l2_2[2] * rPi;
+        }
+    }
+
+    export class EnvironmentTexture extends CubeTexture {
+        
+        constructor(url: string, scene: Scene) {
+           super(url, scene, null, false, null, null, null, undefined, true, ".env", false);
+
+
+        }
+
+        public clone(): EnvironmentTexture {
+            return SerializationHelper.Clone(() => {
+                let scene = this.getScene();
+
+                if (!scene) {
+                    return this;
+                }
+                return new EnvironmentTexture(this.url, scene);
+            }, this);
+        }
+    }
+}