EEVEE-Next: Spherical Harmonics Library

Implement spherical harmonics to be used for irradiance caching.
This commit is contained in:
Clément Foucault
2023-03-19 11:01:08 +01:00
parent 21b6f6f1f6
commit 65b2aed14b
2 changed files with 216 additions and 0 deletions

View File

@@ -490,6 +490,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_shadow_tilemap_finalize_comp.glsl
engines/eevee_next/shaders/eevee_shadow_tilemap_init_comp.glsl
engines/eevee_next/shaders/eevee_shadow_tilemap_lib.glsl
engines/eevee_next/shaders/eevee_spherical_harmonics_lib.glsl
engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl
engines/eevee_next/shaders/eevee_surf_depth_frag.glsl
engines/eevee_next/shaders/eevee_surf_forward_frag.glsl

View File

@@ -0,0 +1,215 @@
/* -------------------------------------------------------------------- */
/** \name Spherical Harmonics Functions
*
* `L` denote the row and `M` the column in the spherical harmonics table (1).
* `p` denote positive column and `n` negative ones.
*
* Use precomputed constants to avoid constant folding differences across compilers.
* Note that (2) doesn't use Condon-Shortley phase whereas our implementation does.
*
* Reference:
* (1) https://en.wikipedia.org/wiki/Spherical_harmonics#/media/File:Sphericalfunctions.svg
* (2) https://en.wikipedia.org/wiki/Table_of_spherical_harmonics#Real_spherical_harmonics
* (3) https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
*
* \{ */
/* L0 Band. */
float spherical_harmonics_L0_M0(vec3 v)
{
return 0.282094792;
}
/* L1 Band. */
float spherical_harmonics_L1_Mn1(vec3 v)
{
return -0.488602512 * v.y;
}
float spherical_harmonics_L1_M0(vec3 v)
{
return 0.488602512 * v.z;
}
float spherical_harmonics_L1_Mp1(vec3 v)
{
return -0.488602512 * v.x;
}
/* L2 Band. */
float spherical_harmonics_L2_Mn2(vec3 v)
{
return 1.092548431 * (v.x * v.y);
}
float spherical_harmonics_L2_Mn1(vec3 v)
{
return -1.092548431 * (v.y * v.z);
}
float spherical_harmonics_L2_M0(vec3 v)
{
return 0.315391565 * (3.0 * v.z * v.z - 1.0);
}
float spherical_harmonics_L2_Mp1(vec3 v)
{
return -1.092548431 * (v.x * v.z);
}
float spherical_harmonics_L2_Mp2(vec3 v)
{
return 0.546274215 * (v.x * v.x - v.y * v.y);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Structure
* \{ */
struct SphericalHarmonicBandL0 {
vec3 M0;
};
struct SphericalHarmonicBandL1 {
vec3 Mn1;
vec3 M0;
vec3 Mp1;
};
struct SphericalHarmonicBandL2 {
vec3 Mn2;
vec3 Mn1;
vec3 M0;
vec3 Mp1;
vec3 Mp2;
};
struct SphericalHarmonicL0 {
SphericalHarmonicBandL0 L0;
};
struct SphericalHarmonicL1 {
SphericalHarmonicBandL0 L0;
SphericalHarmonicBandL1 L1;
};
struct SphericalHarmonicL2 {
SphericalHarmonicBandL0 L0;
SphericalHarmonicBandL1 L1;
SphericalHarmonicBandL2 L2;
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Encode
*
* Decompose an input signal into spherical harmonic coefficients.
* \{ */
void spherical_harmonics_L0_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicBandL0 r_L0)
{
r_L0.M0 += spherical_harmonics_L0_M0(direction) * amplitude;
}
void spherical_harmonics_L1_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicBandL1 r_L1)
{
r_L1.Mn1 += spherical_harmonics_L1_Mn1(direction) * amplitude;
r_L1.M0 += spherical_harmonics_L1_M0(direction) * amplitude;
r_L1.Mp1 += spherical_harmonics_L1_Mp1(direction) * amplitude;
}
void spherical_harmonics_L2_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicBandL2 r_L2)
{
r_L2.Mn2 += spherical_harmonics_L2_Mn2(direction) * amplitude;
r_L2.Mn1 += spherical_harmonics_L2_Mn1(direction) * amplitude;
r_L2.M0 += spherical_harmonics_L2_M0(direction) * amplitude;
r_L2.Mp1 += spherical_harmonics_L2_Mp1(direction) * amplitude;
r_L2.Mp2 += spherical_harmonics_L2_Mp2(direction) * amplitude;
}
void spherical_harmonics_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicL0 sh)
{
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
}
void spherical_harmonics_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicL1 sh)
{
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
spherical_harmonics_L1_encode_signal_sample(direction, amplitude, sh.L1);
}
void spherical_harmonics_encode_signal_sample(vec3 direction,
vec3 amplitude,
inout SphericalHarmonicL2 sh)
{
spherical_harmonics_L0_encode_signal_sample(direction, amplitude, sh.L0);
spherical_harmonics_L1_encode_signal_sample(direction, amplitude, sh.L1);
spherical_harmonics_L2_encode_signal_sample(direction, amplitude, sh.L2);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Decode
*
* Evaluate an encoded signal in a given unit vector direction.
* \{ */
vec3 spherical_harmonics_L0_evaluate(vec3 direction, SphericalHarmonicBandL0 L0)
{
return spherical_harmonics_L0_M0(direction) * L0.M0;
}
vec3 spherical_harmonics_L1_evaluate(vec3 direction, SphericalHarmonicBandL1 L1)
{
return spherical_harmonics_L1_Mn1(direction) * L1.Mn1 +
spherical_harmonics_L1_M0(direction) * L1.M0 +
spherical_harmonics_L1_Mp1(direction) * L1.Mp1;
}
vec3 spherical_harmonics_L2_evaluate(vec3 direction, SphericalHarmonicBandL2 L2)
{
return spherical_harmonics_L2_Mn2(direction) * L2.Mn2 +
spherical_harmonics_L2_Mn1(direction) * L2.Mn1 +
spherical_harmonics_L2_M0(direction) * L2.M0 +
spherical_harmonics_L2_Mp1(direction) * L2.Mp1 +
spherical_harmonics_L2_Mp2(direction) * L2.Mp2;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Evaluation
* \{ */
/**
* Convolve a spherical harmonic encoded irradiance signal as a lambertian reflection.
* Returns the lambertian radiance (cosine lobe divided by PI) so the coefficients simplify to 1,
* 2/3 and 1/4. See this reference for more explanation:
* https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
*/
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL0 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0);
}
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL1 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0) +
spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0);
}
vec3 spherical_harmonics_evaluate_lambert(vec3 N, SphericalHarmonicL2 sh)
{
return spherical_harmonics_L0_evaluate(N, sh.L0) +
spherical_harmonics_L1_evaluate(N, sh.L1) * (2.0 / 3.0) +
spherical_harmonics_L2_evaluate(N, sh.L2) * (1.0 / 4.0);
}
/** \} */