Eevee-next: World Irradiance Caching

This PR adds world lighting to the irradiance cache and allows diffuse materials
to be lit by the world. The scene in the reference image is only lit by an environment
texture.

The reflection probe lib has been split into sampling the world probe and evaluating
the world probe. As evaluating the world probe requires to include more libraries and
data structures that aren't really needed for the irradiance cache shaders.

Pull Request: https://projects.blender.org/blender/blender/pulls/109516
This commit is contained in:
Jeroen Bakker
2023-06-30 13:54:34 +02:00
parent a655aa1fb2
commit 91c3fd7a3b
10 changed files with 237 additions and 67 deletions

View File

@@ -463,6 +463,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_camera_lib.glsl
engines/eevee_next/shaders/eevee_colorspace_lib.glsl
engines/eevee_next/shaders/eevee_cryptomatte_lib.glsl
engines/eevee_next/shaders/eevee_cubemap_lib.glsl
engines/eevee_next/shaders/eevee_debug_surfels_vert.glsl
engines/eevee_next/shaders/eevee_debug_surfels_frag.glsl
engines/eevee_next/shaders/eevee_deferred_light_frag.glsl
@@ -513,6 +514,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl
engines/eevee_next/shaders/eevee_motion_blur_lib.glsl
engines/eevee_next/shaders/eevee_nodetree_lib.glsl
engines/eevee_next/shaders/eevee_reflection_probe_eval_lib.glsl
engines/eevee_next/shaders/eevee_reflection_probe_lib.glsl
engines/eevee_next/shaders/eevee_sampling_lib.glsl
engines/eevee_next/shaders/eevee_shadow_debug_frag.glsl

View File

@@ -100,6 +100,7 @@ void Instance::init_light_bake(Depsgraph *depsgraph, draw::Manager *manager)
shadows.init();
main_view.init();
irradiance_cache.init();
reflection_probes.init();
}
void Instance::set_time(float time)
@@ -516,6 +517,8 @@ void Instance::light_bake_irradiance(
render_sync();
manager->end_sync();
capture_view.render();
irradiance_cache.bake.surfels_create(probe);
irradiance_cache.bake.surfels_lights_eval();
});

View File

@@ -463,6 +463,7 @@ void IrradianceBake::sync()
sub.shader_set(inst_.shaders.static_shader_get(SURFEL_RAY));
sub.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
sub.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
inst_.reflection_probes.bind_resources(&sub);
sub.push_constant("radiance_src", &radiance_src_);
sub.push_constant("radiance_dst", &radiance_dst_);
sub.barrier(GPU_BARRIER_SHADER_STORAGE);
@@ -475,6 +476,7 @@ void IrradianceBake::sync()
pass.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_RAY));
pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
inst_.reflection_probes.bind_resources(&pass);
pass.bind_ssbo("list_start_buf", &list_start_buf_);
pass.bind_ssbo("list_info_buf", &list_info_buf_);
pass.push_constant("radiance_src", &radiance_src_);

View File

