Parcourir la source

Update EXT_lights_imageBased to support partial mipmaps

Gary Hsu il y a 7 ans
Parent
commit
e86f952a97

+ 5 - 3
loaders/src/glTF/2.0/Extensions/EXT_lights_imageBased.ts

@@ -10,6 +10,7 @@ module BABYLON.GLTF2.Extensions {
     interface ILight extends IChildRootProperty {
         intensity: number;
         rotation: number[];
+        specularImageSize: number;
         specularImages: number[][];
         irradianceCoefficients: number[][];
 
@@ -73,8 +74,7 @@ module BABYLON.GLTF2.Extensions {
                 this._loader._parent._logClose();
 
                 light._loaded = Promise.all(promises).then(() => {
-                    const size = Math.pow(2, imageData.length - 1);
-                    const babylonTexture = new RawCubeTexture(this._loader._babylonScene, null, size);
+                    const babylonTexture = new RawCubeTexture(this._loader._babylonScene, null, light.specularImageSize);
                     light._babylonTexture = babylonTexture;
 
                     if (light.intensity != undefined) {
@@ -98,7 +98,9 @@ module BABYLON.GLTF2.Extensions {
                     sphericalHarmonics.convertIrradianceToLambertianRadiance();
                     const sphericalPolynomial = SphericalPolynomial.FromHarmonics(sphericalHarmonics);
 
-                    return babylonTexture.updateRGBDAsync(imageData, sphericalPolynomial);
+                    // Compute the lod generation scale to fit exactly to the number of levels available.
+                    const lodGenerationScale = (imageData.length - 1) / Scalar.Log2(light.specularImageSize);
+                    return babylonTexture.updateRGBDAsync(imageData, sphericalPolynomial, lodGenerationScale);
                 });
             }
 

+ 1 - 1
sandbox/index.js

@@ -166,7 +166,7 @@ if (BABYLON.Engine.isSupported()) {
                 currentScene.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(skyboxPath, currentScene);
             }
 
-            currentSkybox = currentScene.createDefaultSkybox(currentScene.environmentTexture, true, (currentScene.activeCamera.maxZ - currentScene.activeCamera.minZ) / 2, 0.3, false);
+            currentSkybox = currentScene.createDefaultSkybox(currentScene.environmentTexture, true, (currentScene.activeCamera.maxZ - currentScene.activeCamera.minZ) / 2, 0, false);
         }
         else {
             currentScene.createDefaultLight();

+ 18 - 15
src/Engine/babylon.engine.ts

@@ -5227,7 +5227,7 @@
         }
 
         /** @hidden */
-        public _uploadDataToTextureDirectly(texture: InternalTexture, width: number, height: number, imageData: ArrayBufferView, faceIndex: number = 0, lod: number = 0): void {
+        public _uploadDataToTextureDirectly(texture: InternalTexture, imageData: ArrayBufferView, faceIndex: number = 0, lod: number = 0): void {
             var gl = this._gl;
 
             var textureType = this._getWebGLTextureType(texture.type);
@@ -5241,6 +5241,11 @@
                 target = gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex;
             }
 
+            const lodMaxWidth = Math.round(Scalar.Log2(texture.width));
+            const lodMaxHeight = Math.round(Scalar.Log2(texture.height));
+            const width = Math.pow(2, Math.max(lodMaxWidth - lod, 0));
+            const height = Math.pow(2, Math.max(lodMaxHeight - lod, 0));
+
             gl.texImage2D(target, lod, internalFormat, width, height, 0, format, textureType, imageData);
         }
 
@@ -5251,7 +5256,7 @@
 
             this._bindTextureDirectly(bindTarget, texture, true);
 
-            this._uploadDataToTextureDirectly(texture, texture.width, texture.height, imageData, faceIndex, lod);
+            this._uploadDataToTextureDirectly(texture, imageData, faceIndex, lod);
 
             this._bindTextureDirectly(bindTarget, null, true);
         }
@@ -5569,6 +5574,10 @@
                                 let data = imgs[index];
                                 info = DDSTools.GetDDSInfo(data);
 
+                                texture.width = info.width;
+                                texture.height = info.height;
+                                width = info.width;
+
                                 loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
 
                                 this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
@@ -5579,11 +5588,6 @@
                                 if (!noMipmap && !info.isFourCC && info.mipmapCount === 1) {
                                     gl.generateMipmap(gl.TEXTURE_CUBE_MAP);
                                 }
-
-                                texture.width = info.width;
-                                texture.height = info.height;
-                                texture.type = info.textureType;
-                                width = info.width;
                             }
 
                             this.setCubeMapTextureParams(gl, loadMipmap);
@@ -5595,15 +5599,18 @@
                         },
                         files,
                         onError);
