EEVEE-Next: Irradiance Cache: Initial Implementation #108639

Merged
Clément Foucault merged 86 commits from fclem/blender:eevee-next-irradiance-cache into main 2023-06-23 08:39:52 +02:00
15 changed files with 274 additions and 34 deletions
Showing only changes of commit caa0e7edcf - Show all commits

View File

@ -428,7 +428,6 @@ 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_transparency_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
@ -500,6 +499,10 @@ set(GLSL_SRC
engines/eevee_next/shaders/eevee_surf_lib.glsl
engines/eevee_next/shaders/eevee_surf_shadow_frag.glsl
engines/eevee_next/shaders/eevee_surf_world_frag.glsl
engines/eevee_next/shaders/eevee_surfel_light_comp.glsl
engines/eevee_next/shaders/eevee_surfel_list_build_comp.glsl
engines/eevee_next/shaders/eevee_surfel_list_sort_comp.glsl
engines/eevee_next/shaders/eevee_transparency_lib.glsl
engines/eevee_next/shaders/eevee_velocity_lib.glsl
engines/eevee_next/eevee_defines.hh

View File

@ -86,6 +86,9 @@
#define DOF_GATHER_GROUP_SIZE DOF_TILES_SIZE
#define DOF_RESOLVE_GROUP_SIZE (DOF_TILES_SIZE * 2)
/* IrradianceBake. */
#define SURFEL_LIGHT_GROUP_SIZE 256
/* Resource bindings. */
/* Texture. */

View File

@ -1,7 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DNA_lightprobe_types.h"
#include "GPU_capabilities.h"
#include "GPU_debug.h"
#include "BLI_math_rotation.hh"
#include "eevee_instance.hh"
@ -19,10 +23,7 @@ void IrradianceCache::init()
void IrradianceCache::sync()
{
if (inst_.is_baking()) {
bake.sync();
}
else {
if (!inst_.is_baking()) {
debug_pass_sync();
}
}
@ -87,11 +88,7 @@ void IrradianceCache::debug_draw(View &view, GPUFrameBuffer *view_fb)
/** \name Baking
* \{ */
void IrradianceBake::sync()
{
}
void IrradianceBake::surfels_create(const IrradianceGrid & /* grid */)
void IrradianceBake::surfels_create(const IrradianceGrid &grid)
{
/**
fclem marked this conversation as resolved
Review

These can be combined into one line: Vector<IrradianceBrickPacked> allocated(brick_len);

These can be combined into one line: `Vector<IrradianceBrickPacked> allocated(brick_len);`
* We rasterize the scene along the 3 axes. Each generated fragment will write a surface element
@ -100,32 +97,47 @@ void IrradianceBake::surfels_create(const IrradianceGrid & /* grid */)
*/
using namespace blender::math;
/* Attachment-less frame-buffer. */
empty_raster_fb_.ensure(int2(20 * 4));
float4x4 transform(grid.transform);
float3 location, scale;
Quaternion rotation;
math::to_loc_rot_scale(transform, location, rotation, scale);
/** 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 render_axis = [&](Axis axis) {
/* TODO(fclem): get scene bounds GPU or CPU side. Or use the irradiance grid extents. */
float4x4 winmat = math::projection::orthographic(-20.0f, 20.0f, -20.0f, 20.0f, -20.0f, 20.0f);
CartesianBasis basis = from_orthonormal_axes(AxisSigned(axis).next_after(), axis);
view_.sync(from_rotation<float4x4>(basis), winmat);
inst_.pipelines.capture.render(view_);
auto sync_view = [&](View &view, CartesianBasis basis) {
float3 extent = scale;
float4x4 winmat = projection::orthographic(
-extent.x, extent.x, -extent.y, extent.y, -extent.z, extent.z);
float4x4 viewinv = math::from_loc_rot<float4x4>(location,
rotation * to_quaternion<float>(basis));
view.sync(invert(viewinv), winmat);
};
sync_view(view_x_, basis_x_);
sync_view(view_y_, basis_y_);
sync_view(view_z_, basis_z_);
/* Surfel per unit distance. */
float surfel_density = 2.0f;
grid_pixel_extent_ = max(int3(1), int3(surfel_density * 2.0f * scale));
DRW_stats_group_start("IrradianceBake.SurfelsCount");
GPU_debug_capture_begin();
/* Raster the scene to query the number of surfel needed. */
capture_info_buf_.do_surfel_count = true;
capture_info_buf_.do_surfel_output = false;
capture_info_buf_.surfel_len = 0u;
capture_info_buf_.push_update();
render_axis(Axis::X);
render_axis(Axis::Y);
render_axis(Axis::Z);
empty_raster_fb_.ensure(grid_pixel_extent_.yz());
inst_.pipelines.capture.render(view_x_);
empty_raster_fb_.ensure(int2(grid_pixel_extent_.x, grid_pixel_extent_.z));
inst_.pipelines.capture.render(view_y_);
empty_raster_fb_.ensure(grid_pixel_extent_.xy());
inst_.pipelines.capture.render(view_z_);
GPU_debug_capture_end();
DRW_stats_group_end();
/* Allocate surfel pool. */
@ -146,15 +158,71 @@ void IrradianceBake::surfels_create(const IrradianceGrid & /* grid */)
capture_info_buf_.do_surfel_output = true;
capture_info_buf_.surfel_len = 0u;
capture_info_buf_.push_update();
render_axis(Axis::X);
render_axis(Axis::Y);
render_axis(Axis::Z);
empty_raster_fb_.ensure(grid_pixel_extent_.yz());
inst_.pipelines.capture.render(view_x_);
empty_raster_fb_.ensure(int2(grid_pixel_extent_.x, grid_pixel_extent_.z));
inst_.pipelines.capture.render(view_y_);
empty_raster_fb_.ensure(grid_pixel_extent_.xy());
inst_.pipelines.capture.render(view_z_);
DRW_stats_group_end();
/* Sync needs to happen after `surfels_buf_` is resized for correct dispatch size. */
sync();
}
void IrradianceBake::sync()
{
{
PassSimple &pass = surfel_light_eval_ps_;
pass.init();
/* Apply lights contribution to scene surfel representation. */
pass.shader_set(inst_.shaders.static_shader_get(SURFEL_LIGHT));
pass.bind_ssbo(SURFEL_BUF_SLOT, &surfels_buf_);
pass.bind_ssbo(CAPTURE_BUF_SLOT, &capture_info_buf_);
pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
inst_.lights.bind_resources(&pass);
inst_.shadows.bind_resources(&pass);
/* Sync with the surfel creation stage. */
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
pass.dispatch(int3(divide_ceil_u(surfels_buf_.size(), SURFEL_LIGHT_GROUP_SIZE), 1, 1));
pass.barrier(GPU_BARRIER_SHADER_STORAGE);
}
{
PassSimple &pass = surfel_light_propagate_ps_;
pass.init();
{
PassSimple::Sub &sub = pass.sub("ListBuild");
sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_BUILD));
/* TODO */
}
{
PassSimple::Sub &sub = pass.sub("ListSort");
sub.shader_set(inst_.shaders.static_shader_get(SURFEL_LIST_SORT));
/* TODO */
}
}
{
PassSimple &pass = irradiance_capture_ps_;
pass.init();
/* TODO */
}
}
void IrradianceBake::surfels_lights_eval()
{
GPU_debug_capture_begin();
/* Use the last setup view. This should work since the view is orthographic. */
/* 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();
inst_.manager->submit(surfel_light_eval_ps_);
GPU_debug_capture_end();
}
void IrradianceBake::propagate_light_sample()

View File

@ -33,7 +33,26 @@ class IrradianceBake {
CaptureInfoBuf capture_info_buf_;
/** Framebuffer. */
Framebuffer empty_raster_fb_ = {"empty_raster_fb_"};
View view_ = {"ortho_raster_view"};
/** Evaluate light object contribution and store result to surfel. */
PassSimple surfel_light_eval_ps_ = {"LightEval"};
/** Propagate light from surfel to surfel. */
PassSimple surfel_light_propagate_ps_ = {"LightPropagate"};
/** Capture surfel lighting to irradiance samples. */
PassSimple irradiance_capture_ps_ = {"IrradianceCapture"};
/** Basis orientation for each baking projection. */
math::CartesianBasis basis_x_ = math::from_orthonormal_axes(math::AxisSigned::Y_POS,
math::AxisSigned::X_POS);
math::CartesianBasis basis_y_ = math::from_orthonormal_axes(math::AxisSigned::Z_POS,
math::AxisSigned::Y_POS);
math::CartesianBasis basis_z_ = math::from_orthonormal_axes(math::AxisSigned::X_POS,
math::AxisSigned::Z_POS);
/** Views for each baking projection. */
View view_x_ = {"BakingViewX"};
View view_y_ = {"BakingViewY"};
View view_z_ = {"BakingViewZ"};
/** Pixel resolution in each of the projection axes. Match the target surfel density. */
int3 grid_pixel_extent_ = int3(0);
public:
IrradianceBake(Instance &inst) : inst_(inst){};

View File

@ -21,8 +21,7 @@ typedef struct EEVEE_NEXT_LightBake EEVEE_NEXT_LightBake;
* Create the job description.
* This is called for async (modal) bake operator.
* The actual work will be done by `EEVEE_NEXT_lightbake_job()`.
* Will internally call `EEVEE_NEXT_lightbake_job_data_alloc()` or reuse data from an already
* existing baking job.
* Will internally call `EEVEE_NEXT_lightbake_job_data_alloc()`.
* IMPORTANT: Must run on the main thread because of potential GPUContext creation.
*/
struct wmJob *EEVEE_NEXT_lightbake_job_create(struct wmWindowManager *wm,

View File

@ -26,7 +26,7 @@ void LightProbeModule::begin_sync()
cubes.clear();
}
void LightProbeModule::sync_grid(ObjectHandle &handle)
void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle)
{
LightProbe &grid = grid_map_.lookup_or_add_default(handle.object_key);
grid.used = true;
@ -36,7 +36,8 @@ void LightProbeModule::sync_grid(ObjectHandle &handle)
}
if (inst_.is_baking()) {
grids.append({});
const ::LightProbe *light_probe = (const ::LightProbe *)ob->data;
grids.append({float4x4(ob->object_to_world), &light_probe->grid_resolution_x});
}
}
@ -65,7 +66,7 @@ void LightProbeModule::sync_probe(const Object *ob, ObjectHandle &handle)
/* TODO(fclem): Remove support? Add support? */
return;
case LIGHTPROBE_TYPE_GRID:
sync_grid(handle);
sync_grid(ob, handle);
return;
}
BLI_assert_unreachable();

