From e52f8fe9a260a293a668f71da6d45b2c6a261b2e Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Thu, 7 Apr 2022 17:01:28 +0800 Subject: [PATCH 01/11] feat: move HDR loader from @oasis-engine/baker to oasis-engine --- packages/core/src/asset/AssetType.ts | 4 +- packages/loader/src/HDRLoader.ts | 370 +++++++++++++++++++++++++++ packages/loader/src/index.ts | 1 + 3 files changed, 374 insertions(+), 1 deletion(-) create mode 100644 packages/loader/src/HDRLoader.ts diff --git a/packages/core/src/asset/AssetType.ts b/packages/core/src/asset/AssetType.ts index d1ecbfa4f6..ed7408aa7e 100644 --- a/packages/core/src/asset/AssetType.ts +++ b/packages/core/src/asset/AssetType.ts @@ -36,5 +36,7 @@ export enum AssetType { /** Sprite Atlas. */ SpriteAtlas = "sprite-atlas", /** ambient light */ - Env = "environment" + Env = "environment", + /** HDR to cube */ + HDR = "hdr" } diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts new file mode 100644 index 0000000000..3085e62a1a --- /dev/null +++ b/packages/loader/src/HDRLoader.ts @@ -0,0 +1,370 @@ +import { + AssetPromise, + AssetType, + Loader, + LoadItem, + resourceLoader, + ResourceManager, + TextureCube, + TextureCubeFace +} from "@oasis-engine/core"; +import { Color, Vector3 } from "@oasis-engine/math"; + +interface IHDRHeader { + /** + * The width of the texture in pixels. + */ + width: number; + /** + * The height of the texture in pixels. + */ + height: number; + /** + * The index of the beginning of the data in the binary file. + */ + dataPosition: number; +} + +@resourceLoader(AssetType.HDR, ["hdr"]) +class HDRLoader extends Loader { + private static _cubeSize = 256; + + private static _faceRight = [ + new Vector3(1.0, -1.0, -1.0), + new Vector3(1.0, -1.0, 1.0), + new Vector3(1.0, 1.0, -1.0), + new Vector3(1.0, 1.0, 1.0) + ]; + + private static _faceLeft = [ + new Vector3(-1.0, -1.0, 1.0), + new Vector3(-1.0, -1.0, -1.0), + new Vector3(-1.0, 1.0, 1.0), + new Vector3(-1.0, 1.0, -1.0) + ]; + + private static _faceUp = [ + new Vector3(-1.0, -1.0, 1.0), + new Vector3(1.0, -1.0, 1.0), + new Vector3(-1.0, -1.0, -1.0), + new Vector3(1.0, -1.0, -1.0) + ]; + + private static _faceBottom = [ + new Vector3(-1.0, 1.0, -1.0), + new Vector3(1.0, 1.0, -1.0), + new Vector3(-1.0, 1.0, 1.0), + new Vector3(1.0, 1.0, 1.0) + ]; + + private static _faceFront = [ + new Vector3(-1.0, -1.0, -1.0), + new Vector3(1.0, -1.0, -1.0), + new Vector3(-1.0, 1.0, -1.0), + new Vector3(1.0, 1.0, -1.0) + ]; + + private static _faceBack = [ + new Vector3(1.0, -1.0, 1.0), + new Vector3(-1.0, -1.0, 1.0), + new Vector3(1.0, 1.0, 1.0), + new Vector3(-1.0, 1.0, 1.0) + ]; + + private static _tempVector3 = new Vector3(); + private static _temp2Vector3 = new Vector3(); + private static _temp3Vector3 = new Vector3(); + private static _temp4Vector3 = new Vector3(); + private static _temp5Vector3 = new Vector3(); + + private static _convertToCubemap( + pixels: Uint8Array, + inputWidth: number, + inputHeight: number, + size: number + ): Uint8Array[] { + if (!pixels) { + throw "ConvertPanoramaToCubemap: input cannot be null"; + } + + if (pixels.length != inputWidth * inputHeight * 4) { + throw "ConvertPanoramaToCubemap: input size is wrong"; + } + + const textureRight = this._createCubemapData(size, this._faceRight, pixels, inputWidth, inputHeight); + const textureLeft = this._createCubemapData(size, this._faceLeft, pixels, inputWidth, inputHeight); + const textureUp = this._createCubemapData(size, this._faceUp, pixels, inputWidth, inputHeight); + const textureDown = this._createCubemapData(size, this._faceBottom, pixels, inputWidth, inputHeight); + const textureFront = this._createCubemapData(size, this._faceFront, pixels, inputWidth, inputHeight); + const textureBack = this._createCubemapData(size, this._faceBack, pixels, inputWidth, inputHeight); + + return [textureRight, textureLeft, textureUp, textureDown, textureFront, textureBack]; + } + + private static _createCubemapData( + texSize: number, + faceData: Vector3[], + pixels: Uint8Array, + inputWidth: number, + inputHeight: number + ): Uint8Array { + const textureArray = new Uint8Array(texSize * texSize * 4); + const rotDX1 = this._tempVector3 + .setValue(0, 0, 0) + .add(faceData[1]) + .subtract(faceData[0]) + .scale(1 / texSize); + const rotDX2 = this._temp2Vector3 + .setValue(0, 0, 0) + .add(faceData[3]) + .subtract(faceData[2]) + .scale(1 / texSize); + + const dy = 1 / texSize; + let fy = 0; + + for (let y = 0; y < texSize; y++) { + let xv1 = this._temp3Vector3.setValue(0, 0, 0).add(faceData[0]); + let xv2 = this._temp4Vector3.setValue(0, 0, 0).add(faceData[2]); + + for (let x = 0; x < texSize; x++) { + const v = this._temp5Vector3.setValue(0, 0, 0).add(xv2).subtract(xv1).scale(fy).add(xv1); + v.normalize(); + + const color = this._calcProjectionSpherical(v, pixels, inputWidth, inputHeight); + + // 4 channels per pixels + textureArray[y * texSize * 4 + x * 4] = color.r; + textureArray[y * texSize * 4 + x * 4 + 1] = color.g; + textureArray[y * texSize * 4 + x * 4 + 2] = color.b; + textureArray[y * texSize * 4 + x * 4 + 3] = color.a; + + xv1.add(rotDX1); + xv2.add(rotDX2); + } + + fy += dy; + } + + return textureArray; + } + + private static _calcProjectionSpherical( + vDir: Vector3, + pixels: Uint8Array, + inputWidth: number, + inputHeight: number + ): Color { + let theta = Math.atan2(vDir.z, vDir.x); + let phi = Math.acos(vDir.y); + + while (theta < -Math.PI) { + theta += 2 * Math.PI; + } + while (theta > Math.PI) { + theta -= 2 * Math.PI; + } + + let dx = theta / Math.PI; + let dy = phi / Math.PI; + + // recenter. + dx = dx * 0.5 + 0.5; + + let px = Math.round(dx * inputWidth); + if (px < 0) { + px = 0; + } else if (px >= inputWidth) { + px = inputWidth - 1; + } + + let py = Math.round(dy * inputHeight); + if (py < 0) { + py = 0; + } else if (py >= inputHeight) { + py = inputHeight - 1; + } + + const inputY = inputHeight - py - 1; + const r = pixels[inputY * inputWidth * 4 + px * 4]; + const g = pixels[inputY * inputWidth * 4 + px * 4 + 1]; + const b = pixels[inputY * inputWidth * 4 + px * 4 + 2]; + const a = pixels[inputY * inputWidth * 4 + px * 4 + 3]; + + return new Color(r, g, b, a); + } + + private static _readStringLine(uint8array: Uint8Array, startIndex: number): string { + let line = ""; + let character = ""; + + for (let i = startIndex; i < uint8array.length - startIndex; i++) { + character = String.fromCharCode(uint8array[i]); + + if (character == "\n") { + break; + } + + line += character; + } + + return line; + } + + private static _parseHeader(uint8array: Uint8Array): IHDRHeader { + let height: number = 0; + let width: number = 0; + + let line = this._readStringLine(uint8array, 0); + if (line[0] != "#" || line[1] != "?") { + throw "Bad HDR Format."; + } + + let endOfHeader = false; + let findFormat = false; + let lineIndex: number = 0; + + do { + lineIndex += line.length + 1; + line = this._readStringLine(uint8array, lineIndex); + + if (line == "FORMAT=32-bit_rle_rgbe") { + findFormat = true; + } else if (line.length == 0) { + endOfHeader = true; + } + } while (!endOfHeader); + + if (!findFormat) { + throw "HDR Bad header format, unsupported FORMAT"; + } + + lineIndex += line.length + 1; + line = this._readStringLine(uint8array, lineIndex); + + const sizeRegexp = /^\-Y (.*) \+X (.*)$/g; + const match = sizeRegexp.exec(line); + + // TODO. Support +Y and -X if needed. + if (!match || match.length < 3) { + throw "HDR Bad header format, no size"; + } + width = parseInt(match[2]); + height = parseInt(match[1]); + + if (width < 8 || width > 0x7fff) { + throw "HDR Bad header format, unsupported size"; + } + + lineIndex += line.length + 1; + + return { + height: height, + width: width, + dataPosition: lineIndex + }; + } + + private static _readPixels(buffer: Uint8Array, width: number, height: number): Uint8Array { + const scanline_width = width; + + const data_rgba = new Uint8Array(4 * width * height); + + let offset = 0, + pos = 0; + const ptr_end = 4 * scanline_width; + const rgbeStart = new Uint8Array(4); + const scanline_buffer = new Uint8Array(ptr_end); + let num_scanlines = height; // read in each successive scanline + + while (num_scanlines > 0 && pos < buffer.byteLength) { + rgbeStart[0] = buffer[pos++]; + rgbeStart[1] = buffer[pos++]; + rgbeStart[2] = buffer[pos++]; + rgbeStart[3] = buffer[pos++]; + + if (2 != rgbeStart[0] || 2 != rgbeStart[1] || ((rgbeStart[2] << 8) | rgbeStart[3]) != scanline_width) { + throw "HDR Bad header format, wrong scan line width"; + } + + // read each of the four channels for the scanline into the buffer + // first red, then green, then blue, then exponent + + let ptr = 0, + count; + + while (ptr < ptr_end && pos < buffer.byteLength) { + count = buffer[pos++]; + const isEncodedRun = count > 128; + if (isEncodedRun) count -= 128; + + if (0 === count || ptr + count > ptr_end) { + throw "HDR Bad Format, bad scanline data (run)"; + } + + if (isEncodedRun) { + // a (encoded) run of the same value + const byteValue = buffer[pos++]; + + for (let i = 0; i < count; i++) { + scanline_buffer[ptr++] = byteValue; + } //ptr += count; + } else { + // a literal-run + scanline_buffer.set(buffer.subarray(pos, pos + count), ptr); + ptr += count; + pos += count; + } + } // now convert data from buffer into rgba + // first red, then green, then blue, then exponent (alpha) + + const l = scanline_width; //scanline_buffer.byteLength; + + for (let i = 0; i < l; i++) { + let off = 0; + data_rgba[offset] = scanline_buffer[i + off]; + off += scanline_width; //1; + + data_rgba[offset + 1] = scanline_buffer[i + off]; + off += scanline_width; //1; + + data_rgba[offset + 2] = scanline_buffer[i + off]; + off += scanline_width; //1; + + data_rgba[offset + 3] = scanline_buffer[i + off]; + offset += 4; + } + + num_scanlines--; + } + + return data_rgba; + } + + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + return new AssetPromise((resolve, reject) => { + const engine = resourceManager.engine; + const cubeSize = HDRLoader._cubeSize; + + resourceManager + .load({ + url: item.url, + type: AssetType.Buffer + }) + .then((buffer) => { + const uint8Array = new Uint8Array(buffer); + const { width, height, dataPosition } = HDRLoader._parseHeader(uint8Array); + const pixels = HDRLoader._readPixels(uint8Array.subarray(dataPosition), width, height); + const cubeMapData = HDRLoader._convertToCubemap(pixels, width, height, cubeSize); + const texture = new TextureCube(engine, cubeSize); + + for (let faceIndex = 0; faceIndex < 6; faceIndex++) { + texture.setPixelBuffer(TextureCubeFace.PositiveX + faceIndex, cubeMapData[faceIndex], 0); + } + texture.generateMipmaps(); + resolve(texture); + }) + .catch(reject); + }); + } +} diff --git a/packages/loader/src/index.ts b/packages/loader/src/index.ts index bba01c3be6..243948b8ff 100644 --- a/packages/loader/src/index.ts +++ b/packages/loader/src/index.ts @@ -7,6 +7,7 @@ import "./Texture2DLoader"; import "./TextureCubeLoader"; import "./SpriteAtlasLoader"; import "./EnvLoader"; +import "./HDRLoader"; import "./gltf/extensions/index"; export { GLTFResource } from "./gltf/GLTFResource"; From 315cb9fb2669a860606fcf14b54a16673bf0f830 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Thu, 7 Apr 2022 20:16:09 +0800 Subject: [PATCH 02/11] refactor: use auto size --- packages/loader/src/HDRLoader.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts index 3085e62a1a..204d9589b8 100644 --- a/packages/loader/src/HDRLoader.ts +++ b/packages/loader/src/HDRLoader.ts @@ -27,8 +27,6 @@ interface IHDRHeader { @resourceLoader(AssetType.HDR, ["hdr"]) class HDRLoader extends Loader { - private static _cubeSize = 256; - private static _faceRight = [ new Vector3(1.0, -1.0, -1.0), new Vector3(1.0, -1.0, 1.0), @@ -344,7 +342,6 @@ class HDRLoader extends Loader { load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { const engine = resourceManager.engine; - const cubeSize = HDRLoader._cubeSize; resourceManager .load({ @@ -355,6 +352,8 @@ class HDRLoader extends Loader { const uint8Array = new Uint8Array(buffer); const { width, height, dataPosition } = HDRLoader._parseHeader(uint8Array); const pixels = HDRLoader._readPixels(uint8Array.subarray(dataPosition), width, height); + const cubeSize = height >> 1; + const cubeMapData = HDRLoader._convertToCubemap(pixels, width, height, cubeSize); const texture = new TextureCube(engine, cubeSize); From 15e735a7a6050171abcf6c21a30b3ba3b2cea9d1 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Thu, 7 Apr 2022 20:27:25 +0800 Subject: [PATCH 03/11] refactor: invert x axis --- packages/loader/src/HDRLoader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts index 204d9589b8..2ec60c8b41 100644 --- a/packages/loader/src/HDRLoader.ts +++ b/packages/loader/src/HDRLoader.ts @@ -153,7 +153,7 @@ class HDRLoader extends Loader { inputWidth: number, inputHeight: number ): Color { - let theta = Math.atan2(vDir.z, vDir.x); + let theta = Math.atan2(vDir.z, -vDir.x); let phi = Math.acos(vDir.y); while (theta < -Math.PI) { From 64b40d4ebaeb5ba63c8c81b9607299e423558aab Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Fri, 8 Apr 2022 17:23:30 +0800 Subject: [PATCH 04/11] refactor: encoding hdr in RGBM format --- packages/loader/src/HDRLoader.ts | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts index 2ec60c8b41..280eb5aa00 100644 --- a/packages/loader/src/HDRLoader.ts +++ b/packages/loader/src/HDRLoader.ts @@ -80,7 +80,7 @@ class HDRLoader extends Loader { inputWidth: number, inputHeight: number, size: number - ): Uint8Array[] { + ): Uint8ClampedArray[] { if (!pixels) { throw "ConvertPanoramaToCubemap: input cannot be null"; } @@ -105,8 +105,8 @@ class HDRLoader extends Loader { pixels: Uint8Array, inputWidth: number, inputHeight: number - ): Uint8Array { - const textureArray = new Uint8Array(texSize * texSize * 4); + ): Uint8ClampedArray { + const textureArray = new Uint8ClampedArray(texSize * texSize * 4); const rotDX1 = this._tempVector3 .setValue(0, 0, 0) .add(faceData[1]) @@ -130,6 +130,8 @@ class HDRLoader extends Loader { v.normalize(); const color = this._calcProjectionSpherical(v, pixels, inputWidth, inputHeight); + this._RGBEToLinear(color); + this._linearToRGBM(color, 5); // 4 channels per pixels textureArray[y * texSize * 4 + x * 4] = color.r; @@ -339,6 +341,26 @@ class HDRLoader extends Loader { return data_rgba; } + private static _RGBEToLinear(color: Color): void { + const scaleFactor = Math.pow(2, color.a - 136); + color.r *= scaleFactor; + color.g *= scaleFactor; + color.b *= scaleFactor; + color.a = 1; + } + + private static _linearToRGBM(color: Color, maxRange: number): void { + const maxRGB = Math.max(color.r, Math.max(color.g, color.b)); + let M = Math.min(Math.max(0, maxRGB / maxRange), 1); + M = Math.ceil(M * 255.0) / 255.0; + const scaleFactor = 255 / (M * maxRange); + + color.r *= scaleFactor; + color.g *= scaleFactor; + color.b *= scaleFactor; + color.a *= M * 255; + } + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { return new AssetPromise((resolve, reject) => { const engine = resourceManager.engine; From 3d4413a0487459010c0920661966879a588c591e Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Fri, 8 Apr 2022 17:25:53 +0800 Subject: [PATCH 05/11] perf: enhance speed --- packages/loader/src/HDRLoader.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts index 280eb5aa00..4bb18b7b33 100644 --- a/packages/loader/src/HDRLoader.ts +++ b/packages/loader/src/HDRLoader.ts @@ -351,7 +351,7 @@ class HDRLoader extends Loader { private static _linearToRGBM(color: Color, maxRange: number): void { const maxRGB = Math.max(color.r, Math.max(color.g, color.b)); - let M = Math.min(Math.max(0, maxRGB / maxRange), 1); + let M = Math.min(maxRGB / maxRange, 1); M = Math.ceil(M * 255.0) / 255.0; const scaleFactor = 255 / (M * maxRange); From a8363d12aff103950bf5419d84203ecbd067dc83 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Fri, 8 Apr 2022 18:01:44 +0800 Subject: [PATCH 06/11] perf: enhance speed --- packages/loader/src/HDRLoader.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts index 4bb18b7b33..c3ccf1c1e9 100644 --- a/packages/loader/src/HDRLoader.ts +++ b/packages/loader/src/HDRLoader.ts @@ -352,13 +352,13 @@ class HDRLoader extends Loader { private static _linearToRGBM(color: Color, maxRange: number): void { const maxRGB = Math.max(color.r, Math.max(color.g, color.b)); let M = Math.min(maxRGB / maxRange, 1); - M = Math.ceil(M * 255.0) / 255.0; - const scaleFactor = 255 / (M * maxRange); + M = Math.ceil(M * 255); + const scaleFactor = 65025 / (M * maxRange); color.r *= scaleFactor; color.g *= scaleFactor; color.b *= scaleFactor; - color.a *= M * 255; + color.a *= M; } load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { From 064800593b6e216f6b950d83e7fe5176a999f529 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Mon, 11 Apr 2022 11:26:49 +0800 Subject: [PATCH 07/11] refactor: use RGBE for baker is better --- packages/loader/src/HDRLoader.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts index c3ccf1c1e9..b95699eb8b 100644 --- a/packages/loader/src/HDRLoader.ts +++ b/packages/loader/src/HDRLoader.ts @@ -80,7 +80,7 @@ class HDRLoader extends Loader { inputWidth: number, inputHeight: number, size: number - ): Uint8ClampedArray[] { + ): Uint8Array[] { if (!pixels) { throw "ConvertPanoramaToCubemap: input cannot be null"; } @@ -105,8 +105,8 @@ class HDRLoader extends Loader { pixels: Uint8Array, inputWidth: number, inputHeight: number - ): Uint8ClampedArray { - const textureArray = new Uint8ClampedArray(texSize * texSize * 4); + ): Uint8Array { + const textureArray = new Uint8Array(texSize * texSize * 4); const rotDX1 = this._tempVector3 .setValue(0, 0, 0) .add(faceData[1]) @@ -130,8 +130,8 @@ class HDRLoader extends Loader { v.normalize(); const color = this._calcProjectionSpherical(v, pixels, inputWidth, inputHeight); - this._RGBEToLinear(color); - this._linearToRGBM(color, 5); + // this._RGBEToLinear(color); + // this._linearToRGBM(color, 5); // 4 channels per pixels textureArray[y * texSize * 4 + x * 4] = color.r; From d3626ff13cb7cea8281f71000331dbef77b936e6 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Mon, 11 Apr 2022 11:54:17 +0800 Subject: [PATCH 08/11] refactor: compatible with 0.6 --- packages/core/src/asset/AssetType.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/src/asset/AssetType.ts b/packages/core/src/asset/AssetType.ts index ed7408aa7e..28bbbd1f48 100644 --- a/packages/core/src/asset/AssetType.ts +++ b/packages/core/src/asset/AssetType.ts @@ -37,6 +37,6 @@ export enum AssetType { SpriteAtlas = "sprite-atlas", /** ambient light */ Env = "environment", - /** HDR to cube */ - HDR = "hdr" + /** HDR to cube */ + HDR = "HDR" } From a5bc5aef3e875b57c6fe1eba7ac4002692875026 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Wed, 13 Apr 2022 16:47:17 +0800 Subject: [PATCH 09/11] refactor: use rgbm --- packages/loader/src/HDRLoader.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts index b95699eb8b..3242646fae 100644 --- a/packages/loader/src/HDRLoader.ts +++ b/packages/loader/src/HDRLoader.ts @@ -130,8 +130,8 @@ class HDRLoader extends Loader { v.normalize(); const color = this._calcProjectionSpherical(v, pixels, inputWidth, inputHeight); - // this._RGBEToLinear(color); - // this._linearToRGBM(color, 5); + this._RGBEToLinear(color); + this._linearToRGBM(color, 5); // 4 channels per pixels textureArray[y * texSize * 4 + x * 4] = color.r; From 8cb60953e1597dd0351cf0877965ca49e44cc83a Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Thu, 21 Apr 2022 11:44:38 +0800 Subject: [PATCH 10/11] perf: opt code from cr --- packages/loader/src/HDRLoader.ts | 115 +++++++++++++++++-------------- 1 file changed, 62 insertions(+), 53 deletions(-) diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts index 3242646fae..0b3a5f1076 100644 --- a/packages/loader/src/HDRLoader.ts +++ b/packages/loader/src/HDRLoader.ts @@ -10,6 +10,8 @@ import { } from "@oasis-engine/core"; import { Color, Vector3 } from "@oasis-engine/math"; +const PI = Math.PI; + interface IHDRHeader { /** * The width of the texture in pixels. @@ -27,46 +29,50 @@ interface IHDRHeader { @resourceLoader(AssetType.HDR, ["hdr"]) class HDRLoader extends Loader { + private static _rightBottomBack = new Vector3(1.0, -1.0, -1.0); + private static _rightBottomFront = new Vector3(1.0, -1.0, 1.0); + private static _rightUpBack = new Vector3(1.0, 1.0, -1.0); + private static _rightUpFront = new Vector3(1.0, 1.0, 1.0); + private static _leftBottomBack = new Vector3(-1.0, -1.0, -1.0); + private static _leftBottomFront = new Vector3(-1.0, -1.0, 1.0); + private static _leftUpBack = new Vector3(-1.0, 1.0, -1.0); + private static _leftUpFront = new Vector3(-1.0, 1.0, 1.0); + private static _faceRight = [ - new Vector3(1.0, -1.0, -1.0), - new Vector3(1.0, -1.0, 1.0), - new Vector3(1.0, 1.0, -1.0), - new Vector3(1.0, 1.0, 1.0) + HDRLoader._rightBottomBack, + HDRLoader._rightBottomFront, + HDRLoader._rightUpBack, + HDRLoader._rightUpFront ]; - private static _faceLeft = [ - new Vector3(-1.0, -1.0, 1.0), - new Vector3(-1.0, -1.0, -1.0), - new Vector3(-1.0, 1.0, 1.0), - new Vector3(-1.0, 1.0, -1.0) + HDRLoader._leftBottomFront, + HDRLoader._leftBottomBack, + HDRLoader._leftUpFront, + HDRLoader._leftUpBack ]; - private static _faceUp = [ - new Vector3(-1.0, -1.0, 1.0), - new Vector3(1.0, -1.0, 1.0), - new Vector3(-1.0, -1.0, -1.0), - new Vector3(1.0, -1.0, -1.0) + HDRLoader._leftBottomFront, + HDRLoader._rightBottomFront, + HDRLoader._leftBottomBack, + HDRLoader._rightBottomBack ]; - private static _faceBottom = [ - new Vector3(-1.0, 1.0, -1.0), - new Vector3(1.0, 1.0, -1.0), - new Vector3(-1.0, 1.0, 1.0), - new Vector3(1.0, 1.0, 1.0) + HDRLoader._leftUpBack, + HDRLoader._rightUpBack, + HDRLoader._leftUpFront, + HDRLoader._rightUpFront ]; - private static _faceFront = [ - new Vector3(-1.0, -1.0, -1.0), - new Vector3(1.0, -1.0, -1.0), - new Vector3(-1.0, 1.0, -1.0), - new Vector3(1.0, 1.0, -1.0) + HDRLoader._leftBottomBack, + HDRLoader._rightBottomBack, + HDRLoader._leftUpBack, + HDRLoader._rightUpBack ]; - private static _faceBack = [ - new Vector3(1.0, -1.0, 1.0), - new Vector3(-1.0, -1.0, 1.0), - new Vector3(1.0, 1.0, 1.0), - new Vector3(-1.0, 1.0, 1.0) + HDRLoader._rightBottomFront, + HDRLoader._leftBottomFront, + HDRLoader._rightUpFront, + HDRLoader._leftUpFront ]; private static _tempVector3 = new Vector3(); @@ -80,7 +86,7 @@ class HDRLoader extends Loader { inputWidth: number, inputHeight: number, size: number - ): Uint8Array[] { + ): Uint8ClampedArray[] { if (!pixels) { throw "ConvertPanoramaToCubemap: input cannot be null"; } @@ -105,8 +111,8 @@ class HDRLoader extends Loader { pixels: Uint8Array, inputWidth: number, inputHeight: number - ): Uint8Array { - const textureArray = new Uint8Array(texSize * texSize * 4); + ): Uint8ClampedArray { + const textureArray = new Uint8ClampedArray(texSize * texSize * 4); const rotDX1 = this._tempVector3 .setValue(0, 0, 0) .add(faceData[1]) @@ -134,10 +140,11 @@ class HDRLoader extends Loader { this._linearToRGBM(color, 5); // 4 channels per pixels - textureArray[y * texSize * 4 + x * 4] = color.r; - textureArray[y * texSize * 4 + x * 4 + 1] = color.g; - textureArray[y * texSize * 4 + x * 4 + 2] = color.b; - textureArray[y * texSize * 4 + x * 4 + 3] = color.a; + const index = y * texSize * 4 + x * 4; + textureArray[index] = color.r; + textureArray[index + 1] = color.g; + textureArray[index + 2] = color.b; + textureArray[index + 3] = color.a; xv1.add(rotDX1); xv2.add(rotDX2); @@ -158,15 +165,15 @@ class HDRLoader extends Loader { let theta = Math.atan2(vDir.z, -vDir.x); let phi = Math.acos(vDir.y); - while (theta < -Math.PI) { - theta += 2 * Math.PI; + while (theta < -PI) { + theta += 2 * PI; } - while (theta > Math.PI) { - theta -= 2 * Math.PI; + while (theta > PI) { + theta -= 2 * PI; } - let dx = theta / Math.PI; - let dy = phi / Math.PI; + let dx = theta / PI; + let dy = phi / PI; // recenter. dx = dx * 0.5 + 0.5; @@ -186,10 +193,11 @@ class HDRLoader extends Loader { } const inputY = inputHeight - py - 1; - const r = pixels[inputY * inputWidth * 4 + px * 4]; - const g = pixels[inputY * inputWidth * 4 + px * 4 + 1]; - const b = pixels[inputY * inputWidth * 4 + px * 4 + 2]; - const a = pixels[inputY * inputWidth * 4 + px * 4 + 3]; + const index = inputY * inputWidth * 4 + px * 4; + const r = pixels[index]; + const g = pixels[index + 1]; + const b = pixels[index + 2]; + const a = pixels[index + 3]; return new Color(r, g, b, a); } @@ -267,6 +275,7 @@ class HDRLoader extends Loader { private static _readPixels(buffer: Uint8Array, width: number, height: number): Uint8Array { const scanline_width = width; + const byteLength = buffer.byteLength; const data_rgba = new Uint8Array(4 * width * height); @@ -277,7 +286,7 @@ class HDRLoader extends Loader { const scanline_buffer = new Uint8Array(ptr_end); let num_scanlines = height; // read in each successive scanline - while (num_scanlines > 0 && pos < buffer.byteLength) { + while (num_scanlines > 0 && pos < byteLength) { rgbeStart[0] = buffer[pos++]; rgbeStart[1] = buffer[pos++]; rgbeStart[2] = buffer[pos++]; @@ -293,7 +302,7 @@ class HDRLoader extends Loader { let ptr = 0, count; - while (ptr < ptr_end && pos < buffer.byteLength) { + while (ptr < ptr_end && pos < byteLength) { count = buffer[pos++]; const isEncodedRun = count > 128; if (isEncodedRun) count -= 128; @@ -323,13 +332,13 @@ class HDRLoader extends Loader { for (let i = 0; i < l; i++) { let off = 0; data_rgba[offset] = scanline_buffer[i + off]; - off += scanline_width; //1; + off += scanline_width; data_rgba[offset + 1] = scanline_buffer[i + off]; - off += scanline_width; //1; + off += scanline_width; data_rgba[offset + 2] = scanline_buffer[i + off]; - off += scanline_width; //1; + off += scanline_width; data_rgba[offset + 3] = scanline_buffer[i + off]; offset += 4; @@ -342,7 +351,7 @@ class HDRLoader extends Loader { } private static _RGBEToLinear(color: Color): void { - const scaleFactor = Math.pow(2, color.a - 136); + const scaleFactor = Math.pow(2, color.a - 128) / 255; color.r *= scaleFactor; color.g *= scaleFactor; color.b *= scaleFactor; @@ -353,7 +362,7 @@ class HDRLoader extends Loader { const maxRGB = Math.max(color.r, Math.max(color.g, color.b)); let M = Math.min(maxRGB / maxRange, 1); M = Math.ceil(M * 255); - const scaleFactor = 65025 / (M * maxRange); + const scaleFactor = 65025 / (M * maxRange); // 255 * (255 / (M * maxRange) ) color.r *= scaleFactor; color.g *= scaleFactor; From a0f49929b97682cb11c67cd9f6c27a1fe21a234e Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Thu, 21 Apr 2022 16:50:48 +0800 Subject: [PATCH 11/11] refactor: rename --- packages/loader/src/HDRLoader.ts | 42 ++++++++++++++++---------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/loader/src/HDRLoader.ts b/packages/loader/src/HDRLoader.ts index 0b3a5f1076..16ce64004c 100644 --- a/packages/loader/src/HDRLoader.ts +++ b/packages/loader/src/HDRLoader.ts @@ -274,25 +274,25 @@ class HDRLoader extends Loader { } private static _readPixels(buffer: Uint8Array, width: number, height: number): Uint8Array { - const scanline_width = width; + const scanLineWidth = width; const byteLength = buffer.byteLength; - const data_rgba = new Uint8Array(4 * width * height); + const dataRGBA = new Uint8Array(4 * width * height); let offset = 0, pos = 0; - const ptr_end = 4 * scanline_width; + const ptrEnd = 4 * scanLineWidth; const rgbeStart = new Uint8Array(4); - const scanline_buffer = new Uint8Array(ptr_end); - let num_scanlines = height; // read in each successive scanline + const scanLineBuffer = new Uint8Array(ptrEnd); + let numScanLines = height; // read in each successive scanLine - while (num_scanlines > 0 && pos < byteLength) { + while (numScanLines > 0 && pos < byteLength) { rgbeStart[0] = buffer[pos++]; rgbeStart[1] = buffer[pos++]; rgbeStart[2] = buffer[pos++]; rgbeStart[3] = buffer[pos++]; - if (2 != rgbeStart[0] || 2 != rgbeStart[1] || ((rgbeStart[2] << 8) | rgbeStart[3]) != scanline_width) { + if (2 != rgbeStart[0] || 2 != rgbeStart[1] || ((rgbeStart[2] << 8) | rgbeStart[3]) != scanLineWidth) { throw "HDR Bad header format, wrong scan line width"; } @@ -302,12 +302,12 @@ class HDRLoader extends Loader { let ptr = 0, count; - while (ptr < ptr_end && pos < byteLength) { + while (ptr < ptrEnd && pos < byteLength) { count = buffer[pos++]; const isEncodedRun = count > 128; if (isEncodedRun) count -= 128; - if (0 === count || ptr + count > ptr_end) { + if (0 === count || ptr + count > ptrEnd) { throw "HDR Bad Format, bad scanline data (run)"; } @@ -316,38 +316,38 @@ class HDRLoader extends Loader { const byteValue = buffer[pos++]; for (let i = 0; i < count; i++) { - scanline_buffer[ptr++] = byteValue; + scanLineBuffer[ptr++] = byteValue; } //ptr += count; } else { // a literal-run - scanline_buffer.set(buffer.subarray(pos, pos + count), ptr); + scanLineBuffer.set(buffer.subarray(pos, pos + count), ptr); ptr += count; pos += count; } } // now convert data from buffer into rgba // first red, then green, then blue, then exponent (alpha) - const l = scanline_width; //scanline_buffer.byteLength; + const l = scanLineWidth; //scanLine_buffer.byteLength; for (let i = 0; i < l; i++) { let off = 0; - data_rgba[offset] = scanline_buffer[i + off]; - off += scanline_width; + dataRGBA[offset] = scanLineBuffer[i + off]; + off += scanLineWidth; - data_rgba[offset + 1] = scanline_buffer[i + off]; - off += scanline_width; + dataRGBA[offset + 1] = scanLineBuffer[i + off]; + off += scanLineWidth; - data_rgba[offset + 2] = scanline_buffer[i + off]; - off += scanline_width; + dataRGBA[offset + 2] = scanLineBuffer[i + off]; + off += scanLineWidth; - data_rgba[offset + 3] = scanline_buffer[i + off]; + dataRGBA[offset + 3] = scanLineBuffer[i + off]; offset += 4; } - num_scanlines--; + numScanLines--; } - return data_rgba; + return dataRGBA; } private static _RGBEToLinear(color: Color): void {