-
                 } else {
                     this._loadFile(rootUrl,
                         data => {
                             var info = DDSTools.GetDDSInfo(data);
-                            if(createPolynomials){
+
+                            texture.width = info.width;
+                            texture.height = info.height;
+
+                            if (createPolynomials) {
                                 info.sphericalPolynomial = new SphericalPolynomial();
                             }
-                            
+
                             var loadMipmap = (info.isRGB || info.isLuminance || info.mipmapCount > 1) && !noMipmap;
 
                             this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture, true);
@@ -5616,12 +5623,8 @@
                             }
 
                             this.setCubeMapTextureParams(gl, loadMipmap);
-
-                            texture.width = info.width;
-                            texture.height = info.height;
                             texture.isReady = true;
-                            texture.type = info.textureType;
-                            
+
                             if (onLoad) {
                                 onLoad({ isDDS: true, width: info.width, info, data, texture });
                             }

+ 1 - 1
src/Engine/babylon.nullEngine.ts

@@ -472,7 +472,7 @@
         }
 
         /** @hidden */
-        public _uploadDataToTextureDirectly(texture: InternalTexture, width: number, height: number, imageData: ArrayBufferView, faceIndex: number = 0, lod: number = 0): void {
+        public _uploadDataToTextureDirectly(texture: InternalTexture, imageData: ArrayBufferView, faceIndex: number = 0, lod: number = 0): void {
         }
 
         /** @hidden */

+ 4 - 4
src/Tools/babylon.dds.ts

@@ -525,7 +525,7 @@
                             }
 
                             if (floatArray) {
-                                engine._uploadDataToTextureDirectly(texture, width, height, floatArray, face, i);
+                                engine._uploadDataToTextureDirectly(texture, floatArray, face, i);
                             }
                         } else if (info.isRGB) {
                             texture.type = Engine.TEXTURETYPE_UNSIGNED_INT;
@@ -533,12 +533,12 @@
                                 texture.format = Engine.TEXTUREFORMAT_RGB;
                                 dataLength = width * height * 3;
                                 byteArray = DDSTools._GetRGBArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, rOffset, gOffset, bOffset);
