diff --git a/scripts/startup/bl_ui/properties_data_lightprobe.py b/scripts/startup/bl_ui/properties_data_lightprobe.py index 260c905bd03..a2b3d7ff189 100644 --- a/scripts/startup/bl_ui/properties_data_lightprobe.py +++ b/scripts/startup/bl_ui/properties_data_lightprobe.py @@ -116,6 +116,7 @@ class DATA_PT_lightprobe_eevee_next(DataButtonsPanel, Panel): col.prop(probe, "grid_normal_bias") col.prop(probe, "grid_view_bias") col.prop(probe, "grid_irradiance_smoothing") + col.prop(probe, "grid_validity_threshold") col.separator() diff --git a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc index 9da5b2ec86d..d5187efe5fe 100644 --- a/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc +++ b/source/blender/draw/engines/eevee_next/eevee_irradiance_cache.cc @@ -34,6 +34,9 @@ void IrradianceCache::init() int texel_byte_size = 8; /* Assumes GPU_RGBA16F. */ int3 atlas_extent(IRRADIANCE_GRID_BRICK_SIZE); atlas_extent.z *= sh_coef_len; + /* Add space for validity bits. */ + atlas_extent.z += IRRADIANCE_GRID_BRICK_SIZE / 4; + int atlas_col_count = 256; atlas_extent.x *= atlas_col_count; /* Determine the row count depending on the scene settings. */ @@ -279,6 +282,7 @@ void IrradianceCache::set_view(View & /*view*/) grid_upload_ps_.init(); grid_upload_ps_.shader_set(inst_.shaders.static_shader_get(LIGHTPROBE_IRRADIANCE_LOAD)); + grid_upload_ps_.push_constant("validity_threshold", grid->validity_threshold); grid_upload_ps_.push_constant("dilation_threshold", grid->dilation_threshold); grid_upload_ps_.push_constant("dilation_radius", grid->dilation_radius); grid_upload_ps_.push_constant("grid_index", grid->grid_index); @@ -326,6 +330,9 @@ void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb) case eDebugMode::DEBUG_IRRADIANCE_CACHE_SURFELS_IRRADIANCE: inst_.info = "Debug Mode: Surfels Irradiance"; break; + case eDebugMode::DEBUG_IRRADIANCE_CACHE_VALIDITY: + inst_.info = "Debug Mode: Irradiance Validity"; + break; case eDebugMode::DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET: inst_.info = "Debug Mode: Virtual Offset"; break; @@ -370,10 +377,8 @@ void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb) break; } + case eDebugMode::DEBUG_IRRADIANCE_CACHE_VALIDITY: case eDebugMode::DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET: { - if (cache->baking.virtual_offset == nullptr) { - continue; - } int3 grid_size = int3(cache->size); debug_ps_.init(); debug_ps_.state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | @@ -383,14 +388,45 @@ void IrradianceCache::debug_pass_draw(View &view, GPUFrameBuffer *view_fb) debug_ps_.push_constant("debug_mode", int(inst_.debug_mode)); debug_ps_.push_constant("grid_mat", grid.object_to_world); - float4 *data = (float4 *)cache->baking.virtual_offset; - + eGPUTextureUsage usage = GPU_TEXTURE_USAGE_SHADER_READ; Texture debug_data_tx = {"debug_data_tx"}; - debug_data_tx.ensure_3d( - GPU_RGBA16F, grid_size, GPU_TEXTURE_USAGE_SHADER_READ, (float *)data); - debug_ps_.bind_texture("debug_data_tx", debug_data_tx); - debug_ps_.draw_procedural(GPU_PRIM_LINES, 1, grid_size.x * grid_size.y * grid_size.z * 2); + if (inst_.debug_mode == eDebugMode::DEBUG_IRRADIANCE_CACHE_VALIDITY) { + float *data; + if (cache->baking.validity) { + data = (float *)cache->baking.validity; + debug_data_tx.ensure_3d(GPU_R16F, grid_size, usage, (float *)data); + } + else if (cache->connectivity.validity) { + debug_data_tx.ensure_3d(GPU_R8, grid_size, usage); + /* TODO(fclem): Make texture creation API work with different data types. */ + GPU_texture_update_sub(debug_data_tx, + GPU_DATA_UBYTE, + cache->connectivity.validity, + 0, + 0, + 0, + UNPACK3(grid_size)); + } + else { + continue; + } + debug_ps_.push_constant("debug_value", grid.validity_threshold); + debug_ps_.bind_texture("debug_data_tx", debug_data_tx); + debug_ps_.draw_procedural(GPU_PRIM_POINTS, 1, grid_size.x * grid_size.y * grid_size.z); + } + else { + if (cache->baking.virtual_offset) { + float *data = (float *)cache->baking.virtual_offset; + debug_data_tx.ensure_3d(GPU_RGBA16F, grid_size, usage, data); + } + else { + continue; + } + debug_ps_.bind_texture("debug_data_tx", debug_data_tx); + debug_ps_.draw_procedural( + GPU_PRIM_LINES, 1, grid_size.x * grid_size.y * grid_size.z * 2); + } inst_.manager->submit(debug_ps_, view); break; diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc index d399c68ca6b..478ddebc61f 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.cc @@ -44,6 +44,7 @@ void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle) grid.view_bias = lightprobe->grid_view_bias; grid.facing_bias = lightprobe->grid_facing_bias; + grid.validity_threshold = lightprobe->grid_validity_threshold; grid.dilation_threshold = lightprobe->grid_dilation_threshold; grid.dilation_radius = lightprobe->grid_dilation_radius; /* Force reupload. */ diff --git a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh index 6a89f8c6bd9..bcf4cf93b57 100644 --- a/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh +++ b/source/blender/draw/engines/eevee_next/eevee_lightprobe.hh @@ -45,6 +45,7 @@ struct IrradianceGrid : public LightProbe, IrradianceGridData { /** Copy of surfel density for debugging purpose. */ float surfel_density; /** Copy of DNA members. */ + float validity_threshold; float dilation_threshold; float dilation_radius; }; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh index 60c71b0f2b6..7db4b67beb6 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -60,6 +60,7 @@ enum eDebugMode : uint32_t { * Display IrradianceCache virtual offset. */ DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET = 6u, + DEBUG_IRRADIANCE_CACHE_VALIDITY = 7u, /** * Show tiles depending on their status. */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_frag.glsl index f25e120b786..f8a20d971a7 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_frag.glsl @@ -1,5 +1,5 @@ void main() { - out_color = vec4(0.0, 1.0, 1.0, 0.0); + out_color = interp_color; } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_vert.glsl index cb8ea839e3e..9e060c07ba3 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_vert.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_debug_irradiance_grid_vert.glsl @@ -6,7 +6,16 @@ void main() { ivec3 grid_resolution = textureSize(debug_data_tx, 0); ivec3 grid_sample; - int sample_id = gl_VertexID / 2; + int sample_id = 0; + if (debug_mode == DEBUG_IRRADIANCE_CACHE_VALIDITY) { + /* Points. */ + sample_id = gl_VertexID; + } + else if (debug_mode == DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET) { + /* Lines. */ + sample_id = gl_VertexID / 2; + } + grid_sample.x = (sample_id % grid_resolution.x); grid_sample.y = (sample_id / grid_resolution.x) % grid_resolution.y; grid_sample.z = (sample_id / (grid_resolution.x * grid_resolution.y)); @@ -14,7 +23,17 @@ void main() vec3 P = lightprobe_irradiance_grid_sample_position(grid_mat, grid_resolution, grid_sample); vec4 debug_data = texelFetch(debug_data_tx, grid_sample, 0); - if (debug_mode == DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET) { + if (debug_mode == DEBUG_IRRADIANCE_CACHE_VALIDITY) { + interp_color = vec4(1.0 - debug_data.r, debug_data.r, 0.0, 0.0); + gl_PointSize = 3.0; + if (debug_data.r > debug_value) { + /* Only render points that are below threshold. */ + gl_Position = vec4(0.0); + gl_PointSize = 0.0; + return; + } + } + else if (debug_mode == DEBUG_IRRADIANCE_CACHE_VIRTUAL_OFFSET) { if (is_zero(debug_data.xyz)) { /* Only render points that have offset. */ gl_Position = vec4(0.0); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl index 6415f637231..2e4578d340f 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_eval_lib.glsl @@ -36,6 +36,15 @@ vec3 lightprobe_irradiance_grid_atlas_coord(IrradianceGridData grid_data, vec3 cell_lP = brick_lP - 0.5; vec3 cell_start = floor(cell_lP); vec3 cell_fract = cell_lP - cell_start; + + /* NOTE(fclem): Use uint to avoid signed int modulo. */ + uint vis_comp = uint(cell_start.z) % 4u; + /* Visibility is stored after the irradiance. */ + ivec3 vis_coord = ivec3(brick.atlas_coord, IRRADIANCE_GRID_BRICK_SIZE * 4) + ivec3(cell_start); + /* Visibility is stored packed 1 cell per channel. */ + vis_coord.z -= int(vis_comp); + float cell_visibility = texelFetch(irradiance_atlas_tx, vis_coord, 0)[vis_comp]; + int cell_visibility_bits = int(cell_visibility); /** * References: * @@ -51,7 +60,7 @@ vec3 lightprobe_irradiance_grid_atlas_coord(IrradianceGridData grid_data, float trilinear_weights[8]; float total_weight = 0.0; for (int i = 0; i < 8; i++) { - ivec3 sample_position = (ivec3(i) >> ivec3(0, 1, 2)) & 1; + ivec3 sample_position = lightprobe_irradiance_grid_cell_corner(i); vec3 trilinear = select(1.0 - cell_fract, cell_fract, bvec3(sample_position)); float positional_weight = trilinear.x * trilinear.y * trilinear.z; @@ -62,8 +71,7 @@ vec3 lightprobe_irradiance_grid_atlas_coord(IrradianceGridData grid_data, float cos_theta = (len > 1e-8) ? dot(lNg, corner_dir) : 1.0; float geometry_weight = saturate(cos_theta * 0.5 + 0.5); - /* TODO(fclem): Need to bake validity. */ - float validity_weight = 1.0; + float validity_weight = float((cell_visibility_bits >> i) & 1); /* Biases. See McGuire's presentation. */ positional_weight += 0.001; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl index 3dd624a8ba6..8cf49139568 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_irradiance_load_comp.glsl @@ -94,4 +94,23 @@ void main() atlas_store(sh.L1.Mn1, output_coord, 1); atlas_store(sh.L1.M0, output_coord, 2); atlas_store(sh.L1.Mp1, output_coord, 3); + + if (gl_LocalInvocationID.z % 4 == 0u) { + /* Encode 4 cells into one volume sample. */ + ivec4 cell_validity_bits = ivec4(0); + /* Encode validty of each samples in the grid cell. */ + for (int cell = 0; cell < 4; cell++) { + for (int i = 0; i < 8; i++) { + ivec3 offset = lightprobe_irradiance_grid_cell_corner(i); + ivec3 coord = input_coord + offset + ivec3(0, 0, cell); + float validity = texelFetch(validity_tx, coord, 0).r; + if (validity > validity_threshold) { + cell_validity_bits[cell] |= (1 << i); + } + } + } + /* NOTE: We could use another sampler to reduce the memory overhead, but that would take + * another sampler slot for forward materials. */ + atlas_store(vec4(cell_validity_bits), output_coord, 4); + } } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl index 139c744386d..8a54d706ba1 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_lightprobe_lib.glsl @@ -35,3 +35,9 @@ int lightprobe_irradiance_grid_brick_index_get(IrradianceGridData grid_data, ive brick_index += brick_coord.z * grid_size_in_bricks.x * grid_size_in_bricks.y; return brick_index; } + +/* Return cell corner from a corner ID [0..7]. */ +ivec3 lightprobe_irradiance_grid_cell_corner(int cell_corner_id) +{ + return (ivec3(cell_corner_id) >> ivec3(0, 1, 2)) & 1; +} diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh index d4beb240e6f..07d6e17b360 100644 --- a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_irradiance_cache_info.hh @@ -24,12 +24,17 @@ GPU_SHADER_CREATE_INFO(eevee_debug_surfels) .push_constant(Type::INT, "debug_mode") .do_static_compilation(true); +GPU_SHADER_INTERFACE_INFO(eevee_debug_irradiance_grid_iface, "") + .smooth(Type::VEC4, "interp_color"); + GPU_SHADER_CREATE_INFO(eevee_debug_irradiance_grid) .additional_info("eevee_shared", "draw_view") .fragment_out(0, Type::VEC4, "out_color") + .vertex_out(eevee_debug_irradiance_grid_iface) .sampler(0, ImageType::FLOAT_3D, "debug_data_tx") .push_constant(Type::MAT4, "grid_mat") .push_constant(Type::INT, "debug_mode") + .push_constant(Type::FLOAT, "debug_value") .vertex_source("eevee_debug_irradiance_grid_vert.glsl") .fragment_source("eevee_debug_irradiance_grid_frag.glsl") .do_static_compilation(true); @@ -166,6 +171,7 @@ GPU_SHADER_CREATE_INFO(eevee_lightprobe_irradiance_load) IRRADIANCE_GRID_BRICK_SIZE) .additional_info("eevee_shared") .push_constant(Type::INT, "grid_index") + .push_constant(Type::FLOAT, "validity_threshold") .push_constant(Type::FLOAT, "dilation_threshold") .push_constant(Type::FLOAT, "dilation_radius") .uniform_buf(0, "IrradianceGridData", "grids_infos_buf[IRRADIANCE_GRID_MAX]") diff --git a/source/blender/makesdna/DNA_lightprobe_defaults.h b/source/blender/makesdna/DNA_lightprobe_defaults.h index 6598a342070..309e6ac2387 100644 --- a/source/blender/makesdna/DNA_lightprobe_defaults.h +++ b/source/blender/makesdna/DNA_lightprobe_defaults.h @@ -26,6 +26,7 @@ .grid_normal_bias = 0.3f, \ .grid_view_bias = 0.0f, \ .grid_facing_bias = 0.5f, \ + .grid_validity_threshold = 0.40f, \ .grid_dilation_threshold = 0.5f, \ .grid_dilation_radius = 1.0f, \ .surfel_density = 1.0f, \ diff --git a/source/blender/makesdna/DNA_lightprobe_types.h b/source/blender/makesdna/DNA_lightprobe_types.h index 6d85774670a..a011ec6ac2c 100644 --- a/source/blender/makesdna/DNA_lightprobe_types.h +++ b/source/blender/makesdna/DNA_lightprobe_types.h @@ -64,7 +64,7 @@ typedef struct LightProbe { float grid_normal_bias; float grid_view_bias; float grid_facing_bias; - float _pad0; + float grid_validity_threshold; /** Irradiance grid: Dilation. */ float grid_dilation_threshold; float grid_dilation_radius; diff --git a/source/blender/makesrna/intern/rna_lightprobe.cc b/source/blender/makesrna/intern/rna_lightprobe.cc index 0f6b5d317a7..8ce6c3d2a8a 100644 --- a/source/blender/makesrna/intern/rna_lightprobe.cc +++ b/source/blender/makesrna/intern/rna_lightprobe.cc @@ -217,6 +217,14 @@ static void rna_def_lightprobe(BlenderRNA *brna) "Number of surfels per unit distance (higher values improve quality)"); RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING, "rna_LightProbe_recalc"); + prop = RNA_def_property(srna, "grid_validity_threshold", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_text(prop, + "Validity Threshold", + "Ratio of front-facing surface hits under which a grid sample will " + "not be considered for lighting"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING, "rna_LightProbe_recalc"); + prop = RNA_def_property(srna, "grid_dilation_threshold", PROP_FLOAT, PROP_FACTOR); RNA_def_property_ui_text(prop, "Dilation Threshold",