WIP: eevee-next-world-irradiance #108304
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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). */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -63,6 +63,7 @@ enum eShaderType {
|
|||
LIGHT_CULLING_TILE,
|
||||
LIGHT_CULLING_ZBIN,
|
||||
|
||||
LIGHTPROBE_IRRADIANCE_BOUNDS,
|
||||
LIGHTPROBE_IRRADIANCE_RAY,
|
||||
LIGHTPROBE_IRRADIANCE_LOAD,
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue