Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: support spherical harmonic #392

Merged
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 25 additions & 22 deletions packages/core/src/lighting/AmbientLight.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -10,11 +10,11 @@ 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");
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(
Expand All @@ -23,11 +23,13 @@ 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;
private _specularIntensity: number = 1.0;
private _diffuseMode: DiffuseMode = DiffuseMode.SolidColor;
private _shArray: Float32Array = new Float32Array(27);

/**
* Diffuse mode of ambient light.
Expand All @@ -38,10 +40,10 @@ export class AmbientLight {

set diffuseMode(value: DiffuseMode) {
this._diffuseMode = value;
if (value === DiffuseMode.Texture) {
this._scene.shaderData.enableMacro(AmbientLight._diffuseMacro);
if (value === DiffuseMode.SphericalHarmonics) {
this._scene.shaderData.enableMacro(AmbientLight._shMacro);
} else {
this._scene.shaderData.disableMacro(AmbientLight._diffuseMacro);
this._scene.shaderData.disableMacro(AmbientLight._shMacro);
}
}

Expand All @@ -59,6 +61,23 @@ export class AmbientLight {
}
}

/**
* Diffuse reflection spherical harmonics 3.
* @remarks Effective when diffuse reflection mode is `DiffuseMode.SphericalHarmonics`.
*/
get diffuseSphericalHarmonics(): SphericalHarmonics3 {
return this._diffuseSphericalHarmonics;
}

set diffuseSphericalHarmonics(value: SphericalHarmonics3) {
this._diffuseSphericalHarmonics = value;
const shaderData = this._scene.shaderData;

if (value) {
shaderData.setFloatArray(AmbientLight._diffuseSHProperty, value.convertRadianceToIrradiance(this._shArray));
}
}

/**
* Diffuse reflection intensity.
*/
Expand Down Expand Up @@ -112,20 +131,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);
}
}
10 changes: 7 additions & 3 deletions packages/core/src/lighting/enums/DiffuseMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
export enum DiffuseMode {
/** Solid color mode. */
SolidColor,
/** Texture cube mode. */
Texture
// SphericalHarmonics

/**
* SH mode
* @remarks
* Use SH3 to represent irradiance environment maps efficiently, allowing for interactive rendering of diffuse objects under distant illumination.
*/
SphericalHarmonics
}
4 changes: 2 additions & 2 deletions packages/core/src/shaderlib/pbr/envmap_light_frag_define.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ struct EnvMapLight {

uniform EnvMapLight u_envMapLight;

#ifdef O3_USE_DIFFUSE_ENV
uniform samplerCube u_env_diffuseSampler;
#ifdef O3_USE_SH
uniform vec3 u_env_sh[9];
#endif

#ifdef O3_USE_SPECULAR_ENV
Expand Down
24 changes: 6 additions & 18 deletions packages/core/src/shaderlib/pbr/ibl_diffuse_frag.glsl
Original file line number Diff line number Diff line change
@@ -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_SH
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 );


19 changes: 19 additions & 0 deletions packages/core/src/shaderlib/pbr/ibl_diffuse_frag_define.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -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));

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can check this :http://www.fractalforums.com/programming/shader-function-or-instruction-cost-(performance)/

How to say: many engine get irradiance use the func like this:

mediump vec3 shEvalLinearL0L1(mediump vec4 normal)
	{
		mediump vec3 x;
		// Linear (L1) + constant (L0) polynomial terms
		x.r = dot(u_AmbientSHAr, normal);
		x.g = dot(u_AmbientSHAg, normal);	
		x.b = dot(u_AmbientSHAb, normal);
		return x;
	}

	mediump vec3 shEvalLinearL2(mediump vec4 normal)
	{
		mediump vec3 x1,x2;
		// 4 of the quadratic (L2) polynomials
		mediump vec4 vB = normal.xyzz * normal.yzzx;
		x1.r = dot(u_AmbientSHBr, vB);
		x1.g = dot(u_AmbientSHBg, vB);
		x1.b = dot(u_AmbientSHBb, vB);
	
		// Final (5th) quadratic (L2) polynomial
		mediump float vC = normal.x*normal.x - normal.y*normal.y;
		x2 = u_AmbientSHC.rgb * vC;

		return x1 + x2;
	}

Because dot cost only 3 cycles in GPU, so is faster, If you do this you must do some modifies before Incoming shader.

14 changes: 10 additions & 4 deletions packages/math/src/Color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,31 @@ 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): void {
static add(left: Color, right: Color, out: Color): Color {
GuoLei1990 marked this conversation as resolved.
Show resolved Hide resolved
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;
}

/**
* Scale a color by the given value.
* @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): void {
static scale(left: Color, s: number, out: Color): Color {
GuoLei1990 marked this conversation as resolved.
Show resolved Hide resolved
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. */
Expand Down Expand Up @@ -118,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;
Expand All @@ -132,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;
Expand Down
Loading