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.
8 changed files with 575 additions and 3 deletions
Showing only changes of commit 892f7077bc - Show all commits

View File

@ -14,10 +14,48 @@ extern "C" {
struct LightProbe;
struct Main;
struct BlendWriter;
struct BlendDataReader;
struct LightProbeObjectCache;
struct LightProbeGridCacheFrame;
struct Object;
void BKE_lightprobe_type_set(struct LightProbe *probe, short lightprobe_type);
void *BKE_lightprobe_add(struct Main *bmain, const char *name);
void BKE_lightprobe_cache_blend_write(struct BlendWriter *writer,
struct LightProbeObjectCache *cache);
void BKE_lightprobe_cache_blend_read(struct BlendDataReader *reader,
struct LightProbeObjectCache *cache);
/**
* Create a single empty irradiance grid cache.
*/
struct LightProbeGridCacheFrame *BKE_lightprobe_grid_cache_frame_create(void);
/**
* Free a single grid cache.
*/
void BKE_lightprobe_grid_cache_frame_free(struct LightProbeGridCacheFrame *cache);
/**
* Create the grid cache list depending on the lightprobe baking settings.
* The list is left empty to be filled by the baking process.
*/
void BKE_lightprobe_cache_create(struct Object *object);
/**
* Free all irradiance grids allocated for the given object.
*/
void BKE_lightprobe_cache_free(struct Object *object);
/**
* Return the number of sample stored inside an irradiance cache.
* This depends on the light cache type.
*/
int64_t BKE_lightprobe_grid_cache_frame_sample_count(const struct LightProbeGridCacheFrame *cache);
#ifdef __cplusplus
}
#endif

View File