View File

@ -54,7 +54,7 @@ class LightProbeModule {
void begin_sync();
void sync_cube(ObjectHandle &handle);
void sync_grid(ObjectHandle &handle);
void sync_grid(const Object *ob, ObjectHandle &handle);
void sync_probe(const Object *ob, ObjectHandle &handle);

View File

@ -264,6 +264,12 @@ Material &MaterialModule::material_sync(Object *ob,
mat.is_alpha_blend_transparent = (blender_mat->blend_method == MA_BM_BLEND) &&
GPU_material_flag_get(mat.shading.gpumat,
GPU_MATFLAG_TRANSPARENT);
if (inst_.is_baking()) {
/* WORKAROUND(fclem): This is to request the shadow for the surfels. This will well
* over-request the number of shadow tiles. A better way would be to request from the surfels
* directly. */
mat.is_alpha_blend_transparent = true;
}
return mat;
});

View File

@ -172,6 +172,12 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_
return "eevee_shadow_tag_usage_opaque";
case SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT:
return "eevee_shadow_tag_usage_transparent";
case SURFEL_LIGHT:
return "eevee_surfel_light";
case SURFEL_LIST_BUILD:
return "eevee_surfel_list_build";
case SURFEL_LIST_SORT:
return "eevee_surfel_list_sort";
/* To avoid compiler warning about missing case. */
case MAX_SHADER_TYPE:
return "";

