EEVEE-Next: Add correct support for volume anisotropy from probe volumes

This adds the approximation of phase function convolution
of the distant lighting captured inside probe volumes.

This is based on a publication at siggraph from Bartlomiej Wronsky
"Volumetric Fog: Unified compute shader based solution to
atmospheric scattering"

Implementation is quite straightforward. However this isn't as
good as one can expect as there isn't self shadowing from the
volume themself, so the lighting is still quite flat.

To fix this, we have to add support for volumetrics inside
probe volumes baking. But this approach would still be static
so a more general solution is still to be found for dynamic
volumes like smoke simulations.

Pull Request: https://projects.blender.org/blender/blender/pulls/119479
This commit is contained in:
Clément Foucault
2024-03-19 19:01:05 +01:00
committed by Clément Foucault
parent a2bb547b9a
commit 893430a2c7
3 changed files with 43 additions and 13 deletions

View File

@@ -562,3 +562,22 @@ SphericalHarmonicL2 spherical_harmonics_add(SphericalHarmonicL2 a, SphericalHarm
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Dot
* \{ */
vec4 spherical_harmonics_dot(SphericalHarmonicL1 a, SphericalHarmonicL1 b)
{
/* Convert coefficients to per channel column. */
mat4x4 a_mat = transpose(mat4x4(a.L0.M0, a.L1.Mn1, a.L1.M0, a.L1.Mp1));
mat4x4 b_mat = transpose(mat4x4(b.L0.M0, b.L1.Mn1, b.L1.M0, b.L1.Mp1));
vec4 result;
result[0] = dot(a_mat[0], b_mat[0]);
result[1] = dot(a_mat[1], b_mat[1]);
result[2] = dot(a_mat[2], b_mat[2]);
result[3] = dot(a_mat[3], b_mat[3]);
return result;
}
/** \} */

View File

@@ -10,6 +10,7 @@
#pragma BLENDER_REQUIRE(draw_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_shadow_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
/* Based on Frosbite Unified Volumetric.
* https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite */
@@ -68,6 +69,22 @@ float volume_phase_function(vec3 V, vec3 L, float g)
return (1 - sqr_g) / max(1e-8, 4.0 * M_PI * pow(1 + sqr_g - 2 * g * cos_theta, 3.0 / 2.0));
}
SphericalHarmonicL1 volume_phase_function_as_sh_L1(vec3 V, float g)
{
/* Compute rotated zonal harmonic.
* From Bartlomiej Wronsky
* "Volumetric Fog: Unified compute shader based solution to atmospheric scattering" page 55
* Siggraph 2014
* https://bartwronski.files.wordpress.com/2014/08/bwronski_volumetric_fog_siggraph2014.pdf
*/
SphericalHarmonicL1 sh;
sh.L0.M0 = spherical_harmonics_L0_M0(V) * vec4(1.0);
sh.L1.Mn1 = spherical_harmonics_L1_Mn1(V) * vec4(g);
sh.L1.M0 = spherical_harmonics_L1_M0(V) * vec4(g);
sh.L1.Mp1 = spherical_harmonics_L1_Mp1(V) * vec4(g);
return sh;
}
vec3 volume_light(LightData light, const bool is_directional, LightVector lv)
{
float power = 1.0;
@@ -153,16 +170,6 @@ vec3 volume_shadow(
#endif /* VOLUME_SHADOW */
}
vec3 volume_irradiance(vec3 P)
{
#ifdef VOLUME_IRRADIANCE
SphericalHarmonicL1 irradiance = lightprobe_irradiance_sample(P);
return irradiance.L0.M0.rgb * M_PI;
#else
return vec3(0.0);
#endif
}
struct VolumeResolveSample {
vec3 transmittance;
vec3 scattering;

View File

@@ -78,10 +78,13 @@ void main()
float s_anisotropy = phase.x / max(1.0, phase.y);
#ifdef VOLUME_LIGHTING
scattering += volume_irradiance(P) * s_scattering * volume_phase_function_isotropic();
SphericalHarmonicL1 phase_sh = volume_phase_function_as_sh_L1(V, s_anisotropy);
SphericalHarmonicL1 volume_radiance_sh = lightprobe_irradiance_sample(P);
vec3 light_scattering = spherical_harmonics_dot(volume_radiance_sh, phase_sh).xyz;
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
scattering += volume_scatter_light_eval(true, P, V, l_idx, s_anisotropy) * s_scattering;
light_scattering += volume_scatter_light_eval(true, P, V, l_idx, s_anisotropy);
}
LIGHT_FOREACH_END
@@ -89,10 +92,11 @@ void main()
uniform_buf.volumes.viewport_size_inv;
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, pixel, vP.z, l_idx) {
scattering += volume_scatter_light_eval(false, P, V, l_idx, s_anisotropy) * s_scattering;
light_scattering += volume_scatter_light_eval(false, P, V, l_idx, s_anisotropy);
}
LIGHT_FOREACH_END
scattering += light_scattering * s_scattering;
#endif
/* Catch NaNs. */