@ -12,6 +12,8 @@
#include "DNA_lightprobe_types.h"
#include "DNA_object_types.h"
#include "BLI_math_base.h"
#include "BLI_span.hh"
#include "BLI_utildefines.h"
#include "BKE_anim_data.h"
@ -129,3 +131,166 @@ void *BKE_lightprobe_add(Main *bmain, const char *name)
return probe;
}
static void lightprobe_grid_cache_frame_blend_write(BlendWriter *writer,
const LightProbeGridCacheFrame *cache)
{
if (cache->block_infos != nullptr) {
BLO_write_struct_array(writer, LightProbeGridCacheFrame, cache->block_len, cache->block_infos);
}
int64_t sample_count = BKE_lightprobe_grid_cache_frame_sample_count(cache);
if (cache->irradiance.L0 != nullptr) {
BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L0);
}
if (cache->irradiance.L1_a != nullptr) {
BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L1_a);
}
if (cache->irradiance.L1_b != nullptr) {
BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L1_b);
}
if (cache->irradiance.L1_c != nullptr) {
BLO_write_float3_array(writer, sample_count, (float *)cache->irradiance.L1_c);
}
if (cache->visibility.L0 != nullptr) {
BLO_write_int8_array(writer, sample_count, (int8_t *)cache->visibility.L0);
}
if (cache->visibility.L1_a != nullptr) {
BLO_write_int8_array(writer, sample_count, (int8_t *)cache->visibility.L1_a);
}
if (cache->visibility.L1_b != nullptr) {
BLO_write_int8_array(writer, sample_count, (int8_t *)cache->visibility.L1_b);
}
if (cache->visibility.L1_c != nullptr) {
BLO_write_int8_array(writer, sample_count, (int8_t *)cache->visibility.L1_c);
}
if (cache->connectivity.bitmask != nullptr) {
BLO_write_struct_array(
writer, LightProbeGridCacheFrame, sample_count, cache->connectivity.bitmask);
}
}
static void lightprobe_grid_cache_frame_blend_read(BlendDataReader *reader,
LightProbeGridCacheFrame *cache)
{
if (cache->block_infos != nullptr) {
BLO_read_data_address(reader, &cache->block_infos);
}
int64_t sample_count = BKE_lightprobe_grid_cache_frame_sample_count(cache);
/* Baking data is not stored. */
cache->baking.L0 = nullptr;
cache->baking.L1_a = nullptr;
cache->baking.L1_b = nullptr;
cache->baking.L1_c = nullptr;
if (cache->irradiance.L0 != nullptr) {
BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L0);
}
if (cache->irradiance.L1_a != nullptr) {
BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L1_a);
}
if (cache->irradiance.L1_b != nullptr) {
BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L1_b);
}
if (cache->irradiance.L1_c != nullptr) {
BLO_read_float3_array(reader, sample_count, (float **)&cache->irradiance.L1_c);
}
if (cache->visibility.L0 != nullptr) {
BLO_read_int8_array(reader, sample_count, (int8_t **)&cache->visibility.L0);
}
if (cache->visibility.L1_a != nullptr) {
BLO_read_int8_array(reader, sample_count, (int8_t **)&cache->visibility.L1_a);
}
if (cache->visibility.L1_b != nullptr) {
BLO_read_int8_array(reader, sample_count, (int8_t **)&cache->visibility.L1_b);
}
if (cache->visibility.L1_c != nullptr) {
BLO_read_int8_array(reader, sample_count, (int8_t **)&cache->visibility.L1_c);
}
if (cache->connectivity.bitmask != nullptr) {
BLO_read_data_address(reader, &cache->connectivity.bitmask);
}
}
void BKE_lightprobe_cache_blend_write(BlendWriter *writer, LightProbeObjectCache *cache)
{
if (cache->grid_static_cache != nullptr) {
BLO_write_struct(writer, LightProbeGridCacheFrame, cache->grid_static_cache);
lightprobe_grid_cache_frame_blend_write(writer, cache->grid_static_cache);
}
}
void BKE_lightprobe_cache_blend_read(BlendDataReader *reader, LightProbeObjectCache *cache)
{
if (cache->grid_static_cache != nullptr) {
BLO_read_data_address(reader, &cache->grid_static_cache);
lightprobe_grid_cache_frame_blend_read(reader, cache->grid_static_cache);
}
}
template<typename T> static void spherical_harmonic_free(T &data)
{
MEM_SAFE_FREE(data.L0);
MEM_SAFE_FREE(data.L1_a);
MEM_SAFE_FREE(data.L1_b);
MEM_SAFE_FREE(data.L1_c);
}
LightProbeGridCacheFrame *BKE_lightprobe_grid_cache_frame_create()
{
LightProbeGridCacheFrame *cache = static_cast<LightProbeGridCacheFrame *>(
MEM_callocN(sizeof(LightProbeGridCacheFrame), "LightProbeGridCacheFrame"));
return cache;
}
void BKE_lightprobe_grid_cache_frame_free(LightProbeGridCacheFrame *cache)
{
MEM_SAFE_FREE(cache->block_infos);
spherical_harmonic_free(cache->baking);
spherical_harmonic_free(cache->irradiance);
spherical_harmonic_free(cache->visibility);
MEM_SAFE_FREE(cache->connectivity.bitmask);
MEM_SAFE_FREE(cache);
}
void BKE_lightprobe_cache_create(Object *object)
{
BLI_assert(object->lightprobe_cache == nullptr);
object->lightprobe_cache = static_cast<LightProbeObjectCache *>(
MEM_callocN(sizeof(LightProbeObjectCache), "LightProbeObjectCache"));
}
void BKE_lightprobe_cache_free(Object *object)
{
if (object->lightprobe_cache == nullptr) {
return;
}
LightProbeObjectCache *cache = object->lightprobe_cache;
if (cache->shared == false) {
if (cache->grid_static_cache != nullptr) {
BKE_lightprobe_grid_cache_frame_free(cache->grid_static_cache);
}
}
MEM_SAFE_FREE(object->lightprobe_cache);
}
int64_t BKE_lightprobe_grid_cache_frame_sample_count(const LightProbeGridCacheFrame *cache)
{
if (cache->data_layout == LIGHTPROBE_CACHE_ADAPTIVE_RESOLUTION) {
return cache->block_len * cube_i(cache->block_size);
}
/* LIGHTPROBE_CACHE_UNIFORM_GRID */
return cache->size[0] * cache->size[1] * cache->size[2];
}