View File

@ -80,6 +80,10 @@ enum eShaderType {
SHADOW_TILEMAP_TAG_USAGE_OPAQUE,
SHADOW_TILEMAP_TAG_USAGE_TRANSPARENT,
SURFEL_LIGHT,
SURFEL_LIST_BUILD,
SURFEL_LIST_SORT,
MAX_SHADER_TYPE,
};

View File

@ -22,7 +22,7 @@ void light_eval_ex(ClosureDiffuse diffuse,
vec3 P,
vec3 Ng,
vec3 V,
float vP_z,
float vP_z, /* TODO(fclem): Remove, is unused. */
float thickness,
vec4 ltc_mat,
uint l_idx,
@ -111,7 +111,11 @@ void light_eval(ClosureDiffuse diffuse,
}
LIGHT_FOREACH_END
#ifdef GPU_FRAGMENT_SHADER
vec2 px = gl_FragCoord.xy;
#else
vec2 px = vec2(0.0);
#endif
LIGHT_FOREACH_BEGIN_LOCAL (light_cull_buf, light_zbin_buf, light_tile_buf, px, vP_z, l_idx) {
light_eval_ex(diffuse,
reflection,

View File

@ -0,0 +1,79 @@
/**
* Apply lights contribution to scene surfel representation.
*/
#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl)
void light_eval_surfel(
ClosureDiffuse diffuse, vec3 P, vec3 Ng, float thickness, inout vec3 out_diffuse)
{
/* Dummy closure. Not used. */
ClosureReflection reflection;
reflection.N = vec3(1.0, 0.0, 0.0);
reflection.roughness = 0.0;
vec3 out_specular = vec3(0.0);
/* Dummy ltc mat parameters. Not used since we have no reflections. */
vec4 ltc_mat_dummy = utility_tx_sample(utility_tx, vec2(0.0), UTIL_LTC_MAT_LAYER);
vec3 V = Ng;
float vP_z = 0.0;
LIGHT_FOREACH_BEGIN_DIRECTIONAL (light_cull_buf, l_idx) {
light_eval_ex(diffuse,
reflection,
true,
P,
Ng,
V,
vP_z,
thickness,
ltc_mat_dummy,
l_idx,
out_diffuse,
out_specular);
}
LIGHT_FOREACH_END
LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL (light_cull_buf, l_idx) {
light_eval_ex(diffuse,
reflection,
false,
P,
Ng,
V,
vP_z,
thickness,
ltc_mat_dummy,
l_idx,
out_diffuse,
out_specular);
}
LIGHT_FOREACH_END
}
void main()
{
int index = int(gl_GlobalInvocationID.x);
if (index >= capture_info_buf.surfel_len) {
return;
}
Surfel surfel = surfel_buf[index];
ClosureDiffuse diffuse_data;
diffuse_data.N = surfel.normal;
/* TODO: These could saved inside the surfel. */
diffuse_data.sss_radius = vec3(0.0);
diffuse_data.sss_id = 0u;
float thickness = 0.0;
vec3 diffuse_light = vec3(0.0);
vec3 reflection_light = vec3(0.0);
light_eval_surfel(diffuse_data, surfel.position, surfel.normal, thickness, diffuse_light);
surfel.radiance += diffuse_light * surfel.albedo;
surfel_buf[index] = surfel;
}

View File

@ -0,0 +1,10 @@
/**
* Takes scene surfel representation and build list of surfels aligning in a given direction.
*/
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
void main()
{
}

View File

@ -0,0 +1,12 @@
/**
* Sort a buffer of surfel list by distance along a direction.
* The resulting surfel lists are then the equivalent of a series of ray cast in the same
* direction. The fact that the surfels are sorted gives proper occlusion.
*/
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
void main()
{
}

View File

@ -17,3 +17,29 @@ GPU_SHADER_CREATE_INFO(eevee_debug_surfels)
.push_constant(Type::FLOAT, "surfel_radius")
.push_constant(Type::INT, "debug_mode")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_surfel_light)
.local_group_size(CULLING_SELECT_GROUP_SIZE)
.additional_info("eevee_shared",
"draw_view",
"eevee_utility_texture",
"eevee_light_data",
"eevee_shadow_data")
.compute_source("eevee_surfel_light_comp.glsl")
.storage_buf(SURFEL_BUF_SLOT, Qualifier::READ_WRITE, "Surfel", "surfel_buf[]")
.storage_buf(CAPTURE_BUF_SLOT, Qualifier::READ, "CaptureInfoData", "capture_info_buf")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_surfel_list_build)
.local_group_size(CULLING_SELECT_GROUP_SIZE)
.additional_info("eevee_shared", "draw_view")
.compute_source("eevee_surfel_list_build_comp.glsl")
.storage_buf(0, Qualifier::READ_WRITE, "Surfel", "surfels_buf[]")
.do_static_compilation(true);
GPU_SHADER_CREATE_INFO(eevee_surfel_list_sort)
.local_group_size(CULLING_SELECT_GROUP_SIZE)
.additional_info("eevee_shared", "draw_view")
.compute_source("eevee_surfel_list_sort_comp.glsl")
.storage_buf(0, Qualifier::READ_WRITE, "Surfel", "surfels_buf[]")
.do_static_compilation(true);