From 304c4c2c336fed10919957e9eab5204248295d17 Mon Sep 17 00:00:00 2001 From: zhuxudong Date: Fri, 19 Mar 2021 15:07:21 +0800 Subject: [PATCH 01/19] feat: support spherical harmonics --- packages/math/src/SphericalHarmonics3.ts | 172 +++++++++++++++++++++++ packages/math/src/index.ts | 1 + 2 files changed, 173 insertions(+) create mode 100644 packages/math/src/SphericalHarmonics3.ts diff --git a/packages/math/src/SphericalHarmonics3.ts b/packages/math/src/SphericalHarmonics3.ts new file mode 100644 index 0000000000..b74e2e63fd --- /dev/null +++ b/packages/math/src/SphericalHarmonics3.ts @@ -0,0 +1,172 @@ +import { IClone } from "@oasis-engine/design"; +import { Color } from "./Color"; +import { Vector3 } from "./Vector3"; + +/** + * Use SH3 to represent irradiance environment maps efficiently, allowing for interactive rendering of diffuse objects under distant illumination. + * @remarks + * https://graphics.stanford.edu/papers/envmap/envmap.pdf + * http://www.ppsloan.org/publications/StupidSH36.pdf + * https://google.github.io/filament/Filament.md.html#annex/sphericalharmonics + */ +export class SphericalHarmonics3 implements IClone { + private static _basisFunction = [ + 0.282095, // 1/2 * Math.sqrt(1 / PI) + + -0.488603, // -1/2 * Math.sqrt(3 / PI) + 0.488603, // 1/2 * Math.sqrt(3 / PI) + -0.488603, // -1/2 * Math.sqrt(3 / PI) + + 1.092548, // 1/2 * Math.sqrt(15 / PI) + -1.092548, // -1/2 * Math.sqrt(15 / PI) + 0.315392, // 1/4 * Math.sqrt(5 / PI) + -1.092548, // -1/2 * Math.sqrt(15 / PI) + 0.546274 // 1/4 * Math.sqrt(15 / PI) + ]; + + private static _convolutionKernel = [ + 3.141593, // PI + 2.094395, // (2 * PI) / 3, + 0.785398 // PI / 4 + ]; + + private static _tempColor = new Color(); + + /** The Y(0, 0) coefficient of the SH3. */ + y00: Color = new Color(0, 0, 0, 0); + /** The Y(1, -1) coefficient of the SH3. */ + y1_1: Color = new Color(0, 0, 0, 0); + /** The Y(1, 0) coefficient of the SH3. */ + y10: Color = new Color(0, 0, 0, 0); + /** The Y(1, 1) coefficient of the SH3. */ + y11: Color = new Color(0, 0, 0, 0); + /** The Y(2, -2) coefficient of the SH3. */ + y2_2: Color = new Color(0, 0, 0, 0); + /** The Y(2, -1) coefficient of the SH3. */ + y2_1: Color = new Color(0, 0, 0, 0); + /** The Y(2, 0) coefficient of the SH3. */ + y20: Color = new Color(0, 0, 0, 0); + /** The Y(2, 1) coefficient of the SH3. */ + y21: Color = new Color(0, 0, 0, 0); + /** The Y(2, 2) coefficient of the SH3. */ + y22: Color = new Color(0, 0, 0, 0); + + private _coefficients: Float32Array = new Float32Array(27); + + /** + * Get pre-scaled coefficients used in shader. + * @remarks + * Convert radiance to irradiance with the A_l which is convoluted by the cosine lobe and pre-scale the basis function. + * Reference equation [4,5,6,7,8,9] from https://graphics.stanford.edu/papers/envmap/envmap.pdf + */ + get preScaledCoefficients(): Float32Array { + const kernel = SphericalHarmonics3._convolutionKernel; + const basis = SphericalHarmonics3._basisFunction; + const arrayBuffer = this._coefficients; + + /** + * 1. L -> E + * 2. E * basis + */ + + // l0 + arrayBuffer[0] = this.y00.r * kernel[0] * basis[0]; + arrayBuffer[1] = this.y00.g * kernel[0] * basis[0]; + arrayBuffer[2] = this.y00.b * kernel[0] * basis[0]; + + // l1 + arrayBuffer[3] = this.y1_1.r * kernel[1] * basis[1]; + arrayBuffer[4] = this.y1_1.g * kernel[1] * basis[1]; + arrayBuffer[5] = this.y1_1.b * kernel[1] * basis[1]; + arrayBuffer[6] = this.y10.r * kernel[1] * basis[2]; + arrayBuffer[7] = this.y10.g * kernel[1] * basis[2]; + arrayBuffer[8] = this.y10.b * kernel[1] * basis[2]; + arrayBuffer[9] = this.y11.r * kernel[1] * basis[3]; + arrayBuffer[10] = this.y11.g * kernel[1] * basis[3]; + arrayBuffer[11] = this.y11.b * kernel[1] * basis[3]; + + // l2 + arrayBuffer[12] = this.y2_2.r * kernel[2] * basis[4]; + arrayBuffer[13] = this.y2_2.g * kernel[2] * basis[4]; + arrayBuffer[14] = this.y2_2.b * kernel[2] * basis[4]; + arrayBuffer[15] = this.y2_1.r * kernel[2] * basis[5]; + arrayBuffer[16] = this.y2_1.g * kernel[2] * basis[5]; + arrayBuffer[17] = this.y2_1.b * kernel[2] * basis[5]; + arrayBuffer[18] = this.y20.r * kernel[2] * basis[6]; + arrayBuffer[19] = this.y20.g * kernel[2] * basis[6]; + arrayBuffer[20] = this.y20.b * kernel[2] * basis[6]; + arrayBuffer[21] = this.y21.r * kernel[2] * basis[7]; + arrayBuffer[22] = this.y21.g * kernel[2] * basis[7]; + arrayBuffer[23] = this.y21.b * kernel[2] * basis[7]; + arrayBuffer[24] = this.y22.r * kernel[2] * basis[8]; + arrayBuffer[25] = this.y22.g * kernel[2] * basis[8]; + arrayBuffer[26] = this.y22.b * kernel[2] * basis[8]; + + return arrayBuffer; + } + + /** + * Add radiance to the SH3 in specified direction. + * @remarks + * Implements `EvalSHBasis` from [Projection from Cube maps] in http://www.ppsloan.org/publications/StupidSH36.pdf. + * + * @param color - radiance color + * @param direction - radiance direction + */ + addRadiance(color: Color, direction: Vector3): void { + const basis = SphericalHarmonics3._basisFunction; + const tempColor = SphericalHarmonics3._tempColor; + const { x, y, z } = direction; + + this.y00.add(Color.scale(color, basis[0], tempColor)); + + this.y1_1.add(Color.scale(color, basis[1] * y, tempColor)); + this.y10.add(Color.scale(color, basis[2] * z, tempColor)); + this.y11.add(Color.scale(color, basis[3] * x, tempColor)); + + this.y2_2.add(Color.scale(color, basis[4] * x * y, tempColor)); + this.y2_1.add(Color.scale(color, basis[5] * y * z, tempColor)); + this.y20.add(Color.scale(color, basis[6] * (3 * z * z - 1), tempColor)); + this.y21.add(Color.scale(color, basis[7] * x * z, tempColor)); + this.y22.add(Color.scale(color, basis[8] * (x * x - y * y), tempColor)); + } + + /** + * Clear SH3 to zero. + */ + clear(): void { + this.y00.setValue(0, 0, 0, 0); + this.y1_1.setValue(0, 0, 0, 0); + this.y10.setValue(0, 0, 0, 0); + this.y11.setValue(0, 0, 0, 0); + this.y2_2.setValue(0, 0, 0, 0); + this.y2_1.setValue(0, 0, 0, 0); + this.y20.setValue(0, 0, 0, 0); + this.y21.setValue(0, 0, 0, 0); + this.y22.setValue(0, 0, 0, 0); + } + + /** + * @override + */ + clone(): SphericalHarmonics3 { + const v = new SphericalHarmonics3(); + return this.cloneTo(v); + } + + /** + * @override + */ + cloneTo(out: SphericalHarmonics3): SphericalHarmonics3 { + out.y00 = this.y00; + out.y1_1 = this.y1_1; + out.y10 = this.y10; + out.y11 = this.y11; + out.y2_2 = this.y2_2; + out.y2_1 = this.y2_1; + out.y20 = this.y20; + out.y21 = this.y21; + out.y22 = this.y22; + return out; + } +} diff --git a/packages/math/src/index.ts b/packages/math/src/index.ts index 85eb30f115..d972ee4c6d 100755 --- a/packages/math/src/index.ts +++ b/packages/math/src/index.ts @@ -15,3 +15,4 @@ export { Vector4 } from "./Vector4"; export { Plane } from "./Plane"; export { Color } from "./Color"; export { Rect } from "./Rect"; +export { SphericalHarmonics3 } from "./SphericalHarmonics3"; From 696aef9da953b80a9e4314e55c3f6f197a8f9a85 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Thu, 15 Jul 2021 16:59:17 +0800 Subject: [PATCH 02/19] feat: use spherical harmonics to render indirect diffuse radiance --- packages/core/src/lighting/AmbientLight.ts | 40 ++++++++++--------- .../core/src/lighting/enums/DiffuseMode.ts | 5 +-- .../pbr/envmap_light_frag_define.glsl | 2 +- .../src/shaderlib/pbr/ibl_diffuse_frag.glsl | 24 +++-------- .../pbr/ibl_diffuse_frag_define.glsl | 19 +++++++++ 5 files changed, 49 insertions(+), 41 deletions(-) diff --git a/packages/core/src/lighting/AmbientLight.ts b/packages/core/src/lighting/AmbientLight.ts index 2d626a966c..8ab8782a56 100644 --- a/packages/core/src/lighting/AmbientLight.ts +++ b/packages/core/src/lighting/AmbientLight.ts @@ -1,4 +1,4 @@ -import { Color } from "@oasis-engine/math"; +import { Color, SphericalHarmonics3 } from "@oasis-engine/math"; import { Scene } from "../Scene"; import { Shader } from "../shader"; import { ShaderMacro } from "../shader/ShaderMacro"; @@ -14,7 +14,7 @@ export class AmbientLight { private static _specularMacro: ShaderMacro = Shader.getMacroByName("O3_USE_SPECULAR_ENV"); private static _diffuseColorProperty: ShaderProperty = Shader.getPropertyByName("u_envMapLight.diffuse"); - private static _diffuseTextureProperty: ShaderProperty = Shader.getPropertyByName("u_env_diffuseSampler"); + private static _diffuseSHProperty: ShaderProperty = Shader.getPropertyByName("u_env_sh"); private static _diffuseIntensityProperty: ShaderProperty = Shader.getPropertyByName("u_envMapLight.diffuseIntensity"); private static _specularTextureProperty: ShaderProperty = Shader.getPropertyByName("u_env_specularSampler"); private static _specularIntensityProperty: ShaderProperty = Shader.getPropertyByName( @@ -23,6 +23,7 @@ export class AmbientLight { private static _mipLevelProperty: ShaderProperty = Shader.getPropertyByName("u_envMapLight.mipMapLevel"); private _scene: Scene; + private _diffuseSphericalHarmonics: SphericalHarmonics3; private _diffuseSolidColor: Color = new Color(0.212, 0.227, 0.259); private _diffuseIntensity: number = 1.0; private _specularReflection: TextureCubeMap; @@ -38,7 +39,7 @@ export class AmbientLight { set diffuseMode(value: DiffuseMode) { this._diffuseMode = value; - if (value === DiffuseMode.Texture) { + if (value === DiffuseMode.SphericalHarmonics) { this._scene.shaderData.enableMacro(AmbientLight._diffuseMacro); } else { this._scene.shaderData.disableMacro(AmbientLight._diffuseMacro); @@ -59,6 +60,23 @@ export class AmbientLight { } } + /** + * Diffuse reflection sphericalharmonics + * @remarks Effective when diffuse reflection mode is `DiffuseMode.SphericalHarmonics`. + */ + get diffuseSphericalHarmonics(): SphericalHarmonics3 { + return this._diffuseSphericalHarmonics; + } + + set diffuseSphericalHarmonics(sh: SphericalHarmonics3) { + this._diffuseSphericalHarmonics = sh; + const shaderData = this._scene.shaderData; + + if (sh) { + shaderData.setFloatArray(AmbientLight._diffuseSHProperty, sh.preScaledCoefficients); + } + } + /** * Diffuse reflection intensity. */ @@ -112,20 +130,4 @@ export class AmbientLight { shaderData.setFloat(AmbientLight._diffuseIntensityProperty, this._diffuseIntensity); shaderData.setFloat(AmbientLight._specularIntensityProperty, this._specularIntensity); } - - //-----------------------------deprecated--------------------------------------- - - private _diffuseTexture: TextureCubeMap; - - /** - * Diffuse cube texture. - */ - get diffuseTexture(): TextureCubeMap { - return this._diffuseTexture; - } - - set diffuseTexture(value: TextureCubeMap) { - this._diffuseTexture = value; - this._scene.shaderData.setTexture(AmbientLight._diffuseTextureProperty, value); - } } diff --git a/packages/core/src/lighting/enums/DiffuseMode.ts b/packages/core/src/lighting/enums/DiffuseMode.ts index b97591c8c8..06b576a0ea 100644 --- a/packages/core/src/lighting/enums/DiffuseMode.ts +++ b/packages/core/src/lighting/enums/DiffuseMode.ts @@ -4,7 +4,6 @@ export enum DiffuseMode { /** Solid color mode. */ SolidColor, - /** Texture cube mode. */ - Texture - // SphericalHarmonics + /** SH mode */ + SphericalHarmonics } diff --git a/packages/core/src/shaderlib/pbr/envmap_light_frag_define.glsl b/packages/core/src/shaderlib/pbr/envmap_light_frag_define.glsl index d435c207e2..9bdf164c8c 100644 --- a/packages/core/src/shaderlib/pbr/envmap_light_frag_define.glsl +++ b/packages/core/src/shaderlib/pbr/envmap_light_frag_define.glsl @@ -10,7 +10,7 @@ struct EnvMapLight { uniform EnvMapLight u_envMapLight; #ifdef O3_USE_DIFFUSE_ENV - uniform samplerCube u_env_diffuseSampler; + uniform vec3 u_env_sh[9]; #endif #ifdef O3_USE_SPECULAR_ENV diff --git a/packages/core/src/shaderlib/pbr/ibl_diffuse_frag.glsl b/packages/core/src/shaderlib/pbr/ibl_diffuse_frag.glsl index 4acd9a0dd1..308e5987ef 100644 --- a/packages/core/src/shaderlib/pbr/ibl_diffuse_frag.glsl +++ b/packages/core/src/shaderlib/pbr/ibl_diffuse_frag.glsl @@ -1,21 +1,9 @@ -#if defined(RE_IndirectDiffuse) - - vec3 irradiance = vec3(0); - - #ifdef O3_USE_DIFFUSE_ENV - vec3 lightMapIrradiance = textureCube(u_env_diffuseSampler, geometry.normal).rgb * u_envMapLight.diffuseIntensity; - #else - vec3 lightMapIrradiance = u_envMapLight.diffuse * u_envMapLight.diffuseIntensity; - #endif - - #ifndef PHYSICALLY_CORRECT_LIGHTS - lightMapIrradiance *= PI; - #endif - - irradiance += lightMapIrradiance; - - RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight ); - +#ifdef O3_USE_DIFFUSE_ENV + vec3 irradiance = getLightProbeIrradiance(u_env_sh, normal) * u_envMapLight.diffuseIntensity; +#else + vec3 irradiance = u_envMapLight.diffuse * u_envMapLight.diffuseIntensity; #endif +RE_IndirectDiffuse_Physical( irradiance, geometry, material, reflectedLight ); + diff --git a/packages/core/src/shaderlib/pbr/ibl_diffuse_frag_define.glsl b/packages/core/src/shaderlib/pbr/ibl_diffuse_frag_define.glsl index 3283996f60..4f8a307784 100644 --- a/packages/core/src/shaderlib/pbr/ibl_diffuse_frag_define.glsl +++ b/packages/core/src/shaderlib/pbr/ibl_diffuse_frag_define.glsl @@ -3,3 +3,22 @@ void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricCo reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor ); } + + +// sh need be pre-scaled in CPU. +vec3 getLightProbeIrradiance(vec3 sh[9], vec3 normal){ + vec3 result = sh[0] + + + sh[1] * (normal.y) + + sh[2] * (normal.z) + + sh[3] * (normal.x) + + + sh[4] * (normal.y * normal.x) + + sh[5] * (normal.y * normal.z) + + sh[6] * (3.0 * normal.z * normal.z - 1.0) + + sh[7] * (normal.z * normal.x) + + sh[8] * (normal.x * normal.x - normal.y * normal.y); + + return max(result, vec3(0.0)); + +} From 833dc86dea04a6afa482ef91eb970cf5909fe6fc Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Thu, 15 Jul 2021 17:07:46 +0800 Subject: [PATCH 03/19] refactor: return color is convenience --- packages/math/src/Color.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/math/src/Color.ts b/packages/math/src/Color.ts index dc4fb6994e..4cb1fbb103 100644 --- a/packages/math/src/Color.ts +++ b/packages/math/src/Color.ts @@ -56,11 +56,13 @@ export class Color implements IClone { * @param right - The second color to add * @param out - The sum of two colors */ - static add(left: Color, right: Color, out: Color): void { + static add(left: Color, right: Color, out: Color): Color { out.r = left.r + right.r; out.g = left.g + right.g; out.b = left.b + right.b; out.a = left.a + right.a; + + return out; } /** @@ -69,11 +71,13 @@ export class Color implements IClone { * @param scale - The amount by which to scale the color * @param out - The scaled color */ - static scale(left: Color, s: number, out: Color): void { + static scale(left: Color, s: number, out: Color): Color { out.r = left.r * s; out.g = left.g * s; out.b = left.b * s; out.a = left.a * s; + + return out; } /** The red component of the color, 0~1. */ From 113956ac56768a15f1d5d487f12810f1227203e1 Mon Sep 17 00:00:00 2001 From: zhuxudong Date: Tue, 23 Mar 2021 15:04:19 +0800 Subject: [PATCH 04/19] refactor: rename --- packages/math/src/SphericalHarmonics3.ts | 60 ++++++++++++------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/math/src/SphericalHarmonics3.ts b/packages/math/src/SphericalHarmonics3.ts index b74e2e63fd..97fd2027d0 100644 --- a/packages/math/src/SphericalHarmonics3.ts +++ b/packages/math/src/SphericalHarmonics3.ts @@ -62,7 +62,7 @@ export class SphericalHarmonics3 implements IClone { get preScaledCoefficients(): Float32Array { const kernel = SphericalHarmonics3._convolutionKernel; const basis = SphericalHarmonics3._basisFunction; - const arrayBuffer = this._coefficients; + const data = this._coefficients; /** * 1. L -> E @@ -70,39 +70,39 @@ export class SphericalHarmonics3 implements IClone { */ // l0 - arrayBuffer[0] = this.y00.r * kernel[0] * basis[0]; - arrayBuffer[1] = this.y00.g * kernel[0] * basis[0]; - arrayBuffer[2] = this.y00.b * kernel[0] * basis[0]; + data[0] = this.y00.r * kernel[0] * basis[0]; + data[1] = this.y00.g * kernel[0] * basis[0]; + data[2] = this.y00.b * kernel[0] * basis[0]; // l1 - arrayBuffer[3] = this.y1_1.r * kernel[1] * basis[1]; - arrayBuffer[4] = this.y1_1.g * kernel[1] * basis[1]; - arrayBuffer[5] = this.y1_1.b * kernel[1] * basis[1]; - arrayBuffer[6] = this.y10.r * kernel[1] * basis[2]; - arrayBuffer[7] = this.y10.g * kernel[1] * basis[2]; - arrayBuffer[8] = this.y10.b * kernel[1] * basis[2]; - arrayBuffer[9] = this.y11.r * kernel[1] * basis[3]; - arrayBuffer[10] = this.y11.g * kernel[1] * basis[3]; - arrayBuffer[11] = this.y11.b * kernel[1] * basis[3]; + data[3] = this.y1_1.r * kernel[1] * basis[1]; + data[4] = this.y1_1.g * kernel[1] * basis[1]; + data[5] = this.y1_1.b * kernel[1] * basis[1]; + data[6] = this.y10.r * kernel[1] * basis[2]; + data[7] = this.y10.g * kernel[1] * basis[2]; + data[8] = this.y10.b * kernel[1] * basis[2]; + data[9] = this.y11.r * kernel[1] * basis[3]; + data[10] = this.y11.g * kernel[1] * basis[3]; + data[11] = this.y11.b * kernel[1] * basis[3]; // l2 - arrayBuffer[12] = this.y2_2.r * kernel[2] * basis[4]; - arrayBuffer[13] = this.y2_2.g * kernel[2] * basis[4]; - arrayBuffer[14] = this.y2_2.b * kernel[2] * basis[4]; - arrayBuffer[15] = this.y2_1.r * kernel[2] * basis[5]; - arrayBuffer[16] = this.y2_1.g * kernel[2] * basis[5]; - arrayBuffer[17] = this.y2_1.b * kernel[2] * basis[5]; - arrayBuffer[18] = this.y20.r * kernel[2] * basis[6]; - arrayBuffer[19] = this.y20.g * kernel[2] * basis[6]; - arrayBuffer[20] = this.y20.b * kernel[2] * basis[6]; - arrayBuffer[21] = this.y21.r * kernel[2] * basis[7]; - arrayBuffer[22] = this.y21.g * kernel[2] * basis[7]; - arrayBuffer[23] = this.y21.b * kernel[2] * basis[7]; - arrayBuffer[24] = this.y22.r * kernel[2] * basis[8]; - arrayBuffer[25] = this.y22.g * kernel[2] * basis[8]; - arrayBuffer[26] = this.y22.b * kernel[2] * basis[8]; - - return arrayBuffer; + data[12] = this.y2_2.r * kernel[2] * basis[4]; + data[13] = this.y2_2.g * kernel[2] * basis[4]; + data[14] = this.y2_2.b * kernel[2] * basis[4]; + data[15] = this.y2_1.r * kernel[2] * basis[5]; + data[16] = this.y2_1.g * kernel[2] * basis[5]; + data[17] = this.y2_1.b * kernel[2] * basis[5]; + data[18] = this.y20.r * kernel[2] * basis[6]; + data[19] = this.y20.g * kernel[2] * basis[6]; + data[20] = this.y20.b * kernel[2] * basis[6]; + data[21] = this.y21.r * kernel[2] * basis[7]; + data[22] = this.y21.g * kernel[2] * basis[7]; + data[23] = this.y21.b * kernel[2] * basis[7]; + data[24] = this.y22.r * kernel[2] * basis[8]; + data[25] = this.y22.g * kernel[2] * basis[8]; + data[26] = this.y22.b * kernel[2] * basis[8]; + + return data; } /** From 9e44f6f23ed7b441ca281aeede7499e0d0bc07da Mon Sep 17 00:00:00 2001 From: zhuxudong Date: Tue, 23 Mar 2021 17:23:06 +0800 Subject: [PATCH 05/19] refactor: scale the solid angle by light --- packages/math/src/SphericalHarmonics3.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/math/src/SphericalHarmonics3.ts b/packages/math/src/SphericalHarmonics3.ts index 97fd2027d0..f759b4a62b 100644 --- a/packages/math/src/SphericalHarmonics3.ts +++ b/packages/math/src/SphericalHarmonics3.ts @@ -110,14 +110,17 @@ export class SphericalHarmonics3 implements IClone { * @remarks * Implements `EvalSHBasis` from [Projection from Cube maps] in http://www.ppsloan.org/publications/StupidSH36.pdf. * - * @param color - radiance color - * @param direction - radiance direction + * @param color - Radiance color + * @param direction - Radiance direction + * @param solidAngle - Radiance solid angle, dA / (r^2) */ - addRadiance(color: Color, direction: Vector3): void { + addRadiance(color: Color, direction: Vector3, solidAngle: number): void { const basis = SphericalHarmonics3._basisFunction; const tempColor = SphericalHarmonics3._tempColor; const { x, y, z } = direction; + color.scale(solidAngle); + this.y00.add(Color.scale(color, basis[0], tempColor)); this.y1_1.add(Color.scale(color, basis[1] * y, tempColor)); From 2f7555f0ea1d93bcd3d9e53bdccf0b9ed3607ac0 Mon Sep 17 00:00:00 2001 From: zhuxudong Date: Wed, 24 Mar 2021 16:32:50 +0800 Subject: [PATCH 06/19] feat: support scale spherical harmonics --- packages/math/src/SphericalHarmonics3.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/math/src/SphericalHarmonics3.ts b/packages/math/src/SphericalHarmonics3.ts index f759b4a62b..e5966397b2 100644 --- a/packages/math/src/SphericalHarmonics3.ts +++ b/packages/math/src/SphericalHarmonics3.ts @@ -134,6 +134,21 @@ export class SphericalHarmonics3 implements IClone { this.y22.add(Color.scale(color, basis[8] * (x * x - y * y), tempColor)); } + /** + * Scale the coefficients. + */ + scale(value: number) { + this.y00.scale(value); + this.y1_1.scale(value); + this.y10.scale(value); + this.y11.scale(value); + this.y2_2.scale(value); + this.y2_1.scale(value); + this.y20.scale(value); + this.y21.scale(value); + this.y22.scale(value); + } + /** * Clear SH3 to zero. */ From c2a8e13a90ae5268db1b02838c927d2b79c3df8d Mon Sep 17 00:00:00 2001 From: zhuxudong Date: Wed, 24 Mar 2021 17:33:37 +0800 Subject: [PATCH 07/19] refactor: use deep clone --- packages/math/src/SphericalHarmonics3.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/math/src/SphericalHarmonics3.ts b/packages/math/src/SphericalHarmonics3.ts index e5966397b2..acf52ac85f 100644 --- a/packages/math/src/SphericalHarmonics3.ts +++ b/packages/math/src/SphericalHarmonics3.ts @@ -176,15 +176,15 @@ export class SphericalHarmonics3 implements IClone { * @override */ cloneTo(out: SphericalHarmonics3): SphericalHarmonics3 { - out.y00 = this.y00; - out.y1_1 = this.y1_1; - out.y10 = this.y10; - out.y11 = this.y11; - out.y2_2 = this.y2_2; - out.y2_1 = this.y2_1; - out.y20 = this.y20; - out.y21 = this.y21; - out.y22 = this.y22; + this.y00.cloneTo(out.y00); + this.y1_1.cloneTo(out.y1_1); + this.y10.cloneTo(out.y10); + this.y11.cloneTo(out.y11); + this.y2_2.cloneTo(out.y2_2); + this.y2_1.cloneTo(out.y2_1); + this.y20.cloneTo(out.y20); + this.y21.cloneTo(out.y21); + this.y22.cloneTo(out.y22); return out; } } From bd04d3fa0309892c63cda34c267ff2748942f1f0 Mon Sep 17 00:00:00 2001 From: zhuxudong Date: Sun, 23 May 2021 23:53:58 +0800 Subject: [PATCH 08/19] feat: [SH] support import/export sh --- packages/math/src/SphericalHarmonics3.ts | 55 ++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/packages/math/src/SphericalHarmonics3.ts b/packages/math/src/SphericalHarmonics3.ts index acf52ac85f..6595642c1e 100644 --- a/packages/math/src/SphericalHarmonics3.ts +++ b/packages/math/src/SphericalHarmonics3.ts @@ -10,6 +10,25 @@ import { Vector3 } from "./Vector3"; * https://google.github.io/filament/Filament.md.html#annex/sphericalharmonics */ export class SphericalHarmonics3 implements IClone { + static fromArray(shArray: number[]): SphericalHarmonics3 { + if (shArray.length != 27) { + console.error("sh coefficients must be as large as 27"); + } + + const sh = new SphericalHarmonics3(); + sh.y00 = new Color(shArray[0], shArray[1], shArray[2], 0); + sh.y1_1 = new Color(shArray[3], shArray[4], shArray[5], 0); + sh.y10 = new Color(shArray[6], shArray[7], shArray[8], 0); + sh.y11 = new Color(shArray[9], shArray[10], shArray[11], 0); + sh.y2_2 = new Color(shArray[12], shArray[13], shArray[14], 0); + sh.y2_1 = new Color(shArray[15], shArray[16], shArray[17], 0); + sh.y20 = new Color(shArray[18], shArray[19], shArray[20], 0); + sh.y21 = new Color(shArray[21], shArray[22], shArray[23], 0); + sh.y22 = new Color(shArray[24], shArray[25], shArray[26], 0); + + return sh; + } + private static _basisFunction = [ 0.282095, // 1/2 * Math.sqrt(1 / PI) @@ -164,6 +183,42 @@ export class SphericalHarmonics3 implements IClone { this.y22.setValue(0, 0, 0, 0); } + /** + * Clone the value of this coefficients to an array. + * @param out - The array + */ + toArray(out: number[]): void { + out[0] = this.y00.r; + out[1] = this.y00.g; + out[2] = this.y00.b; + + out[3] = this.y1_1.r; + out[4] = this.y1_1.g; + out[5] = this.y1_1.b; + out[6] = this.y10.r; + out[7] = this.y10.g; + out[8] = this.y10.b; + out[9] = this.y11.r; + out[10] = this.y11.g; + out[11] = this.y11.b; + + out[12] = this.y2_2.r; + out[13] = this.y2_2.g; + out[14] = this.y2_2.b; + out[15] = this.y2_1.r; + out[16] = this.y2_1.g; + out[17] = this.y2_1.b; + out[18] = this.y20.r; + out[19] = this.y20.g; + out[20] = this.y20.b; + out[21] = this.y21.r; + out[22] = this.y21.g; + out[23] = this.y21.b; + out[24] = this.y22.r; + out[25] = this.y22.g; + out[26] = this.y22.b; + } + /** * @override */ From 34c61a1ca539255fc05f59a2682b4b2c56d8da9b Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Thu, 15 Jul 2021 17:54:32 +0800 Subject: [PATCH 09/19] docs: update comment --- packages/core/src/lighting/AmbientLight.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/lighting/AmbientLight.ts b/packages/core/src/lighting/AmbientLight.ts index 8ab8782a56..525e46db67 100644 --- a/packages/core/src/lighting/AmbientLight.ts +++ b/packages/core/src/lighting/AmbientLight.ts @@ -61,7 +61,7 @@ export class AmbientLight { } /** - * Diffuse reflection sphericalharmonics + * Diffuse reflection spherical harmonics 3. * @remarks Effective when diffuse reflection mode is `DiffuseMode.SphericalHarmonics`. */ get diffuseSphericalHarmonics(): SphericalHarmonics3 { From aa81785bf57bb7cf8eb249cb78ef255afe76bff1 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Fri, 16 Jul 2021 11:27:54 +0800 Subject: [PATCH 10/19] perf: prevent gc --- packages/math/src/SphericalHarmonics3.ts | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/math/src/SphericalHarmonics3.ts b/packages/math/src/SphericalHarmonics3.ts index 6595642c1e..bf09b1cae2 100644 --- a/packages/math/src/SphericalHarmonics3.ts +++ b/packages/math/src/SphericalHarmonics3.ts @@ -10,23 +10,20 @@ import { Vector3 } from "./Vector3"; * https://google.github.io/filament/Filament.md.html#annex/sphericalharmonics */ export class SphericalHarmonics3 implements IClone { - static fromArray(shArray: number[]): SphericalHarmonics3 { + static fromArray(shArray: number[], out: SphericalHarmonics3): void { if (shArray.length != 27) { console.error("sh coefficients must be as large as 27"); } - const sh = new SphericalHarmonics3(); - sh.y00 = new Color(shArray[0], shArray[1], shArray[2], 0); - sh.y1_1 = new Color(shArray[3], shArray[4], shArray[5], 0); - sh.y10 = new Color(shArray[6], shArray[7], shArray[8], 0); - sh.y11 = new Color(shArray[9], shArray[10], shArray[11], 0); - sh.y2_2 = new Color(shArray[12], shArray[13], shArray[14], 0); - sh.y2_1 = new Color(shArray[15], shArray[16], shArray[17], 0); - sh.y20 = new Color(shArray[18], shArray[19], shArray[20], 0); - sh.y21 = new Color(shArray[21], shArray[22], shArray[23], 0); - sh.y22 = new Color(shArray[24], shArray[25], shArray[26], 0); - - return sh; + out.y00.setValue(shArray[0], shArray[1], shArray[2], 0); + out.y1_1.setValue(shArray[3], shArray[4], shArray[5], 0); + out.y10.setValue(shArray[6], shArray[7], shArray[8], 0); + out.y11.setValue(shArray[9], shArray[10], shArray[11], 0); + out.y2_2.setValue(shArray[12], shArray[13], shArray[14], 0); + out.y2_1.setValue(shArray[15], shArray[16], shArray[17], 0); + out.y20.setValue(shArray[18], shArray[19], shArray[20], 0); + out.y21.setValue(shArray[21], shArray[22], shArray[23], 0); + out.y22.setValue(shArray[24], shArray[25], shArray[26], 0); } private static _basisFunction = [ From 3c86ab7b99f9d53c6dd722af22c2cedaa189f4f8 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Wed, 4 Aug 2021 15:58:39 +0800 Subject: [PATCH 11/19] doc: add comments --- packages/math/src/Color.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/math/src/Color.ts b/packages/math/src/Color.ts index 3fd866ba00..ff5198c88e 100644 --- a/packages/math/src/Color.ts +++ b/packages/math/src/Color.ts @@ -55,6 +55,7 @@ export class Color implements IClone { * @param left - The first color to add * @param right - The second color to add * @param out - The sum of two colors + * @returns The added color */ static add(left: Color, right: Color, out: Color): Color { out.r = left.r + right.r; @@ -70,6 +71,7 @@ export class Color implements IClone { * @param left - The color to scale * @param s - The amount by which to scale the color * @param out - The scaled color + * @returns The scaled color */ static scale(left: Color, s: number, out: Color): Color { out.r = left.r * s; @@ -122,7 +124,7 @@ export class Color implements IClone { /** * Determines the sum of this color and the specified color. * @param color - The specified color - * @returns This color + * @returns The added color */ add(color: Color): Color { this.r += color.r; @@ -136,7 +138,7 @@ export class Color implements IClone { /** * Scale this color by the given value. * @param s - The amount by which to scale the color - * @returns This color + * @returns The scaled color */ scale(s: number): Color { this.r *= s; From 0ac023d95d421398e26dbd744d902ad791d613b8 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Wed, 4 Aug 2021 16:07:57 +0800 Subject: [PATCH 12/19] refactor: use `setValueByArray` and `toArray` and adding `offset` of array --- packages/math/src/SphericalHarmonics3.ts | 98 ++++++++++++------------ 1 file changed, 51 insertions(+), 47 deletions(-) diff --git a/packages/math/src/SphericalHarmonics3.ts b/packages/math/src/SphericalHarmonics3.ts index bf09b1cae2..2af64ba60d 100644 --- a/packages/math/src/SphericalHarmonics3.ts +++ b/packages/math/src/SphericalHarmonics3.ts @@ -10,22 +10,6 @@ import { Vector3 } from "./Vector3"; * https://google.github.io/filament/Filament.md.html#annex/sphericalharmonics */ export class SphericalHarmonics3 implements IClone { - static fromArray(shArray: number[], out: SphericalHarmonics3): void { - if (shArray.length != 27) { - console.error("sh coefficients must be as large as 27"); - } - - out.y00.setValue(shArray[0], shArray[1], shArray[2], 0); - out.y1_1.setValue(shArray[3], shArray[4], shArray[5], 0); - out.y10.setValue(shArray[6], shArray[7], shArray[8], 0); - out.y11.setValue(shArray[9], shArray[10], shArray[11], 0); - out.y2_2.setValue(shArray[12], shArray[13], shArray[14], 0); - out.y2_1.setValue(shArray[15], shArray[16], shArray[17], 0); - out.y20.setValue(shArray[18], shArray[19], shArray[20], 0); - out.y21.setValue(shArray[21], shArray[22], shArray[23], 0); - out.y22.setValue(shArray[24], shArray[25], shArray[26], 0); - } - private static _basisFunction = [ 0.282095, // 1/2 * Math.sqrt(1 / PI) @@ -181,39 +165,59 @@ export class SphericalHarmonics3 implements IClone { } /** - * Clone the value of this coefficients to an array. + * Set the value of this spherical harmonics by an array. + * @param array - The array + * @param offset - The start offset of the array + * @returns This spherical harmonics + */ + setValueByArray(array: ArrayLike, offset: number = 0): SphericalHarmonics3 { + this.y00.setValue(array[0 + offset], array[1 + offset], array[2 + offset], 0); + this.y1_1.setValue(array[3 + offset], array[4 + offset], array[5 + offset], 0); + this.y10.setValue(array[6 + offset], array[7 + offset], array[8 + offset], 0); + this.y11.setValue(array[9 + offset], array[10 + offset], array[11 + offset], 0); + this.y2_2.setValue(array[12 + offset], array[13 + offset], array[14 + offset], 0); + this.y2_1.setValue(array[15 + offset], array[16 + offset], array[17 + offset], 0); + this.y20.setValue(array[18 + offset], array[19 + offset], array[20 + offset], 0); + this.y21.setValue(array[21 + offset], array[22 + offset], array[23 + offset], 0); + this.y22.setValue(array[24 + offset], array[25 + offset], array[26 + offset], 0); + return this; + } + + /** + * Clone the value of this spherical harmonics to an array. * @param out - The array + * @param outOffset - The start offset of the array */ - toArray(out: number[]): void { - out[0] = this.y00.r; - out[1] = this.y00.g; - out[2] = this.y00.b; - - out[3] = this.y1_1.r; - out[4] = this.y1_1.g; - out[5] = this.y1_1.b; - out[6] = this.y10.r; - out[7] = this.y10.g; - out[8] = this.y10.b; - out[9] = this.y11.r; - out[10] = this.y11.g; - out[11] = this.y11.b; - - out[12] = this.y2_2.r; - out[13] = this.y2_2.g; - out[14] = this.y2_2.b; - out[15] = this.y2_1.r; - out[16] = this.y2_1.g; - out[17] = this.y2_1.b; - out[18] = this.y20.r; - out[19] = this.y20.g; - out[20] = this.y20.b; - out[21] = this.y21.r; - out[22] = this.y21.g; - out[23] = this.y21.b; - out[24] = this.y22.r; - out[25] = this.y22.g; - out[26] = this.y22.b; + toArray(out: number[] | Float32Array | Float64Array, outOffset: number = 0): void { + out[0 + outOffset] = this.y00.r; + out[1 + outOffset] = this.y00.g; + out[2 + outOffset] = this.y00.b; + + out[3 + outOffset] = this.y1_1.r; + out[4 + outOffset] = this.y1_1.g; + out[5 + outOffset] = this.y1_1.b; + out[6 + outOffset] = this.y10.r; + out[7 + outOffset] = this.y10.g; + out[8 + outOffset] = this.y10.b; + out[9 + outOffset] = this.y11.r; + out[10 + outOffset] = this.y11.g; + out[11 + outOffset] = this.y11.b; + + out[12 + outOffset] = this.y2_2.r; + out[13 + outOffset] = this.y2_2.g; + out[14 + outOffset] = this.y2_2.b; + out[15 + outOffset] = this.y2_1.r; + out[16 + outOffset] = this.y2_1.g; + out[17 + outOffset] = this.y2_1.b; + out[18 + outOffset] = this.y20.r; + out[19 + outOffset] = this.y20.g; + out[20 + outOffset] = this.y20.b; + out[21 + outOffset] = this.y21.r; + out[22 + outOffset] = this.y21.g; + out[23 + outOffset] = this.y21.b; + out[24 + outOffset] = this.y22.r; + out[25 + outOffset] = this.y22.g; + out[26 + outOffset] = this.y22.b; } /** From 852fe4ca68991637a5940652988c09c3d80d875c Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Wed, 4 Aug 2021 17:28:34 +0800 Subject: [PATCH 13/19] refactor: delete color --- packages/math/src/SphericalHarmonics3.ts | 304 ++++++++++++----------- 1 file changed, 163 insertions(+), 141 deletions(-) diff --git a/packages/math/src/SphericalHarmonics3.ts b/packages/math/src/SphericalHarmonics3.ts index 2af64ba60d..7903820aaa 100644 --- a/packages/math/src/SphericalHarmonics3.ts +++ b/packages/math/src/SphericalHarmonics3.ts @@ -30,39 +30,20 @@ export class SphericalHarmonics3 implements IClone { 0.785398 // PI / 4 ]; - private static _tempColor = new Color(); - - /** The Y(0, 0) coefficient of the SH3. */ - y00: Color = new Color(0, 0, 0, 0); - /** The Y(1, -1) coefficient of the SH3. */ - y1_1: Color = new Color(0, 0, 0, 0); - /** The Y(1, 0) coefficient of the SH3. */ - y10: Color = new Color(0, 0, 0, 0); - /** The Y(1, 1) coefficient of the SH3. */ - y11: Color = new Color(0, 0, 0, 0); - /** The Y(2, -2) coefficient of the SH3. */ - y2_2: Color = new Color(0, 0, 0, 0); - /** The Y(2, -1) coefficient of the SH3. */ - y2_1: Color = new Color(0, 0, 0, 0); - /** The Y(2, 0) coefficient of the SH3. */ - y20: Color = new Color(0, 0, 0, 0); - /** The Y(2, 1) coefficient of the SH3. */ - y21: Color = new Color(0, 0, 0, 0); - /** The Y(2, 2) coefficient of the SH3. */ - y22: Color = new Color(0, 0, 0, 0); - private _coefficients: Float32Array = new Float32Array(27); /** - * Get pre-scaled coefficients used in shader. - * @remarks * Convert radiance to irradiance with the A_l which is convoluted by the cosine lobe and pre-scale the basis function. + * @remarks * Reference equation [4,5,6,7,8,9] from https://graphics.stanford.edu/papers/envmap/envmap.pdf + * + * @param out - The array + * @returns Pre-scaled array */ - get preScaledCoefficients(): Float32Array { + convertRadianceToIrradiance(out: Float32Array): Float32Array { const kernel = SphericalHarmonics3._convolutionKernel; const basis = SphericalHarmonics3._basisFunction; - const data = this._coefficients; + const src = this._coefficients; /** * 1. L -> E @@ -70,39 +51,39 @@ export class SphericalHarmonics3 implements IClone { */ // l0 - data[0] = this.y00.r * kernel[0] * basis[0]; - data[1] = this.y00.g * kernel[0] * basis[0]; - data[2] = this.y00.b * kernel[0] * basis[0]; + out[0] = src[0] * kernel[0] * basis[0]; + out[1] = src[1] * kernel[0] * basis[0]; + out[2] = src[2] * kernel[0] * basis[0]; // l1 - data[3] = this.y1_1.r * kernel[1] * basis[1]; - data[4] = this.y1_1.g * kernel[1] * basis[1]; - data[5] = this.y1_1.b * kernel[1] * basis[1]; - data[6] = this.y10.r * kernel[1] * basis[2]; - data[7] = this.y10.g * kernel[1] * basis[2]; - data[8] = this.y10.b * kernel[1] * basis[2]; - data[9] = this.y11.r * kernel[1] * basis[3]; - data[10] = this.y11.g * kernel[1] * basis[3]; - data[11] = this.y11.b * kernel[1] * basis[3]; + out[3] = src[3] * kernel[1] * basis[1]; + out[4] = src[4] * kernel[1] * basis[1]; + out[5] = src[5] * kernel[1] * basis[1]; + out[6] = src[6] * kernel[1] * basis[2]; + out[7] = src[7] * kernel[1] * basis[2]; + out[8] = src[8] * kernel[1] * basis[2]; + out[9] = src[9] * kernel[1] * basis[3]; + out[10] = src[10] * kernel[1] * basis[3]; + out[11] = src[11] * kernel[1] * basis[3]; // l2 - data[12] = this.y2_2.r * kernel[2] * basis[4]; - data[13] = this.y2_2.g * kernel[2] * basis[4]; - data[14] = this.y2_2.b * kernel[2] * basis[4]; - data[15] = this.y2_1.r * kernel[2] * basis[5]; - data[16] = this.y2_1.g * kernel[2] * basis[5]; - data[17] = this.y2_1.b * kernel[2] * basis[5]; - data[18] = this.y20.r * kernel[2] * basis[6]; - data[19] = this.y20.g * kernel[2] * basis[6]; - data[20] = this.y20.b * kernel[2] * basis[6]; - data[21] = this.y21.r * kernel[2] * basis[7]; - data[22] = this.y21.g * kernel[2] * basis[7]; - data[23] = this.y21.b * kernel[2] * basis[7]; - data[24] = this.y22.r * kernel[2] * basis[8]; - data[25] = this.y22.g * kernel[2] * basis[8]; - data[26] = this.y22.b * kernel[2] * basis[8]; - - return data; + out[12] = src[12] * kernel[2] * basis[4]; + out[13] = src[13] * kernel[2] * basis[4]; + out[14] = src[14] * kernel[2] * basis[4]; + out[15] = src[15] * kernel[2] * basis[5]; + out[16] = src[16] * kernel[2] * basis[5]; + out[17] = src[17] * kernel[2] * basis[5]; + out[18] = src[18] * kernel[2] * basis[6]; + out[19] = src[19] * kernel[2] * basis[6]; + out[20] = src[20] * kernel[2] * basis[6]; + out[21] = src[21] * kernel[2] * basis[7]; + out[22] = src[22] * kernel[2] * basis[7]; + out[23] = src[23] * kernel[2] * basis[7]; + out[24] = src[24] * kernel[2] * basis[8]; + out[25] = src[25] * kernel[2] * basis[8]; + out[26] = src[26] * kernel[2] * basis[8]; + + return out; } /** @@ -116,71 +97,117 @@ export class SphericalHarmonics3 implements IClone { */ addRadiance(color: Color, direction: Vector3, solidAngle: number): void { const basis = SphericalHarmonics3._basisFunction; - const tempColor = SphericalHarmonics3._tempColor; + const src = this._coefficients; const { x, y, z } = direction; + const xy = x * y; + const yz = y * z; + const z3 = 3 * z * z - 1; + const xz = x * z; + const x2y2 = x * x - y * y; color.scale(solidAngle); - this.y00.add(Color.scale(color, basis[0], tempColor)); + src[0] += color.r * basis[0]; + src[1] += color.g * basis[0]; + src[2] += color.b * basis[0]; - this.y1_1.add(Color.scale(color, basis[1] * y, tempColor)); - this.y10.add(Color.scale(color, basis[2] * z, tempColor)); - this.y11.add(Color.scale(color, basis[3] * x, tempColor)); + src[3] += color.r * basis[1] * y; + src[4] += color.g * basis[1] * y; + src[5] += color.b * basis[1] * y; + src[6] += color.r * basis[2] * z; + src[7] += color.g * basis[2] * z; + src[8] += color.b * basis[2] * z; + src[9] += color.r * basis[3] * x; + src[10] += color.g * basis[3] * x; + src[11] += color.b * basis[3] * x; - this.y2_2.add(Color.scale(color, basis[4] * x * y, tempColor)); - this.y2_1.add(Color.scale(color, basis[5] * y * z, tempColor)); - this.y20.add(Color.scale(color, basis[6] * (3 * z * z - 1), tempColor)); - this.y21.add(Color.scale(color, basis[7] * x * z, tempColor)); - this.y22.add(Color.scale(color, basis[8] * (x * x - y * y), tempColor)); + src[12] += color.r * basis[4] * xy; + src[13] += color.g * basis[4] * xy; + src[14] += color.b * basis[4] * xy; + src[15] += color.r * basis[5] * yz; + src[16] += color.g * basis[5] * yz; + src[17] += color.b * basis[5] * yz; + src[18] += color.r * basis[6] * z3; + src[19] += color.g * basis[6] * z3; + src[20] += color.b * basis[6] * z3; + src[21] += color.r * basis[7] * xz; + src[22] += color.g * basis[7] * xz; + src[23] += color.b * basis[7] * xz; + src[24] += color.r * basis[8] * x2y2; + src[25] += color.g * basis[8] * x2y2; + src[26] += color.b * basis[8] * x2y2; } /** * Scale the coefficients. */ - scale(value: number) { - this.y00.scale(value); - this.y1_1.scale(value); - this.y10.scale(value); - this.y11.scale(value); - this.y2_2.scale(value); - this.y2_1.scale(value); - this.y20.scale(value); - this.y21.scale(value); - this.y22.scale(value); - } + scale(value: number): void { + const src = this._coefficients; - /** - * Clear SH3 to zero. - */ - clear(): void { - this.y00.setValue(0, 0, 0, 0); - this.y1_1.setValue(0, 0, 0, 0); - this.y10.setValue(0, 0, 0, 0); - this.y11.setValue(0, 0, 0, 0); - this.y2_2.setValue(0, 0, 0, 0); - this.y2_1.setValue(0, 0, 0, 0); - this.y20.setValue(0, 0, 0, 0); - this.y21.setValue(0, 0, 0, 0); - this.y22.setValue(0, 0, 0, 0); + src[0] *= value; + src[1] *= value; + src[2] *= value; + src[3] *= value; + src[4] *= value; + src[5] *= value; + src[6] *= value; + src[7] *= value; + src[8] *= value; + src[9] *= value; + src[10] *= value; + src[11] *= value; + src[12] *= value; + src[13] *= value; + src[14] *= value; + src[15] *= value; + src[16] *= value; + src[17] *= value; + src[18] *= value; + src[19] *= value; + src[20] *= value; + src[21] *= value; + src[22] *= value; + src[23] *= value; + src[24] *= value; + src[25] *= value; + src[26] *= value; } /** * Set the value of this spherical harmonics by an array. * @param array - The array * @param offset - The start offset of the array - * @returns This spherical harmonics */ - setValueByArray(array: ArrayLike, offset: number = 0): SphericalHarmonics3 { - this.y00.setValue(array[0 + offset], array[1 + offset], array[2 + offset], 0); - this.y1_1.setValue(array[3 + offset], array[4 + offset], array[5 + offset], 0); - this.y10.setValue(array[6 + offset], array[7 + offset], array[8 + offset], 0); - this.y11.setValue(array[9 + offset], array[10 + offset], array[11 + offset], 0); - this.y2_2.setValue(array[12 + offset], array[13 + offset], array[14 + offset], 0); - this.y2_1.setValue(array[15 + offset], array[16 + offset], array[17 + offset], 0); - this.y20.setValue(array[18 + offset], array[19 + offset], array[20 + offset], 0); - this.y21.setValue(array[21 + offset], array[22 + offset], array[23 + offset], 0); - this.y22.setValue(array[24 + offset], array[25 + offset], array[26 + offset], 0); - return this; + setValueByArray(array: ArrayLike, offset: number = 0): void { + const src = this._coefficients; + + src[0] = array[0 + offset]; + src[1] = array[1 + offset]; + src[2] = array[2 + offset]; + src[3] = array[3 + offset]; + src[4] = array[4 + offset]; + src[5] = array[5 + offset]; + src[6] = array[6 + offset]; + src[7] = array[7 + offset]; + src[8] = array[8 + offset]; + src[9] = array[9 + offset]; + src[10] = array[10 + offset]; + src[11] = array[11 + offset]; + src[12] = array[12 + offset]; + src[13] = array[13 + offset]; + src[14] = array[14 + offset]; + src[15] = array[15 + offset]; + src[16] = array[16 + offset]; + src[17] = array[17 + offset]; + src[18] = array[18 + offset]; + src[19] = array[19 + offset]; + src[20] = array[20 + offset]; + src[21] = array[21 + offset]; + src[22] = array[22 + offset]; + src[23] = array[23 + offset]; + src[24] = array[24 + offset]; + src[25] = array[25 + offset]; + src[26] = array[26 + offset]; } /** @@ -189,35 +216,37 @@ export class SphericalHarmonics3 implements IClone { * @param outOffset - The start offset of the array */ toArray(out: number[] | Float32Array | Float64Array, outOffset: number = 0): void { - out[0 + outOffset] = this.y00.r; - out[1 + outOffset] = this.y00.g; - out[2 + outOffset] = this.y00.b; - - out[3 + outOffset] = this.y1_1.r; - out[4 + outOffset] = this.y1_1.g; - out[5 + outOffset] = this.y1_1.b; - out[6 + outOffset] = this.y10.r; - out[7 + outOffset] = this.y10.g; - out[8 + outOffset] = this.y10.b; - out[9 + outOffset] = this.y11.r; - out[10 + outOffset] = this.y11.g; - out[11 + outOffset] = this.y11.b; - - out[12 + outOffset] = this.y2_2.r; - out[13 + outOffset] = this.y2_2.g; - out[14 + outOffset] = this.y2_2.b; - out[15 + outOffset] = this.y2_1.r; - out[16 + outOffset] = this.y2_1.g; - out[17 + outOffset] = this.y2_1.b; - out[18 + outOffset] = this.y20.r; - out[19 + outOffset] = this.y20.g; - out[20 + outOffset] = this.y20.b; - out[21 + outOffset] = this.y21.r; - out[22 + outOffset] = this.y21.g; - out[23 + outOffset] = this.y21.b; - out[24 + outOffset] = this.y22.r; - out[25 + outOffset] = this.y22.g; - out[26 + outOffset] = this.y22.b; + const src = this._coefficients; + + out[0 + outOffset] = src[0]; + out[1 + outOffset] = src[1]; + out[2 + outOffset] = src[2]; + + out[3 + outOffset] = src[3]; + out[4 + outOffset] = src[4]; + out[5 + outOffset] = src[5]; + out[6 + outOffset] = src[6]; + out[7 + outOffset] = src[7]; + out[8 + outOffset] = src[8]; + out[9 + outOffset] = src[9]; + out[10 + outOffset] = src[10]; + out[11 + outOffset] = src[11]; + + out[12 + outOffset] = src[12]; + out[13 + outOffset] = src[13]; + out[14 + outOffset] = src[14]; + out[15 + outOffset] = src[15]; + out[16 + outOffset] = src[16]; + out[17 + outOffset] = src[17]; + out[18 + outOffset] = src[18]; + out[19 + outOffset] = src[19]; + out[20 + outOffset] = src[20]; + out[21 + outOffset] = src[21]; + out[22 + outOffset] = src[22]; + out[23 + outOffset] = src[23]; + out[24 + outOffset] = src[24]; + out[25 + outOffset] = src[25]; + out[26 + outOffset] = src[26]; } /** @@ -225,22 +254,15 @@ export class SphericalHarmonics3 implements IClone { */ clone(): SphericalHarmonics3 { const v = new SphericalHarmonics3(); - return this.cloneTo(v); + this.cloneTo(v); + + return v; } /** * @override */ - cloneTo(out: SphericalHarmonics3): SphericalHarmonics3 { - this.y00.cloneTo(out.y00); - this.y1_1.cloneTo(out.y1_1); - this.y10.cloneTo(out.y10); - this.y11.cloneTo(out.y11); - this.y2_2.cloneTo(out.y2_2); - this.y2_1.cloneTo(out.y2_1); - this.y20.cloneTo(out.y20); - this.y21.cloneTo(out.y21); - this.y22.cloneTo(out.y22); - return out; + cloneTo(out: SphericalHarmonics3): void { + this.toArray(out._coefficients); } } From be252b7f996d41b1f0b9a2b72a9ffdeb00b4c4da Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Wed, 4 Aug 2021 17:32:47 +0800 Subject: [PATCH 14/19] refactor: use new sh api --- packages/core/src/lighting/AmbientLight.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/src/lighting/AmbientLight.ts b/packages/core/src/lighting/AmbientLight.ts index 525e46db67..7516d0459e 100644 --- a/packages/core/src/lighting/AmbientLight.ts +++ b/packages/core/src/lighting/AmbientLight.ts @@ -29,6 +29,7 @@ export class AmbientLight { private _specularReflection: TextureCubeMap; private _specularIntensity: number = 1.0; private _diffuseMode: DiffuseMode = DiffuseMode.SolidColor; + private _shArray: Float32Array = new Float32Array(27); /** * Diffuse mode of ambient light. @@ -73,7 +74,7 @@ export class AmbientLight { const shaderData = this._scene.shaderData; if (sh) { - shaderData.setFloatArray(AmbientLight._diffuseSHProperty, sh.preScaledCoefficients); + shaderData.setFloatArray(AmbientLight._diffuseSHProperty, sh.convertRadianceToIrradiance(this._shArray)); } } From 12bbf4118f5e8fcf47c5f6761792b3bf377957c6 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Wed, 4 Aug 2021 17:34:11 +0800 Subject: [PATCH 15/19] refactor: use new macro name --- packages/core/src/lighting/AmbientLight.ts | 6 +++--- .../core/src/shaderlib/pbr/envmap_light_frag_define.glsl | 2 +- packages/core/src/shaderlib/pbr/ibl_diffuse_frag.glsl | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/lighting/AmbientLight.ts b/packages/core/src/lighting/AmbientLight.ts index 7516d0459e..6b304ff026 100644 --- a/packages/core/src/lighting/AmbientLight.ts +++ b/packages/core/src/lighting/AmbientLight.ts @@ -10,7 +10,7 @@ import { DiffuseMode } from "./enums/DiffuseMode"; * Ambient light. */ export class AmbientLight { - private static _diffuseMacro: ShaderMacro = Shader.getMacroByName("O3_USE_DIFFUSE_ENV"); + private static _shMacro: ShaderMacro = Shader.getMacroByName("O3_USE_SH"); private static _specularMacro: ShaderMacro = Shader.getMacroByName("O3_USE_SPECULAR_ENV"); private static _diffuseColorProperty: ShaderProperty = Shader.getPropertyByName("u_envMapLight.diffuse"); @@ -41,9 +41,9 @@ export class AmbientLight { set diffuseMode(value: DiffuseMode) { this._diffuseMode = value; if (value === DiffuseMode.SphericalHarmonics) { - this._scene.shaderData.enableMacro(AmbientLight._diffuseMacro); + this._scene.shaderData.enableMacro(AmbientLight._shMacro); } else { - this._scene.shaderData.disableMacro(AmbientLight._diffuseMacro); + this._scene.shaderData.disableMacro(AmbientLight._shMacro); } } diff --git a/packages/core/src/shaderlib/pbr/envmap_light_frag_define.glsl b/packages/core/src/shaderlib/pbr/envmap_light_frag_define.glsl index 9bdf164c8c..10cee932c8 100644 --- a/packages/core/src/shaderlib/pbr/envmap_light_frag_define.glsl +++ b/packages/core/src/shaderlib/pbr/envmap_light_frag_define.glsl @@ -9,7 +9,7 @@ struct EnvMapLight { uniform EnvMapLight u_envMapLight; -#ifdef O3_USE_DIFFUSE_ENV +#ifdef O3_USE_SH uniform vec3 u_env_sh[9]; #endif diff --git a/packages/core/src/shaderlib/pbr/ibl_diffuse_frag.glsl b/packages/core/src/shaderlib/pbr/ibl_diffuse_frag.glsl index 308e5987ef..00fc46f0b4 100644 --- a/packages/core/src/shaderlib/pbr/ibl_diffuse_frag.glsl +++ b/packages/core/src/shaderlib/pbr/ibl_diffuse_frag.glsl @@ -1,4 +1,4 @@ -#ifdef O3_USE_DIFFUSE_ENV +#ifdef O3_USE_SH vec3 irradiance = getLightProbeIrradiance(u_env_sh, normal) * u_envMapLight.diffuseIntensity; #else vec3 irradiance = u_envMapLight.diffuse * u_envMapLight.diffuseIntensity; From 441c885fae45b8c3fec6cf76a88b20a5a0b05dd5 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Wed, 4 Aug 2021 17:39:57 +0800 Subject: [PATCH 16/19] doc: add comment --- packages/core/src/lighting/AmbientLight.ts | 8 ++++---- packages/core/src/lighting/enums/DiffuseMode.ts | 7 ++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/core/src/lighting/AmbientLight.ts b/packages/core/src/lighting/AmbientLight.ts index 6b304ff026..649c5f3a9a 100644 --- a/packages/core/src/lighting/AmbientLight.ts +++ b/packages/core/src/lighting/AmbientLight.ts @@ -69,12 +69,12 @@ export class AmbientLight { return this._diffuseSphericalHarmonics; } - set diffuseSphericalHarmonics(sh: SphericalHarmonics3) { - this._diffuseSphericalHarmonics = sh; + set diffuseSphericalHarmonics(value: SphericalHarmonics3) { + this._diffuseSphericalHarmonics = value; const shaderData = this._scene.shaderData; - if (sh) { - shaderData.setFloatArray(AmbientLight._diffuseSHProperty, sh.convertRadianceToIrradiance(this._shArray)); + if (value) { + shaderData.setFloatArray(AmbientLight._diffuseSHProperty, value.convertRadianceToIrradiance(this._shArray)); } } diff --git a/packages/core/src/lighting/enums/DiffuseMode.ts b/packages/core/src/lighting/enums/DiffuseMode.ts index 06b576a0ea..6b1a9d6e00 100644 --- a/packages/core/src/lighting/enums/DiffuseMode.ts +++ b/packages/core/src/lighting/enums/DiffuseMode.ts @@ -4,6 +4,11 @@ export enum DiffuseMode { /** Solid color mode. */ SolidColor, - /** SH mode */ + + /** + * SH mode + * @remarks + * Use SH3 to represent irradiance environment maps efficiently, allowing for interactive rendering of diffuse objects under distant illumination. + */ SphericalHarmonics } From f4b74a3243e49cec3592a62f98785894659ffd27 Mon Sep 17 00:00:00 2001 From: GuoLei Date: Fri, 6 Aug 2021 11:26:44 +0800 Subject: [PATCH 17/19] refcator: opt code (#4) * refcator: opt code --- packages/core/src/lighting/AmbientLight.ts | 68 +++- packages/math/src/SphericalHarmonics3.ts | 342 +++++++++------------ 2 files changed, 208 insertions(+), 202 deletions(-) diff --git a/packages/core/src/lighting/AmbientLight.ts b/packages/core/src/lighting/AmbientLight.ts index 649c5f3a9a..781cfddd52 100644 --- a/packages/core/src/lighting/AmbientLight.ts +++ b/packages/core/src/lighting/AmbientLight.ts @@ -74,7 +74,10 @@ export class AmbientLight { const shaderData = this._scene.shaderData; if (value) { - shaderData.setFloatArray(AmbientLight._diffuseSHProperty, value.convertRadianceToIrradiance(this._shArray)); + shaderData.setFloatArray( + AmbientLight._diffuseSHProperty, + this._convertRadianceToIrradiance(value, this._shArray) + ); } } @@ -131,4 +134,67 @@ export class AmbientLight { shaderData.setFloat(AmbientLight._diffuseIntensityProperty, this._diffuseIntensity); shaderData.setFloat(AmbientLight._specularIntensityProperty, this._specularIntensity); } + + private _convertRadianceToIrradiance(sh: SphericalHarmonics3, out: Float32Array): Float32Array { + /** + * Basis constants + * + * 0: 1/2 * Math.sqrt(1 / Math.PI) + * + * 1: -1/2 * Math.sqrt(3 / Math.PI) + * 2: 1/2 * Math.sqrt(3 / Math.PI) + * 3: -1/2 * Math.sqrt(3 / Math.PI) + * + * 4: 1/2 * Math.sqrt(15 / Math.PI) + * 5: -1/2 * Math.sqrt(15 / Math.PI) + * 6: 1/4 * Math.sqrt(5 / Math.PI) + * 7: -1/2 * Math.sqrt(15 / Math.PI) + * 8: 1/4 * Math.sqrt(15 / Math.PI) + */ + + /** + * Convolution kernel + * + * 0: Math.PI + * 1: (2 * Math.PI) / 3 + * 2: Math.PI / 4 + */ + + const src = sh.coefficients; + + // l0 + out[0] = src[0] * 0.886227; // kernel0 * basis0 = 0.886227 + out[1] = src[1] * 0.886227; + out[2] = src[2] * 0.886227; + + // l1 + out[3] = src[3] * -1.023327; // kernel1 * basis1 = -1.023327; + out[4] = src[4] * -1.023327; + out[5] = src[5] * -1.023327; + out[6] = src[6] * 1.023327; // kernel1 * basis2 = 1.023327 + out[7] = src[7] * 1.023327; + out[8] = src[8] * 1.023327; + out[9] = src[9] * -1.023327; // kernel1 * basis3 = -1.023327 + out[10] = src[10] * -1.023327; + out[11] = src[11] * -1.023327; + + // l2 + out[12] = src[12] * 0.858086; // kernel2 * basis4 = 0.858086 + out[13] = src[13] * 0.858086; + out[14] = src[14] * 0.858086; + out[15] = src[15] * -0.858086; // kernel2 * basis5 = -0.858086 + out[16] = src[16] * -0.858086; + out[17] = src[17] * -0.858086; + out[18] = src[18] * 0.247708; // kernel2 * basis6 = 0.247708 + out[19] = src[19] * 0.247708; + out[20] = src[20] * 0.247708; + out[21] = src[21] * -0.858086; // kernel2 * basis7 = -0.858086 + out[22] = src[22] * -0.858086; + out[23] = src[23] * -0.858086; + out[24] = src[24] * 0.429042; // kernel2 * basis8 = 0.429042 + out[25] = src[25] * 0.429042; + out[26] = src[26] * 0.429042; + + return out; + } } diff --git a/packages/math/src/SphericalHarmonics3.ts b/packages/math/src/SphericalHarmonics3.ts index 7903820aaa..376cfa0037 100644 --- a/packages/math/src/SphericalHarmonics3.ts +++ b/packages/math/src/SphericalHarmonics3.ts @@ -10,167 +10,140 @@ import { Vector3 } from "./Vector3"; * https://google.github.io/filament/Filament.md.html#annex/sphericalharmonics */ export class SphericalHarmonics3 implements IClone { - private static _basisFunction = [ - 0.282095, // 1/2 * Math.sqrt(1 / PI) - - -0.488603, // -1/2 * Math.sqrt(3 / PI) - 0.488603, // 1/2 * Math.sqrt(3 / PI) - -0.488603, // -1/2 * Math.sqrt(3 / PI) - - 1.092548, // 1/2 * Math.sqrt(15 / PI) - -1.092548, // -1/2 * Math.sqrt(15 / PI) - 0.315392, // 1/4 * Math.sqrt(5 / PI) - -1.092548, // -1/2 * Math.sqrt(15 / PI) - 0.546274 // 1/4 * Math.sqrt(15 / PI) - ]; - - private static _convolutionKernel = [ - 3.141593, // PI - 2.094395, // (2 * PI) / 3, - 0.785398 // PI / 4 - ]; - - private _coefficients: Float32Array = new Float32Array(27); + /** The coefficients of SphericalHarmonics3. */ + coefficients: Float32Array = new Float32Array(27); /** - * Convert radiance to irradiance with the A_l which is convoluted by the cosine lobe and pre-scale the basis function. - * @remarks - * Reference equation [4,5,6,7,8,9] from https://graphics.stanford.edu/papers/envmap/envmap.pdf - * - * @param out - The array - * @returns Pre-scaled array + * Add light to SphericalHarmonics3. + * @param direction - Light direction + * @param color - Light color + * @param deltaSolidAngle - The delta solid angle of the light */ - convertRadianceToIrradiance(out: Float32Array): Float32Array { - const kernel = SphericalHarmonics3._convolutionKernel; - const basis = SphericalHarmonics3._basisFunction; - const src = this._coefficients; - + addLight(direction: Vector3, color: Color, deltaSolidAngle: number): void { /** - * 1. L -> E - * 2. E * basis + * Implements `EvalSHBasis` from [Projection from Cube maps] in http://www.ppsloan.org/publications/StupidSH36.pdf. + * + * Basis constants + * 0: Math.sqrt(1/(4 * Math.PI)) + * + * 1: -Math.sqrt(3 / (4 * Math.PI)) + * 2: Math.sqrt(3 / (4 * Math.PI)) + * 3: -Math.sqrt(3 / (4 * Math.PI)) + * + * 4: Math.sqrt(15 / (4 * Math.PI)) + * 5: -Math.sqrt(15 / (4 * Math.PI)) + * 6: Math.sqrt(5 / (16 * Math.PI)) + * 7: -Math.sqrt(15 / (4 * Math.PI)) + * 8: Math.sqrt(15 / (16 * Math.PI)) */ - // l0 - out[0] = src[0] * kernel[0] * basis[0]; - out[1] = src[1] * kernel[0] * basis[0]; - out[2] = src[2] * kernel[0] * basis[0]; - - // l1 - out[3] = src[3] * kernel[1] * basis[1]; - out[4] = src[4] * kernel[1] * basis[1]; - out[5] = src[5] * kernel[1] * basis[1]; - out[6] = src[6] * kernel[1] * basis[2]; - out[7] = src[7] * kernel[1] * basis[2]; - out[8] = src[8] * kernel[1] * basis[2]; - out[9] = src[9] * kernel[1] * basis[3]; - out[10] = src[10] * kernel[1] * basis[3]; - out[11] = src[11] * kernel[1] * basis[3]; - - // l2 - out[12] = src[12] * kernel[2] * basis[4]; - out[13] = src[13] * kernel[2] * basis[4]; - out[14] = src[14] * kernel[2] * basis[4]; - out[15] = src[15] * kernel[2] * basis[5]; - out[16] = src[16] * kernel[2] * basis[5]; - out[17] = src[17] * kernel[2] * basis[5]; - out[18] = src[18] * kernel[2] * basis[6]; - out[19] = src[19] * kernel[2] * basis[6]; - out[20] = src[20] * kernel[2] * basis[6]; - out[21] = src[21] * kernel[2] * basis[7]; - out[22] = src[22] * kernel[2] * basis[7]; - out[23] = src[23] * kernel[2] * basis[7]; - out[24] = src[24] * kernel[2] * basis[8]; - out[25] = src[25] * kernel[2] * basis[8]; - out[26] = src[26] * kernel[2] * basis[8]; - - return out; + const coe = this.coefficients; + const { x, y, z } = direction; + const { r, g, b } = color; + + color.scale(deltaSolidAngle); + + const bv0 = 0.282095; // basis0 = 0.886227 + const bv1 = -0.488603 * y; // basis1 = -0.488603 + const bv2 = 0.488603 * z; // basis2 = 0.488603 + const bv3 = -0.488603 * x; // basis3 = -0.488603 + const bv4 = 1.092548 * (x * y); // basis4 = 1.092548 + const bv5 = -1.092548 * (y * z); // basis5 = -1.092548 + const bv6 = 0.315392 * (3 * z * z - 1); // basis6 = 0.315392 + const bv7 = -1.092548 * (x * z); // basis7 = -1.092548 + const bv8 = 0.546274 * (x * x - y * y); // basis8 = 0.546274 + + (coe[0] += r * bv0), (coe[1] += g * bv0), (coe[2] += b * bv0); + + (coe[3] += r * bv1), (coe[4] += g * bv1), (coe[5] += b * bv1); + (coe[6] += r * bv2), (coe[7] += g * bv2), (coe[8] += b * bv2); + (coe[9] += r * bv3), (coe[10] += g * bv3), (coe[11] += b * bv3); + + (coe[12] += r * bv4), (coe[13] += g * bv4), (coe[14] += b * bv4); + (coe[15] += r * bv5), (coe[16] += g * bv5), (coe[17] += b * bv5); + (coe[18] += r * bv6), (coe[19] += g * bv6), (coe[20] += b * bv6); + (coe[21] += r * bv7), (coe[22] += g * bv7), (coe[23] += b * bv7); + (coe[24] += r * bv8), (coe[25] += g * bv8), (coe[26] += b * bv8); } /** - * Add radiance to the SH3 in specified direction. - * @remarks - * Implements `EvalSHBasis` from [Projection from Cube maps] in http://www.ppsloan.org/publications/StupidSH36.pdf. - * - * @param color - Radiance color - * @param direction - Radiance direction - * @param solidAngle - Radiance solid angle, dA / (r^2) + * Evaluates the color for the specified direction. + * @param direction - Specified direction + * @param out - Out color */ - addRadiance(color: Color, direction: Vector3, solidAngle: number): void { - const basis = SphericalHarmonics3._basisFunction; - const src = this._coefficients; + evaluate(direction: Vector3, out: Color): Color { + /** + * Equations based on data from: http://ppsloan.org/publications/StupidSH36.pdf + * + * + * Basis constants + * 0: Math.sqrt(1/(4 * Math.PI)) + * + * 1: -Math.sqrt(3 / (4 * Math.PI)) + * 2: Math.sqrt(3 / (4 * Math.PI)) + * 3: -Math.sqrt(3 / (4 * Math.PI)) + * + * 4: Math.sqrt(15 / (4 * Math.PI)) + * 5: -Math.sqrt(15 / (4 * Math.PI)) + * 6: Math.sqrt(5 / (16 * Math.PI)) + * 7: -Math.sqrt(15 / (4 * Math.PI)) + * 8: Math.sqrt(15 / (16 * Math.PI)) + * + * + * Convolution kernel + * 0: Math.PI + * 1: (2 * Math.PI) / 3 + * 2: Math.PI / 4 + */ + + const coe = this.coefficients; const { x, y, z } = direction; - const xy = x * y; - const yz = y * z; - const z3 = 3 * z * z - 1; - const xz = x * z; - const x2y2 = x * x - y * y; - color.scale(solidAngle); + const bv0 = 0.886227; // kernel0 * basis0 = 0.886227 + const bv1 = -1.023327 * y; // kernel1 * basis1 = -1.023327 + const bv2 = 1.023327 * z; // kernel1 * basis2 = 1.023327 + const bv3 = -1.023327 * x; // kernel1 * basis3 = -1.023327 + const bv4 = 0.858086 * y * x; // kernel2 * basis4 = 0.858086 + const bv5 = -0.858086 * y * z; // kernel2 * basis5 = -0.858086 + const bv6 = 0.247708 * (3 * z * z - 1); // kernel2 * basis6 = 0.247708 + const bv7 = -0.858086 * z * x; // kernel2 * basis7 = -0.858086 + const bv8 = 0.429042 * (x * x - y * y); // kernel2 * basis8 = 0.429042 + + // l0 + let r = coe[0] * bv0; + let g = coe[1] * bv0; + let b = coe[2] * bv0; - src[0] += color.r * basis[0]; - src[1] += color.g * basis[0]; - src[2] += color.b * basis[0]; + // l1 + r += coe[3] * bv1 + coe[6] * bv2 + coe[9] * bv3; + g += coe[4] * bv1 + coe[7] * bv2 + coe[10] * bv3; + b += coe[5] * bv1 + coe[8] * bv2 + coe[11] * bv3; - src[3] += color.r * basis[1] * y; - src[4] += color.g * basis[1] * y; - src[5] += color.b * basis[1] * y; - src[6] += color.r * basis[2] * z; - src[7] += color.g * basis[2] * z; - src[8] += color.b * basis[2] * z; - src[9] += color.r * basis[3] * x; - src[10] += color.g * basis[3] * x; - src[11] += color.b * basis[3] * x; + // l2 + r += coe[12] * bv4 + coe[15] * bv5 + coe[18] * bv6 + coe[21] * bv7 + coe[24] * bv8; + g += coe[13] * bv4 + coe[16] * bv5 + coe[19] * bv6 + coe[22] * bv7 + coe[25] * bv8; + b += coe[14] * bv4 + coe[17] * bv5 + coe[20] * bv6 + coe[23] * bv7 + coe[26] * bv8; - src[12] += color.r * basis[4] * xy; - src[13] += color.g * basis[4] * xy; - src[14] += color.b * basis[4] * xy; - src[15] += color.r * basis[5] * yz; - src[16] += color.g * basis[5] * yz; - src[17] += color.b * basis[5] * yz; - src[18] += color.r * basis[6] * z3; - src[19] += color.g * basis[6] * z3; - src[20] += color.b * basis[6] * z3; - src[21] += color.r * basis[7] * xz; - src[22] += color.g * basis[7] * xz; - src[23] += color.b * basis[7] * xz; - src[24] += color.r * basis[8] * x2y2; - src[25] += color.g * basis[8] * x2y2; - src[26] += color.b * basis[8] * x2y2; + out.setValue(r, g, b, 1.0); + return out; } /** * Scale the coefficients. + * @param s - The amount by which to scale the SphericalHarmonics3 */ - scale(value: number): void { - const src = this._coefficients; - - src[0] *= value; - src[1] *= value; - src[2] *= value; - src[3] *= value; - src[4] *= value; - src[5] *= value; - src[6] *= value; - src[7] *= value; - src[8] *= value; - src[9] *= value; - src[10] *= value; - src[11] *= value; - src[12] *= value; - src[13] *= value; - src[14] *= value; - src[15] *= value; - src[16] *= value; - src[17] *= value; - src[18] *= value; - src[19] *= value; - src[20] *= value; - src[21] *= value; - src[22] *= value; - src[23] *= value; - src[24] *= value; - src[25] *= value; - src[26] *= value; + scale(s: number): void { + const src = this.coefficients; + + (src[0] *= s), (src[1] *= s), (src[2] *= s); + (src[3] *= s), (src[4] *= s), (src[5] *= s); + (src[6] *= s), (src[7] *= s), (src[8] *= s); + (src[9] *= s), (src[10] *= s), (src[11] *= s); + (src[12] *= s), (src[13] *= s), (src[14] *= s); + (src[15] *= s), (src[16] *= s), (src[17] *= s); + (src[18] *= s), (src[19] *= s), (src[20] *= s); + (src[21] *= s), (src[22] *= s), (src[23] *= s); + (src[24] *= s), (src[25] *= s), (src[26] *= s); } /** @@ -179,35 +152,17 @@ export class SphericalHarmonics3 implements IClone { * @param offset - The start offset of the array */ setValueByArray(array: ArrayLike, offset: number = 0): void { - const src = this._coefficients; - - src[0] = array[0 + offset]; - src[1] = array[1 + offset]; - src[2] = array[2 + offset]; - src[3] = array[3 + offset]; - src[4] = array[4 + offset]; - src[5] = array[5 + offset]; - src[6] = array[6 + offset]; - src[7] = array[7 + offset]; - src[8] = array[8 + offset]; - src[9] = array[9 + offset]; - src[10] = array[10 + offset]; - src[11] = array[11 + offset]; - src[12] = array[12 + offset]; - src[13] = array[13 + offset]; - src[14] = array[14 + offset]; - src[15] = array[15 + offset]; - src[16] = array[16 + offset]; - src[17] = array[17 + offset]; - src[18] = array[18 + offset]; - src[19] = array[19 + offset]; - src[20] = array[20 + offset]; - src[21] = array[21 + offset]; - src[22] = array[22 + offset]; - src[23] = array[23 + offset]; - src[24] = array[24 + offset]; - src[25] = array[25 + offset]; - src[26] = array[26 + offset]; + const s = this.coefficients; + + (s[0] = array[offset]), (s[1] = array[1 + offset]), (s[2] = array[2 + offset]); + (s[3] = array[3 + offset]), (s[4] = array[4 + offset]), (s[5] = array[5 + offset]); + (s[6] = array[6 + offset]), (s[7] = array[7 + offset]), (s[8] = array[8 + offset]); + (s[9] = array[9 + offset]), (s[10] = array[10 + offset]), (s[11] = array[11 + offset]); + (s[12] = array[12 + offset]), (s[13] = array[13 + offset]), (s[14] = array[14 + offset]); + (s[15] = array[15 + offset]), (s[16] = array[16 + offset]), (s[17] = array[17 + offset]); + (s[18] = array[18 + offset]), (s[19] = array[19 + offset]), (s[20] = array[20 + offset]); + (s[21] = array[21 + offset]), (s[22] = array[22 + offset]), (s[23] = array[23 + offset]); + (s[24] = array[24 + offset]), (s[25] = array[25 + offset]), (s[26] = array[26 + offset]); } /** @@ -216,41 +171,24 @@ export class SphericalHarmonics3 implements IClone { * @param outOffset - The start offset of the array */ toArray(out: number[] | Float32Array | Float64Array, outOffset: number = 0): void { - const src = this._coefficients; + const s = this.coefficients; - out[0 + outOffset] = src[0]; - out[1 + outOffset] = src[1]; - out[2 + outOffset] = src[2]; + (out[0 + outOffset] = s[0]), (out[1 + outOffset] = s[1]), (out[2 + outOffset] = s[2]); - out[3 + outOffset] = src[3]; - out[4 + outOffset] = src[4]; - out[5 + outOffset] = src[5]; - out[6 + outOffset] = src[6]; - out[7 + outOffset] = src[7]; - out[8 + outOffset] = src[8]; - out[9 + outOffset] = src[9]; - out[10 + outOffset] = src[10]; - out[11 + outOffset] = src[11]; + (out[3 + outOffset] = s[3]), (out[4 + outOffset] = s[4]), (out[5 + outOffset] = s[5]); + (out[6 + outOffset] = s[6]), (out[7 + outOffset] = s[7]), (out[8 + outOffset] = s[8]); + (out[9 + outOffset] = s[9]), (out[10 + outOffset] = s[10]), (out[11 + outOffset] = s[11]); - out[12 + outOffset] = src[12]; - out[13 + outOffset] = src[13]; - out[14 + outOffset] = src[14]; - out[15 + outOffset] = src[15]; - out[16 + outOffset] = src[16]; - out[17 + outOffset] = src[17]; - out[18 + outOffset] = src[18]; - out[19 + outOffset] = src[19]; - out[20 + outOffset] = src[20]; - out[21 + outOffset] = src[21]; - out[22 + outOffset] = src[22]; - out[23 + outOffset] = src[23]; - out[24 + outOffset] = src[24]; - out[25 + outOffset] = src[25]; - out[26 + outOffset] = src[26]; + (out[12 + outOffset] = s[12]), (out[13 + outOffset] = s[13]), (out[14 + outOffset] = s[14]); + (out[15 + outOffset] = s[15]), (out[16 + outOffset] = s[16]), (out[17 + outOffset] = s[17]); + (out[18 + outOffset] = s[18]), (out[19 + outOffset] = s[19]), (out[20 + outOffset] = s[20]); + (out[21 + outOffset] = s[21]), (out[22 + outOffset] = s[22]), (out[23 + outOffset] = s[23]); + (out[24 + outOffset] = s[24]), (out[25 + outOffset] = s[25]), (out[26 + outOffset] = s[26]); } /** - * @override + * Creates a clone of this SphericalHarmonics3. + * @returns A clone of this SphericalHarmonics3 */ clone(): SphericalHarmonics3 { const v = new SphericalHarmonics3(); @@ -260,9 +198,11 @@ export class SphericalHarmonics3 implements IClone { } /** - * @override + * Clones this SphericalHarmonics3 to the specified SphericalHarmonics3. + * @param out - The specified SphericalHarmonics3 + * @returns The specified SphericalHarmonics3 */ cloneTo(out: SphericalHarmonics3): void { - this.toArray(out._coefficients); + this.toArray(out.coefficients); } } From b8770623b9a74a7b5640cd61963a18eff0450514 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Fri, 6 Aug 2021 13:36:48 +0800 Subject: [PATCH 18/19] fix: scale color --- packages/math/src/SphericalHarmonics3.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/math/src/SphericalHarmonics3.ts b/packages/math/src/SphericalHarmonics3.ts index 376cfa0037..c9fe995659 100644 --- a/packages/math/src/SphericalHarmonics3.ts +++ b/packages/math/src/SphericalHarmonics3.ts @@ -37,12 +37,13 @@ export class SphericalHarmonics3 implements IClone { * 8: Math.sqrt(15 / (16 * Math.PI)) */ + color.scale(deltaSolidAngle); + const coe = this.coefficients; + const { x, y, z } = direction; const { r, g, b } = color; - color.scale(deltaSolidAngle); - const bv0 = 0.282095; // basis0 = 0.886227 const bv1 = -0.488603 * y; // basis1 = -0.488603 const bv2 = 0.488603 * z; // basis2 = 0.488603 From 46b8c9557c056828fe5158938a94c6031f1ebc15 Mon Sep 17 00:00:00 2001 From: "shensi.zxd" Date: Fri, 6 Aug 2021 15:47:21 +0800 Subject: [PATCH 19/19] refactor: rename --- packages/core/src/lighting/AmbientLight.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/core/src/lighting/AmbientLight.ts b/packages/core/src/lighting/AmbientLight.ts index 781cfddd52..79cb3227db 100644 --- a/packages/core/src/lighting/AmbientLight.ts +++ b/packages/core/src/lighting/AmbientLight.ts @@ -74,10 +74,7 @@ export class AmbientLight { const shaderData = this._scene.shaderData; if (value) { - shaderData.setFloatArray( - AmbientLight._diffuseSHProperty, - this._convertRadianceToIrradiance(value, this._shArray) - ); + shaderData.setFloatArray(AmbientLight._diffuseSHProperty, this._preComputeSH(value, this._shArray)); } } @@ -135,7 +132,7 @@ export class AmbientLight { shaderData.setFloat(AmbientLight._specularIntensityProperty, this._specularIntensity); } - private _convertRadianceToIrradiance(sh: SphericalHarmonics3, out: Float32Array): Float32Array { + private _preComputeSH(sh: SphericalHarmonics3, out: Float32Array): Float32Array { /** * Basis constants *