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.
13 changed files with 196 additions and 41 deletions
Showing only changes of commit 12095b8e16 - Show all commits

View File

@ -33,7 +33,7 @@ class DATA_PT_context_lightprobe(DataButtonsPanel, Panel):
class DATA_PT_lightprobe(DataButtonsPanel, Panel):
bl_label = "Probe"
COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT'}
COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_RENDER'}
def draw(self, context):
layout = self.layout
@ -80,6 +80,37 @@ class DATA_PT_lightprobe(DataButtonsPanel, Panel):
sub.prop(probe, "clip_end", text="End")
class DATA_PT_lightprobe_eevee_next(DataButtonsPanel, Panel):
bl_label = "Probe"
COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
probe = context.lightprobe
# layout.prop(probe, "type")
if probe.type == 'GRID':
col = layout.column()
sub = col.column(align=True)
sub.prop(probe, "grid_resolution_x", text="Resolution X")
sub.prop(probe, "grid_resolution_y", text="Y")
sub.prop(probe, "grid_resolution_z", text="Z")
col.operator("object.lightprobe_cache_bake").subset = "ACTIVE"
col.operator("object.lightprobe_cache_free")
elif probe.type == 'PLANAR':
# Currently unsupported
pass
else:
# Currently unsupported
pass
class DATA_PT_lightprobe_visibility(DataButtonsPanel, Panel):
bl_label = "Visibility"
bl_parent_id = "DATA_PT_lightprobe"
@ -166,6 +197,7 @@ class DATA_PT_lightprobe_display(DataButtonsPanel, Panel):
classes = (
DATA_PT_context_lightprobe,
DATA_PT_lightprobe,
DATA_PT_lightprobe_eevee_next,
DATA_PT_lightprobe_visibility,
DATA_PT_lightprobe_parallax,
DATA_PT_lightprobe_display,

View File

@ -466,6 +466,7 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_light_iter_lib.glsl
engines/eevee_next/shaders/eevee_light_lib.glsl
engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl
engines/eevee_next/shaders/eevee_lightprobe_irradiance_bounds_comp.glsl
engines/eevee_next/shaders/eevee_lightprobe_irradiance_ray_comp.glsl
engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl
engines/eevee_next/shaders/eevee_lightprobe_lib.glsl

View File

@ -95,6 +95,7 @@
#define SURFEL_LIST_GROUP_SIZE 256
#define IRRADIANCE_GRID_GROUP_SIZE 4 /* In each dimension, so 4x4x4 workgroup size. */
#define IRRADIANCE_GRID_BRICK_SIZE 4 /* In each dimension, so 4x4x4 brick size. */
#define IRRADIANCE_BOUNDS_GROUP_SIZE 64
/* Resource bindings. */

View File

@ -474,7 +474,6 @@ void Instance::light_bake_irradiance(Object &probe,
};
custom_pipeline_wrapper([&]() {
irradiance_cache.bake.surfel_raster_views_sync(probe);
/* TODO: lightprobe visibility group option. */
manager->begin_sync();
render_sync();

View File

@ -435,24 +435,23 @@ void IrradianceBake::sync()
}
}
void IrradianceBake::surfel_raster_views_sync(const Object &probe_object)
void IrradianceBake::surfel_raster_views_sync(const float3 &scene_min, const float3 &scene_max)
{
using namespace blender::math;
const float4x4 transform(probe_object.object_to_world);
float3 scale;
math::to_loc_rot_scale(transform, grid_location_, grid_orientation_, scale);
grid_pixel_extent_ = max(int3(1), int3(surfel_density_ * (scene_max - scene_min)));
grid_pixel_extent_ = max(int3(1), int3(surfel_density_ * 2.0f * scale));
grid_pixel_extent_ = min(grid_pixel_extent_, int3(16384));
/* We could use multi-view rendering here to avoid multiple submissions but it is unlikely to
* make any difference. The bottleneck is still the light propagation loop. */
auto sync_view = [&](View &view, CartesianBasis basis) {
float3 extent = transform_point(invert(basis), scale);
float3 extent_min = transform_point(invert(basis), scene_min);
float3 extent_max = transform_point(invert(basis), scene_max);
float4x4 winmat = projection::orthographic(
-extent.x, extent.x, -extent.y, extent.y, -extent.z, extent.z);
float4x4 viewinv = math::from_loc_rot<float4x4>(
grid_location_, grid_orientation_ * to_quaternion<float>(basis));
extent_min.x, extent_max.x, extent_min.y, extent_max.y, extent_min.z, extent_max.z);
float4x4 viewinv = from_rotation<float4x4>(to_quaternion<float>(basis));
view.visibility_test(false);
view.sync(invert(viewinv), winmat);
};
@ -499,20 +498,73 @@ void IrradianceBake::surfels_create(const Object &probe_object)
irradiance_L1_b_tx_.clear(float4(0.0f));
irradiance_L1_c_tx_.clear(float4(0.0f));
/* Extract bounding box. Order is arbitrary as it is not important for our usage. */
const std::array<float3, 8> bbox_corners({float3{+1, +1, +1},
float3{-1, +1, +1},
float3{+1, -1, +1},
float3{-1, -1, +1},
float3{+1, +1, -1},
float3{-1, +1, -1},
float3{+1, -1, -1},
float3{-1, -1, -1}});
grid_bbox_vertices.clear();
for (const float3 &point : bbox_corners) {
grid_bbox_vertices.append(transform_point(grid_local_to_world, point));
DRW_stats_group_start("IrradianceBake.SceneBounds");
{
draw::Manager &manager = *inst_.manager;
PassSimple &pass = irradiance_bounds_ps_;
pass.init();
pass.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_BOUNDS));
pass.bind_ssbo("capture_info_buf", &capture_info_buf_);
pass.bind_ssbo("bounds_buf", &manager.bounds_buf.current());
pass.push_constant("resource_len", int(manager.resource_handle_count()));
pass.dispatch(
int3(divide_ceil_u(manager.resource_handle_count(), IRRADIANCE_BOUNDS_GROUP_SIZE), 1, 1));
}
/* Raster the scene to query the number of surfel needed. */
capture_info_buf_.do_surfel_count = false;
capture_info_buf_.do_surfel_output = false;
int neg_flt_max = int(0xFF7FFFFFu ^ 0x7FFFFFFFu); /* floatBitsToOrderedInt(-FLT_MAX) */
int pos_flt_max = 0x7F7FFFFF; /* floatBitsToOrderedInt(FLT_MAX) */
capture_info_buf_.scene_bound_x_min = pos_flt_max;
capture_info_buf_.scene_bound_y_min = pos_flt_max;
capture_info_buf_.scene_bound_z_min = pos_flt_max;
capture_info_buf_.scene_bound_x_max = neg_flt_max;
capture_info_buf_.scene_bound_y_max = neg_flt_max;
capture_info_buf_.scene_bound_z_max = neg_flt_max;
capture_info_buf_.push_update();
inst_.manager->submit(irradiance_bounds_ps_);
GPU_memory_barrier(GPU_BARRIER_BUFFER_UPDATE);
capture_info_buf_.read();
auto ordered_int_bits_to_float = [](int32_t int_value) -> float {
int32_t float_bits = (int_value < 0) ? (int_value ^ 0x7FFFFFFF) : int_value;
return *reinterpret_cast<float *>(&float_bits);
};
float3 scene_min = float3(ordered_int_bits_to_float(capture_info_buf_.scene_bound_x_min),
ordered_int_bits_to_float(capture_info_buf_.scene_bound_y_min),
ordered_int_bits_to_float(capture_info_buf_.scene_bound_z_min));
float3 scene_max = float3(ordered_int_bits_to_float(capture_info_buf_.scene_bound_x_max),
ordered_int_bits_to_float(capture_info_buf_.scene_bound_y_max),
ordered_int_bits_to_float(capture_info_buf_.scene_bound_z_max));
/* To avoid loosing any surface to the clipping planes, add some padding. */
float epsilon = 1.0f / surfel_density_;
scene_min -= epsilon;
scene_max += epsilon;
surfel_raster_views_sync(scene_min, scene_max);
/* Extract bounding box. Order is arbitrary as it is not important for our usage. */
scene_bbox_vertices_.clear();
scene_bbox_vertices_.append(float3{scene_max.x, scene_max.y, scene_max.z});
scene_bbox_vertices_.append(float3{scene_min.x, scene_max.y, scene_max.z});
scene_bbox_vertices_.append(float3{scene_max.x, scene_min.y, scene_max.z});
scene_bbox_vertices_.append(float3{scene_min.x, scene_min.y, scene_max.z});
scene_bbox_vertices_.append(float3{scene_max.x, scene_max.y, scene_min.z});
scene_bbox_vertices_.append(float3{scene_min.x, scene_max.y, scene_min.z});
scene_bbox_vertices_.append(float3{scene_max.x, scene_min.y, scene_min.z});
scene_bbox_vertices_.append(float3{scene_min.x, scene_min.y, scene_min.z});
DRW_stats_group_end();
/* WORKAROUND: Sync camera with correct bounds for light culling. */
inst_.camera.sync();
DRW_stats_group_start("IrradianceBake.SurfelsCount");
/* Raster the scene to query the number of surfel needed. */
@ -570,7 +622,6 @@ void IrradianceBake::surfels_lights_eval()
/* TODO(fclem): Remove this. It is only present to avoid crash inside `shadows.set_view` */
inst_.render_buffers.acquire(int2(1));
inst_.lights.set_view(view_z_, grid_pixel_extent_.xy());
/* TODO: Instead of using the volume tagging we should tag using the surfels. */
inst_.shadows.set_view(view_z_);
inst_.render_buffers.release();
@ -584,21 +635,14 @@ void IrradianceBake::propagate_light_sample()
float2 rand_uv = inst_.sampling.rng_2d_get(eSamplingDimension::SAMPLING_FILTER_U);
const float3 ray_direction = inst_.sampling.sample_hemisphere(rand_uv);
const float3 up = ray_direction;
/* Find the closest axis. */
const float3 grid_local_ray_direction = transform_point(grid_orientation_, ray_direction);
Axis closest_grid_axis = Axis::from_int(dominant_axis(grid_local_ray_direction));
/* Use one of the other 2 grid axes to get a reference right vector. */
Axis right_axis = AxisSigned(closest_grid_axis).next_after().axis();
const float3 grid_right = from_rotation<float3x3>(grid_orientation_)[right_axis.as_int()];
/* Create a view around the grid position with the ray direction as up axis.
* The other axes are aligned to the grid local axes to avoid to allocate too many list start. */
const float4x4 viewmat = invert(
from_orthonormal_axes<float4x4>(grid_location_, normalize(cross(up, grid_right)), up));
const float3 forward = cross(up, normalize(orthogonal(up)));
const float4x4 viewinv = from_orthonormal_axes<float4x4>(float3(0.0f), forward, up);
const float4x4 viewmat = invert(viewinv);
/* Compute projection bounds. */
float2 min, max;
INIT_MINMAX2(min, max);
for (const float3 &point : grid_bbox_vertices) {
for (const float3 &point : scene_bbox_vertices_) {
min_max(transform_point(viewmat, point).xy(), min, max);
}

View File

@ -47,6 +47,8 @@ class IrradianceBake {
PassSimple surfel_light_bounce_ps_ = {"LightBounce"};
/** Capture surfel lighting to irradiance samples. */
PassSimple irradiance_capture_ps_ = {"IrradianceCapture"};
/** Compute scene bounding box. */
PassSimple irradiance_bounds_ps_ = {"IrradianceBounds"};
/**
* Basis orientation for each baking projection.
@ -82,12 +84,8 @@ class IrradianceBake {
Texture irradiance_L1_b_tx_ = {"irradiance_L1_b_tx_"};
Texture irradiance_L1_c_tx_ = {"irradiance_L1_c_tx_"};
/* Orientation of the irradiance grid being baked. */
math::Quaternion grid_orientation_;
/* Object center of the irradiance grid being baked. */
float3 grid_location_;
/* Bounding box vertices of the irradiance grid being baked. In world space. */
Vector<float3> grid_bbox_vertices;
Vector<float3> scene_bbox_vertices_;
/* Surfel per unit distance. */
float surfel_density_ = 2.0f;
@ -97,7 +95,7 @@ class IrradianceBake {
void sync();
/** Create the views used to rasterize the scene into surfel representation. */
void surfel_raster_views_sync(const Object &probe_object);
void surfel_raster_views_sync(const float3 &scene_min, const float3 &scene_max);
/** Create a surfel representation of the scene from the probe using the capture pipeline. */
void surfels_create(const Object &probe_object);
/** Evaluate direct lighting (and also clear the surfels radiance). */

View File

@ -148,6 +148,8 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_light_culling_tile";
case LIGHT_CULLING_ZBIN:
return "eevee_light_culling_zbin";
case LIGHTPROBE_IRRADIANCE_BOUNDS:
return "eevee_lightprobe_irradiance_bounds";
case LIGHTPROBE_IRRADIANCE_RAY:
return "eevee_lightprobe_irradiance_ray";
case LIGHTPROBE_IRRADIANCE_LOAD:

View File

@ -63,6 +63,7 @@ enum eShaderType {
LIGHT_CULLING_TILE,
LIGHT_CULLING_ZBIN,
LIGHTPROBE_IRRADIANCE_BOUNDS,
LIGHTPROBE_IRRADIANCE_RAY,
LIGHTPROBE_IRRADIANCE_LOAD,

View File

@ -879,6 +879,16 @@ struct CaptureInfoData {
/** Transform vectors from world space to local space. Does not have location component. */
/** TODO(fclem): This could be a float3x4 or a float3x3 if padded correctly. */
float4x4 irradiance_grid_world_to_local_rotation;
/** Scene bounds. Stored as min & max and as int for atomic operations. */
int scene_bound_x_min;
int scene_bound_y_min;
int scene_bound_z_min;
int scene_bound_x_max;
int scene_bound_y_max;
int scene_bound_z_max;
int _pad0;
int _pad1;
int _pad2;
};
BLI_STATIC_ASSERT_ALIGN(CaptureInfoData, 16)

View File

@ -0,0 +1,48 @@
/**
* Surface Capture: Output surface parameters to diverse storage.
*
* The resources expected to be defined are:
* - capture_info_buf
*/
#pragma BLENDER_REQUIRE(gpu_shader_utildefines_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(common_intersect_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_shadow_tilemap_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl)
void main()
{
uint index = gl_GlobalInvocationID.x;
if (index >= resource_len) {
return;
}
ObjectBounds bounds = bounds_buf[index];
/* Bounds are not correct as culling is disabled for these. */
if (bounds._inner_sphere_radius <= 0.0) {
return;
}
IsectBox box = isect_data_setup(bounds.bounding_corners[0].xyz,
bounds.bounding_corners[1].xyz,
bounds.bounding_corners[2].xyz,
bounds.bounding_corners[3].xyz);
vec3 local_min = vec3(FLT_MAX);
vec3 local_max = vec3(-FLT_MAX);
for (int i = 0; i < 8; i++) {
local_min = min(local_min, box.corners[i].xyz);
local_max = max(local_max, box.corners[i].xyz);
}
atomicMin(capture_info_buf.scene_bound_x_min, floatBitsToOrderedInt(local_min.x));
atomicMax(capture_info_buf.scene_bound_x_max, floatBitsToOrderedInt(local_max.x));
atomicMin(capture_info_buf.scene_bound_y_min, floatBitsToOrderedInt(local_min.y));
atomicMax(capture_info_buf.scene_bound_y_max, floatBitsToOrderedInt(local_max.y));
atomicMin(capture_info_buf.scene_bound_z_min, floatBitsToOrderedInt(local_min.z));
atomicMax(capture_info_buf.scene_bound_z_max, floatBitsToOrderedInt(local_max.z));
}

View File

@ -91,6 +91,16 @@ GPU_SHADER_CREATE_INFO(eevee_surfel_ray)
.compute_source("eevee_surfel_ray_comp.glsl")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_bounds)
.do_static_compilation(true)
.local_group_size(IRRADIANCE_BOUNDS_GROUP_SIZE)
.storage_buf(0, Qualifier::READ_WRITE, "CaptureInfoData", "capture_info_buf")
.storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]")
.push_constant(Type::INT, "resource_len")
.typedef_source("draw_shader_shared.h")
.additional_info("eevee_shared")
.compute_source("eevee_lightprobe_irradiance_bounds_comp.glsl");
GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_ray)
.local_group_size(IRRADIANCE_GRID_GROUP_SIZE,
IRRADIANCE_GRID_GROUP_SIZE,

View File

@ -149,6 +149,7 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward)
GPU_SHADER_CREATE_INFO(eevee_surf_capture)
.vertex_out(eevee_surf_iface)
.define("MAT_CAPTURE")
.storage_buf(SURFEL_BUF_SLOT, Qualifier::WRITE, "Surfel", "surfel_buf[]")
.storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ_WRITE, "CaptureInfoData", "capture_info_buf")
.fragment_source("eevee_surf_capture_frag.glsl")

View File

@ -178,6 +178,14 @@ class Manager {
acquired_textures.append(texture);
}
/**
* Return the number of resource handles allocated.
*/
uint resource_handle_count() const
{
return resource_len_;
}
/** TODO(fclem): The following should become private at some point. */
void begin_sync();
void end_sync();