@@ -0,0 +1,139 @@
/* Fallback implementation for hardware not supporting cubemap arrays.
* `samplerCubeArray` fallback declaration as sampler2DArray in `glsl_shader_defines.glsl`. */
#ifndef GPU_ARB_texture_cube_map_array
float cubemap_face_index(vec3 P)
{
vec3 aP = abs(P);
if (all(greaterThan(aP.xx, aP.yz))) {
return (P.x > 0.0) ? 0.0 : 1.0;
}
else if (all(greaterThan(aP.yy, aP.xz))) {
return (P.y > 0.0) ? 2.0 : 3.0;
}
else {
return (P.z > 0.0) ? 4.0 : 5.0;
}
}
vec2 cubemap_face_coord(vec3 P, float face)
{
if (face < 2.0) {
return (P.zy / P.x) * vec2(-0.5, -sign(P.x) * 0.5) + 0.5;
}
else if (face < 4.0) {
return (P.xz / P.y) * vec2(sign(P.y) * 0.5, 0.5) + 0.5;
}
else {
return (P.xy / P.z) * vec2(0.5, -sign(P.z) * 0.5) + 0.5;
}
}
vec3 cubemap_adj_x(float face)
{
bool y_axis = (face == 2.0 || face == 3.0);
return y_axis ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
}
vec3 cubemap_adj_y(float face)
{
bool x_axis = (face < 2.0);
return x_axis ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
}
vec3 cubemap_adj_xy(float face)
{
if (face < 2.0) {
return vec3(0.0, 1.0, 1.0);
}
else if (face < 4.0) {
return vec3(1.0, 0.0, 1.0);
}
else {
return vec3(1.0, 1.0, 0.0);
}
}
# ifdef GPU_METAL
template<typename T>
vec4 cubemap_seamless(thread _mtl_combined_image_sampler_2d_array<T, access::sample> *tex,
vec4 cubevec,
float lod)
# else
vec4 cubemap_seamless(sampler2DArray tex, vec4 cubevec, float lod)
# endif
{
/* Manual Cube map Layer indexing. */
float face = cubemap_face_index(cubevec.xyz);
vec2 uv = cubemap_face_coord(cubevec.xyz, face);
vec3 coord = vec3(uv, cubevec.w * 6.0 + face);
vec4 col = textureLod(tex, coord, lod);
float cube_size = float(textureSize(tex, int(lod)).x);
vec2 uv_border = (abs(uv - 0.5) + (0.5 / cube_size - 0.5)) * 2.0 * cube_size;
bvec2 border = greaterThan(uv_border, vec2(0.0));
if (all(border)) {
/* Corners case. */
vec3 cubevec_adj;
float face_adj;
/* Get the other face coords. */
cubevec_adj = cubevec.xyz * cubemap_adj_x(face);
face_adj = cubemap_face_index(cubevec_adj);
/* Still use the original cubevec to get the outer texels or the face. */
uv = cubemap_face_coord(cubevec.xyz, face_adj);
coord = vec3(uv, cubevec.w * 6.0 + face_adj);
vec4 col1 = textureLod(tex, coord, lod);
/* Get the 3rd face coords. */
cubevec_adj = cubevec.xyz * cubemap_adj_y(face);
face_adj = cubemap_face_index(cubevec_adj);
/* Still use the original cubevec to get the outer texels or the face. */
uv = cubemap_face_coord(cubevec.xyz, face_adj);
coord = vec3(uv, cubevec.w * 6.0 + face_adj);
vec4 col2 = textureLod(tex, coord, lod);
/* Mix all colors to get the corner color. */
vec4 col3 = (col + col1 + col2) / 3.0;
vec2 mix_fac = saturate(uv_border * 0.5);
return mix(mix(col, col2, mix_fac.x), mix(col1, col3, mix_fac.x), mix_fac.y);
}
else if (any(border)) {
/* Edges case. */
/* Get the other face coords. */
vec3 cubevec_adj = cubevec.xyz * cubemap_adj_xy(face);
face = cubemap_face_index(cubevec_adj);
/* Still use the original cubevec to get the outer texels or the face. */
uv = cubemap_face_coord(cubevec.xyz, face);
coord = vec3(uv, cubevec.w * 6.0 + face);
float mix_fac = saturate(max(uv_border.x, uv_border.y) * 0.5);
return mix(col, textureLod(tex, coord, lod), mix_fac);
}
else {
return col;
}
}
# ifdef GPU_METAL
template<typename T, access A>
vec4 textureLod_cubemapArray(thread _mtl_combined_image_sampler_2d_array<T, A> tex,
vec4 cubevec,
float lod)
# else
vec4 textureLod_cubemapArray(sampler2DArray tex, vec4 cubevec, float lod)
# endif
{
float lod1 = floor(lod);
float lod2 = ceil(lod);
vec4 col_lod1 = cubemap_seamless(tex, cubevec, lod1);
vec4 col_lod2 = cubemap_seamless(tex, cubevec, lod2);
return mix(col_lod1, col_lod2, lod - lod1);
}
#endif

View File

@@ -9,7 +9,7 @@
#pragma BLENDER_REQUIRE(eevee_gbuffer_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_reflection_probe_eval_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_lightprobe_eval_lib.glsl)
void main()

View File