-                                engine._uploadDataToTextureDirectly(texture, width, height, byteArray, face, i);
+                                engine._uploadDataToTextureDirectly(texture, byteArray, face, i);
                             } else { // 32
                                 texture.format = Engine.TEXTUREFORMAT_RGBA;
                                 dataLength = width * height * 4;
                                 byteArray = DDSTools._GetRGBAArrayBuffer(width, height, dataOffset, dataLength, arrayBuffer, rOffset, gOffset, bOffset, aOffset);
-                                engine._uploadDataToTextureDirectly(texture, width, height, byteArray, face, i);
+                                engine._uploadDataToTextureDirectly(texture, byteArray, face, i);
                             }
                         } else if (info.isLuminance) {
                             var unpackAlignment = engine._getUnpackAlignement();
@@ -550,7 +550,7 @@
                             texture.format = Engine.TEXTUREFORMAT_LUMINANCE;
                             texture.type = Engine.TEXTURETYPE_UNSIGNED_INT;
 
-                            engine._uploadDataToTextureDirectly(texture, width, height, byteArray, face, i);
+                            engine._uploadDataToTextureDirectly(texture, byteArray, face, i);
                         } else {
                             dataLength = Math.max(4, width) / 4 * Math.max(4, height) / 4 * blockBytes;
                             byteArray = new Uint8Array(arrayBuffer, dataOffset, dataLength);

+ 49 - 10
src/Tools/babylon.environmentTextureTools.ts

@@ -52,6 +52,10 @@ module BABYLON {
          * This contains all the images data needed to reconstruct the cubemap.
          */
         mipmaps: Array<BufferImageData>
+        /**
+         * Defines the scale applied to environment texture. This manages the range of LOD level used for IBL according to the roughness.
+         */
+        lodGenerationScale: number
     }
 
     /**
@@ -110,6 +114,7 @@ module BABYLON {
             if (manifest.specular) {
                 // Extend the header with the position of the payload.
                 manifest.specular.specularDataPosition = pos;
+                manifest.specular.lodGenerationScale = manifest.specular.lodGenerationScale || 0.8;
             }
 
             return manifest;
@@ -211,7 +216,8 @@ module BABYLON {
                     width: cubeWidth,
                     irradiance: this._CreateEnvTextureIrradiance(texture),
                     specular: {
-                        mipmaps: []
+                        mipmaps: [],
+                        lodGenerationScale: texture.lodGenerationScale
                     }
                 };
 
@@ -303,7 +309,7 @@ module BABYLON {
          */
         public static UploadEnvLevelsAsync(texture: InternalTexture, arrayBuffer: any, info: EnvironmentTextureInfo): Promise<void> {
             if (info.version !== 1) {
-                Tools.Warn('Unsupported babylon environment map version "' + info.version + '"');
+                throw new Error(`Unsupported babylon environment map version "${info.version}"`);
             }
 
             let specularInfo = info.specular as EnvironmentTextureSpecularInfoV1;
@@ -316,9 +322,11 @@ module BABYLON {
             let mipmapsCount = Scalar.Log2(info.width);
             mipmapsCount = Math.round(mipmapsCount) + 1;
             if (specularInfo.mipmaps.length !== 6 * mipmapsCount) {
-                Tools.Warn('Unsupported specular mipmaps number "' + specularInfo.mipmaps.length + '"');
+                throw new Error(`Unsupported specular mipmaps number "${specularInfo.mipmaps.length}"`);
             }
 
+            texture._lodGenerationScale = specularInfo.lodGenerationScale;
+
             const imageData = new Array<Array<ArrayBufferView>>(mipmapsCount);
             for (let i = 0; i < mipmapsCount; i++) {
                 imageData[i] = new Array<ArrayBufferView>(6);
@@ -338,7 +346,11 @@ module BABYLON {
          * @returns a promise
          */
         public static UploadLevelsAsync(texture: InternalTexture, imageData: ArrayBufferView[][]): Promise<void> {
-            const mipmapsCount = imageData.length;
+            if (!Tools.IsExponentOfTwo(texture.width)) {
+                throw new Error("Texture size must be a power of two");
+            }
+
+            const mipmapsCount = Math.round(Scalar.Log2(texture.width)) + 1;
 
             // Gets everything ready.
             let engine = texture.getEngine();
@@ -351,7 +363,8 @@ module BABYLON {
 
             texture.format = Engine.TEXTUREFORMAT_RGBA;
             texture.type = Engine.TEXTURETYPE_UNSIGNED_INT;
-            texture.samplingMode = Texture.TRILINEAR_SAMPLINGMODE;
+            texture.generateMipMaps = true;
+            engine.updateTextureSamplingMode(Texture.TRILINEAR_SAMPLINGMODE, texture);
 
             // Add extra process if texture lod is not supported
             if (!caps.textureLOD) {
@@ -439,8 +452,8 @@ module BABYLON {
             }
 
             let promises: Promise<void>[] = [];
-            // All mipmaps
-            for (let i = 0; i < mipmapsCount; i++) {
+            // All mipmaps up to provided number of images
+            for (let i = 0; i < imageData.length; i++) {
                 // All faces
                 for (let face = 0; face < 6; face++) {
                     // Constructs an image element from image data
@@ -479,7 +492,7 @@ module BABYLON {
                             else {
                                 engine._uploadImageToTexture(texture, image, face, i);
 
-                                // Upload the face to the none lod texture support
+                                // Upload the face to the non lod texture support
                                 if (generateNonLODTextures) {
                                     let lodTexture = lodTextures![i];
                                     if (lodTexture) {
@@ -497,14 +510,40 @@ module BABYLON {
                 }
             }
 
+            // Fill remaining mipmaps with black textures.
+            if (imageData.length < mipmapsCount) {
+                let data: ArrayBufferView;
+                const size = Math.pow(2, mipmapsCount - 1 - imageData.length);
+                const dataLength = size * size * 4;
+                switch (texture.type) {
+                    case Engine.TEXTURETYPE_UNSIGNED_INT: {
+                        data = new Uint8Array(dataLength);
+                        break;
+                    }
+                    case Engine.TEXTURETYPE_HALF_FLOAT: {
+                        data = new Uint16Array(dataLength);
+                        break;
+                    }
+                    case Engine.TEXTURETYPE_FLOAT: {
+                        data = new Float32Array(dataLength);
+                        break;
+                    }
+                }
+                for (let i = imageData.length; i < mipmapsCount; i++) {
+                    for (let face = 0; face < 6; face++) {
+                        engine._uploadArrayBufferViewToTexture(texture, data!, face, i);
+                    }
+                }
+            }
+
             // Once all done, finishes the cleanup and return
             return Promise.all(promises).then(() => {
-                // Relase temp RTT.
+                // Release temp RTT.
                 if (cubeRtt) {
                     engine._releaseFramebufferObjects(cubeRtt);
                     cubeRtt._swapAndDie(texture);
                 }
-                // Relase temp Post Process.
+                // Release temp Post Process.
                 if (rgbdPostProcess) {
                     rgbdPostProcess.dispose();
                 }

+ 1 - 1
src/Tools/babylon.tga.ts

@@ -196,7 +196,7 @@
             var imageData = (<any>TGATools)[func](header, palettes, pixel_data, y_start, y_step, y_end, x_start, x_step, x_end);
 
             const engine = texture.getEngine();
-            engine._uploadDataToTextureDirectly(texture, texture.width, texture.height, imageData);
+            engine._uploadDataToTextureDirectly(texture, imageData);
         }
 
         static _getImageData8bits(header: any, palettes: Uint8Array, pixel_data: Uint8Array, y_start: number, y_step: number, y_end: number, x_start: number, x_step: number, x_end: number): Uint8Array {

BIN
tests/validation/ReferenceImages/dds.png


+ 5 - 0
tests/validation/config.json

@@ -480,6 +480,11 @@
       "title": "TGA",
       "playgroundId": "#ZI77S7#0",
       "referenceImage": "tga.png"
+    },
+    {
+      "title": "DDS",
+      "playgroundId": "#ZI77S7#2",
+      "referenceImage": "dds.png"
     }
   ]
 }