EEVEE-Next: Irradiance Volume validity weighting #110851

Merged
Clément Foucault merged 1 commits from fclem/blender:eevee-next-irradiance-validity-sampling into main 2023-08-06 13:43:23 +02:00
14 changed files with 123 additions and 16 deletions

View File

@ -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()

View File

@ -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;

View File

@ -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. */

View File

@ -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;
};

View File

@ -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.
*/

View File

@ -1,5 +1,5 @@
void main()
{
out_color = vec4(0.0, 1.0, 1.0, 0.0);
out_color = interp_color;
}

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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]")

View File

@ -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, \

View File

@ -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;

View File

@ -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",