@@ -12,6 +12,7 @@
#pragma BLENDER_REQUIRE(eevee_spherical_harmonics_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_surfel_list_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_lightprobe_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
void irradiance_capture(vec3 L, vec3 irradiance, inout SphericalHarmonicL1 sh)
@@ -40,6 +41,11 @@ void irradiance_capture(Surfel surfel, vec3 P, inout SphericalHarmonicL1 sh)
irradiance_capture(L, irradiance, sh);
}
vec3 irradiance_sky_sample(vec3 R)
{
return light_world_sample(R, 0.0);
}
void main()
{
ivec3 grid_coord = ivec3(gl_GlobalInvocationID);
@@ -88,8 +94,8 @@ void main()
irradiance_capture(surfel, P, sh);
}
else {
/* TODO(fclem): Sky radiance. */
irradiance_capture(sky_L, vec3(0.0), sh);
vec3 world_radiance = irradiance_sky_sample(-sky_L);
irradiance_capture(sky_L, world_radiance, sh);
}
if (surfel_prev > -1) {
@@ -97,8 +103,8 @@ void main()
irradiance_capture(surfel, P, sh);
}
else {
/* TODO(fclem): Sky radiance. */
irradiance_capture(-sky_L, vec3(0.0), sh);
vec3 world_radiance = irradiance_sky_sample(sky_L);
irradiance_capture(-sky_L, world_radiance, sh);
}
/* Normalize for storage. We accumulated 2 samples. */

View File

@@ -0,0 +1,62 @@
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
void light_world_eval(ClosureReflection reflection, vec3 P, vec3 V, inout vec3 out_specular)
{
ivec3 texture_size = textureSize(reflectionProbes, 0);
/* TODO: This should be based by actual resolution. Currently the resolution is fixed but
* eventually this should based on a user setting and part of the reflection probe data that will
* be introduced by the reflection probe patch. */
float lod_cube_max = 12.0;
/* Pow2f to distributed across lod more evenly */
float roughness = clamp(pow2f(reflection.roughness), 1e-4f, 0.9999f);
#if defined(GPU_COMPUTE_SHADER)
vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5;
#else
vec2 frag_coord = gl_FragCoord.xy;
#endif
vec2 noise = utility_tx_fetch(utility_tx, frag_coord, UTIL_BLUE_NOISE_LAYER).gb;
vec2 rand = fract(noise + sampling_rng_2D_get(SAMPLING_RAYTRACE_U));
vec3 Xi = sample_cylinder(rand);
/* Microfacet normal */
vec3 T, B;
make_orthonormal_basis(reflection.N, T, B);
float pdf;
vec3 H = sample_ggx(Xi, roughness, V, reflection.N, T, B, pdf);
vec3 L = -reflect(V, H);
float NL = dot(reflection.N, L);
if (NL > 0.0) {
/* Coarse Approximation of the mapping distortion
* Unit Sphere -> Cubemap Face */
const float dist = 4.0 * M_PI / 6.0;
/* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */
/* TODO: lod_factor should be precalculated and stored inside the reflection probe data. */
const float bias = 0;
const float lod_factor = bias + 0.5 * log(float(square_i(texture_size.x))) / log(2);
/* -2: Don't use LOD levels that are smaller than 4x4 pixels. */
float lod = clamp(lod_factor - 0.5 * log2(pdf * dist), 0.0, lod_cube_max - 2.0);
vec3 l_col = light_world_sample(L, lod);
/* Clamped brightness. */
/* For artistic freedom this should be read from the scene/reflection probe.
* Note: Eevee-legacy read the firefly_factor from gi_glossy_clamp.
* Note: Firefly removal should be moved to a different shader and also take SSR into
* account.*/
float luma = max(1e-8, max_v3(l_col));
const float firefly_factor = 1e16;
l_col *= 1.0 - max(0.0, luma - firefly_factor) / luma;
/* TODO: for artistic freedom want to read this from the reflection probe. That will be part of
* the reflection probe patch. */
const float intensity_factor = 1.0;
out_specular += vec3(intensity_factor * l_col);
}
}

View File

