WIP: eevee-next-world-irradiance #108304

Closed
Jeroen Bakker wants to merge 79 commits from Jeroen-Bakker:eevee-next-world-irradiance into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
5 changed files with 73 additions and 23 deletions
Showing only changes of commit 1be9514e8b - Show all commits

View File

@ -486,8 +486,10 @@ void Instance::light_bake_irradiance(
});
int bounce_len = scene->eevee.gi_diffuse_bounces;
/* Start at bounce 1 as 0 bounce is no indirect lighting. */
for (int bounce = 1; bounce <= bounce_len; bounce++) {
for (int bounce = 0; bounce <= bounce_len; bounce++) {
/* Last iteration only captures lighting. */
const bool is_last_bounce = (bounce == bounce_len);
sampling.init(scene);
while (!sampling.finished()) {
context_wrapper([&]() {
@ -495,9 +497,16 @@ void Instance::light_bake_irradiance(
* the update function & context switch. */
for (int i = 0; i < 16 && !sampling.finished(); i++) {
sampling.step();
irradiance_cache.bake.propagate_light_sample();
irradiance_cache.bake.raylists_build();
if (!is_last_bounce) {
irradiance_cache.bake.propagate_light();
}
if (is_last_bounce) {
irradiance_cache.bake.irradiance_capture();
}
}
if (sampling.finished()) {
if (sampling.finished() && !is_last_bounce) {
irradiance_cache.bake.accumulate_bounce();
}
@ -511,7 +520,7 @@ void Instance::light_bake_irradiance(
}
float bounce_progress = sampling.sample_index() / float(sampling.sample_count());
float progress = (bounce - 1 + bounce_progress) / float(bounce_len);
float progress = (bounce + bounce_progress) / float(bounce_len + 1);
result_update(cache_frame, progress);
});

View File

@ -399,7 +399,7 @@ void IrradianceBake::sync()
pass.dispatch(&dispatch_per_surfel_);
}
{
PassSimple &pass = surfel_light_propagate_ps_;
PassSimple &pass = surfel_ray_build_ps_;
pass.init();
{
PassSimple::Sub &sub = pass.sub("ListBuild");
@ -421,6 +421,10 @@ void IrradianceBake::sync()
sub.barrier(GPU_BARRIER_SHADER_STORAGE);
sub.dispatch(&dispatch_per_list_);
}
}
{
PassSimple &pass = surfel_light_propagate_ps_;
pass.init();
{
PassSimple::Sub &sub = pass.sub("RayEval");
sub.shader_set(inst_.shaders.static_shader_get(SURFEL_RAY));
@ -500,10 +504,7 @@ void IrradianceBake::surfels_create(const Object &probe_object)
capture_info_buf_.irradiance_grid_local_to_world = grid_local_to_world;
capture_info_buf_.irradiance_grid_world_to_local_rotation = float4x4(
(invert(normalize(float3x3(grid_local_to_world)))));
capture_info_buf_.irradiance_accum_solid_angle = 0.0f;
/* Divide by twice the sample count because each ray is evaluated in both directions. */
capture_info_buf_.irradiance_sample_solid_angle = 4.0f * float(M_PI) /
(2 * inst_.sampling.sample_count());
capture_info_buf_.irradiance_accum_sample_count = 0;
eGPUTextureUsage texture_usage = GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_SHADER_WRITE |
GPU_TEXTURE_USAGE_HOST_READ | GPU_TEXTURE_USAGE_ATTACHMENT;
@ -633,6 +634,8 @@ void IrradianceBake::surfels_create(const Object &probe_object)
/* Sync with any other following pass using the surfel buffer. */
GPU_memory_barrier(GPU_BARRIER_SHADER_STORAGE);
/* Read back so that following push_update will contain correct surfel count. */
capture_info_buf_.read();
DRW_stats_group_end();
}
@ -649,7 +652,7 @@ void IrradianceBake::surfels_lights_eval()
inst_.manager->submit(surfel_light_eval_ps_, view_z_);
}
void IrradianceBake::propagate_light_sample()
void IrradianceBake::raylists_build()
{
using namespace blender::math;
@ -670,8 +673,7 @@ void IrradianceBake::propagate_light_sample()
/* NOTE: Z values do not really matter since we are not doing any rasterization. */
const float4x4 winmat = projection::orthographic<float>(min.x, max.x, min.y, max.y, 0, 1);
View ray_view = {"RayProjectionView"};
ray_view.sync(viewmat, winmat);
ray_view_.sync(viewmat, winmat);
/* This avoid light leaking by making sure that for one surface there will always be at least 1
* surfel capture inside a ray list. Since the surface with the maximum distance (after
@ -694,8 +696,22 @@ void IrradianceBake::propagate_light_sample()
list_start_buf_.resize(ceil_to_multiple_u(list_info_buf_.list_max, 4));
GPU_storagebuf_clear(list_start_buf_, -1);
inst_.manager->submit(surfel_light_propagate_ps_, ray_view);
inst_.manager->submit(irradiance_capture_ps_, ray_view);
inst_.manager->submit(surfel_ray_build_ps_, ray_view_);
}
void IrradianceBake::propagate_light()
{
inst_.manager->submit(surfel_light_propagate_ps_, ray_view_);
}
void IrradianceBake::irradiance_capture()
{
capture_info_buf_.push_update();
inst_.manager->submit(irradiance_capture_ps_, ray_view_);
/* Increment twice because each ray is evaluated in both directions. */
capture_info_buf_.irradiance_accum_sample_count += 2;
}
void IrradianceBake::accumulate_bounce()

View File

@ -41,6 +41,8 @@ class IrradianceBake {
Framebuffer empty_raster_fb_ = {"empty_raster_fb_"};
/** Evaluate light object contribution and store result to surfel. */
PassSimple surfel_light_eval_ps_ = {"LightEval"};
/** Create linked list of surfel to emulated raycast. */
PassSimple surfel_ray_build_ps_ = {"RayBuild"};
/** Propagate light from surfel to surfel. */
PassSimple surfel_light_propagate_ps_ = {"LightPropagate"};
/** Start of a light bounce. Accumulate light from previous propagation. */
@ -78,6 +80,9 @@ class IrradianceBake {
/* Dispatch size for per grid sample workload. */
int3 dispatch_per_grid_sample_ = int3(1);
/** View used to flatten the surfels into surfel lists representing rays. */
View ray_view_ = {"RayProjectionView"};
/** Irradiance textures for baking. Only represents one grid in there. */
Texture irradiance_L0_tx_ = {"irradiance_L0_tx_"};
Texture irradiance_L1_a_tx_ = {"irradiance_L1_a_tx_"};
@ -100,8 +105,12 @@ class IrradianceBake {
void surfels_create(const Object &probe_object);
/** Evaluate direct lighting (and also clear the surfels radiance). */
void surfels_lights_eval();
/** Create a surfel lists to emulate ray-casts for the current sample random direction. */
void raylists_build();
/** Propagate light from surfel to surfel in a random direction over the sphere. */
void propagate_light_sample();
void propagate_light();
/** Store surfel irradiance inside the irradiance grid samples. */
void irradiance_capture();
/** Accumulate light inside `surfel.radiance_bounce` to `surfel.radiance`. */
void accumulate_bounce();

View File

@ -870,10 +870,10 @@ struct CaptureInfoData {
bool1 do_surfel_count;
/** Number of surfels inside the surfel buffer or the needed len. */
uint surfel_len;
/** Solid angle subtended by a single ray sample. Equal to `4 * pi / sample_count`. */
float irradiance_sample_solid_angle;
/** Accumulated solid angle. Should reach `4 * pi` at the end of the accumulation. */
float irradiance_accum_solid_angle;
float _pad3;
/** Accumulated directional samples in irradiance grid. */
int irradiance_accum_sample_count;
/** Transform of the lightprobe object. */
float4x4 irradiance_grid_local_to_world;
/** Transform vectors from world space to local space. Does not have location component. */

View File

@ -18,15 +18,15 @@ void irradiance_capture(vec3 L, vec3 irradiance, inout SphericalHarmonicL1 sh)
{
vec3 lL = transform_direction(capture_info_buf.irradiance_grid_world_to_local_rotation, L);
spherical_harmonics_encode_signal_sample(
lL, vec4(irradiance, 1.0) * capture_info_buf.irradiance_sample_solid_angle, sh);
spherical_harmonics_encode_signal_sample(lL, vec4(irradiance, 1.0), sh);
}
void irradiance_capture(Surfel surfel_emitter, vec3 P, inout SphericalHarmonicL1 sh)
{
vec3 L = safe_normalize(surfel_emitter.position - P);
bool facing = dot(-L, surfel_emitter.normal) > 0.0;
vec3 irradiance = facing ? surfel_emitter.radiance_front : surfel_emitter.radiance_back;
vec3 irradiance = facing ? surfel_emitter.outgoing_light_front :
surfel_emitter.outgoing_light_back;
irradiance_capture(L, irradiance, sh);
}
@ -67,6 +67,15 @@ void main()
sh.L1.M0 = imageLoad(irradiance_L1_b_img, grid_coord);
sh.L1.Mp1 = imageLoad(irradiance_L1_c_img, grid_coord);
/* Spherical harmonics need to be weighted by sphere area. */
const float sphere_area = 4.0 * M_PI;
/* Un-normalize for accumulation. */
float weight_captured = float(capture_info_buf.irradiance_accum_sample_count) / sphere_area;
sh.L0.M0 *= weight_captured;
sh.L1.Mn1 *= weight_captured;
sh.L1.M0 *= weight_captured;
sh.L1.Mp1 *= weight_captured;
if (surfel_next > -1) {
irradiance_capture(surfel_buf[surfel_next], P, sh);
}
@ -83,6 +92,13 @@ void main()
irradiance_capture(-sky_L, vec3(0.0), sh);
}
/* Normalize for storage. */
weight_captured += 2.0 / sphere_area;
sh.L0.M0 /= weight_captured;
sh.L1.Mn1 /= weight_captured;
sh.L1.M0 /= weight_captured;
sh.L1.Mp1 /= weight_captured;
imageStore(irradiance_L0_img, grid_coord, sh.L0.M0);
imageStore(irradiance_L1_a_img, grid_coord, sh.L1.Mn1);
imageStore(irradiance_L1_b_img, grid_coord, sh.L1.M0);