View File

@ -267,6 +267,18 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in
if (ob_src->lightgroup) {
ob_dst->lightgroup = (LightgroupMembership *)MEM_dupallocN(ob_src->lightgroup);
}
if ((flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) != 0) {
if (ob_src->lightprobe_cache) {
/* Reference the original object data. */
ob_dst->lightprobe_cache = (LightProbeObjectCache *)MEM_dupallocN(ob_src->lightprobe_cache);
ob_dst->lightprobe_cache->shared = true;
}
}
else {
/* Do not copy lightprobe's cache. */
ob_dst->lightprobe_cache = nullptr;
}
}
static void object_free_data(ID *id)
@ -319,6 +331,8 @@ static void object_free_data(ID *id)
BKE_previewimg_free(&ob->preview);
MEM_SAFE_FREE(ob->lightgroup);
BKE_lightprobe_cache_free(ob);
}
static void library_foreach_modifiersForeachIDLink(void *user_data,
@ -597,6 +611,11 @@ static void object_blend_write(BlendWriter *writer, ID *id, const void *id_addre
if (ob->lightgroup) {
BLO_write_struct(writer, LightgroupMembership, ob->lightgroup);
}
if (ob->lightprobe_cache) {
BLO_write_struct(writer, LightProbeObjectCache, ob->lightprobe_cache);
BKE_lightprobe_cache_blend_write(writer, ob->lightprobe_cache);
}
}
/* XXX deprecated - old animation system */
@ -815,6 +834,11 @@ static void object_blend_read_data(BlendDataReader *reader, ID *id)
BKE_previewimg_blend_read(reader, ob->preview);
BLO_read_data_address(reader, &ob->lightgroup);
BLO_read_data_address(reader, &ob->lightprobe_cache);
if (ob->lightprobe_cache) {
BKE_lightprobe_cache_blend_read(reader, ob->lightprobe_cache);
}
}
/* XXX deprecated - old animation system */

View File

@ -41,6 +41,9 @@ void SCENE_OT_view_layer_remove_unused_lightgroups(struct wmOperatorType *ot);
void SCENE_OT_light_cache_bake(struct wmOperatorType *ot);
void SCENE_OT_light_cache_free(struct wmOperatorType *ot);
void OBJECT_OT_lightprobe_cache_bake(struct wmOperatorType *ot);
void OBJECT_OT_lightprobe_cache_free(struct wmOperatorType *ot);
void SCENE_OT_render_view_add(struct wmOperatorType *ot);
void SCENE_OT_render_view_remove(struct wmOperatorType *ot);

View File

@ -28,6 +28,9 @@ void ED_operatortypes_render()
WM_operatortype_append(OBJECT_OT_material_slot_move);
WM_operatortype_append(OBJECT_OT_material_slot_remove_unused);
WM_operatortype_append(OBJECT_OT_lightprobe_cache_bake);
WM_operatortype_append(OBJECT_OT_lightprobe_cache_free);
WM_operatortype_append(MATERIAL_OT_new);
WM_operatortype_append(TEXTURE_OT_new);
WM_operatortype_append(WORLD_OT_new);

View File