@@ -1,61 +1,6 @@
#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_cubemap_lib.glsl)
void light_world_eval(ClosureReflection reflection, vec3 P, vec3 V, inout vec3 out_specular)
vec3 light_world_sample(vec3 L, float lod)
{
ivec3 texture_size = textureSize(reflectionProbes, 0);
/* TODO: This should be based by actual resolution. Currently the resolution is fixed but
* eventually this should based on a user setting and part of the reflection probe data that will
* be introduced by the reflection probe patch. */
float lod_cube_max = 12.0;
/* Pow2f to distributed across lod more evenly */
float roughness = clamp(pow2f(reflection.roughness), 1e-4f, 0.9999f);
#if defined(GPU_COMPUTE_SHADER)
vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5;
#else
vec2 frag_coord = gl_FragCoord.xy;
#endif
vec2 noise = utility_tx_fetch(utility_tx, frag_coord, UTIL_BLUE_NOISE_LAYER).gb;
vec2 rand = fract(noise + sampling_rng_2D_get(SAMPLING_RAYTRACE_U));
vec3 Xi = sample_cylinder(rand);
/* Microfacet normal */
vec3 T, B;
make_orthonormal_basis(reflection.N, T, B);
float pdf;
vec3 H = sample_ggx(Xi, roughness, V, reflection.N, T, B, pdf);
vec3 L = -reflect(V, H);
float NL = dot(reflection.N, L);
if (NL > 0.0) {
/* Coarse Approximation of the mapping distortion
* Unit Sphere -> Cubemap Face */
const float dist = 4.0 * M_PI / 6.0;
/* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */
/* TODO: lod_factor should be precalculated and stored inside the reflection probe data. */
const float bias = 0;
const float lod_factor = bias + 0.5 * log(float(square_i(texture_size.x))) / log(2);
/* -2: Don't use LOD levels that are smaller than 4x4 pixels. */
float lod = clamp(lod_factor - 0.5 * log2(pdf * dist), 0.0, lod_cube_max - 2.0);
vec3 l_col = textureLod_cubemapArray(reflectionProbes, vec4(L, 0.0), lod).rgb;
/* Clamped brightness. */
/* For artistic freedom this should be read from the scene/reflection probe.
* Note: Eevee-legacy read the firefly_factor from gi_glossy_clamp.
* Note: Firefly removal should be moved to a different shader and also take SSR into
* account.*/
float luma = max(1e-8, max_v3(l_col));
const float firefly_factor = 1e16;
l_col *= 1.0 - max(0.0, luma - firefly_factor) / luma;
/* TODO: for artistic freedom want to read this from the reflection probe. That will be part of
* the reflection probe patch. */
const float intensity_factor = 1.0;
out_specular += vec3(intensity_factor * l_col);
}
return textureLod_cubemapArray(reflectionProbes, vec4(L, 0.0), lod).rgb;
}

View File

@@ -10,6 +10,7 @@
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(gpu_shader_math_base_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_reflection_probe_lib.glsl)
#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
@@ -62,10 +63,14 @@ void radiance_transfer_surfel(inout Surfel receiver, Surfel sender)
radiance_transfer(receiver, radiance, L);
}
vec3 radiance_sky_sample(vec3 R)
{
return light_world_sample(R, 0.0);
}
void radiance_transfer_world(inout Surfel receiver, vec3 sky_L)
{
/* TODO(fclem): Sky radiance. */
vec3 radiance = vec3(0.0);
vec3 radiance = radiance_sky_sample(-sky_L);
radiance_transfer(receiver, radiance, -sky_L);
}

View File

@@ -83,7 +83,10 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_list_sort)
GPU_SHADER_CREATE_INFO(eevee_surfel_ray)
.local_group_size(SURFEL_GROUP_SIZE)
.additional_info("eevee_shared", "eevee_surfel_common", "draw_view")
.additional_info("eevee_shared",
"eevee_surfel_common",
"eevee_reflection_probe_data",
"draw_view")
.push_constant(Type::INT, "radiance_src")
.push_constant(Type::INT, "radiance_dst")
.compute_source("eevee_surfel_ray_comp.glsl")
@@ -103,7 +106,10 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray)
.local_group_size(IRRADIANCE_GRID_GROUP_SIZE,
IRRADIANCE_GRID_GROUP_SIZE,
IRRADIANCE_GRID_GROUP_SIZE)
.additional_info("eevee_shared", "eevee_surfel_common", "draw_view")
.additional_info("eevee_shared",
"eevee_surfel_common",
"eevee_reflection_probe_data",
"draw_view")
.push_constant(Type::INT, "radiance_src")
.storage_buf(0, Qualifier::READ, "int", "list_start_buf[]")
.storage_buf(6, Qualifier::READ, "SurfelListInfoData", "list_info_buf")