WIP: eevee-next-world-irradiance #108304
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue