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.
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)
{
/**
* 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);