EEVEE-Next: Irradiance Cache: Add manual trilinear weights #110312
@ -111,6 +111,12 @@ class DATA_PT_lightprobe_eevee_next(DataButtonsPanel, Panel):
|
||||
col.prop(probe, "grid_bake_samples")
|
||||
col.prop(probe, "surfel_density")
|
||||
|
||||
col.separator()
|
||||
|
||||
col.prop(probe, "grid_normal_bias")
|
||||
col.prop(probe, "grid_view_bias")
|
||||
col.prop(probe, "grid_irradiance_smoothing")
|
||||
|
||||
elif probe.type == 'CUBEMAP':
|
||||
col = layout.column()
|
||||
col.prop(probe, "resolution")
|
||||
|
@ -378,10 +378,13 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
|
||||
/* Keep this block, even when empty. */
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "LightProbe", "int", "grid_bake_sample_count")) {
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "LightProbe", "int", "grid_bake_samples")) {
|
||||
LISTBASE_FOREACH (LightProbe *, lightprobe, &bmain->lightprobes) {
|
||||
lightprobe->grid_bake_samples = 2048;
|
||||
lightprobe->surfel_density = 1.0f;
|
||||
lightprobe->grid_normal_bias = 0.3f;
|
||||
lightprobe->grid_view_bias = 0.0f;
|
||||
lightprobe->grid_facing_bias = 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,8 @@ void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle)
|
||||
IrradianceGrid &grid = grid_map_.lookup_or_add_default(handle.object_key);
|
||||
grid.used = true;
|
||||
if (handle.recalc != 0 || grid.initialized == false) {
|
||||
const ::LightProbe *lightprobe = static_cast<const ::LightProbe *>(ob->data);
|
||||
|
||||
grid.initialized = true;
|
||||
grid.updated = true;
|
||||
grid.object_to_world = float4x4(ob->object_to_world);
|
||||
@ -37,6 +39,9 @@ void LightProbeModule::sync_grid(const Object *ob, ObjectHandle &handle)
|
||||
math::normalize(math::transpose(float3x3(grid.object_to_world))));
|
||||
|
||||
grid.cache = ob->lightprobe_cache;
|
||||
grid.normal_bias = lightprobe->grid_normal_bias;
|
||||
grid.view_bias = lightprobe->grid_view_bias;
|
||||
grid.facing_bias = lightprobe->grid_facing_bias;
|
||||
/* Force reupload. */
|
||||
inst_.irradiance_cache.bricks_free(grid.bricks);
|
||||
}
|
||||
|
@ -922,6 +922,11 @@ struct IrradianceGridData {
|
||||
packed_int3 grid_size;
|
||||
/** Index in brick descriptor list of the first brick of this grid. */
|
||||
int brick_offset;
|
||||
/** Biases to apply to the shading point in order to sample a valid probe. */
|
||||
float normal_bias;
|
||||
float view_bias;
|
||||
float facing_bias;
|
||||
int _pad1;
|
||||
};
|
||||
BLI_STATIC_ASSERT_ALIGN(IrradianceGridData, 16)
|
||||
|
||||
|
@ -12,8 +12,15 @@
|
||||
/**
|
||||
* Return sample coordinates of the first SH coef in unormalized texture space.
|
||||
*/
|
||||
vec3 lightprobe_irradiance_grid_atlas_coord(IrradianceGridData grid_data, vec3 lP)
|
||||
vec3 lightprobe_irradiance_grid_atlas_coord(IrradianceGridData grid_data,
|
||||
vec3 lP,
|
||||
vec3 lV,
|
||||
vec3 lNg)
|
||||
{
|
||||
/* Shading point bias. */
|
||||
lP += lNg * grid_data.normal_bias;
|
||||
lP += lV * grid_data.view_bias;
|
||||
|
||||
ivec3 brick_coord = ivec3((lP - 0.5) / float(IRRADIANCE_GRID_BRICK_SIZE - 1));
|
||||
/* Avoid sampling adjacent bricks. */
|
||||
brick_coord = max(brick_coord, ivec3(0));
|
||||
@ -23,8 +30,58 @@ vec3 lightprobe_irradiance_grid_atlas_coord(IrradianceGridData grid_data, vec3 l
|
||||
vec3 brick_lP = lP - vec3(brick_coord) * float(IRRADIANCE_GRID_BRICK_SIZE - 1);
|
||||
|
||||
int brick_index = lightprobe_irradiance_grid_brick_index_get(grid_data, brick_coord);
|
||||
|
||||
IrradianceBrick brick = irradiance_brick_unpack(bricks_infos_buf[brick_index]);
|
||||
|
||||
/* A cell is the interpolation region between 8 texels. */
|
||||
vec3 cell_lP = brick_lP - 0.5;
|
||||
vec3 cell_start = floor(cell_lP);
|
||||
vec3 cell_fract = cell_lP - cell_start;
|
||||
/**
|
||||
* References:
|
||||
*
|
||||
* "Probe-based lighting, strand-based hair system, and physical hair shading in Unity’s Enemies"
|
||||
* by Francesco Cifariello Ciardi, Lasse Jon Fuglsang Pedersen and John Parsaie.
|
||||
*
|
||||
* "Multi-Scale Global Illumination in Quantum Break"
|
||||
* by Ari Silvennoinen and Ville Timonen.
|
||||
*
|
||||
* “Dynamic Diffuse Global Illumination with Ray-Traced Irradiance Fields”
|
||||
* by Morgan McGuire.
|
||||
*/
|
||||
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;
|
||||
|
||||
vec3 trilinear = select(1.0 - cell_fract, cell_fract, bvec3(sample_position));
|
||||
float positional_weight = trilinear.x * trilinear.y * trilinear.z;
|
||||
|
||||
float len;
|
||||
vec3 corner_vec = vec3(sample_position) - cell_fract;
|
||||
vec3 corner_dir = normalize_and_get_length(corner_vec, len);
|
||||
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;
|
||||
|
||||
/* Biases. See McGuire's presentation. */
|
||||
positional_weight += 0.001;
|
||||
geometry_weight = sqr(geometry_weight) + 0.2 + grid_data.facing_bias;
|
||||
|
||||
trilinear_weights[i] = saturate(positional_weight * geometry_weight * validity_weight);
|
||||
total_weight += trilinear_weights[i];
|
||||
}
|
||||
float total_weight_inv = safe_rcp(total_weight);
|
||||
|
||||
vec3 trilinear_coord = vec3(0.0);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
vec3 sample_position = vec3((ivec3(i) >> ivec3(0, 1, 2)) & 1);
|
||||
trilinear_coord += sample_position * trilinear_weights[i] * total_weight_inv;
|
||||
}
|
||||
/* Replace sampling coordinates with manually weighted trilinear coordinates. */
|
||||
brick_lP = 0.5 + cell_start + trilinear_coord;
|
||||
|
||||
vec3 output_coord = vec3(vec2(brick.atlas_coord), 0.0) + brick_lP;
|
||||
|
||||
return output_coord;
|
||||
@ -35,7 +92,7 @@ vec4 textureUnormalizedCoord(sampler3D tx, vec3 co)
|
||||
return texture(tx, co / vec3(textureSize(tx, 0)));
|
||||
}
|
||||
|
||||
SphericalHarmonicL1 lightprobe_irradiance_sample(sampler3D atlas_tx, vec3 P)
|
||||
SphericalHarmonicL1 lightprobe_irradiance_sample(sampler3D atlas_tx, vec3 P, vec3 V, vec3 Ng)
|
||||
{
|
||||
vec3 lP;
|
||||
int grid_index;
|
||||
@ -52,16 +109,24 @@ SphericalHarmonicL1 lightprobe_irradiance_sample(sampler3D atlas_tx, vec3 P)
|
||||
}
|
||||
}
|
||||
|
||||
vec3 atlas_coord = lightprobe_irradiance_grid_atlas_coord(grids_infos_buf[grid_index], lP);
|
||||
/* TODO(fclem): Make sure this is working as expected. */
|
||||
mat3x3 world_to_grid_transposed = mat3x3(grids_infos_buf[grid_index].world_to_grid_transposed);
|
||||
vec3 lNg = safe_normalize(world_to_grid_transposed * Ng);
|
||||
vec3 lV = safe_normalize(V * world_to_grid_transposed);
|
||||
|
||||
vec3 atlas_coord = lightprobe_irradiance_grid_atlas_coord(
|
||||
grids_infos_buf[grid_index], lP, lV, lNg);
|
||||
|
||||
vec4 texture_coord = vec4(atlas_coord, float(IRRADIANCE_GRID_BRICK_SIZE)) /
|
||||
vec3(textureSize(atlas_tx, 0)).xyzz;
|
||||
SphericalHarmonicL1 sh;
|
||||
sh.L0.M0 = textureUnormalizedCoord(atlas_tx, atlas_coord);
|
||||
atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE);
|
||||
sh.L1.Mn1 = textureUnormalizedCoord(atlas_tx, atlas_coord);
|
||||
atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE);
|
||||
sh.L1.M0 = textureUnormalizedCoord(atlas_tx, atlas_coord);
|
||||
atlas_coord.z += float(IRRADIANCE_GRID_BRICK_SIZE);
|
||||
sh.L1.Mp1 = textureUnormalizedCoord(atlas_tx, atlas_coord);
|
||||
sh.L0.M0 = textureLod(atlas_tx, texture_coord.xyz, 0.0);
|
||||
texture_coord.z += texture_coord.w;
|
||||
sh.L1.Mn1 = textureLod(atlas_tx, texture_coord.xyz, 0.0);
|
||||
texture_coord.z += texture_coord.w;
|
||||
sh.L1.M0 = textureLod(atlas_tx, texture_coord.xyz, 0.0);
|
||||
texture_coord.z += texture_coord.w;
|
||||
sh.L1.Mp1 = textureLod(atlas_tx, texture_coord.xyz, 0.0);
|
||||
return sh;
|
||||
}
|
||||
|
||||
@ -73,7 +138,10 @@ void lightprobe_eval(ClosureDiffuse diffuse,
|
||||
inout vec3 out_diffuse,
|
||||
inout vec3 out_specular)
|
||||
{
|
||||
SphericalHarmonicL1 irradiance = lightprobe_irradiance_sample(irradiance_atlas_tx, P);
|
||||
/* NOTE: Use the diffuse normal for biasing the probe sampling location since it is smoother than
|
||||
* geometric normal. Could also try to use interp.N. */
|
||||
SphericalHarmonicL1 irradiance = lightprobe_irradiance_sample(
|
||||
irradiance_atlas_tx, P, V, diffuse.N);
|
||||
|
||||
out_diffuse += spherical_harmonics_evaluate_lambert(diffuse.N, irradiance);
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ void main()
|
||||
if (z_delta > 0.0) {
|
||||
float fac = 1.0 - z_delta * 10000.0;
|
||||
/* Smooth blend to avoid flickering. */
|
||||
finalColor = mix(colorBackground, finalColor, clamp(fac, 0.5, 1.0));
|
||||
finalColor = mix(colorBackground, finalColor, clamp(fac, 0.2, 1.0));
|
||||
}
|
||||
|
||||
view_clipping_distances(ws_cell_location);
|
||||
|
@ -21,6 +21,9 @@
|
||||
.grid_resolution_y = 4, \
|
||||
.grid_resolution_z = 4, \
|
||||
.grid_bake_samples = 2048, \
|
||||
.grid_normal_bias = 0.3f, \
|
||||
.grid_view_bias = 0.0f, \
|
||||
.grid_facing_bias = 0.5f, \
|
||||
.surfel_density = 1.0f, \
|
||||
.distinf = 2.5f, \
|
||||
.distpar = 2.5f, \
|
||||
|
@ -57,6 +57,11 @@ typedef struct LightProbe {
|
||||
int grid_resolution_z;
|
||||
/** Irradiance grid: number of directions to evaluate light transfer in. */
|
||||
int grid_bake_samples;
|
||||
/** Irradiance grid: Sampling biases. */
|
||||
float grid_normal_bias;
|
||||
float grid_view_bias;
|
||||
float grid_facing_bias;
|
||||
float _pad0;
|
||||
|
||||
/** Surface element density for scene surface cache. In surfel per unit distance. */
|
||||
float surfel_density;
|
||||
|
@ -166,6 +166,32 @@ static void rna_def_lightprobe(BlenderRNA *brna)
|
||||
prop, "Resolution Z", "Number of samples along the z axis of the volume");
|
||||
RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING, "rna_LightProbe_recalc");
|
||||
|
||||
prop = RNA_def_property(srna, "grid_normal_bias", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Normal Bias",
|
||||
"Offset sampling of the irradiance grid in "
|
||||
"the surface normal direction to reduce light bleeding");
|
||||
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, 3);
|
||||
RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING, "rna_LightProbe_recalc");
|
||||
|
||||
prop = RNA_def_property(srna, "grid_view_bias", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"View Bias",
|
||||
"Offset sampling of the irradiance grid in "
|
||||
"the viewing direction to reduce light bleeding");
|
||||
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, 3);
|
||||
RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING, "rna_LightProbe_recalc");
|
||||
|
||||
prop = RNA_def_property(srna, "grid_irradiance_smoothing", PROP_FLOAT, PROP_FACTOR);
|
||||
RNA_def_property_float_sdna(prop, nullptr, "grid_facing_bias");
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Facing Bias", "Smoother irradiance interpolation but introduce light bleeding");
|
||||
RNA_def_property_range(prop, 0.0f, FLT_MAX);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 1, 3);
|
||||
RNA_def_property_update(prop, NC_MATERIAL | ND_SHADING, "rna_LightProbe_recalc");
|
||||
|
||||
prop = RNA_def_property(srna, "grid_bake_samples", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Bake Samples", "Number of ray directions to evaluate when baking");
|
||||
|
Loading…
Reference in New Issue
Block a user