@ -37,6 +37,7 @@
#include "BKE_image.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lightprobe.h"
#include "BKE_linestyle.h"
#include "BKE_main.h"
#include "BKE_material.h"
@ -53,6 +54,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_query.h"
#ifdef WITH_FREESTYLE
# include "BKE_freestyle.h"
@ -1333,6 +1335,8 @@ enum {
LIGHTCACHE_SUBSET_ALL = 0,
LIGHTCACHE_SUBSET_DIRTY,
LIGHTCACHE_SUBSET_CUBE,
LIGHTCACHE_SUBSET_SELECTED,
LIGHTCACHE_SUBSET_ACTIVE,
};
static void light_cache_bake_tag_cache(Scene *scene, wmOperator *op)
@ -1349,6 +1353,9 @@ static void light_cache_bake_tag_cache(Scene *scene, wmOperator *op)
case LIGHTCACHE_SUBSET_DIRTY:
/* Leave tag untouched. */
break;
default:
BLI_assert_unreachable();
break;
}
}
}
@ -1505,6 +1512,169 @@ void SCENE_OT_light_cache_bake(wmOperatorType *ot)
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
}
/* NOTE: New version destined to replace the old lightcache bake operator. */
static void lightprobe_cache_bake_start(bContext *C, wmOperator *op)
{
ViewLayer *view_layer = CTX_data_view_layer(C);
Scene *scene = CTX_data_scene(C);
auto is_irradiance_volume = [](Object *ob) -> bool {
return ob->type == OB_LIGHTPROBE &&
static_cast<LightProbe *>(ob->data)->type == LIGHTPROBE_TYPE_GRID;
};
auto irradiance_volume_setup = [](Object *ob) {
BKE_lightprobe_cache_free(ob);
BKE_lightprobe_cache_create(ob);
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
};
int subset = RNA_enum_get(op->ptr, "subset");
switch (subset) {
case LIGHTCACHE_SUBSET_ALL: {
FOREACH_OBJECT_BEGIN (scene, view_layer, ob) {
if (is_irradiance_volume(ob)) {
irradiance_volume_setup(ob);
}
}
FOREACH_OBJECT_END;
break;
}
case LIGHTCACHE_SUBSET_DIRTY: {
FOREACH_OBJECT_BEGIN (scene, view_layer, ob) {
if (is_irradiance_volume(ob) && ob->lightprobe_cache && ob->lightprobe_cache->dirty) {
irradiance_volume_setup(ob);
}
}
FOREACH_OBJECT_END;
break;
}
case LIGHTCACHE_SUBSET_SELECTED: {
uint objects_len = 0;
ObjectsInViewLayerParams parameters;
parameters.filter_fn = nullptr;
parameters.no_dup_data = true;
Object **objects = BKE_view_layer_array_selected_objects_params(
view_layer, nullptr, &objects_len, &parameters);
for (Object *ob : blender::MutableSpan<Object *>(objects, objects_len)) {
if (is_irradiance_volume(ob)) {
irradiance_volume_setup(ob);
}
}
MEM_freeN(objects);
break;
}
case LIGHTCACHE_SUBSET_ACTIVE: {
Object *active_ob = CTX_data_active_object(C);
if (is_irradiance_volume(active_ob)) {
irradiance_volume_setup(active_ob);
}
break;
}
default:
BLI_assert_unreachable();
break;
}
}
static int lightprobe_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
Scene *scene = CTX_data_scene(C);
lightprobe_cache_bake_start(C, op);
WM_event_add_modal_handler(C, op);
/* store actual owner of job, so modal operator could check for it,
* the reason of this is that active scene could change when rendering
* several layers from compositor #31800. */
op->customdata = scene;
WM_cursor_wait(false);
return OPERATOR_RUNNING_MODAL;
}
static int lightprobe_cache_bake_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Scene *scene = (Scene *)op->customdata;
/* No running bake, remove handler and pass through. */
if (0 == WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_LIGHT_BAKE)) {
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
}
/* Running bake. */
switch (event->type) {
case EVT_ESCKEY:
return OPERATOR_RUNNING_MODAL;
}
return OPERATOR_PASS_THROUGH;
}
static void lightprobe_cache_bake_cancel(bContext *C, wmOperator *op)
{
wmWindowManager *wm = CTX_wm_manager(C);
Scene *scene = (Scene *)op->customdata;
/* Kill on cancel, because job is using op->reports. */
WM_jobs_kill_type(wm, scene, WM_JOB_TYPE_LIGHT_BAKE);
}
/* Executes blocking bake. */
static int lightprobe_cache_bake_exec(bContext *C, wmOperator *op)
{
lightprobe_cache_bake_start(C, op);
return OPERATOR_FINISHED;
}
void OBJECT_OT_lightprobe_cache_bake(wmOperatorType *ot)
{
static const EnumPropertyItem light_cache_subset_items[] = {
{LIGHTCACHE_SUBSET_ALL, "ALL", 0, "All Light Probes", "Bake all light probes"},
{LIGHTCACHE_SUBSET_DIRTY,
"DIRTY",
0,
"Dirty Only",
"Only bake light probes that are marked as dirty"},
{LIGHTCACHE_SUBSET_SELECTED,
"SELECTED",
0,
"Selected Only",
"Only bake selected light probes"},
{LIGHTCACHE_SUBSET_ACTIVE, "ACTIVE", 0, "Active Only", "Only bake the active light probe"},
{0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
ot->name = "Bake Light Cache";
ot->idname = "OBJECT_OT_lightprobe_cache_bake";
ot->description = "Bake the active view layer lighting";
/* api callbacks */
ot->invoke = lightprobe_cache_bake_invoke;
ot->modal = lightprobe_cache_bake_modal;
ot->cancel = lightprobe_cache_bake_cancel;
ot->exec = lightprobe_cache_bake_exec;
ot->prop = RNA_def_int(ot->srna,
"delay",
0,
0,
2000,
"Delay",
"Delay in millisecond before baking starts",
0,
2000);
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
ot->prop = RNA_def_enum(
ot->srna, "subset", light_cache_subset_items, 0, "Subset", "Subset of probes to update");
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
}
/** \} */
/* -------------------------------------------------------------------- */
@ -1558,6 +1728,49 @@ void SCENE_OT_light_cache_free(wmOperatorType *ot)
ot->poll = light_cache_free_poll;
}
/* NOTE: New version destined to replace the old lightcache bake operator. */
static bool lightprobe_cache_free_poll(bContext *C)
{
Object *object = CTX_data_active_object(C);
return object && object->lightprobe_cache != nullptr;
}
static int lightprobe_cache_free_exec(bContext *C, wmOperator * /*op*/)
{
Scene *scene = CTX_data_scene(C);
Object *object = CTX_data_active_object(C);
/* Kill potential bake job first (see #57011). */
wmWindowManager *wm = CTX_wm_manager(C);
WM_jobs_kill_type(wm, scene, WM_JOB_TYPE_LIGHT_BAKE);
if (object->lightprobe_cache == nullptr) {
return OPERATOR_CANCELLED;
}
BKE_lightprobe_cache_free(object);
DEG_id_tag_update(&object->id, ID_RECALC_COPY_ON_WRITE);
WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, scene);
return OPERATOR_FINISHED;
}
void OBJECT_OT_lightprobe_cache_free(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Delete Light Cache";
ot->idname = "OBJECT_OT_lightprobe_cache_free";
ot->description = "Delete cached indirect lighting";
/* api callbacks */
ot->exec = lightprobe_cache_free_exec;
ot->poll = lightprobe_cache_free_poll;
}
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -61,9 +61,6 @@ typedef struct LightProbe {
struct Image *image;
/** Object visibility group, inclusive or exclusive. */
struct Collection *visibility_grp;
/* Runtime display data */
float distfalloff, distgridinf;
} LightProbe;
/* Probe->type */
@ -223,6 +220,129 @@ enum {
LIGHTCACHETEX_UINT = (1 << 2),
};
/* -------------------------------------------------------------------- */
/** \name Irradiance grid data storage
*
* Each spherical harmonic band is stored separately. This allow loading only a specific band.
* The layout of each array is set by the #LightProbeGridType.
* Any unavailable data is be set to nullptr.
* \{ */
/**
* Irradiance data (RGB) stored along visibility (A).
* This is the format used during baking and is used for visualizing the baking process.
*/
typedef struct LightProbeBakingData {
float (*L0)[4];
float (*L1_a)[4];
float (*L1_b)[4];
float (*L1_c)[4];
} LightProbeBakingData;
/**
* Irradiance stored as RGB triple using scene referred color space.
*/
typedef struct LightProbeIrradianceData {
float (*L0)[3];
float (*L1_a)[3];
float (*L1_b)[3];
float (*L1_c)[3];
} LightProbeIrradianceData;
/**
* Normalized visibility of distant light. Used for compositing grids together.
*/
typedef struct LightProbeVisibilityData {
uint8_t *L0;
uint8_t *L1_a;
uint8_t *L1_b;
uint8_t *L1_c;
} LightProbeVisibilityData;
/**
* Used to avoid light leaks. Validate visibility between each grid sample.
*/
typedef struct LightProbeConnectivityData {
/** Stores a bitmask of valid connections within a cell. */
uint8_t *bitmask;
} LightProbeConnectivityData;
/**
* Defines one block of data inside the grid cache data arrays.
* The block size if the same for all the blocks.
*/
typedef struct LightProbeBlockData {
/* Offset inside the level-of-detail this block starts. */
int offset[3];
/* Level-of-detail this block is from. */
int level;
} LightProbeBlockData;
/** \} */
/* -------------------------------------------------------------------- */
/** \name LightProbeGridCacheFrame
*
* \{ */
/**
* A frame worth of baked lighting data.
*/
typedef struct LightProbeGridCacheFrame {
/** Number of samples in the highest level of detail. */
int size[3];
/** Spatial layout type of the data stored inside the data arrays. */
int data_layout;
/** Sparse or adaptive layout only: number of blocks inside data arrays. */
int block_len;
/** Sparse or adaptive layout only: size of a block in samples. All 3 dimensions are equal. */
int block_size;
/** Sparse or adaptive layout only: specify the blocks positions. */
LightProbeBlockData *block_infos;
/** Baked data. */
LightProbeBakingData baking;
LightProbeIrradianceData irradiance;
LightProbeVisibilityData visibility;
LightProbeConnectivityData connectivity;
} LightProbeGridCacheFrame;
/** #LightProbeGridCacheFrame.data_layout (int) */
enum {
/** Simple uniform grid. Raw output from GPU. Used during the baking process. */
LIGHTPROBE_CACHE_UNIFORM_GRID = 0,
/** Fills the space with different level of resolution. More efficient storage. */
LIGHTPROBE_CACHE_ADAPTIVE_RESOLUTION = 1,
};
/**
* Per object container of baked data.
* Should be called #LightProbeCache but name is already taken.
*/
typedef struct LightProbeObjectCache {
/** Allow correct versioning / different types of data for the same layout. */
int cache_type;
/** True if this cache references the original object's cache. */
char shared;
/** True if the cache has been tagged for automatic baking. */
char dirty;
char _pad0[2];
struct LightProbeGridCacheFrame *grid_static_cache;
} LightProbeObjectCache;
/** #LightProbeObjectCache.type (int) */
enum {
/** Light cache was just created and is not yet baked. Keep as 0 for default value. */
LIGHTPROBE_CACHE_TYPE_NONE = 0,
/** Light cache is baked for one specific frame and capture all indirect lighting. */
LIGHTPROBE_CACHE_TYPE_STATIC = 1,
};
/** \} */
#ifdef __cplusplus
}
#endif

View File

@ -34,6 +34,7 @@ struct FluidsimSettings;
struct GeometrySet;
struct Ipo;
struct LightgroupMembership;
struct LightProbeGridCacheFrame;
struct Material;
struct Mesh;
struct Object;
@ -448,6 +449,11 @@ typedef struct Object {
/** Lightgroup membership information. */
struct LightgroupMembership *lightgroup;
/** Irradiance caches baked for this object (light-probes only). */
struct LightProbeObjectCache *lightprobe_cache;
void *_pad9;
/** Runtime evaluation data (keep last). */
Object_Runtime runtime;
} Object;