Sfoglia il codice sorgente

Cube Texture Fixed Sampler Generation

Sebastien Vandenberghe 8 anni fa
parent
commit
26d6d5b0a9

+ 13 - 29
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -1124,33 +1124,13 @@
                             this._uniformBuffer.setTexture("reflectionSampler", reflectionTexture);
                         }
                         else {
-                            this._uniformBuffer.setTexture("reflectionSampler", reflectionTexture);
-                            this._uniformBuffer.setTexture("reflectionSamplerLow", reflectionTexture);
-                            this._uniformBuffer.setTexture("reflectionSamplerHigh", reflectionTexture);
-
-                            const mipSlices = 3;
-                            const reflectionTextureWidth = reflectionTexture.getSize().width;
-                            for (let i = 0; i < mipSlices; i++) {
-                                //initialize lod texture if it doesn't already exist
-                                if (lod == null && reflectionTextureWidth) {
-                                    //compute LOD from even spacing in smoothness (matching shader calculation)
-                                    let smoothness = i / (mipSlices - 1);
-                                    let roughness = 1 - smoothness;
-                                    const kMinimumVariance = 0.0005;
-                                    let alphaG = roughness * roughness + kMinimumVariance;
-                                    let microsurfaceAverageSlopeTexels = alphaG * reflectionTextureWidth;
-
-                                    let environmentSpecularLOD = reflectionTexture.lodGenerationScale * ( MathTools.Log2(microsurfaceAverageSlopeTexels)) + reflectionTexture.lodGenerationOffset;
-
-                                    let maxLODIndex = MathTools.Log2(reflectionTextureWidth);
-                                    let mipmapIndex = Math.min(Math.max(Math.round(environmentSpecularLOD), 0), maxLODIndex);
-
-
-                                    lod = TextureUtils.GetBabylonCubeTexture(this._engineScene, new TextureCube(PixelFormat.RGBA, PixelType.UNSIGNED_BYTE, [environmentSpecular.source[mipmapIndex]]), Scene._EnvironmentSingleMipSampling, false);
-                                    (<any>environmentSpecular)[lodKey] = lod;
-                                }
-                                
+                            if (!reflectionTexture._lodTextureMid) {
+                                refractionTexture._generateFixedLodSamplers();
                             }
+
+                            this._uniformBuffer.setTexture("reflectionSampler", reflectionTexture._lodTextureMid || reflectionTexture);
+                            this._uniformBuffer.setTexture("reflectionSamplerLow", reflectionTexture._lodTextureLow || reflectionTexture);
+                            this._uniformBuffer.setTexture("reflectionSamplerHigh", reflectionTexture._lodTextureHigh || reflectionTexture);
                         }
                     }
 
@@ -1159,9 +1139,13 @@
                             this._uniformBuffer.setTexture("refractionSampler", refractionTexture);
                         }
                         else {
-                            this._uniformBuffer.setTexture("refractionSampler", refractionTexture);
-                            this._uniformBuffer.setTexture("refractionSamplerLow", refractionTexture);
-                            this._uniformBuffer.setTexture("refractionSamplerHigh", refractionTexture);
+                            if (!refractionTexture._lodTextureMid) {
+                                refractionTexture._generateFixedLodSamplers();
+                            }
+
+                            this._uniformBuffer.setTexture("refractionSampler", refractionTexture._lodTextureMid || refractionTexture);
+                            this._uniformBuffer.setTexture("refractionSamplerLow", refractionTexture._lodTextureLow || refractionTexture);
+                            this._uniformBuffer.setTexture("refractionSamplerHigh", refractionTexture._lodTextureHigh || refractionTexture);
                         }
                     }
 

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

@@ -105,6 +105,12 @@
         public _texture: WebGLTexture;
         private _uid: string;
 
+        // 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.
+        public _lodTextureHigh: BaseTexture = null;
+        public _lodTextureMid: BaseTexture = null;
+        public _lodTextureLow: BaseTexture = null;
+
         public get isBlocking(): boolean {
             return true;
         }
@@ -221,6 +227,14 @@
             return (this._texture.type !== undefined) ? this._texture.type : Engine.TEXTURETYPE_UNSIGNED_INT;
         }
 
+        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, lodIndex = 0): ArrayBufferView {
             if (!this._texture) {
                 return null;
@@ -256,7 +270,60 @@
             return this._texture._sphericalPolynomial;
         }
 
+        public setSphericalPolynomial(value: SphericalPolynomial) {
+            if (this._texture) {
+                this._texture._sphericalPolynomial = value;
+            }
+        }
+
+        public _generateFixedLodSamplers(): void {
+            // Only Cube Texture Supports so far as this is dedicated to PBR reflection
+            // and refraction. This should be open in case of user request.
+            if (!this.isCube) {
+                return;
+            }
+
+            const mipSlices = 3;
+            const width = this.getSize().width;
+            if (!width) {
+                return;
+            }
+
+            const textures: BaseTexture[] = [];
+            const engine = this._scene.getEngine();
+            for (let i = 0; i < mipSlices; i++) {
+                //compute LOD from even spacing in smoothness (matching PBR shader calculation)
+                let smoothness = i / (mipSlices - 1);
+                let roughness = 1 - smoothness;
+                const kMinimumVariance = 0.0005;
+                let alphaG = roughness * roughness + kMinimumVariance;
+                let microsurfaceAverageSlopeTexels = alphaG * width;
+
+                let environmentSpecularLOD = this.lodGenerationScale * (MathTools.Log2(microsurfaceAverageSlopeTexels)) + this.lodGenerationOffset;
+
+                let maxLODIndex = MathTools.Log2(width);
+                let mipmapIndex = Math.min(Math.max(Math.round(environmentSpecularLOD), 0), maxLODIndex);
+
+                textures[i] = engine._createCubeTextureFromLOD(this, this.name + i, mipmapIndex);
+            }
+
+            this._lodTextureHigh = textures[2];
+            this._lodTextureMid = textures[1];
+            this._lodTextureLow = textures[0];
+        }
+
         public dispose(): void {
+            // Intergated fixed lod samplers.
+            if (this._lodTextureHigh) {
+                this._lodTextureHigh.dispose();
+            }
+            if (this._lodTextureMid) {
+                this._lodTextureMid.dispose();
+            }
+            if (this._lodTextureLow) {
+                this._lodTextureLow.dispose();
+            }
+
             // Animations
             this.getScene().stopAnimation(this);
 

+ 24 - 106
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.
          */
@@ -161,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.setSphericalPolynomial(sphericalPolynomial);
 
                 // Fill pixel data.
                 mipLevels = intArrayView[29]; // Number of mip levels.
@@ -189,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);
@@ -231,9 +230,6 @@ module BABYLON {
                             }
                         }
                     }
-                    else {
-                        dataFace = data[k];
-                    }
 
                     // Fill the array accordingly.
                     if (byteArray) {
@@ -265,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.setSphericalPolynomial(sphericalPolynomial);
                 }
 
                 var results = [];
@@ -491,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;
         }
     }
 }

+ 50 - 39
src/babylon.engine.ts

@@ -3277,22 +3277,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);
                 }
             }
 
@@ -3311,6 +3306,7 @@
             var texture = gl.createTexture();
             texture.isCube = true;
             texture.references = 1;
+            texture.noMipmap = !generateMipMaps;
 
             var textureType = this._getWebGLTextureType(type);
             var internalFormat = this._getInternalFormat(format);
@@ -3365,8 +3361,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;
         }
 
@@ -3382,6 +3376,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);
@@ -3391,19 +3386,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);
@@ -3416,28 +3403,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);
                         }
                     }
 
@@ -3445,7 +3421,7 @@
                 }
                 else {
                     texture.generateMipMaps = !noMipmap;
-                    this.updateRawCubeTexture(texture, rgbeDataArrays, format, type, invertY);
+                    this.updateRawCubeTexture(texture, faceDataArrays, format, type, invertY);
                 }
 
                 texture.isReady = true;
@@ -3994,7 +3970,42 @@
             gl.bindFramebuffer(gl.FRAMEBUFFER, null);
 
             return buffer;
-        }    
+        }
+
+        public _createCubeTextureFromLOD(texture: BaseTexture, name: string, lodIndex: number): BaseTexture {
+            if (!texture.isCube) {
+                return null;
+            }
+
+            const gl = this._gl;
+            const maxSize = texture.getSize().width;
+            const numLod = MathTools.Log2(maxSize);
+            const targetSize = Math.pow(2, numLod - lodIndex);
+
+            var data: ArrayBufferView[] = [];
+            for (var i = 0; i < 6; i++) {
+                data.push(texture.readPixels(i, lodIndex));
+            }
+
+            const glTextureFromLod = this.createRawCubeTexture(data, targetSize, texture.textureFormat, texture.textureType, false, false, texture._texture.samplingMode);
+
+            // Wrap in a base texture for easy binding.
+            var lodTexture = new BaseTexture(texture.getScene());
+            lodTexture.isCube = true;
+            lodTexture.anisotropicFilteringLevel = texture.anisotropicFilteringLevel;
+            lodTexture.coordinatesIndex = texture.coordinatesIndex;
+            lodTexture.coordinatesMode = texture.coordinatesMode;
+            lodTexture.gammaSpace = texture.gammaSpace;
+            lodTexture.getAlphaFromRGB = texture.getAlphaFromRGB;
+            lodTexture.hasAlpha = texture.hasAlpha;
+            lodTexture.invertZ = texture.invertZ;
+            lodTexture.level = texture.level;
+            lodTexture.name = name;
+            lodTexture.wrapU = texture.wrapU;
+            lodTexture.wrapV = texture.wrapV;
+
+            return lodTexture;
+        }
 
         private _canRenderToFloatFramebuffer(): boolean {
             if (this._webGLVersion > 1) {

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