diff options
Diffstat (limited to 'source')
36 files changed, 2681 insertions, 1341 deletions
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 1b309d76f61..2b2fc2fc767 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -102,6 +102,9 @@ #include "DEG_depsgraph_query.h" #include "RE_engine.h" +#include "RE_engine.h" + +#include "engines/eevee/eevee_lightcache.h" #include "PIL_time.h" @@ -316,6 +319,9 @@ void BKE_scene_copy_data(Main *bmain, Scene *sce_dst, const Scene *sce_src, cons else { sce_dst->preview = NULL; } + + sce_dst->eevee.light_cache = NULL; + /* TODO Copy the cache. */ } Scene *BKE_scene_copy(Main *bmain, Scene *sce, int type) @@ -511,6 +517,11 @@ void BKE_scene_free_ex(Scene *sce, const bool do_id_user) sce->master_collection = NULL; } + if (sce->eevee.light_cache) { + EEVEE_lightcache_free(sce->eevee.light_cache); + sce->eevee.light_cache = NULL; + } + /* These are freed on doversion. */ BLI_assert(sce->layer_properties == NULL); } @@ -814,6 +825,8 @@ void BKE_scene_init(Scene *sce) sce->eevee.gi_diffuse_bounces = 3; sce->eevee.gi_cubemap_resolution = 512; sce->eevee.gi_visibility_resolution = 32; + sce->eevee.gi_cubemap_draw_size = 0.3f; + sce->eevee.gi_irradiance_draw_size = 0.1f; sce->eevee.taa_samples = 16; sce->eevee.taa_render_samples = 64; @@ -856,6 +869,8 @@ void BKE_scene_init(Scene *sce) sce->eevee.shadow_cube_size = 512; sce->eevee.shadow_cascade_size = 1024; + sce->eevee.light_cache = NULL; + sce->eevee.flag = SCE_EEVEE_VOLUMETRIC_LIGHTS | SCE_EEVEE_VOLUMETRIC_COLORED | diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index b340aa28324..72fa155553d 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -30,6 +30,7 @@ set(INC ../blenlib ../blentranslation ../depsgraph + ../draw ../imbuf ../makesdna ../makesrna diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index 7488d62bb3c..6fd77c34977 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -393,6 +393,9 @@ BlendFileData *BLO_read_from_memfile( /* makes lookup of existing images in old main */ blo_make_image_pointer_map(fd, oldmain); + /* makes lookup of existing light caches in old main */ + blo_make_scene_pointer_map(fd, oldmain); + /* makes lookup of existing video clips in old main */ blo_make_movieclip_pointer_map(fd, oldmain); @@ -403,6 +406,9 @@ BlendFileData *BLO_read_from_memfile( bfd = blo_read_file_internal(fd, filename); + /* ensures relinked light caches are not freed */ + blo_end_scene_pointer_map(fd, oldmain); + /* ensures relinked images are not freed */ blo_end_image_pointer_map(fd, oldmain); diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 3eb0626307b..db3c1894e8c 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -158,6 +158,8 @@ #include "BKE_colortools.h" #include "BKE_workspace.h" +#include "DRW_engine.h" + #include "DEG_depsgraph.h" #include "NOD_common.h" @@ -1339,6 +1341,8 @@ void blo_freefiledata(FileData *fd) oldnewmap_free(fd->imamap); if (fd->movieclipmap) oldnewmap_free(fd->movieclipmap); + if (fd->scenemap) + oldnewmap_free(fd->scenemap); if (fd->soundmap) oldnewmap_free(fd->soundmap); if (fd->packedmap) @@ -1526,6 +1530,13 @@ static void *newimaadr(FileData *fd, const void *adr) /* used to restore im return NULL; } +static void *newsceadr(FileData *fd, const void *adr) /* used to restore scene data after undo */ +{ + if (fd->scenemap && adr) + return oldnewmap_lookup_and_inc(fd->scenemap, adr, true); + return NULL; +} + static void *newmclipadr(FileData *fd, const void *adr) /* used to restore movie clip data after undo */ { if (fd->movieclipmap && adr) @@ -1631,6 +1642,37 @@ void blo_clear_proxy_pointers_from_lib(Main *oldmain) } } +void blo_make_scene_pointer_map(FileData *fd, Main *oldmain) +{ + Scene *sce = oldmain->scene.first; + + fd->scenemap = oldnewmap_new(); + + for (; sce; sce = sce->id.next) { + if (sce->eevee.light_cache) { + struct LightCache *light_cache = sce->eevee.light_cache; + oldnewmap_insert(fd->scenemap, light_cache, light_cache, 0); + } + } +} + +void blo_end_scene_pointer_map(FileData *fd, Main *oldmain) +{ + OldNew *entry = fd->scenemap->entries; + Scene *sce = oldmain->scene.first; + int i; + + /* used entries were restored, so we put them to zero */ + for (i = 0; i < fd->scenemap->nentries; i++, entry++) { + if (entry->nr > 0) + entry->newp = NULL; + } + + for (; sce; sce = sce->id.next) { + sce->eevee.light_cache = newsceadr(fd, sce->eevee.light_cache); + } +} + void blo_make_image_pointer_map(FileData *fd, Main *oldmain) { Image *ima = oldmain->image.first; @@ -2301,6 +2343,11 @@ static void direct_link_id(FileData *fd, ID *id) id->override_static = newdataadr(fd, id->override_static); link_list_ex(fd, &id->override_static->properties, direct_link_id_override_property_cb); } + + DrawDataList *drawdata = DRW_drawdatalist_from_id(id); + if (drawdata) { + BLI_listbase_clear((ListBase *)drawdata); + } } /* ************ READ CurveMapping *************** */ @@ -5492,7 +5539,6 @@ static void direct_link_object(FileData *fd, Object *ob) ob->derivedFinal = NULL; BKE_object_runtime_reset(ob); BLI_listbase_clear(&ob->gpulamp); - BLI_listbase_clear(&ob->drawdata); link_list(fd, &ob->pc_ids); /* Runtime curve data */ @@ -5739,6 +5785,41 @@ static void lib_link_sequence_modifiers(FileData *fd, Scene *scene, ListBase *lb } } +static void direct_link_lightcache_texture(FileData *fd, LightCacheTexture *lctex) +{ + lctex->tex = NULL; + + if (lctex->data) { + lctex->data = newdataadr(fd, lctex->data); + if (fd->flags & FD_FLAGS_SWITCH_ENDIAN) { + int data_size = lctex->components * lctex->tex_size[0] * lctex->tex_size[1] * lctex->tex_size[2]; + + if (lctex->data_type == LIGHTCACHETEX_FLOAT) { + BLI_endian_switch_float_array((float *)lctex->data, data_size * sizeof(float)); + } + else if (lctex->data_type == LIGHTCACHETEX_UINT) { + BLI_endian_switch_uint32_array((unsigned int *)lctex->data, data_size * sizeof(unsigned int)); + } + } + } +} + +static void direct_link_lightcache(FileData *fd, LightCache *cache) +{ + direct_link_lightcache_texture(fd, &cache->cube_tx); + direct_link_lightcache_texture(fd, &cache->grid_tx); + + if (cache->cube_mips) { + cache->cube_mips = newdataadr(fd, cache->cube_mips); + for (int i = 0; i < cache->mips_len; ++i) { + direct_link_lightcache_texture(fd, &cache->cube_mips[i]); + } + } + + cache->cube_data = newdataadr(fd, cache->cube_data); + cache->grid_data = newdataadr(fd, cache->grid_data); +} + /* check for cyclic set-scene, * libs can cause this case which is normally prevented, see (T#####) */ #define USE_SETSCENE_CHECK @@ -6299,6 +6380,19 @@ static void direct_link_scene(FileData *fd, Scene *sce) direct_link_view_layer(fd, view_layer); } + if (fd->memfile) { + /* If it's undo try to recover the cache. */ + if (fd->scenemap) sce->eevee.light_cache = newsceadr(fd, sce->eevee.light_cache); + else sce->eevee.light_cache = NULL; + } + else { + /* else read the cache from file. */ + if (sce->eevee.light_cache) { + sce->eevee.light_cache = newdataadr(fd, sce->eevee.light_cache); + direct_link_lightcache(fd, sce->eevee.light_cache); + } + } + sce->layer_properties = newdataadr(fd, sce->layer_properties); IDP_DirectLinkGroup_OrFree(&sce->layer_properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd); } diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index 9c699db5583..10f0c7a2942 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -90,6 +90,7 @@ typedef struct FileData { struct OldNewMap *libmap; struct OldNewMap *imamap; struct OldNewMap *movieclipmap; + struct OldNewMap *scenemap; struct OldNewMap *soundmap; struct OldNewMap *packedmap; @@ -140,6 +141,8 @@ FileData *blo_openblendermemfile(struct MemFile *memfile, struct ReportList *rep void blo_clear_proxy_pointers_from_lib(Main *oldmain); void blo_make_image_pointer_map(FileData *fd, Main *oldmain); void blo_end_image_pointer_map(FileData *fd, Main *oldmain); +void blo_make_scene_pointer_map(FileData *fd, Main *oldmain); +void blo_end_scene_pointer_map(FileData *fd, Main *oldmain); void blo_make_movieclip_pointer_map(FileData *fd, Main *oldmain); void blo_end_movieclip_pointer_map(FileData *fd, Main *oldmain); void blo_make_sound_pointer_map(FileData *fd, Main *oldmain); diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 5e27d72caa2..c9ab174d5e2 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -1541,6 +1541,13 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "gi_cubemap_draw_size")) { + for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) { + scene->eevee.gi_irradiance_draw_size = 0.1f; + scene->eevee.gi_cubemap_draw_size = 0.3f; + } + } + for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) { if (scene->toolsettings->manipulator_flag == 0) { scene->toolsettings->manipulator_flag = SCE_MANIP_TRANSLATE | SCE_MANIP_ROTATE | SCE_MANIP_SCALE; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 0a2d09d0c86..049b2e8617c 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2479,6 +2479,36 @@ static void write_view_layer(WriteData *wd, ViewLayer *view_layer) write_layer_collections(wd, &view_layer->layer_collections); } +static void write_lightcache_texture(WriteData *wd, LightCacheTexture *tex) +{ + if (tex->data) { + size_t data_size = tex->components * tex->tex_size[0] * tex->tex_size[1] * tex->tex_size[2]; + if (tex->data_type == LIGHTCACHETEX_FLOAT) { + data_size *= sizeof(float); + } + else if (tex->data_type == LIGHTCACHETEX_UINT) { + data_size *= sizeof(unsigned int); + } + writedata(wd, DATA, data_size, tex->data); + } +} + +static void write_lightcache(WriteData *wd, LightCache *cache) +{ + write_lightcache_texture(wd, &cache->grid_tx); + write_lightcache_texture(wd, &cache->cube_tx); + + if (cache->cube_mips) { + writestruct(wd, DATA, LightCacheTexture, cache->mips_len, cache->cube_mips); + for (int i = 0; i < cache->mips_len; ++i) { + write_lightcache_texture(wd, &cache->cube_mips[i]); + } + } + + writestruct(wd, DATA, LightGridCache, cache->grid_len, cache->grid_data); + writestruct(wd, DATA, LightProbeCache, cache->cube_len, cache->cube_data); +} + static void write_scene(WriteData *wd, Scene *sce) { /* write LibData */ @@ -2679,6 +2709,12 @@ static void write_scene(WriteData *wd, Scene *sce) write_collection_nolib(wd, sce->master_collection); } + /* Eevee Lightcache */ + if (sce->eevee.light_cache && !wd->use_memfile) { + writestruct(wd, DATA, LightCache, 1, sce->eevee.light_cache); + write_lightcache(wd, sce->eevee.light_cache); + } + /* Freed on doversion. */ BLI_assert(sce->layer_properties == NULL); } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index 794cc22f4e3..20aec0ba5ed 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -555,6 +555,13 @@ void update_special_pointers(const Depsgraph *depsgraph, update_particle_system_orig_pointers(object_orig, object_cow); break; } + case ID_SCE: + { + Scene *scene_cow = (Scene *)id_cow; + const Scene *scene_orig = (const Scene *)id_orig; + scene_cow->eevee.light_cache = scene_orig->eevee.light_cache; + break; + } default: break; } @@ -926,6 +933,12 @@ void discard_mesh_edit_mode_pointers(ID *id_cow) mesh_cow->edit_btmesh = NULL; } +void discard_scene_pointers(ID *id_cow) +{ + Scene *scene_cow = (Scene *)id_cow; + scene_cow->eevee.light_cache = NULL; +} + /* NULL-ify all edit mode pointers which points to data from * original object. */ @@ -948,6 +961,11 @@ void discard_edit_mode_pointers(ID *id_cow) case ID_LT: discard_lattice_edit_mode_pointers(id_cow); break; + case ID_SCE: + /* Not really edit mode but still needs to run before + * BKE_libblock_free_datablock() */ + discard_scene_pointers(id_cow); + break; default: break; } diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index d7b7129114e..2183bc26755 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -96,6 +96,7 @@ set(SRC engines/eevee/eevee_depth_of_field.c engines/eevee/eevee_effects.c engines/eevee/eevee_engine.c + engines/eevee/eevee_lightcache.c engines/eevee/eevee_lightprobes.c engines/eevee/eevee_lights.c engines/eevee/eevee_lookdev.c @@ -137,6 +138,7 @@ set(SRC modes/edit_mesh_mode_intern.h engines/basic/basic_engine.h engines/eevee/eevee_engine.h + engines/eevee/eevee_lightcache.h engines/eevee/eevee_lut.h engines/eevee/eevee_private.h engines/external/external_engine.h diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index bf039871d52..96e784b524c 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -28,6 +28,7 @@ #include "DRW_render.h" #include "eevee_private.h" +#include "eevee_lightcache.h" static void eevee_view_layer_data_free(void *storage) { @@ -53,6 +54,11 @@ static void eevee_view_layer_data_free(void *storage) MEM_SAFE_FREE(sldata->shcasters_buffers[1].shadow_casters); MEM_SAFE_FREE(sldata->shcasters_buffers[1].flags); + if (sldata->fallback_lightcache) { + EEVEE_lightcache_free(sldata->fallback_lightcache); + sldata->fallback_lightcache = NULL; + } + /* Probes */ MEM_SAFE_FREE(sldata->probes); DRW_UBO_FREE_SAFE(sldata->probe_ubo); @@ -60,15 +66,6 @@ static void eevee_view_layer_data_free(void *storage) DRW_UBO_FREE_SAFE(sldata->planar_ubo); DRW_UBO_FREE_SAFE(sldata->common_ubo); DRW_UBO_FREE_SAFE(sldata->clip_ubo); - GPU_FRAMEBUFFER_FREE_SAFE(sldata->probe_filter_fb); - for (int i = 0; i < 6; ++i) { - GPU_FRAMEBUFFER_FREE_SAFE(sldata->probe_face_fb[i]); - } - DRW_TEXTURE_FREE_SAFE(sldata->probe_rt); - DRW_TEXTURE_FREE_SAFE(sldata->probe_depth_rt); - DRW_TEXTURE_FREE_SAFE(sldata->probe_pool); - DRW_TEXTURE_FREE_SAFE(sldata->irradiance_pool); - DRW_TEXTURE_FREE_SAFE(sldata->irradiance_rt); } EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void) @@ -77,6 +74,18 @@ EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void) &draw_engine_eevee_type); } +EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer) +{ + EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure_ex( + view_layer, &draw_engine_eevee_type, &eevee_view_layer_data_free); + + if (*sldata == NULL) { + *sldata = MEM_callocN(sizeof(**sldata), "EEVEE_ViewLayerData"); + } + + return *sldata; +} + EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void) { EEVEE_ViewLayerData **sldata = (EEVEE_ViewLayerData **)DRW_view_layer_engine_data_ensure( diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 70af3b4067c..adbe165354a 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -123,7 +123,7 @@ static void eevee_cache_init(void *vedata) EEVEE_volumes_cache_init(sldata, vedata); } -static void eevee_cache_populate(void *vedata, Object *ob) +void EEVEE_cache_populate(void *vedata, Object *ob) { EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); @@ -146,7 +146,7 @@ static void eevee_cache_populate(void *vedata, Object *ob) /* TODO: Special case for dupli objects because we cannot save the object pointer. */ } else { - EEVEE_lightprobes_cache_add(sldata, ob); + EEVEE_lightprobes_cache_add(sldata, vedata, ob); } } else if (ob->type == OB_LAMP) { @@ -282,7 +282,9 @@ static void eevee_draw_background(void *vedata) EEVEE_subsurface_compute(sldata, vedata); EEVEE_reflection_compute(sldata, vedata); EEVEE_occlusion_draw_debug(sldata, vedata); - DRW_draw_pass(psl->probe_display); + if (psl->probe_display) { + DRW_draw_pass(psl->probe_display); + } EEVEE_refraction_compute(sldata, vedata); /* Opaque refraction */ @@ -367,7 +369,7 @@ static void eevee_id_object_update(void *UNUSED(vedata), Object *object) { EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(object); if (ped != NULL && ped->dd.recalc != 0) { - ped->need_full_update = true; + ped->need_update = (ped->dd.recalc & (ID_RECALC_TRANSFORM | ID_RECALC_COPY_ON_WRITE)) != 0; ped->dd.recalc = 0; } EEVEE_LampEngineData *led = EEVEE_lamp_data_get(object); @@ -385,10 +387,14 @@ static void eevee_id_object_update(void *UNUSED(vedata), Object *object) static void eevee_id_world_update(void *vedata, World *wo) { EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; + LightCache *lcache = stl->g_data->light_cache; EEVEE_WorldEngineData *wedata = EEVEE_world_data_ensure(wo); if (wedata != NULL && wedata->dd.recalc != 0) { + if ((lcache->flag & (LIGHTCACHE_BAKED | LIGHTCACHE_BAKING)) == 0) { + lcache->flag |= LIGHTCACHE_UPDATE_WORLD; + } wedata->dd.recalc = 0; } } @@ -446,7 +452,7 @@ DrawEngineType draw_engine_eevee_type = { &eevee_engine_init, &eevee_engine_free, &eevee_cache_init, - &eevee_cache_populate, + &EEVEE_cache_populate, &eevee_cache_finish, &eevee_draw_background, NULL, /* Everything is drawn in the background pass (see comment on function) */ diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c new file mode 100644 index 00000000000..bf3ee34a03e --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -0,0 +1,1141 @@ +/* + * Copyright 2016, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Blender Institute + * + */ + +/** \file eevee_lightcache.c + * \ingroup draw_engine + * + * Eevee's indirect lighting cache. + */ + +#include "DRW_render.h" + +#include "BKE_global.h" +#include "BKE_blender.h" + +#include "BLI_threads.h" + +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "BKE_object.h" + +#include "DNA_lightprobe_types.h" +#include "DNA_group_types.h" + +#include "PIL_time.h" + +#include "eevee_lightcache.h" +#include "eevee_private.h" + +#include "../../../intern/gawain/gawain/gwn_context.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_window.h" + +/* Rounded to nearest PowerOfTwo */ +#if defined(IRRADIANCE_SH_L2) +#define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */ +#define IRRADIANCE_SAMPLE_SIZE_Y 4 /* 3 in reality */ +#elif defined(IRRADIANCE_CUBEMAP) +#define IRRADIANCE_SAMPLE_SIZE_X 8 +#define IRRADIANCE_SAMPLE_SIZE_Y 8 +#elif defined(IRRADIANCE_HL2) +#define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */ +#define IRRADIANCE_SAMPLE_SIZE_Y 2 +#endif + +#ifdef IRRADIANCE_SH_L2 +/* we need a signed format for Spherical Harmonics */ +# define IRRADIANCE_FORMAT GPU_RGBA16F +#else +# define IRRADIANCE_FORMAT GPU_RGBA8 +#endif + +#define IRRADIANCE_MAX_POOL_LAYER 256 /* OpenGL 3.3 core requirement, can be extended but it's already very big */ +#define IRRADIANCE_MAX_POOL_SIZE 1024 +#define MAX_IRRADIANCE_SAMPLES \ + (IRRADIANCE_MAX_POOL_SIZE / IRRADIANCE_SAMPLE_SIZE_X) * \ + (IRRADIANCE_MAX_POOL_SIZE / IRRADIANCE_SAMPLE_SIZE_Y) + +/* TODO should be replace by a more elegant alternative. */ +extern void DRW_opengl_context_enable(void); +extern void DRW_opengl_context_disable(void); + +extern void DRW_opengl_render_context_enable(void *re_gl_context); +extern void DRW_opengl_render_context_disable(void *re_gl_context); +extern void DRW_gawain_render_context_enable(void *re_gwn_context); +extern void DRW_gawain_render_context_disable(void *re_gwn_context); + +typedef struct EEVEE_LightBake { + Depsgraph *depsgraph; + ViewLayer *view_layer; + ViewLayer *view_layer_input; + LightCache *lcache; + Scene *scene; + struct Main *bmain; + + LightProbe **probe; /* Current probe being rendered. */ + GPUTexture *rt_color; /* Target cube color texture. */ + GPUTexture *rt_depth; /* Target cube depth texture. */ + GPUFrameBuffer *rt_fb[6]; /* Target cube framebuffers. */ + GPUFrameBuffer *store_fb; /* Storage framebuffer. */ + int rt_res; /* Cube render target resolution. */ + + /* Shared */ + int layer; /* Target layer to store the data to. */ + float samples_ct, invsamples_ct; /* Sample count for the convolution. */ + float lod_factor; /* Sampling bias during convolution step. */ + float lod_max; /* Max cubemap LOD to sample when convolving. */ + int cube_len, grid_len; /* Number of probes to render + world probe. */ + + /* Irradiance grid */ + EEVEE_LightGrid *grid; /* Current probe being rendered (UBO data). */ + int irr_cube_res; /* Target cubemap at MIP 0. */ + int irr_size[3]; /* Size of the irradiance texture. */ + int total_irr_samples; /* Total for all grids */ + int grid_sample; /* Nth sample of the current grid being rendered. */ + int grid_sample_len; /* Total number of samples for the current grid. */ + int grid_curr; /* Nth grid in the cache being rendered. */ + int bounce_curr, bounce_len; /* The current light bounce being evaluated. */ + float vis_range, vis_blur; /* Sample Visibility compression and bluring. */ + float vis_res; /* Resolution of the Visibility shadowmap. */ + GPUTexture *grid_prev; /* Result of previous light bounce. */ + LightProbe **grid_prb; /* Pointer to the id.data of the probe object. */ + + /* Reflection probe */ + EEVEE_LightProbe *cube; /* Current probe being rendered (UBO data). */ + int ref_cube_res; /* Target cubemap at MIP 0. */ + int cube_offset; /* Index of the current cube. */ + float probemat[6][4][4]; /* ViewProjection matrix for each cube face. */ + float texel_size, padding_size; /* Texel and padding size for the final octahedral map. */ + float roughness; /* Roughness level of the current mipmap. */ + LightProbe **cube_prb; /* Pointer to the id.data of the probe object. */ + + /* Dummy Textures */ + struct GPUTexture *dummy_color, *dummy_depth; + struct GPUTexture *dummy_layer_color; + + int total, done; /* to compute progress */ + short *stop, *do_update; + float *progress; + + bool resource_only; /* For only handling the resources. */ + bool own_resources; + bool own_light_cache; /* If the lightcache was created for baking, it's first owned by the baker. */ + int delay; /* ms. delay the start of the baking to not slowdown interactions (TODO remove) */ + + void *gl_context, *gwn_context; /* If running in parallel (in a separate thread), use this context. */ +} EEVEE_LightBake; + +/* -------------------------------------------------------------------- */ + +/** \name Light Cache + * \{ */ + +/* Return memory footprint in bytes. */ +static unsigned int eevee_lightcache_memsize_get(LightCache *lcache) +{ + unsigned int size = 0; + if (lcache->grid_tx.data) { + size += MEM_allocN_len(lcache->grid_tx.data); + } + if (lcache->cube_tx.data) { + size += MEM_allocN_len(lcache->cube_tx.data); + for (int mip = 0; mip < lcache->mips_len; ++mip) { + size += MEM_allocN_len(lcache->cube_mips[mip].data); + } + } + return size; +} + +static int eevee_lightcache_irradiance_sample_count(LightCache *lcache) +{ + int total_irr_samples = 0; + + for (int i = 1; i < lcache->grid_len; ++i) { + EEVEE_LightGrid *egrid = lcache->grid_data + i; + total_irr_samples += egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2]; + } + return total_irr_samples; +} + +void EEVEE_lightcache_info_update(SceneEEVEE *eevee) +{ + LightCache *lcache = eevee->light_cache; + + if (lcache != NULL) { + char formatted_mem[15]; + BLI_str_format_byte_unit(formatted_mem, eevee_lightcache_memsize_get(lcache), true); + + int irr_samples = eevee_lightcache_irradiance_sample_count(lcache); + + BLI_snprintf(eevee->light_cache_info, sizeof(eevee->light_cache_info), IFACE_("%d Ref. Cubemaps, %d Irr. Samples (%s in memory)"), lcache->cube_len - 1, irr_samples, formatted_mem); + } + else { + BLI_strncpy(eevee->light_cache_info, IFACE_("No light cache in this scene."), sizeof(eevee->light_cache_info)); + } +} + +static void irradiance_pool_size_get(int visibility_size, int total_samples, int r_size[3]) +{ + /* Compute how many irradiance samples we can store per visibility sample. */ + int irr_per_vis = (visibility_size / IRRADIANCE_SAMPLE_SIZE_X) * + (visibility_size / IRRADIANCE_SAMPLE_SIZE_Y); + + /* The irradiance itself take one layer, hence the +1 */ + int layer_ct = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); + + int texel_ct = (int)ceilf((float)total_samples / (float)(layer_ct - 1)); + r_size[0] = visibility_size * max_ii(1, min_ii(texel_ct, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + r_size[1] = visibility_size * max_ii(1, (texel_ct / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); + r_size[2] = layer_ct; +} + +static bool EEVEE_lightcache_validate( + const LightCache *light_cache, + const int cube_len, + const int cube_res, + const int grid_len, + const int irr_size[3]) +{ + if (light_cache) { + /* See if we need the same amount of texture space. */ + if ((irr_size[0] == light_cache->grid_tx.tex_size[0]) && + (irr_size[1] == light_cache->grid_tx.tex_size[1]) && + (irr_size[2] == light_cache->grid_tx.tex_size[2]) && + (grid_len != light_cache->grid_len)) + { + int mip_len = (int)(floorf(log2f(cube_res)) - MIN_CUBE_LOD_LEVEL); + if ((cube_res == light_cache->cube_tx.tex_size[0]) && + (cube_len == light_cache->cube_tx.tex_size[2]) && + (mip_len == light_cache->mips_len)) + { + return true; + } + } + } + return false; +} + +LightCache *EEVEE_lightcache_create( + const int grid_len, + const int cube_len, + const int cube_size, + const int vis_size, + const int irr_size[3]) +{ + LightCache *light_cache = MEM_callocN(sizeof(LightCache), "LightCache"); + + light_cache->cube_data = MEM_callocN(sizeof(EEVEE_LightProbe) * cube_len, "EEVEE_LightProbe"); + light_cache->grid_data = MEM_callocN(sizeof(EEVEE_LightGrid) * grid_len, "EEVEE_LightGrid"); + + light_cache->grid_tx.tex = DRW_texture_create_2D_array(irr_size[0], irr_size[1], irr_size[2], IRRADIANCE_FORMAT, DRW_TEX_FILTER, NULL); + light_cache->grid_tx.tex_size[0] = irr_size[0]; + light_cache->grid_tx.tex_size[1] = irr_size[1]; + light_cache->grid_tx.tex_size[2] = irr_size[2]; + + light_cache->cube_tx.tex = DRW_texture_create_2D_array(cube_size, cube_size, cube_len, GPU_R11F_G11F_B10F, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); + light_cache->cube_tx.tex_size[0] = cube_size; + light_cache->cube_tx.tex_size[1] = cube_size; + light_cache->cube_tx.tex_size[2] = cube_len; + + light_cache->mips_len = (int)(floorf(log2f(cube_size)) - MIN_CUBE_LOD_LEVEL); + light_cache->vis_res = vis_size; + light_cache->ref_res = cube_size; + + light_cache->cube_mips = MEM_callocN(sizeof(LightCacheTexture) * light_cache->mips_len, "LightCacheTexture"); + + for (int mip = 0; mip < light_cache->mips_len; ++mip) { + GPU_texture_get_mipmap_size(light_cache->cube_tx.tex, mip + 1, light_cache->cube_mips[mip].tex_size); + } + + light_cache->flag = LIGHTCACHE_UPDATE_WORLD | LIGHTCACHE_UPDATE_CUBE | LIGHTCACHE_UPDATE_GRID; + + return light_cache; +} + +void EEVEE_lightcache_load(LightCache *lcache) +{ + if (lcache->grid_tx.tex == NULL && lcache->grid_tx.data) { + lcache->grid_tx.tex = GPU_texture_create_nD(lcache->grid_tx.tex_size[0], + lcache->grid_tx.tex_size[1], + lcache->grid_tx.tex_size[2], + 2, + lcache->grid_tx.data, + IRRADIANCE_FORMAT, + GPU_DATA_UNSIGNED_BYTE, + 0, + false, + NULL); + GPU_texture_bind(lcache->grid_tx.tex, 0); + GPU_texture_filter_mode(lcache->grid_tx.tex, true); + GPU_texture_unbind(lcache->grid_tx.tex); + } + + if (lcache->cube_tx.tex == NULL && lcache->cube_tx.data) { + lcache->cube_tx.tex = GPU_texture_create_nD(lcache->cube_tx.tex_size[0], + lcache->cube_tx.tex_size[1], + lcache->cube_tx.tex_size[2], + 2, + lcache->cube_tx.data, + GPU_R11F_G11F_B10F, + GPU_DATA_10_11_11_REV, + 0, + false, + NULL); + GPU_texture_bind(lcache->cube_tx.tex, 0); + GPU_texture_mipmap_mode(lcache->cube_tx.tex, true, true); + for (int mip = 0; mip < lcache->mips_len; ++mip) { + GPU_texture_add_mipmap(lcache->cube_tx.tex, GPU_DATA_10_11_11_REV, mip + 1, lcache->cube_mips[mip].data); + } + GPU_texture_unbind(lcache->cube_tx.tex); + } +} + +static void eevee_lightbake_readback_irradiance(LightCache *lcache) +{ + MEM_SAFE_FREE(lcache->grid_tx.data); + lcache->grid_tx.data = GPU_texture_read(lcache->grid_tx.tex, GPU_DATA_UNSIGNED_BYTE, 0); + lcache->grid_tx.data_type = LIGHTCACHETEX_BYTE; + lcache->grid_tx.components = 4; +} + +static void eevee_lightbake_readback_reflections(LightCache *lcache) +{ + MEM_SAFE_FREE(lcache->cube_tx.data); + lcache->cube_tx.data = GPU_texture_read(lcache->cube_tx.tex, GPU_DATA_10_11_11_REV, 0); + lcache->cube_tx.data_type = LIGHTCACHETEX_UINT; + lcache->cube_tx.components = 1; + + for (int mip = 0; mip < lcache->mips_len; ++mip) { + LightCacheTexture *cube_mip = lcache->cube_mips + mip; + MEM_SAFE_FREE(cube_mip->data); + GPU_texture_get_mipmap_size(lcache->cube_tx.tex, mip + 1, cube_mip->tex_size); + + cube_mip->data = GPU_texture_read(lcache->cube_tx.tex, GPU_DATA_10_11_11_REV, mip + 1); + cube_mip->data_type = LIGHTCACHETEX_UINT; + cube_mip->components = 1; + } +} + +void EEVEE_lightcache_free(LightCache *lcache) +{ + DRW_TEXTURE_FREE_SAFE(lcache->cube_tx.tex); + MEM_SAFE_FREE(lcache->cube_tx.data); + DRW_TEXTURE_FREE_SAFE(lcache->grid_tx.tex); + MEM_SAFE_FREE(lcache->grid_tx.data); + + if (lcache->cube_mips) { + for (int i = 0; i < lcache->mips_len; ++i) { + MEM_SAFE_FREE(lcache->cube_mips[i].data); + } + MEM_SAFE_FREE(lcache->cube_mips); + } + + MEM_SAFE_FREE(lcache->cube_data); + MEM_SAFE_FREE(lcache->grid_data); + MEM_freeN(lcache); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Light Bake Context + * \{ */ + +static void eevee_lightbake_context_enable(EEVEE_LightBake *lbake) +{ + if (lbake->gl_context) { + DRW_opengl_render_context_enable(lbake->gl_context); + if (lbake->gwn_context == NULL) { + lbake->gwn_context = GWN_context_create(); + } + DRW_gawain_render_context_enable(lbake->gwn_context); + } + else { + DRW_opengl_context_enable(); + } +} + +static void eevee_lightbake_context_disable(EEVEE_LightBake *lbake) +{ + if (lbake->gl_context) { + DRW_gawain_render_context_disable(lbake->gwn_context); + DRW_opengl_render_context_disable(lbake->gl_context); + } + else { + DRW_opengl_context_disable(); + } +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ + +/** \name Light Bake Job + * \{ */ + +static void eevee_lightbake_count_probes(EEVEE_LightBake *lbake) +{ + Depsgraph *depsgraph = lbake->depsgraph; + + /* At least one of each for the world */ + lbake->grid_len = lbake->cube_len = lbake->total_irr_samples = 1; + + DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN(depsgraph, ob) + { + if (!BKE_object_is_visible(ob, OB_VISIBILITY_CHECK_FOR_RENDER)) { + continue; + } + + if (ob->type == OB_LIGHTPROBE) { + LightProbe *prb = (LightProbe *)ob->data; + + if (prb->type == LIGHTPROBE_TYPE_GRID) { + lbake->total_irr_samples += prb->grid_resolution_x * prb->grid_resolution_y * prb->grid_resolution_z; + lbake->grid_len++; + } + else if (prb->type == LIGHTPROBE_TYPE_CUBE) { + lbake->cube_len++; + } + } + } + DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END; +} + +static void eevee_lightbake_create_render_target(EEVEE_LightBake *lbake, int rt_res) +{ + lbake->rt_depth = DRW_texture_create_cube(rt_res, GPU_DEPTH_COMPONENT24, 0, NULL); + lbake->rt_color = DRW_texture_create_cube(rt_res, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); + + for (int i = 0; i < 6; ++i) { + GPU_framebuffer_ensure_config(&lbake->rt_fb[i], { + GPU_ATTACHMENT_TEXTURE_CUBEFACE(lbake->rt_depth, i), + GPU_ATTACHMENT_TEXTURE_CUBEFACE(lbake->rt_color, i) + }); + } + + GPU_framebuffer_ensure_config(&lbake->store_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_NONE + }); +} + +static void eevee_lightbake_create_resources(EEVEE_LightBake *lbake) +{ + Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph); + SceneEEVEE *eevee = &scene_eval->eevee; + + lbake->bounce_len = eevee->gi_diffuse_bounces; + lbake->vis_res = eevee->gi_visibility_resolution; + lbake->rt_res = eevee->gi_cubemap_resolution; + + irradiance_pool_size_get(lbake->vis_res, lbake->total_irr_samples, lbake->irr_size); + + lbake->ref_cube_res = OCTAHEDRAL_SIZE_FROM_CUBESIZE(lbake->rt_res); + + lbake->cube_prb = MEM_callocN(sizeof(LightProbe *) * lbake->cube_len, "EEVEE Cube visgroup ptr"); + lbake->grid_prb = MEM_callocN(sizeof(LightProbe *) * lbake->grid_len, "EEVEE Grid visgroup ptr"); + + lbake->grid_prev = DRW_texture_create_2D_array(lbake->irr_size[0], lbake->irr_size[1], lbake->irr_size[2], + IRRADIANCE_FORMAT, DRW_TEX_FILTER, NULL); + + /* Ensure Light Cache is ready to accept new data. If not recreate one. + * WARNING: All the following must be threadsafe. It's currently protected + * by the DRW mutex. */ + lbake->lcache = eevee->light_cache; + + /* TODO validate irradiance and reflection cache independantly... */ + if (lbake->lcache != NULL && + !EEVEE_lightcache_validate(lbake->lcache, lbake->cube_len, lbake->ref_cube_res, lbake->grid_len, lbake->irr_size)) + { + eevee->light_cache = lbake->lcache = NULL; + } + + if (lbake->lcache == NULL) { + lbake->lcache = EEVEE_lightcache_create(lbake->grid_len, + lbake->cube_len, + lbake->ref_cube_res, + lbake->vis_res, + lbake->irr_size); + lbake->lcache->flag = LIGHTCACHE_UPDATE_WORLD | LIGHTCACHE_UPDATE_CUBE | LIGHTCACHE_UPDATE_GRID; + lbake->lcache->vis_res = lbake->vis_res; + lbake->own_light_cache = true; + + eevee->light_cache = lbake->lcache; + } + + EEVEE_lightcache_load(eevee->light_cache); + + lbake->lcache->flag |= LIGHTCACHE_BAKING; + lbake->lcache->cube_len = 1; +} + +wmJob *EEVEE_lightbake_job_create( + struct wmWindowManager *wm, struct wmWindow *win, struct Main *bmain, + struct ViewLayer *view_layer, struct Scene *scene, int delay) +{ + EEVEE_LightBake *lbake = NULL; + + /* only one render job at a time */ + if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) + return NULL; + + wmJob *wm_job = WM_jobs_get(wm, win, scene, "Bake Lighting", + WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS, WM_JOB_TYPE_LIGHT_BAKE); + + /* If job exists do not recreate context and depsgraph. */ + EEVEE_LightBake *old_lbake = (EEVEE_LightBake *)WM_jobs_customdata_get(wm_job); + + if (old_lbake && (old_lbake->view_layer_input == view_layer) && (old_lbake->bmain == bmain)) { + lbake = MEM_callocN(sizeof(EEVEE_LightBake), "EEVEE_LightBake"); + /* Cannot reuse depsgraph for now because we cannot get the update from the + * main database directly. TODO reuse depsgraph and only update positions. */ + /* lbake->depsgraph = old_lbake->depsgraph; */ + lbake->depsgraph = DEG_graph_new(scene, view_layer, DAG_EVAL_RENDER); + + lbake->scene = scene; + lbake->bmain = bmain; + lbake->view_layer_input = view_layer; + lbake->gl_context = old_lbake->gl_context; + lbake->own_resources = true; + lbake->delay = delay; + + old_lbake->own_resources = false; + + if (old_lbake->stop != NULL) { + *old_lbake->stop = 1; + } + } + else { + lbake = EEVEE_lightbake_job_data_alloc(bmain, view_layer, scene, true); + lbake->delay = delay; + } + + WM_jobs_customdata_set(wm_job, lbake, EEVEE_lightbake_job_data_free); + WM_jobs_timer(wm_job, 0.4, NC_SCENE | NA_EDITED, 0); + WM_jobs_callbacks(wm_job, EEVEE_lightbake_job, NULL, EEVEE_lightbake_update, EEVEE_lightbake_update); + + G.is_break = false; + + return wm_job; +} + +/* MUST run on the main thread. */ +void *EEVEE_lightbake_job_data_alloc( + struct Main *bmain, struct ViewLayer *view_layer, struct Scene *scene, bool run_as_job) +{ + BLI_assert(BLI_thread_is_main()); + + EEVEE_LightBake *lbake = MEM_callocN(sizeof(EEVEE_LightBake), "EEVEE_LightBake"); + + lbake->depsgraph = DEG_graph_new(scene, view_layer, DAG_EVAL_RENDER); + lbake->scene = scene; + lbake->bmain = bmain; + lbake->view_layer_input = view_layer; + lbake->own_resources = true; + lbake->own_light_cache = false; + + if (run_as_job) { + lbake->gl_context = WM_opengl_context_create(); + wm_window_reset_drawable(); + } + + return lbake; +} + +void EEVEE_lightbake_job_data_free(void *custom_data) +{ + EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data; + + /* TODO reuse depsgraph. */ + /* if (lbake->own_resources) { */ + DEG_graph_free(lbake->depsgraph); + /* } */ + + MEM_SAFE_FREE(lbake->cube_prb); + MEM_SAFE_FREE(lbake->grid_prb); + + MEM_freeN(lbake); +} + +static void eevee_lightbake_delete_resources(EEVEE_LightBake *lbake) +{ + if (lbake->gl_context) { + DRW_opengl_render_context_enable(lbake->gl_context); + DRW_gawain_render_context_enable(lbake->gwn_context); + } + else if (!lbake->resource_only) { + DRW_opengl_context_enable(); + } + + if (lbake->own_light_cache) { + EEVEE_lightcache_free(lbake->lcache); + lbake->lcache = NULL; + } + + DRW_TEXTURE_FREE_SAFE(lbake->rt_depth); + DRW_TEXTURE_FREE_SAFE(lbake->rt_color); + DRW_TEXTURE_FREE_SAFE(lbake->grid_prev); + GPU_FRAMEBUFFER_FREE_SAFE(lbake->store_fb); + for (int i = 0; i < 6; ++i) { + GPU_FRAMEBUFFER_FREE_SAFE(lbake->rt_fb[i]); + } + + if (lbake->gwn_context) { + DRW_gawain_render_context_disable(lbake->gwn_context); + DRW_gawain_render_context_enable(lbake->gwn_context); + GWN_context_discard(lbake->gwn_context); + } + + if (lbake->gl_context && lbake->own_resources) { + /* Delete the baking context. */ + DRW_opengl_render_context_disable(lbake->gl_context); + WM_opengl_context_dispose(lbake->gl_context); + lbake->gwn_context = NULL; + lbake->gl_context = NULL; + } + else if (lbake->gl_context) { + DRW_opengl_render_context_disable(lbake->gl_context); + } + else if (!lbake->resource_only) { + DRW_opengl_context_disable(); + } +} + +/* Cache as in draw cache not light cache. */ +static void eevee_lightbake_cache_create(EEVEE_Data *vedata, EEVEE_LightBake *lbake) +{ + EEVEE_StorageList *stl = vedata->stl; + EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); + Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph); + /* Disable all effects BUT high bitdepth shadows. */ + scene_eval->eevee.flag &= SCE_EEVEE_SHADOW_HIGH_BITDEPTH; + scene_eval->eevee.taa_samples = 1; + + stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); + stl->g_data->background_alpha = 1.0f; + + /* XXX TODO remove this. This is in order to make the init functions work. */ + DRWMatrixState dummy_mats = {{{{{0}}}}}; + DRW_viewport_matrix_override_set_all(&dummy_mats); + + if (sldata->common_ubo == NULL) { + sldata->common_ubo = DRW_uniformbuffer_create(sizeof(sldata->common_data), &sldata->common_data); + } + if (sldata->clip_ubo == NULL) { + sldata->clip_ubo = DRW_uniformbuffer_create(sizeof(sldata->clip_data), &sldata->clip_data); + } + + EEVEE_effects_init(sldata, vedata, NULL); + EEVEE_materials_init(sldata, stl, fbl); + EEVEE_lights_init(sldata); + EEVEE_lightprobes_init(sldata, vedata); + + EEVEE_effects_cache_init(sldata, vedata); + EEVEE_materials_cache_init(sldata, vedata); + EEVEE_lights_cache_init(sldata, vedata); + EEVEE_lightprobes_cache_init(sldata, vedata); + + EEVEE_lightbake_cache_init(sldata, vedata, lbake->rt_color, lbake->rt_depth); + + if (lbake->probe) { + EEVEE_LightProbesInfo *pinfo = sldata->probes; + LightProbe *prb = *lbake->probe; + pinfo->vis_data.collection = prb->visibility_grp; + pinfo->vis_data.invert = prb->flag & LIGHTPROBE_FLAG_INVERT_GROUP; + pinfo->vis_data.cached = false; + } + DRW_render_object_iter(vedata, NULL, lbake->depsgraph, EEVEE_render_cache); + + EEVEE_materials_cache_finish(vedata); + EEVEE_lights_cache_finish(sldata); + EEVEE_lightprobes_cache_finish(sldata, vedata); + + DRW_render_instance_buffer_finish(); + DRW_hair_update(); +} + +static void eevee_lightbake_copy_irradiance(EEVEE_LightBake *lbake, LightCache *lcache) +{ + DRW_TEXTURE_FREE_SAFE(lbake->grid_prev); + + /* Copy texture by reading back and reuploading it. */ + float *tex = GPU_texture_read(lcache->grid_tx.tex, GPU_DATA_FLOAT, 0); + lbake->grid_prev = DRW_texture_create_2D_array(lbake->irr_size[0], lbake->irr_size[1], lbake->irr_size[2], + IRRADIANCE_FORMAT, DRW_TEX_FILTER, tex); + + MEM_freeN(tex); +} + +static void eevee_lightbake_render_world_sample(void *ved, void *user_data) +{ + EEVEE_Data *vedata = (EEVEE_Data *)ved; + EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); + EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data; + Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph); + LightCache *lcache = scene_eval->eevee.light_cache; + + /* TODO do this once for the whole bake when we have independant DRWManagers. */ + eevee_lightbake_cache_create(vedata, lbake); + + EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb); + EEVEE_lightbake_filter_glossy(sldata, vedata, lbake->rt_color, lbake->store_fb, 0, 1.0f, lcache->mips_len); + EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake->rt_color, lbake->store_fb, 0, 1.0f); + + /* Clear the cache to avoid white values in the grid. */ + GPU_framebuffer_texture_attach(lbake->store_fb, lbake->grid_prev, 0, 0); + GPU_framebuffer_bind(lbake->store_fb); + /* Clear to 1.0f for visibility. */ + GPU_framebuffer_clear_color(lbake->store_fb, ((float[4]){1.0f, 1.0f, 1.0f, 1.0f})); + DRW_draw_pass(vedata->psl->probe_grid_fill); + + SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex); + + /* Make a copy for later. */ + eevee_lightbake_copy_irradiance(lbake, lcache); + + lcache->cube_len = 1; + lcache->grid_len = lbake->grid_len; + + lcache->flag |= LIGHTCACHE_CUBE_READY | LIGHTCACHE_GRID_READY; + lcache->flag &= ~LIGHTCACHE_UPDATE_WORLD; +} + +static void cell_id_to_grid_loc(EEVEE_LightGrid *egrid, int cell_idx, int r_local_cell[3]) +{ + /* Keep in sync with lightprobe_grid_display_vert */ + r_local_cell[2] = cell_idx % egrid->resolution[2]; + r_local_cell[1] = (cell_idx / egrid->resolution[2]) % egrid->resolution[1]; + r_local_cell[0] = cell_idx / (egrid->resolution[2] * egrid->resolution[1]); +} + +static void compute_cell_id( + EEVEE_LightGrid *egrid, LightProbe *probe, + int cell_idx, int *r_final_idx, int r_local_cell[3], int *r_stride) +{ + const int cell_count = probe->grid_resolution_x * probe->grid_resolution_y * probe->grid_resolution_z; + + /* Add one for level 0 */ + int max_lvl = (int)floorf(log2f((float)MAX3(probe->grid_resolution_x, + probe->grid_resolution_y, + probe->grid_resolution_z))); + + int visited_cells = 0; + for (int lvl = max_lvl; lvl >= 0; --lvl) { + *r_stride = 1 << lvl; + int prev_stride = *r_stride << 1; + for (int i = 0; i < cell_count; ++i) { + *r_final_idx = i; + cell_id_to_grid_loc(egrid, *r_final_idx, r_local_cell); + if (((r_local_cell[0] % *r_stride) == 0) && + ((r_local_cell[1] % *r_stride) == 0) && + ((r_local_cell[2] % *r_stride) == 0)) + { + if (!(((r_local_cell[0] % prev_stride) == 0) && + ((r_local_cell[1] % prev_stride) == 0) && + ((r_local_cell[2] % prev_stride) == 0)) || + ((i == 0) && (lvl == max_lvl))) + { + if (visited_cells == cell_idx) { + return; + } + else { + visited_cells++; + } + } + } + } + } + + BLI_assert(0); +} + +static void grid_loc_to_world_loc(EEVEE_LightGrid *egrid, int local_cell[3], float r_pos[3]) +{ + copy_v3_v3(r_pos, egrid->corner); + madd_v3_v3fl(r_pos, egrid->increment_x, local_cell[0]); + madd_v3_v3fl(r_pos, egrid->increment_y, local_cell[1]); + madd_v3_v3fl(r_pos, egrid->increment_z, local_cell[2]); +} + +static void eevee_lightbake_render_grid_sample(void *ved, void *user_data) +{ + EEVEE_Data *vedata = (EEVEE_Data *)ved; + EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); + EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; + EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data; + EEVEE_LightGrid *egrid = lbake->grid; + LightProbe *prb = *lbake->probe; + Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph); + LightCache *lcache = scene_eval->eevee.light_cache; + int grid_loc[3], sample_id, sample_offset, stride; + float pos[3]; + const bool is_last_bounce_sample = ((egrid->offset + lbake->grid_sample) == (lbake->total_irr_samples - 1)); + + /* No bias for rendering the probe. */ + egrid->level_bias = 1.0f; + + /* Use the previous bounce for rendering this bounce. */ + SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex); + + /* TODO do this once for the whole bake when we have independant DRWManagers. + * Warning: Some of the things above require this. */ + eevee_lightbake_cache_create(vedata, lbake); + + /* Compute sample position */ + compute_cell_id(egrid, prb, lbake->grid_sample, &sample_id, grid_loc, &stride); + sample_offset = egrid->offset + sample_id; + + grid_loc_to_world_loc(egrid, grid_loc, pos); + + /* Disable specular lighting when rendering probes to avoid feedback loops (looks bad). */ + common_data->spec_toggle = false; + common_data->prb_num_planar = 0; + common_data->prb_num_render_cube = 0; + if (lbake->bounce_curr == 0) { + common_data->prb_num_render_grid = 0; + } + DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + + EEVEE_lightbake_render_scene(sldata, vedata, lbake->rt_fb, pos, prb->clipsta, prb->clipend); + + /* Restore before filtering. */ + SWAP(GPUTexture *, lbake->grid_prev, lcache->grid_tx.tex); + + EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake->rt_color, lbake->store_fb, sample_offset, prb->intensity); + + if (lbake->bounce_curr == 0) { + /* We only need to filter the visibility for the first bounce. */ + EEVEE_lightbake_filter_visibility(sldata, vedata, lbake->rt_depth, lbake->store_fb, sample_offset, + prb->clipsta, prb->clipend, egrid->visibility_range, + prb->vis_blur, lbake->vis_res); + } + + /* Update level for progressive update. */ + if (is_last_bounce_sample) { + egrid->level_bias = 1.0f; + } + else if (lbake->bounce_curr == 0) { + egrid->level_bias = (float)(stride << 1); + } + + /* Only run this for the last sample of a bounce. */ + if (is_last_bounce_sample) { + eevee_lightbake_copy_irradiance(lbake, lcache); + } + + /* If it is the last sample grid sample (and last bounce). */ + if ((lbake->bounce_curr == lbake->bounce_len - 1) && + (lbake->grid_curr == lbake->grid_len - 1) && + (lbake->grid_sample == lbake->grid_sample_len - 1)) + { + lcache->flag &= ~LIGHTCACHE_UPDATE_GRID; + } +} + +static void eevee_lightbake_render_probe_sample(void *ved, void *user_data) +{ + EEVEE_Data *vedata = (EEVEE_Data *)ved; + EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); + EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; + EEVEE_LightBake *lbake = (EEVEE_LightBake *)user_data; + Scene *scene_eval = DEG_get_evaluated_scene(lbake->depsgraph); + LightCache *lcache = scene_eval->eevee.light_cache; + EEVEE_LightProbe *eprobe = lbake->cube; + LightProbe *prb = *lbake->probe; + + /* TODO do this once for the whole bake when we have independant DRWManagers. */ + eevee_lightbake_cache_create(vedata, lbake); + + /* Disable specular lighting when rendering probes to avoid feedback loops (looks bad). */ + common_data->spec_toggle = false; + common_data->prb_num_planar = 0; + common_data->prb_num_render_cube = 0; + DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + + EEVEE_lightbake_render_scene(sldata, vedata, lbake->rt_fb, eprobe->position, prb->clipsta, prb->clipend); + EEVEE_lightbake_filter_glossy(sldata, vedata, lbake->rt_color, lbake->store_fb, lbake->cube_offset, prb->intensity, lcache->mips_len); + + lcache->cube_len += 1; + + /* If it's the last probe. */ + if (lbake->cube_offset == lbake->cube_len - 1) { + lcache->flag &= ~LIGHTCACHE_UPDATE_CUBE; + } +} + +static float eevee_lightbake_grid_influence_volume(EEVEE_LightGrid *grid) +{ + return mat4_to_scale(grid->mat); +} + +static float eevee_lightbake_cube_influence_volume(EEVEE_LightProbe *eprb) +{ + return mat4_to_scale(eprb->attenuationmat); +} + +static bool eevee_lightbake_grid_comp(EEVEE_LightGrid *grid_a, EEVEE_LightGrid *grid_b) +{ + float vol_a = eevee_lightbake_grid_influence_volume(grid_a); + float vol_b = eevee_lightbake_grid_influence_volume(grid_b); + return (vol_a < vol_b); +} + +static bool eevee_lightbake_cube_comp(EEVEE_LightProbe *prb_a, EEVEE_LightProbe *prb_b) +{ + float vol_a = eevee_lightbake_cube_influence_volume(prb_a); + float vol_b = eevee_lightbake_cube_influence_volume(prb_b); + return (vol_a < vol_b); +} + +#define SORT_PROBE(elems_type, prbs, elems, elems_len, comp_fn) \ +{ \ + bool sorted = false; \ + while (!sorted) { \ + sorted = true; \ + for (int i = 0; i < (elems_len) - 1; ++i) { \ + if ((comp_fn)((elems) + i, (elems) + i+1)) { \ + SWAP(elems_type, (elems)[i], (elems)[i+1]); \ + SWAP(LightProbe *, (prbs)[i], (prbs)[i+1]); \ + sorted = false; \ + } \ + } \ + } \ +} + +static void eevee_lightbake_gather_probes(EEVEE_LightBake *lbake) +{ + Depsgraph *depsgraph = lbake->depsgraph; + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + LightCache *lcache = scene_eval->eevee.light_cache; + + /* At least one for the world */ + int grid_len = 1; + int cube_len = 1; + int total_irr_samples = 1; + + /* Convert all lightprobes to tight UBO data from all lightprobes in the scene. + * This allows a large number of probe to be precomputed (even dupli ones). */ + DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN(depsgraph, ob) + { + if (!BKE_object_is_visible(ob, OB_VISIBILITY_CHECK_FOR_RENDER)) { + continue; + } + + if (ob->type == OB_LIGHTPROBE) { + LightProbe *prb = (LightProbe *)ob->data; + + if (prb->type == LIGHTPROBE_TYPE_GRID) { + lbake->grid_prb[grid_len] = prb; + EEVEE_LightGrid *egrid = &lcache->grid_data[grid_len++]; + EEVEE_lightprobes_grid_data_from_object(ob, egrid, &total_irr_samples); + } + else if (prb->type == LIGHTPROBE_TYPE_CUBE) { + lbake->cube_prb[cube_len] = prb; + EEVEE_LightProbe *eprobe = &lcache->cube_data[cube_len++]; + EEVEE_lightprobes_cube_data_from_object(ob, eprobe); + } + } + } + DEG_OBJECT_ITER_FOR_RENDER_ENGINE_END; + + SORT_PROBE(EEVEE_LightGrid, lbake->grid_prb + 1, lcache->grid_data + 1, lbake->grid_len - 1, eevee_lightbake_grid_comp); + SORT_PROBE(EEVEE_LightProbe, lbake->cube_prb + 1, lcache->cube_data + 1, lbake->cube_len - 1, eevee_lightbake_cube_comp); + + lbake->total = lbake->total_irr_samples * lbake->bounce_len + lbake->cube_len; + lbake->done = 0; +} + +void EEVEE_lightbake_update(void *custom_data) +{ + EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data; + Scene *scene_orig = lbake->scene; + + /* If a new lightcache was created, free the old one and reference the new. */ + if (lbake->lcache && scene_orig->eevee.light_cache != lbake->lcache) { + if (scene_orig->eevee.light_cache != NULL) { + EEVEE_lightcache_free(scene_orig->eevee.light_cache); + } + scene_orig->eevee.light_cache = lbake->lcache; + lbake->own_light_cache = false; + } + + EEVEE_lightcache_info_update(&lbake->scene->eevee); + + DEG_id_tag_update(&scene_orig->id, DEG_TAG_COPY_ON_WRITE); +} + +static bool lightbake_do_sample(EEVEE_LightBake *lbake, void (*render_callback)(void *ved, void *user_data)) +{ + if (G.is_break == true || *lbake->stop) { + return false; + } + + Depsgraph *depsgraph = lbake->depsgraph; + + /* TODO: make DRW manager instanciable (and only lock on drawing) */ + eevee_lightbake_context_enable(lbake); + DRW_custom_pipeline(&draw_engine_eevee_type, depsgraph, render_callback, lbake); + lbake->done += 1; + *lbake->progress = lbake->done / (float)lbake->total; + *lbake->do_update = 1; + eevee_lightbake_context_disable(lbake); + + return true; +} + +void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float *progress) +{ + EEVEE_LightBake *lbake = (EEVEE_LightBake *)custom_data; + Depsgraph *depsgraph = lbake->depsgraph; + int frame = 0; /* TODO make it user param. */ + + DEG_graph_relations_update(depsgraph, lbake->bmain, lbake->scene, lbake->view_layer_input); + DEG_evaluate_on_framechange(lbake->bmain, depsgraph, frame); + + lbake->view_layer = DEG_get_evaluated_view_layer(depsgraph); + lbake->stop = stop; + lbake->do_update = do_update; + lbake->progress = progress; + + /* Count lightprobes */ + eevee_lightbake_count_probes(lbake); + + /* We need to create the FBOs in the right context. + * We cannot do it in the main thread. */ + eevee_lightbake_context_enable(lbake); + eevee_lightbake_create_resources(lbake); + eevee_lightbake_create_render_target(lbake, lbake->rt_res); + eevee_lightbake_context_disable(lbake); + + /* Gather all probes data */ + eevee_lightbake_gather_probes(lbake); + + LightCache *lcache = lbake->lcache; + + /* HACK: Sleep to delay the first rendering operation + * that causes a small freeze (caused by VBO generation) + * because this step is locking at this moment. */ + /* TODO remove this. */ + if (lbake->delay) { + PIL_sleep_ms(lbake->delay); + } + + /* Render world irradiance and reflection first */ + if (lcache->flag & LIGHTCACHE_UPDATE_WORLD) { + lbake->probe = NULL; + lightbake_do_sample(lbake, eevee_lightbake_render_world_sample); + } + + /* Render irradiance grids */ + if (lcache->flag & LIGHTCACHE_UPDATE_GRID) { + for (lbake->bounce_curr = 0; lbake->bounce_curr < lbake->bounce_len; ++lbake->bounce_curr) { + /* Bypass world, start at 1. */ + lbake->probe = lbake->grid_prb + 1; + lbake->grid = lcache->grid_data + 1; + for (lbake->grid_curr = 1; + lbake->grid_curr < lbake->grid_len; + ++lbake->grid_curr, ++lbake->probe, ++lbake->grid) + { + LightProbe *prb = *lbake->probe; + lbake->grid_sample_len = prb->grid_resolution_x * + prb->grid_resolution_y * + prb->grid_resolution_z; + for (lbake->grid_sample = 0; + lbake->grid_sample < lbake->grid_sample_len; + ++lbake->grid_sample) + { + lightbake_do_sample(lbake, eevee_lightbake_render_grid_sample); + } + } + } + } + + /* Render reflections */ + if (lcache->flag & LIGHTCACHE_UPDATE_CUBE) { + /* Bypass world, start at 1. */ + lbake->probe = lbake->cube_prb + 1; + lbake->cube = lcache->cube_data + 1; + for (lbake->cube_offset = 1; + lbake->cube_offset < lbake->cube_len; + ++lbake->cube_offset, ++lbake->probe, ++lbake->cube) + { + lightbake_do_sample(lbake, eevee_lightbake_render_probe_sample); + } + } + + /* Read the resulting lighting data to save it to file/disk. */ + eevee_lightbake_context_enable(lbake); + eevee_lightbake_readback_irradiance(lcache); + eevee_lightbake_readback_reflections(lcache); + eevee_lightbake_context_disable(lbake); + + lcache->flag |= LIGHTCACHE_BAKED; + lcache->flag &= ~LIGHTCACHE_BAKING; + + /* Assume that if lbake->gl_context is NULL + * we are not running in this in a job, so update + * the scene lightcache pointer before deleting it. */ + if (lbake->gl_context == NULL) { + BLI_assert(BLI_thread_is_main()); + EEVEE_lightbake_update(lbake); + } + + eevee_lightbake_delete_resources(lbake); +} + +/* This is to update the world irradiance and reflection contribution from + * within the viewport drawing (does not have the overhead of a full light cache rebuild.) */ +void EEVEE_lightbake_update_world_quick(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, const Scene *scene) +{ + LightCache *lcache = vedata->stl->g_data->light_cache; + + EEVEE_LightBake lbake = { + .resource_only = true + }; + + /* Create resources. */ + eevee_lightbake_create_render_target(&lbake, scene->eevee.gi_cubemap_resolution); + + EEVEE_lightbake_cache_init(sldata, vedata, lbake.rt_color, lbake.rt_depth); + + EEVEE_lightbake_render_world(sldata, vedata, lbake.rt_fb); + EEVEE_lightbake_filter_glossy(sldata, vedata, lbake.rt_color, lbake.store_fb, 0, 1.0f, lcache->mips_len); + EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake.rt_color, lbake.store_fb, 0, 1.0f); + + /* Don't hide grids if they are already rendered. */ + lcache->grid_len = max_ii(1, lcache->grid_len); + lcache->cube_len = 1; + + lcache->flag |= LIGHTCACHE_CUBE_READY | LIGHTCACHE_GRID_READY; + lcache->flag &= ~LIGHTCACHE_UPDATE_WORLD; + + eevee_lightbake_delete_resources(&lbake); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.h b/source/blender/draw/engines/eevee/eevee_lightcache.h new file mode 100644 index 00000000000..b58a0544c59 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_lightcache.h @@ -0,0 +1,59 @@ +/* + * Copyright 2018, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Blender Institute + * + */ + +/** \file eevee_lightcache.h + * \ingroup eevee + */ + +#ifndef __EEVEE_LIGHTCACHE_H__ +#define __EEVEE_LIGHTCACHE_H__ + +#include "BLI_sys_types.h" /* for bool */ + +struct ViewLayer; +struct Scene; +struct SceneEEVEE; +struct LightCache; +struct EEVEE_ViewLayerData; +struct EEVEE_Data; +struct EEVEE_LightBake; + +/* Light Bake */ +struct wmJob *EEVEE_lightbake_job_create( + struct wmWindowManager *wm, struct wmWindow *win, struct Main *bmain, + struct ViewLayer *view_layer, struct Scene *scene, int delay); +void *EEVEE_lightbake_job_data_alloc(struct Main *bmain, struct ViewLayer *viewlayer, struct Scene *scene, bool run_as_job); +void EEVEE_lightbake_job_data_free(void *custom_data); +void EEVEE_lightbake_update(void *custom_data); +void EEVEE_lightbake_job(void *custom_data, short *stop, short *do_update, float *progress); + +void EEVEE_lightbake_update_world_quick(struct EEVEE_ViewLayerData *sldata, struct EEVEE_Data *vedata, const Scene *scene); + +/* Light Cache */ +struct LightCache *EEVEE_lightcache_create( + const int grid_len, const int cube_len, + const int cube_size, const int vis_size, + const int irr_size[3]); +void EEVEE_lightcache_free(struct LightCache *lcache); +void EEVEE_lightcache_load(struct LightCache *lcache); +void EEVEE_lightcache_info_update(struct SceneEEVEE *eevee); + +#endif /* __EEVEE_LIGHTCACHE_H__ */
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index 95b015cb044..1cb9a957211 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -20,7 +20,7 @@ */ /** \file eevee_lightprobes.c - * \ingroup DNA + * \ingroup draw_engine */ #include "DRW_render.h" @@ -46,27 +46,14 @@ #include "DEG_depsgraph_query.h" #include "eevee_engine.h" +#include "eevee_lightcache.h" #include "eevee_private.h" #include "ED_screen.h" -/* Rounded to nearest PowerOfTwo */ -#if defined(IRRADIANCE_SH_L2) -#define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */ -#define IRRADIANCE_SAMPLE_SIZE_Y 4 /* 3 in reality */ -#elif defined(IRRADIANCE_CUBEMAP) -#define IRRADIANCE_SAMPLE_SIZE_X 8 -#define IRRADIANCE_SAMPLE_SIZE_Y 8 -#elif defined(IRRADIANCE_HL2) -#define IRRADIANCE_SAMPLE_SIZE_X 4 /* 3 in reality */ -#define IRRADIANCE_SAMPLE_SIZE_Y 2 -#endif +#include "WM_api.h" +#include "WM_types.h" -#define IRRADIANCE_MAX_POOL_LAYER 256 /* OpenGL 3.3 core requirement, can be extended but it's already very big */ -#define IRRADIANCE_MAX_POOL_SIZE 1024 -#define MAX_IRRADIANCE_SAMPLES \ - (IRRADIANCE_MAX_POOL_SIZE / IRRADIANCE_SAMPLE_SIZE_X) * \ - (IRRADIANCE_MAX_POOL_SIZE / IRRADIANCE_SAMPLE_SIZE_Y) #define HAMMERSLEY_SIZE 1024 static struct { @@ -120,19 +107,30 @@ extern GlobalsUboStorage ts; /* *********** FUNCTIONS *********** */ -static void irradiance_pool_size_get(int visibility_size, int total_samples, int r_size[3]) +/* TODO find a better way than this. This does not support dupli objects if + * the original object is hidden. */ +bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data) { - /* Compute how many irradiance samples we can store per visibility sample. */ - int irr_per_vis = (visibility_size / IRRADIANCE_SAMPLE_SIZE_X) * - (visibility_size / IRRADIANCE_SAMPLE_SIZE_Y); + EEVEE_ObjectEngineData *oed = (EEVEE_ObjectEngineData *)user_data; + + /* test disabled if group is NULL */ + if (oed->test_data->collection == NULL) + return vis_in; + + if (oed->test_data->cached == false) + oed->ob_vis_dirty = true; + + /* early out, don't need to compute ob_vis yet. */ + if (vis_in == false) + return vis_in; - /* The irradiance itself take one layer, hence the +1 */ - int layer_len = MIN2(irr_per_vis + 1, IRRADIANCE_MAX_POOL_LAYER); + if (oed->ob_vis_dirty) { + oed->ob_vis_dirty = false; + oed->ob_vis = BKE_collection_has_object_recursive(oed->test_data->collection, oed->ob); + oed->ob_vis = (oed->test_data->invert) ? !oed->ob_vis : oed->ob_vis; + } - int texel_len = (int)ceilf((float)total_samples / (float)(layer_len - 1)); - r_size[0] = visibility_size * max_ii(1, min_ii(texel_len, (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); - r_size[1] = visibility_size * max_ii(1, (texel_len / (IRRADIANCE_MAX_POOL_SIZE / visibility_size))); - r_size[2] = layer_len; + return vis_in && oed->ob_vis; } static struct GPUTexture *create_hammersley_sample_texture(int samples) @@ -156,11 +154,11 @@ static struct GPUTexture *create_hammersley_sample_texture(int samples) static void planar_pool_ensure_alloc(EEVEE_Data *vedata, int num_planar_ref) { + EEVEE_TextureList *txl = vedata->txl; + /* XXX TODO OPTIMISATION : This is a complete waist of texture memory. * Instead of allocating each planar probe for each viewport, * only alloc them once using the biggest viewport resolution. */ - EEVEE_TextureList *txl = vedata->txl; - const float *viewport_size = DRW_viewport_size_get(); /* TODO get screen percentage from layer setting */ @@ -275,7 +273,7 @@ static void lightprobe_shaders_init(void) datatoc_common_view_lib_glsl, datatoc_lightprobe_cube_display_vert_glsl); - e_data.probe_cube_display_sh = DRW_shader_create(vert_str, NULL, shader_str, NULL); + e_data.probe_cube_display_sh = DRW_shader_create(vert_str, NULL, shader_str, SHADER_DEFINES); MEM_freeN(vert_str); MEM_freeN(shader_str); @@ -302,178 +300,65 @@ static void lightprobe_shaders_init(void) e_data.hammersley = create_hammersley_sample_texture(HAMMERSLEY_SIZE); } -void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(vedata)) +void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - bool update_all = false; + EEVEE_StorageList *stl = vedata->stl; + const DRWContextState *draw_ctx = DRW_context_state_get(); const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); - /* Shaders */ if (!e_data.probe_filter_glossy_sh) { lightprobe_shaders_init(); } + if ((scene_eval->eevee.light_cache == NULL) && + (sldata->fallback_lightcache == NULL)) { +#if defined(IRRADIANCE_SH_L2) + int grid_res = 4; +#elif defined(IRRADIANCE_CUBEMAP) + int grid_res = 8; +#elif defined(IRRADIANCE_HL2) + int grid_res = 4; +#endif + int cube_res = OCTAHEDRAL_SIZE_FROM_CUBESIZE(scene_eval->eevee.gi_cubemap_resolution); + int vis_res = scene_eval->eevee.gi_visibility_resolution; + + sldata->fallback_lightcache = EEVEE_lightcache_create(1, 1, cube_res, vis_res, (int[3]){grid_res, grid_res, 1}); + } + + stl->g_data->light_cache = (scene_eval->eevee.light_cache) ? scene_eval->eevee.light_cache : sldata->fallback_lightcache; + + EEVEE_lightcache_load(stl->g_data->light_cache); + if (!sldata->probes) { sldata->probes = MEM_callocN(sizeof(EEVEE_LightProbesInfo), "EEVEE_LightProbesInfo"); - sldata->probes->grid_initialized = false; sldata->probe_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightProbe) * MAX_PROBE, NULL); sldata->grid_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightGrid) * MAX_GRID, NULL); sldata->planar_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR, NULL); } - /* Only start doing probes if all materials have finished compiling. */ - sldata->probes->all_materials_updated = true; + common_data->prb_num_planar = 0; + common_data->prb_num_render_cube = 1; + common_data->prb_num_render_grid = 1; common_data->spec_toggle = true; common_data->ssr_toggle = true; common_data->sss_toggle = true; - int prop_bounce_num = scene_eval->eevee.gi_diffuse_bounces; - if (sldata->probes->num_bounce != prop_bounce_num) { - sldata->probes->num_bounce = prop_bounce_num; - update_all = true; - } - - int prop_cubemap_res = scene_eval->eevee.gi_cubemap_resolution; - if (sldata->probes->cubemap_res != prop_cubemap_res) { - sldata->probes->cubemap_res = prop_cubemap_res; - update_all = true; - - sldata->probes->target_size = prop_cubemap_res >> 1; - - DRW_TEXTURE_FREE_SAFE(sldata->probe_depth_rt); - DRW_TEXTURE_FREE_SAFE(sldata->probe_rt); - DRW_TEXTURE_FREE_SAFE(sldata->probe_pool); - } - - const int visibility_res = scene_eval->eevee.gi_visibility_resolution; - if (common_data->prb_irradiance_vis_size != visibility_res) { - common_data->prb_irradiance_vis_size = visibility_res; - update_all = true; - } - - if (update_all) { - sldata->probes->update_world |= PROBE_UPDATE_ALL; - sldata->probes->updated_bounce = 0; - sldata->probes->grid_initialized = false; - } - - /* Setup Render Target Cubemap */ - if (!sldata->probe_rt) { - sldata->probe_depth_rt = DRW_texture_create_cube(sldata->probes->target_size, GPU_DEPTH_COMPONENT24, 0, NULL); - sldata->probe_rt = DRW_texture_create_cube(sldata->probes->target_size, GPU_RGBA16F, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); - } - - for (int i = 0; i < 6; ++i) { - GPU_framebuffer_ensure_config(&sldata->probe_face_fb[i], { - GPU_ATTACHMENT_TEXTURE_CUBEFACE(sldata->probe_depth_rt, i), - GPU_ATTACHMENT_TEXTURE_CUBEFACE(sldata->probe_rt, i) - }); - } - /* Placeholder planar pool: used when rendering planar reflections (avoid dependency loop). */ if (!e_data.planar_pool_placeholder) { e_data.planar_pool_placeholder = DRW_texture_create_2D_array(1, 1, 1, GPU_RGBA8, DRW_TEX_FILTER, NULL); } - - if (!e_data.depth_placeholder) { - e_data.depth_placeholder = DRW_texture_create_2D(1, 1, GPU_DEPTH_COMPONENT24, 0, NULL); - } - if (!e_data.depth_array_placeholder) { - e_data.depth_array_placeholder = DRW_texture_create_2D_array(1, 1, 1, GPU_DEPTH_COMPONENT24, 0, NULL); - } } -void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) +/* Only init the passes usefull for rendering the light cache. */ +void EEVEE_lightbake_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, GPUTexture *rt_color, GPUTexture *rt_depth) { - EEVEE_TextureList *txl = vedata->txl; EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; + LightCache *light_cache = vedata->stl->g_data->light_cache; EEVEE_LightProbesInfo *pinfo = sldata->probes; - /* Make sure no aditionnal visibility check runs by default. */ - pinfo->vis_data.collection = NULL; - - pinfo->do_cube_update = false; - pinfo->num_cube = 1; /* at least one for the world */ - pinfo->num_grid = 1; - pinfo->num_planar = 0; - pinfo->total_irradiance_samples = 1; - memset(pinfo->probes_cube_ref, 0, sizeof(pinfo->probes_cube_ref)); - memset(pinfo->probes_grid_ref, 0, sizeof(pinfo->probes_grid_ref)); - memset(pinfo->probes_planar_ref, 0, sizeof(pinfo->probes_planar_ref)); - - { - psl->probe_background = DRW_pass_create("World Probe Background Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); - - struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get(); - DRWShadingGroup *grp = NULL; - - const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; - World *wo = scene->world; - - float *col = ts.colorBackground; - - /* LookDev */ - EEVEE_lookdev_cache_init(vedata, &grp, e_data.probe_default_studiolight_sh, psl->probe_background, wo, pinfo); - /* END */ - if (!grp && wo) { - col = &wo->horr; - bool wo_sh_compiled = true; - - if (wo->use_nodes && wo->nodetree) { - static float error_col[3] = {1.0f, 0.0f, 1.0f}; - static float compile_col[3] = {0.5f, 0.5f, 0.5f}; - struct GPUMaterial *gpumat = EEVEE_material_world_lightprobe_get(scene, wo); - - GPUMaterialStatus status = GPU_material_status(gpumat); - - switch (status) { - case GPU_MAT_SUCCESS: - grp = DRW_shgroup_material_create(gpumat, psl->probe_background); - DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1); - DRW_shgroup_call_add(grp, geom, NULL); - wo_sh_compiled = true; - break; - case GPU_MAT_QUEUED: - pinfo->all_materials_updated = false; - wo_sh_compiled = false; - /* TODO Bypass probe compilation. */ - col = compile_col; - break; - case GPU_MAT_FAILED: - default: - wo_sh_compiled = true; - col = error_col; - break; - } - } - - if (pinfo->prev_world != wo || pinfo->prev_wo_sh_compiled != wo_sh_compiled) { - pinfo->update_world |= PROBE_UPDATE_ALL; - pinfo->studiolight_index = 0; - pinfo->prev_wo_sh_compiled = wo_sh_compiled; - pinfo->prev_world = wo; - } - } - else if (pinfo->prev_world) { - pinfo->update_world |= PROBE_UPDATE_ALL; - pinfo->studiolight_index = 0; - pinfo->prev_wo_sh_compiled = false; - pinfo->prev_world = NULL; - } - - /* Fallback if shader fails or if not using nodetree. */ - if (grp == NULL) { - grp = DRW_shgroup_create(e_data.probe_default_sh, psl->probe_background); - DRW_shgroup_uniform_vec3(grp, "color", col, 1); - DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1); - DRW_shgroup_call_add(grp, geom, NULL); - } - } - { psl->probe_glossy_compute = DRW_pass_create("LightProbe Glossy Compute", DRW_STATE_WRITE_COLOR); @@ -489,7 +374,7 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat DRW_shgroup_uniform_int(grp, "Layer", &pinfo->layer, 1); DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley); // DRW_shgroup_uniform_texture(grp, "texJitter", e_data.jitter); - DRW_shgroup_uniform_texture(grp, "probeHdr", sldata->probe_rt); + DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color); DRW_shgroup_call_add(grp, DRW_cache_fullscreen_quad_get(), NULL); } @@ -507,7 +392,7 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley); #endif DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1); - DRW_shgroup_uniform_texture(grp, "probeHdr", sldata->probe_rt); + DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color); struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get(); DRW_shgroup_call_add(grp, geom, NULL); @@ -526,7 +411,7 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat DRW_shgroup_uniform_float(grp, "nearClip", &pinfo->near_clip, 1); DRW_shgroup_uniform_float(grp, "farClip", &pinfo->far_clip, 1); DRW_shgroup_uniform_texture(grp, "texHammersley", e_data.hammersley); - DRW_shgroup_uniform_texture(grp, "probeDepth", sldata->probe_depth_rt); + DRW_shgroup_uniform_texture(grp, "probeDepth", rt_depth); struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get(); DRW_shgroup_call_add(grp, geom, NULL); @@ -536,37 +421,115 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat psl->probe_grid_fill = DRW_pass_create("LightProbe Grid Floodfill", DRW_STATE_WRITE_COLOR); DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_grid_fill_sh, psl->probe_grid_fill); - DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &sldata->irradiance_pool); + DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &light_cache->grid_tx.tex); struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get(); DRW_shgroup_call_add(grp, geom, NULL); } +} + +void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) +{ + EEVEE_TextureList *txl = vedata->txl; + EEVEE_PassList *psl = vedata->psl; + EEVEE_StorageList *stl = vedata->stl; + EEVEE_LightProbesInfo *pinfo = sldata->probes; + LightCache *lcache = stl->g_data->light_cache; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); + + pinfo->num_planar = 0; + pinfo->vis_data.collection = NULL; + pinfo->do_grid_update = false; + pinfo->do_cube_update = false; { + psl->probe_background = DRW_pass_create("World Probe Background Pass", DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); + + struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get(); + DRWShadingGroup *grp = NULL; + + Scene *scene = draw_ctx->scene; + World *wo = scene->world; + + float *col = ts.colorBackground; + + /* LookDev */ + EEVEE_lookdev_cache_init(vedata, &grp, e_data.probe_default_studiolight_sh, psl->probe_background, wo, pinfo); + /* END */ + if (!grp && wo) { + col = &wo->horr; + + if (wo->use_nodes && wo->nodetree) { + static float error_col[3] = {1.0f, 0.0f, 1.0f}; + struct GPUMaterial *gpumat = EEVEE_material_world_lightprobe_get(scene, wo); + + GPUMaterialStatus status = GPU_material_status(gpumat); + + switch (status) { + case GPU_MAT_SUCCESS: + grp = DRW_shgroup_material_create(gpumat, psl->probe_background); + DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1); + DRW_shgroup_call_add(grp, geom, NULL); + break; + default: + col = error_col; + break; + } + } + } + + /* Fallback if shader fails or if not using nodetree. */ + if (grp == NULL) { + grp = DRW_shgroup_create(e_data.probe_default_sh, psl->probe_background); + DRW_shgroup_uniform_vec3(grp, "color", col, 1); + DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1); + DRW_shgroup_call_add(grp, geom, NULL); + } + } + + if (DRW_state_draw_support()) { DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK; psl->probe_display = DRW_pass_create("LightProbe Display", state); - DRW_shgroup_instance_format(e_data.format_probe_display_cube, { - {"probe_id", DRW_ATTRIB_INT, 1}, - {"probe_location", DRW_ATTRIB_FLOAT, 3}, - {"sphere_size", DRW_ATTRIB_FLOAT, 1}, - }); + /* Cube Display */ + if (scene_eval->eevee.flag & SCE_EEVEE_SHOW_CUBEMAPS && lcache->cube_len > 1) { + int cube_len = lcache->cube_len - 1; /* don't count the world. */ + DRWShadingGroup *grp = DRW_shgroup_empty_tri_batch_create(e_data.probe_cube_display_sh, + psl->probe_display, cube_len * 2); + DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_vec3(grp, "screen_vecs[0]", DRW_viewport_screenvecs_get(), 2); + DRW_shgroup_uniform_float_copy(grp, "sphere_size", scene_eval->eevee.gi_cubemap_draw_size * 0.5f); + } - DRWShadingGroup *grp = DRW_shgroup_instance_create( - e_data.probe_cube_display_sh, - psl->probe_display, - DRW_cache_sphere_get(), - e_data.format_probe_display_cube); - stl->g_data->cube_display_shgrp = grp; - DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &sldata->probe_pool); - DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + /* Grid Display */ + if (scene_eval->eevee.flag & SCE_EEVEE_SHOW_IRRADIANCE) { + EEVEE_LightGrid *egrid = lcache->grid_data + 1; + for (int p = 1; p < lcache->grid_len; ++p, egrid++) { + DRWShadingGroup *shgrp = DRW_shgroup_create(e_data.probe_grid_display_sh, psl->probe_display); + DRW_shgroup_uniform_int(shgrp, "offset", &egrid->offset, 1); + DRW_shgroup_uniform_ivec3(shgrp, "grid_resolution", egrid->resolution, 1); + DRW_shgroup_uniform_vec3(shgrp, "corner", egrid->corner, 1); + DRW_shgroup_uniform_vec3(shgrp, "increment_x", egrid->increment_x, 1); + DRW_shgroup_uniform_vec3(shgrp, "increment_y", egrid->increment_y, 1); + DRW_shgroup_uniform_vec3(shgrp, "increment_z", egrid->increment_z, 1); + DRW_shgroup_uniform_vec3(shgrp, "screen_vecs[0]", DRW_viewport_screenvecs_get(), 2); + DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex); + DRW_shgroup_uniform_float_copy(shgrp, "sphere_size", scene_eval->eevee.gi_irradiance_draw_size * 0.5f); + int tri_count = egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2] * 2; + DRW_shgroup_call_procedural_triangles_add(shgrp, tri_count, NULL); + } + } + /* Planar Display */ DRW_shgroup_instance_format(e_data.format_probe_display_planar, { {"probe_id", DRW_ATTRIB_INT, 1}, {"probe_mat", DRW_ATTRIB_FLOAT, 16}, }); - grp = DRW_shgroup_instance_create( + DRWShadingGroup *grp = DRW_shgroup_instance_create( e_data.probe_planar_display_sh, psl->probe_display, DRW_cache_quad_get(), @@ -574,6 +537,9 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat stl->g_data->planar_display_shgrp = grp; DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &txl->planar_pool); } + else { + stl->g_data->planar_display_shgrp = NULL; + } { psl->probe_planar_downsample_ps = DRW_pass_create("LightProbe Planar Downsample", DRW_STATE_WRITE_COLOR); @@ -585,514 +551,560 @@ void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedat } } -bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data) +static bool eevee_lightprobes_culling_test(Object *ob) { - EEVEE_ObjectEngineData *oed = (EEVEE_ObjectEngineData *)user_data; - - /* test disabled if group is NULL */ - if (oed->test_data->collection == NULL) - return vis_in; - - if (oed->test_data->cached == false) - oed->ob_vis_dirty = true; - - /* early out, don't need to compute ob_vis yet. */ - if (vis_in == false) - return vis_in; + LightProbe *probe = (LightProbe *)ob->data; - if (oed->ob_vis_dirty) { - oed->ob_vis_dirty = false; - oed->ob_vis = BKE_collection_has_object_recursive(oed->test_data->collection, oed->ob); - oed->ob_vis = (oed->test_data->invert) ? !oed->ob_vis : oed->ob_vis; + switch (probe->type) { + case LIGHTPROBE_TYPE_PLANAR: + { + /* See if this planar probe is inside the view frustum. If not, no need to update it. */ + /* NOTE: this could be bypassed if we want feedback loop mirrors for rendering. */ + BoundBox bbox; float tmp[4][4]; + const float min[3] = {-1.0f, -1.0f, -1.0f}; + const float max[3] = { 1.0f, 1.0f, 1.0f}; + BKE_boundbox_init_from_minmax(&bbox, min, max); + + copy_m4_m4(tmp, ob->obmat); + normalize_v3(tmp[2]); + mul_v3_fl(tmp[2], probe->distinf); + + for (int v = 0; v < 8; ++v) { + mul_m4_v3(tmp, bbox.vec[v]); + } + return DRW_culling_box_test(&bbox); + } + case LIGHTPROBE_TYPE_CUBE: + return true; /* TODO */ + case LIGHTPROBE_TYPE_GRID: + return true; /* TODO */ } - - return vis_in && oed->ob_vis; + BLI_assert(0); + return true; } -void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, Object *ob) +void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob) { EEVEE_LightProbesInfo *pinfo = sldata->probes; LightProbe *probe = (LightProbe *)ob->data; if ((probe->type == LIGHTPROBE_TYPE_CUBE && pinfo->num_cube >= MAX_PROBE) || (probe->type == LIGHTPROBE_TYPE_GRID && pinfo->num_grid >= MAX_PROBE) || - (probe->type == LIGHTPROBE_TYPE_PLANAR && pinfo->num_grid >= MAX_PLANAR)) + (probe->type == LIGHTPROBE_TYPE_PLANAR && pinfo->num_planar >= MAX_PLANAR)) { - printf("Too much probes in the scene !!!\n"); + printf("Too many probes in the view !!!\n"); return; } if (probe->type == LIGHTPROBE_TYPE_PLANAR) { - /* See if this planar probe is inside the view frustum. If not, no need to update it. */ - /* NOTE: this could be bypassed if we want feedback loop mirrors for rendering. */ - BoundBox bbox; float tmp[4][4]; - const float min[3] = {-1.0f, -1.0f, -1.0f}; - const float max[3] = { 1.0f, 1.0f, 1.0f}; - BKE_boundbox_init_from_minmax(&bbox, min, max); - - copy_m4_m4(tmp, ob->obmat); - normalize_v3(tmp[2]); - mul_v3_fl(tmp[2], probe->distinf); - - for (int v = 0; v < 8; ++v) { - mul_m4_v3(tmp, bbox.vec[v]); - } - if (!DRW_culling_box_test(&bbox)) { + if (!eevee_lightprobes_culling_test(ob)) { return; /* Culled */ } + EEVEE_lightprobes_planar_data_from_object(ob, + &pinfo->planar_data[pinfo->num_planar], + &pinfo->planar_vis_tests[pinfo->num_planar]); + /* Debug Display */ + DRWShadingGroup *grp = vedata->stl->g_data->planar_display_shgrp; + if (grp && (probe->flag & LIGHTPROBE_FLAG_SHOW_DATA)) { + DRW_shgroup_call_dynamic_add(grp, &pinfo->num_planar, ob->obmat); + } + + pinfo->num_planar++; } + else { + EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); + if (ped->need_update) { + if (probe->type == LIGHTPROBE_TYPE_GRID) { + pinfo->do_grid_update = true; + } + else { + pinfo->do_cube_update = true; + } + ped->need_update = false; + } + } +} - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); +void EEVEE_lightprobes_grid_data_from_object(Object *ob, EEVEE_LightGrid *egrid, int *offset) +{ + LightProbe *probe = (LightProbe *)ob->data; - ped->num_cell = probe->grid_resolution_x * probe->grid_resolution_y * probe->grid_resolution_z; + copy_v3_v3_int(egrid->resolution, &probe->grid_resolution_x); + + /* Save current offset and advance it for the next grid. */ + egrid->offset = *offset; + *offset += egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2]; + + /* Add one for level 0 */ + float fac = 1.0f / max_ff(1e-8f, probe->falloff); + egrid->attenuation_scale = fac / max_ff(1e-8f, probe->distinf); + egrid->attenuation_bias = fac; + + /* Update transforms */ + float cell_dim[3], half_cell_dim[3]; + cell_dim[0] = 2.0f / egrid->resolution[0]; + cell_dim[1] = 2.0f / egrid->resolution[1]; + cell_dim[2] = 2.0f / egrid->resolution[2]; + + mul_v3_v3fl(half_cell_dim, cell_dim, 0.5f); + + /* Matrix converting world space to cell ranges. */ + invert_m4_m4(egrid->mat, ob->obmat); + + /* First cell. */ + copy_v3_fl(egrid->corner, -1.0f); + add_v3_v3(egrid->corner, half_cell_dim); + mul_m4_v3(ob->obmat, egrid->corner); + + /* Opposite neighbor cell. */ + copy_v3_fl3(egrid->increment_x, cell_dim[0], 0.0f, 0.0f); + add_v3_v3(egrid->increment_x, half_cell_dim); + add_v3_fl(egrid->increment_x, -1.0f); + mul_m4_v3(ob->obmat, egrid->increment_x); + sub_v3_v3(egrid->increment_x, egrid->corner); + + copy_v3_fl3(egrid->increment_y, 0.0f, cell_dim[1], 0.0f); + add_v3_v3(egrid->increment_y, half_cell_dim); + add_v3_fl(egrid->increment_y, -1.0f); + mul_m4_v3(ob->obmat, egrid->increment_y); + sub_v3_v3(egrid->increment_y, egrid->corner); + + copy_v3_fl3(egrid->increment_z, 0.0f, 0.0f, cell_dim[2]); + add_v3_v3(egrid->increment_z, half_cell_dim); + add_v3_fl(egrid->increment_z, -1.0f); + mul_m4_v3(ob->obmat, egrid->increment_z); + sub_v3_v3(egrid->increment_z, egrid->corner); + + /* Visibility bias */ + egrid->visibility_bias = 0.05f * probe->vis_bias; + egrid->visibility_bleed = probe->vis_bleedbias; + egrid->visibility_range = 1.0f + sqrtf(max_fff(len_squared_v3(egrid->increment_x), + len_squared_v3(egrid->increment_y), + len_squared_v3(egrid->increment_z))); +} - if ((probe->type == LIGHTPROBE_TYPE_GRID) && - ((pinfo->total_irradiance_samples + ped->num_cell) >= MAX_IRRADIANCE_SAMPLES)) - { - printf("Too much grid samples !!!\n"); - return; - } +void EEVEE_lightprobes_cube_data_from_object(Object *ob, EEVEE_LightProbe *eprobe) +{ + LightProbe *probe = (LightProbe *)ob->data; - if (ped->need_full_update) { - ped->need_full_update = false; + /* Update transforms */ + copy_v3_v3(eprobe->position, ob->obmat[3]); - ped->need_update = true; - ped->probe_id = 0; - if (probe->type == LIGHTPROBE_TYPE_GRID) { - ped->updated_cells = 0; - ped->updated_lvl = 0; - pinfo->updated_bounce = 0; - pinfo->grid_initialized = false; - } - } + /* Attenuation */ + eprobe->attenuation_type = probe->attenuation_type; + eprobe->attenuation_fac = 1.0f / max_ff(1e-8f, probe->falloff); - if (pinfo->update_world) { - ped->need_update = true; - ped->updated_cells = 0; - ped->updated_lvl = 0; - ped->probe_id = 0; - } + unit_m4(eprobe->attenuationmat); + scale_m4_fl(eprobe->attenuationmat, probe->distinf); + mul_m4_m4m4(eprobe->attenuationmat, ob->obmat, eprobe->attenuationmat); + invert_m4(eprobe->attenuationmat); - pinfo->do_cube_update |= ped->need_update; + /* Parallax */ + unit_m4(eprobe->parallaxmat); - switch (probe->type) { - case LIGHTPROBE_TYPE_CUBE: - pinfo->probes_cube_ref[pinfo->num_cube] = ob; - pinfo->num_cube++; - break; - case LIGHTPROBE_TYPE_PLANAR: - pinfo->probes_planar_ref[pinfo->num_planar] = ob; - pinfo->num_planar++; - break; - case LIGHTPROBE_TYPE_GRID: - pinfo->probes_grid_ref[pinfo->num_grid] = ob; - pinfo->num_grid++; - pinfo->total_irradiance_samples += ped->num_cell; - break; + if ((probe->flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) != 0) { + eprobe->parallax_type = probe->parallax_type; + scale_m4_fl(eprobe->parallaxmat, probe->distpar); + } + else { + eprobe->parallax_type = probe->attenuation_type; + scale_m4_fl(eprobe->parallaxmat, probe->distinf); } + + mul_m4_m4m4(eprobe->parallaxmat, ob->obmat, eprobe->parallaxmat); + invert_m4(eprobe->parallaxmat); +} + +void EEVEE_lightprobes_planar_data_from_object(Object *ob, EEVEE_PlanarReflection *eplanar, EEVEE_LightProbeVisTest *vis_test) +{ + LightProbe *probe = (LightProbe *)ob->data; + float normat[4][4], imat[4][4]; + + vis_test->collection = probe->visibility_grp; + vis_test->invert = probe->flag & LIGHTPROBE_FLAG_INVERT_GROUP; + vis_test->cached = false; + + /* Computing mtx : matrix that mirror position around object's XY plane. */ + normalize_m4_m4(normat, ob->obmat); /* object > world */ + invert_m4_m4(imat, normat); /* world > object */ + /* XY reflection plane */ + imat[0][2] = -imat[0][2]; + imat[1][2] = -imat[1][2]; + imat[2][2] = -imat[2][2]; + imat[3][2] = -imat[3][2]; /* world > object > mirrored obj */ + mul_m4_m4m4(eplanar->mtx, normat, imat); /* world > object > mirrored obj > world */ + + /* Compute clip plane equation / normal. */ + copy_v3_v3(eplanar->plane_equation, ob->obmat[2]); + normalize_v3(eplanar->plane_equation); /* plane normal */ + eplanar->plane_equation[3] = -dot_v3v3(eplanar->plane_equation, ob->obmat[3]); + eplanar->clipsta = probe->clipsta; + + /* Compute XY clip planes. */ + normalize_v3_v3(eplanar->clip_vec_x, ob->obmat[0]); + normalize_v3_v3(eplanar->clip_vec_y, ob->obmat[1]); + + float vec[3] = {0.0f, 0.0f, 0.0f}; + vec[0] = 1.0f; vec[1] = 0.0f; vec[2] = 0.0f; + mul_m4_v3(ob->obmat, vec); /* Point on the edge */ + eplanar->clip_edge_x_pos = dot_v3v3(eplanar->clip_vec_x, vec); + + vec[0] = 0.0f; vec[1] = 1.0f; vec[2] = 0.0f; + mul_m4_v3(ob->obmat, vec); /* Point on the edge */ + eplanar->clip_edge_y_pos = dot_v3v3(eplanar->clip_vec_y, vec); + + vec[0] = -1.0f; vec[1] = 0.0f; vec[2] = 0.0f; + mul_m4_v3(ob->obmat, vec); /* Point on the edge */ + eplanar->clip_edge_x_neg = dot_v3v3(eplanar->clip_vec_x, vec); + + vec[0] = 0.0f; vec[1] = -1.0f; vec[2] = 0.0f; + mul_m4_v3(ob->obmat, vec); /* Point on the edge */ + eplanar->clip_edge_y_neg = dot_v3v3(eplanar->clip_vec_y, vec); + + /* Facing factors */ + float max_angle = max_ff(1e-2f, probe->falloff) * M_PI * 0.5f; + float min_angle = 0.0f; + eplanar->facing_scale = 1.0f / max_ff(1e-8f, cosf(min_angle) - cosf(max_angle)); + eplanar->facing_bias = -min_ff(1.0f - 1e-8f, cosf(max_angle)) * eplanar->facing_scale; + + /* Distance factors */ + float max_dist = probe->distinf; + float min_dist = min_ff(1.0f - 1e-8f, 1.0f - probe->falloff) * probe->distinf; + eplanar->attenuation_scale = -1.0f / max_ff(1e-8f, max_dist - min_dist); + eplanar->attenuation_bias = max_dist * -eplanar->attenuation_scale; } -/* TODO find a nice name to push it to math_matrix.c */ -static void scale_m4_v3(float R[4][4], float v[3]) +static void lightbake_planar_compute_render_matrices( + EEVEE_PlanarReflection *eplanar, DRWMatrixState *r_matstate, const float viewmat[4][4]) { - for (int i = 0; i < 4; ++i) - mul_v3_v3(R[i], v); + /* Reflect Camera Matrix. */ + mul_m4_m4m4(r_matstate->viewmat, viewmat, eplanar->mtx); + /* TODO FOV margin */ + /* Temporal sampling jitter should be already applied to the DRW_MAT_WIN. */ + DRW_viewport_matrix_get(r_matstate->winmat, DRW_MAT_WIN); + /* Apply Projection Matrix. */ + mul_m4_m4m4(r_matstate->persmat, r_matstate->winmat, r_matstate->viewmat); + + /* This is the matrix used to reconstruct texture coordinates. + * We use the original view matrix because it does not create + * visual artifacts if receiver is not perfectly aligned with + * the planar reflection probe. */ + mul_m4_m4m4(eplanar->reflectionmat, r_matstate->winmat, viewmat); /* TODO FOV margin */ + /* Convert from [-1, 1] to [0, 1] (NDC to Texture coord). */ + mul_m4_m4m4(eplanar->reflectionmat, texcomat, eplanar->reflectionmat); } -static void EEVEE_planar_reflections_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_StorageList *stl) +static void eevee_lightprobes_extract_from_cache(EEVEE_LightProbesInfo *pinfo, LightCache *lcache) { + /* copy the entire cache for now (up to MAX_PROBE) */ + /* TODO Frutum cull to only add visible probes. */ + memcpy(pinfo->probe_data, lcache->cube_data, sizeof(EEVEE_LightProbe) * max_ii(1, min_ii(lcache->cube_len, MAX_PROBE))); + /* TODO compute the max number of grid based on sample count. */ + memcpy(pinfo->grid_data, lcache->grid_data, sizeof(EEVEE_LightGrid) * max_ii(1, min_ii(lcache->grid_len, MAX_GRID))); +} + +void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) +{ + EEVEE_StorageList *stl = vedata->stl; + LightCache *light_cache = stl->g_data->light_cache; EEVEE_LightProbesInfo *pinfo = sldata->probes; - Object *ob; - for (int i = 0; (ob = pinfo->probes_planar_ref[i]) && (i < MAX_PLANAR); i++) { - LightProbe *probe = (LightProbe *)ob->data; - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); + eevee_lightprobes_extract_from_cache(sldata->probes, light_cache); - ped->probe_id = i; + DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data); + DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data); - /* Debug Display */ - if (DRW_state_draw_support() && - (probe->flag & LIGHTPROBE_FLAG_SHOW_DATA)) - { - DRW_shgroup_call_dynamic_add(stl->g_data->planar_display_shgrp, &ped->probe_id, ob->obmat); + /* For shading, save max level of the octahedron map */ + sldata->common_data.prb_lod_cube_max = (float)light_cache->mips_len - 1.0f; + sldata->common_data.prb_lod_planar_max = (float)MAX_PLANAR_LOD_LEVEL; + sldata->common_data.prb_irradiance_vis_size = light_cache->vis_res; + sldata->common_data.prb_num_render_cube = max_ii(1, light_cache->cube_len); + sldata->common_data.prb_num_render_grid = max_ii(1, light_cache->grid_len); + sldata->common_data.prb_num_planar = pinfo->num_planar; + + if (pinfo->num_planar != pinfo->cache_num_planar) { + DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_pool); + DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_depth); + pinfo->cache_num_planar = pinfo->num_planar; + } + planar_pool_ensure_alloc(vedata, pinfo->num_planar); + + /* If lightcache auto-update is enable we tag the relevant part + * of the cache to update and fire up a baking job. */ + if (!DRW_state_is_image_render() && !DRW_state_is_opengl_render() && + (pinfo->do_grid_update || pinfo->do_cube_update)) + { + const DRWContextState *draw_ctx = DRW_context_state_get(); + BLI_assert(draw_ctx->evil_C); + + if (draw_ctx->scene->eevee.flag & SCE_EEVEE_GI_AUTOBAKE) { + Scene *scene_orig = DEG_get_input_scene(draw_ctx->depsgraph); + if (scene_orig->eevee.light_cache != NULL) { + if (pinfo->do_grid_update) { + scene_orig->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_GRID; + } + /* If we update grid we need to update the cubemaps too. + * So always refresh cubemaps. */ + scene_orig->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_CUBE; + } + + /* Use a notifier to trigger the operator after drawing. */ + WM_event_add_notifier(draw_ctx->evil_C, NC_LIGHTPROBE, scene_orig); } } } -static void EEVEE_planar_reflections_updates(EEVEE_ViewLayerData *sldata) +/* -------------------------------------------------------------------- */ + +/** \name Rendering + * \{ */ + +typedef struct EEVEE_BakeRenderData{ + EEVEE_Data *vedata; + EEVEE_ViewLayerData *sldata; + struct GPUFrameBuffer **face_fb; /* should contain 6 framebuffer */ +} EEVEE_BakeRenderData; + +static void render_cubemap( + void (*callback)(int face, EEVEE_BakeRenderData *user_data), EEVEE_BakeRenderData *user_data, + const float pos[3], float clipsta, float clipend) { - EEVEE_LightProbesInfo *pinfo = sldata->probes; - Object *ob; - float mtx[4][4], normat[4][4], imat[4][4], rangemat[4][4]; + DRWMatrixState matstate; - float viewmat[4][4]; - DRW_viewport_matrix_get(viewmat, DRW_MAT_VIEW); + /* Move to capture position */ + float posmat[4][4]; + unit_m4(posmat); + negate_v3_v3(posmat[3], pos); - zero_m4(rangemat); - rangemat[0][0] = rangemat[1][1] = rangemat[2][2] = 0.5f; - rangemat[3][0] = rangemat[3][1] = rangemat[3][2] = 0.5f; - rangemat[3][3] = 1.0f; + perspective_m4(matstate.winmat, -clipsta, clipsta, -clipsta, clipsta, clipsta, clipend); + invert_m4_m4(matstate.wininv, matstate.winmat); - /* PLANAR REFLECTION */ - for (int i = 0; (ob = pinfo->probes_planar_ref[i]) && (i < MAX_PLANAR); i++) { - LightProbe *probe = (LightProbe *)ob->data; - EEVEE_PlanarReflection *eplanar = &pinfo->planar_data[i]; - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); - /* Computing mtx : matrix that mirror position around object's XY plane. */ - normalize_m4_m4(normat, ob->obmat); /* object > world */ - invert_m4_m4(imat, normat); /* world > object */ - float reflect[3] = {1.0f, 1.0f, -1.0f}; /* XY reflection plane */ - scale_m4_v3(imat, reflect); /* world > object > mirrored obj */ - mul_m4_m4m4(mtx, normat, imat); /* world > object > mirrored obj > world */ - /* Reflect Camera Matrix. */ - mul_m4_m4m4(ped->mats.mat[DRW_MAT_VIEW], viewmat, mtx); - /* TODO FOV margin */ - /* Temporal sampling jitter should be already applied to the DRW_MAT_WIN. */ - DRW_viewport_matrix_get(ped->mats.mat[DRW_MAT_WIN], DRW_MAT_WIN); - /* Apply Projection Matrix. */ - mul_m4_m4m4(ped->mats.mat[DRW_MAT_PERS], ped->mats.mat[DRW_MAT_WIN], ped->mats.mat[DRW_MAT_VIEW]); - /* This is the matrix used to reconstruct texture coordinates. - * We use the original view matrix because it does not create - * visual artifacts if receiver is not perfectly aligned with - * the planar reflection probe. */ - mul_m4_m4m4(eplanar->reflectionmat, ped->mats.mat[DRW_MAT_WIN], viewmat); /* TODO FOV margin */ - /* Convert from [-1, 1] to [0, 1] (NDC to Texture coord). */ - mul_m4_m4m4(eplanar->reflectionmat, rangemat, eplanar->reflectionmat); - - /* Compute clip plane equation / normal. */ - float refpoint[3]; - copy_v3_v3(eplanar->plane_equation, ob->obmat[2]); - normalize_v3(eplanar->plane_equation); /* plane normal */ - eplanar->plane_equation[3] = -dot_v3v3(eplanar->plane_equation, ob->obmat[3]); - - /* Compute offset plane equation (fix missing texels near reflection plane). */ - copy_v3_v3(ped->planer_eq_offset, eplanar->plane_equation); - mul_v3_v3fl(refpoint, eplanar->plane_equation, -probe->clipsta); - add_v3_v3(refpoint, ob->obmat[3]); - ped->planer_eq_offset[3] = -dot_v3v3(eplanar->plane_equation, refpoint); - - /* Compute XY clip planes. */ - normalize_v3_v3(eplanar->clip_vec_x, ob->obmat[0]); - normalize_v3_v3(eplanar->clip_vec_y, ob->obmat[1]); - - float vec[3] = {0.0f, 0.0f, 0.0f}; - vec[0] = 1.0f; vec[1] = 0.0f; vec[2] = 0.0f; - mul_m4_v3(ob->obmat, vec); /* Point on the edge */ - eplanar->clip_edge_x_pos = dot_v3v3(eplanar->clip_vec_x, vec); - - vec[0] = 0.0f; vec[1] = 1.0f; vec[2] = 0.0f; - mul_m4_v3(ob->obmat, vec); /* Point on the edge */ - eplanar->clip_edge_y_pos = dot_v3v3(eplanar->clip_vec_y, vec); - - vec[0] = -1.0f; vec[1] = 0.0f; vec[2] = 0.0f; - mul_m4_v3(ob->obmat, vec); /* Point on the edge */ - eplanar->clip_edge_x_neg = dot_v3v3(eplanar->clip_vec_x, vec); - - vec[0] = 0.0f; vec[1] = -1.0f; vec[2] = 0.0f; - mul_m4_v3(ob->obmat, vec); /* Point on the edge */ - eplanar->clip_edge_y_neg = dot_v3v3(eplanar->clip_vec_y, vec); - - /* Facing factors */ - float max_angle = max_ff(1e-2f, probe->falloff) * M_PI * 0.5f; - float min_angle = 0.0f; - eplanar->facing_scale = 1.0f / max_ff(1e-8f, cosf(min_angle) - cosf(max_angle)); - eplanar->facing_bias = -min_ff(1.0f - 1e-8f, cosf(max_angle)) * eplanar->facing_scale; - - /* Distance factors */ - float max_dist = probe->distinf; - float min_dist = min_ff(1.0f - 1e-8f, 1.0f - probe->falloff) * probe->distinf; - eplanar->attenuation_scale = -1.0f / max_ff(1e-8f, max_dist - min_dist); - eplanar->attenuation_bias = max_dist * -eplanar->attenuation_scale; + /* 1 - Render to each cubeface individually. + * We do this instead of using geometry shader because a) it's faster, + * b) it's easier than fixing the nodetree shaders (for view dependant effects). */ + for (int i = 0; i < 6; ++i) { + /* Setup custom matrices */ + mul_m4_m4m4(matstate.viewmat, cubefacemat[i], posmat); + mul_m4_m4m4(matstate.persmat, matstate.winmat, matstate.viewmat); + invert_m4_m4(matstate.persinv, matstate.persmat); + invert_m4_m4(matstate.viewinv, matstate.viewmat); + invert_m4_m4(matstate.wininv, matstate.winmat); + + DRW_viewport_matrix_override_set_all(&matstate); + + callback(i, user_data); } } -static void EEVEE_lightprobes_updates(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl, EEVEE_StorageList *stl) +static void render_reflections( + void (*callback)(int face, EEVEE_BakeRenderData *user_data), EEVEE_BakeRenderData *user_data, + EEVEE_PlanarReflection *planar_data, int ref_count) { - EEVEE_LightProbesInfo *pinfo = sldata->probes; - Object *ob; + DRWMatrixState matstate; - /* CUBE REFLECTION */ - for (int i = 1; (ob = pinfo->probes_cube_ref[i]) && (i < MAX_PROBE); i++) { - LightProbe *probe = (LightProbe *)ob->data; - EEVEE_LightProbe *eprobe = &pinfo->probe_data[i]; - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); + float original_viewmat[4][4]; + DRW_viewport_matrix_get(original_viewmat, DRW_MAT_VIEW); - /* Update transforms */ - copy_v3_v3(eprobe->position, ob->obmat[3]); + for (int i = 0; i < ref_count; ++i) { + /* Setup custom matrices */ + lightbake_planar_compute_render_matrices(planar_data + i, &matstate, original_viewmat); + invert_m4_m4(matstate.persinv, matstate.persmat); + invert_m4_m4(matstate.viewinv, matstate.viewmat); + invert_m4_m4(matstate.wininv, matstate.winmat); + DRW_viewport_matrix_override_set_all(&matstate); - /* Attenuation */ - eprobe->attenuation_type = probe->attenuation_type; - eprobe->attenuation_fac = 1.0f / max_ff(1e-8f, probe->falloff); + callback(i, user_data); + } +} - unit_m4(eprobe->attenuationmat); - scale_m4_fl(eprobe->attenuationmat, probe->distinf); - mul_m4_m4m4(eprobe->attenuationmat, ob->obmat, eprobe->attenuationmat); - invert_m4(eprobe->attenuationmat); +static void lightbake_render_world_face(int face, EEVEE_BakeRenderData *user_data) +{ + EEVEE_PassList *psl = user_data->vedata->psl; + struct GPUFrameBuffer **face_fb = user_data->face_fb; - /* Parallax */ - float dist; - if ((probe->flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) != 0) { - eprobe->parallax_type = probe->parallax_type; - dist = probe->distpar; - } - else { - eprobe->parallax_type = probe->attenuation_type; - dist = probe->distinf; - } + /* For world probe, we don't need to clear the color buffer + * since we render the background directly. */ + GPU_framebuffer_bind(face_fb[face]); + GPU_framebuffer_clear_depth(face_fb[face], 1.0f); + DRW_draw_pass(psl->probe_background); +} - unit_m4(eprobe->parallaxmat); - scale_m4_fl(eprobe->parallaxmat, dist); - mul_m4_m4m4(eprobe->parallaxmat, ob->obmat, eprobe->parallaxmat); - invert_m4(eprobe->parallaxmat); +void EEVEE_lightbake_render_world(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata, struct GPUFrameBuffer *face_fb[6]) +{ + EEVEE_BakeRenderData brdata = { + .vedata = vedata, + .face_fb = face_fb + }; - /* Debug Display */ - if (DRW_state_draw_support() && - (probe->flag & LIGHTPROBE_FLAG_SHOW_DATA)) - { - ped->probe_size = probe->data_draw_size * 0.1f; - DRW_shgroup_call_dynamic_add( - stl->g_data->cube_display_shgrp, &ped->probe_id, ob->obmat[3], &ped->probe_size); - } - } + render_cubemap(lightbake_render_world_face, &brdata, (float[3]){0.0f}, 1.0f, 10.0f); +} - /* IRRADIANCE GRID */ - int offset = 1; /* to account for the world probe */ - for (int i = 1; (ob = pinfo->probes_grid_ref[i]) && (i < MAX_GRID); i++) { - LightProbe *probe = (LightProbe *)ob->data; - EEVEE_LightGrid *egrid = &pinfo->grid_data[i]; - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); +static void lightbake_render_scene_face(int face, EEVEE_BakeRenderData *user_data) +{ + EEVEE_ViewLayerData *sldata = user_data->sldata; + EEVEE_PassList *psl = user_data->vedata->psl; + struct GPUFrameBuffer **face_fb = user_data->face_fb; - /* If one grid has move we need to recompute all the lighting. */ - if (!pinfo->grid_initialized) { - ped->updated_cells = 0; - ped->updated_lvl = 0; - ped->need_update = true; - } + /* Be sure that cascaded shadow maps are updated. */ + EEVEE_draw_shadows(sldata, psl); - /* Add one for level 0 */ - ped->max_lvl = 1.0f + floorf(log2f((float)MAX3(probe->grid_resolution_x, - probe->grid_resolution_y, - probe->grid_resolution_z))); - - egrid->offset = offset; - float fac = 1.0f / max_ff(1e-8f, probe->falloff); - egrid->attenuation_scale = fac / max_ff(1e-8f, probe->distinf); - egrid->attenuation_bias = fac; - - /* Set offset for the next grid */ - offset += ped->num_cell; - - /* Update transforms */ - float cell_dim[3], half_cell_dim[3]; - cell_dim[0] = 2.0f / (float)(probe->grid_resolution_x); - cell_dim[1] = 2.0f / (float)(probe->grid_resolution_y); - cell_dim[2] = 2.0f / (float)(probe->grid_resolution_z); - - mul_v3_v3fl(half_cell_dim, cell_dim, 0.5f); - - /* Matrix converting world space to cell ranges. */ - invert_m4_m4(egrid->mat, ob->obmat); - - /* First cell. */ - copy_v3_fl(egrid->corner, -1.0f); - add_v3_v3(egrid->corner, half_cell_dim); - mul_m4_v3(ob->obmat, egrid->corner); - - /* Opposite neighbor cell. */ - copy_v3_fl3(egrid->increment_x, cell_dim[0], 0.0f, 0.0f); - add_v3_v3(egrid->increment_x, half_cell_dim); - add_v3_fl(egrid->increment_x, -1.0f); - mul_m4_v3(ob->obmat, egrid->increment_x); - sub_v3_v3(egrid->increment_x, egrid->corner); - - copy_v3_fl3(egrid->increment_y, 0.0f, cell_dim[1], 0.0f); - add_v3_v3(egrid->increment_y, half_cell_dim); - add_v3_fl(egrid->increment_y, -1.0f); - mul_m4_v3(ob->obmat, egrid->increment_y); - sub_v3_v3(egrid->increment_y, egrid->corner); - - copy_v3_fl3(egrid->increment_z, 0.0f, 0.0f, cell_dim[2]); - add_v3_v3(egrid->increment_z, half_cell_dim); - add_v3_fl(egrid->increment_z, -1.0f); - mul_m4_v3(ob->obmat, egrid->increment_z); - sub_v3_v3(egrid->increment_z, egrid->corner); - - copy_v3_v3_int(egrid->resolution, &probe->grid_resolution_x); - - /* Visibility bias */ - egrid->visibility_bias = 0.05f * probe->vis_bias; - egrid->visibility_bleed = probe->vis_bleedbias; - egrid->visibility_range = ( - sqrtf(max_fff(len_squared_v3(egrid->increment_x), - len_squared_v3(egrid->increment_y), - len_squared_v3(egrid->increment_z))) + 1.0f); + GPU_framebuffer_bind(face_fb[face]); + GPU_framebuffer_clear_depth(face_fb[face], 1.0f); - /* Debug Display */ - if (DRW_state_draw_support() && - (probe->flag & LIGHTPROBE_FLAG_SHOW_DATA)) - { - DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_grid_display_sh, psl->probe_display); - DRW_shgroup_uniform_int(grp, "offset", &egrid->offset, 1); - DRW_shgroup_uniform_ivec3(grp, "grid_resolution", egrid->resolution, 1); - DRW_shgroup_uniform_vec3(grp, "corner", egrid->corner, 1); - DRW_shgroup_uniform_vec3(grp, "increment_x", egrid->increment_x, 1); - DRW_shgroup_uniform_vec3(grp, "increment_y", egrid->increment_y, 1); - DRW_shgroup_uniform_vec3(grp, "increment_z", egrid->increment_z, 1); - DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &sldata->irradiance_pool); - DRW_shgroup_uniform_float(grp, "sphere_size", &probe->data_draw_size, 1); - DRW_shgroup_call_instances_add(grp, DRW_cache_sphere_get(), NULL, (uint *)&ped->num_cell); - } - } + DRW_draw_pass(psl->depth_pass); + DRW_draw_pass(psl->depth_pass_cull); + DRW_draw_pass(psl->probe_background); + DRW_draw_pass(psl->material_pass); + DRW_draw_pass(psl->sss_pass); /* Only output standard pass */ + EEVEE_draw_default_passes(psl); } -void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) +/* Render the scene to the probe_rt texture. */ +void EEVEE_lightbake_render_scene( + EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUFrameBuffer *face_fb[6], + const float pos[3], float near_clip, float far_clip) { - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; + EEVEE_BakeRenderData brdata = { + .vedata = vedata, + .sldata = sldata, + .face_fb = face_fb + }; + + render_cubemap(lightbake_render_scene_face, &brdata, pos, near_clip, far_clip); +} + +static void lightbake_render_scene_reflected(int layer, EEVEE_BakeRenderData *user_data) +{ + EEVEE_Data *vedata = user_data->vedata; + EEVEE_ViewLayerData *sldata = user_data->sldata; + EEVEE_PassList *psl = vedata->psl; + EEVEE_TextureList *txl = vedata->txl; + EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_LightProbesInfo *pinfo = sldata->probes; - Object *ob; + EEVEE_PlanarReflection *eplanar = pinfo->planar_data + layer; - /* Setup enough layers. */ - /* Free textures if number mismatch. */ - if (pinfo->num_cube != pinfo->cache_num_cube) { - DRW_TEXTURE_FREE_SAFE(sldata->probe_pool); - pinfo->cache_num_cube = pinfo->num_cube; - } + GPU_framebuffer_ensure_config(&fbl->planarref_fb, { + GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_depth, layer), + GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_pool, layer) + }); - if (pinfo->num_planar != pinfo->cache_num_planar) { - DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_pool); - DRW_TEXTURE_FREE_SAFE(vedata->txl->planar_depth); - pinfo->cache_num_planar = pinfo->num_planar; - } + /* Use visibility info for this planar reflection. */ + pinfo->vis_data = pinfo->planar_vis_tests[layer]; - int irr_size[3]; - irradiance_pool_size_get(common_data->prb_irradiance_vis_size, pinfo->total_irradiance_samples, irr_size); + /* Avoid using the texture attached to framebuffer when rendering. */ + /* XXX */ + GPUTexture *tmp_planar_pool = txl->planar_pool; + GPUTexture *tmp_planar_depth = txl->planar_depth; + txl->planar_pool = e_data.planar_pool_placeholder; + txl->planar_depth = e_data.depth_array_placeholder; - if ((irr_size[0] != pinfo->cache_irradiance_size[0]) || - (irr_size[1] != pinfo->cache_irradiance_size[1]) || - (irr_size[2] != pinfo->cache_irradiance_size[2])) - { - DRW_TEXTURE_FREE_SAFE(sldata->irradiance_pool); - DRW_TEXTURE_FREE_SAFE(sldata->irradiance_rt); - copy_v3_v3_int(pinfo->cache_irradiance_size, irr_size); - } + /* Be sure that cascaded shadow maps are updated. */ + DRW_stats_group_start("Planar Reflection"); - /* XXX this should be run each frame as it ensure planar_depth is set */ - planar_pool_ensure_alloc(vedata, pinfo->num_planar); + /* Be sure that cascaded shadow maps are updated. */ + EEVEE_draw_shadows(sldata, psl); + /* Since we are rendering with an inverted view matrix, we need + * to invert the facing for backface culling to be the same. */ + DRW_state_invert_facing(); + /* Compute offset plane equation (fix missing texels near reflection plane). */ + copy_v4_v4(sldata->clip_data.clip_planes[0], eplanar->plane_equation); + sldata->clip_data.clip_planes[0][3] += eplanar->clipsta; + /* Set clipping plane */ + DRW_uniformbuffer_update(sldata->clip_ubo, &sldata->clip_data); + DRW_state_clip_planes_count_set(1); - if (!sldata->probe_pool) { - sldata->probe_pool = DRW_texture_create_2D_array(pinfo->cubemap_res, pinfo->cubemap_res, max_ff(1, pinfo->num_cube), - GPU_R11F_G11F_B10F, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); - if (sldata->probe_filter_fb) { - GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0); - } - /* Tag probes to refresh */ - pinfo->update_world |= PROBE_UPDATE_CUBE; - } + GPU_framebuffer_bind(fbl->planarref_fb); + GPU_framebuffer_clear_depth(fbl->planarref_fb, 1.0); - if ((pinfo->update_world & PROBE_UPDATE_CUBE) != 0) { - common_data->prb_num_render_cube = 0; - for (int i = 1; (ob = pinfo->probes_cube_ref[i]) && (i < MAX_PROBE); i++) { - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); - ped->need_update = true; - ped->ready_to_shade = false; - ped->probe_id = 0; - } - } + /* Slight modification: we handle refraction as normal + * shading and don't do SSRefraction. */ -#ifdef IRRADIANCE_SH_L2 - /* we need a signed format for Spherical Harmonics */ - int irradiance_format = GPU_RGBA16F; -#else - int irradiance_format = GPU_RGBA8; -#endif + DRW_draw_pass(psl->depth_pass_clip); + DRW_draw_pass(psl->depth_pass_clip_cull); + DRW_draw_pass(psl->refract_depth_pass); + DRW_draw_pass(psl->refract_depth_pass_cull); - if (!sldata->irradiance_pool || !sldata->irradiance_rt) { - if (!sldata->irradiance_pool) { - sldata->irradiance_pool = DRW_texture_create_2D_array(irr_size[0], irr_size[1], irr_size[2], - irradiance_format, DRW_TEX_FILTER, NULL); - } - if (!sldata->irradiance_rt) { - sldata->irradiance_rt = DRW_texture_create_2D_array(irr_size[0], irr_size[1], irr_size[2], - irradiance_format, DRW_TEX_FILTER, NULL); - } - /* Tag probes to refresh */ - pinfo->update_world |= PROBE_UPDATE_GRID; - pinfo->grid_initialized = false; - } + DRW_draw_pass(psl->probe_background); + EEVEE_create_minmax_buffer(vedata, tmp_planar_depth, layer); + EEVEE_occlusion_compute(sldata, vedata, tmp_planar_depth, layer); - if ((pinfo->update_world & PROBE_UPDATE_GRID) != 0) { - common_data->prb_num_render_grid = 0; - pinfo->updated_bounce = 0; - for (int i = 1; (ob = pinfo->probes_grid_ref[i]) && (i < MAX_PROBE); i++) { - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); - ped->need_update = true; - ped->updated_cells = 0; - } - } + GPU_framebuffer_bind(fbl->planarref_fb); - if (common_data->prb_num_render_grid > pinfo->num_grid) { - /* This can happen when deleting a probe. */ - common_data->prb_num_render_grid = pinfo->num_grid; + /* Shading pass */ + EEVEE_draw_default_passes(psl); + DRW_draw_pass(psl->material_pass); + DRW_draw_pass(psl->sss_pass); /* Only output standard pass */ + DRW_draw_pass(psl->refract_pass); + + /* Transparent */ + if (DRW_state_is_image_render()) { + /* Do the reordering only for offline because it can be costly. */ + DRW_pass_sort_shgroup_z(psl->transparent_pass); } + DRW_draw_pass(psl->transparent_pass); - EEVEE_planar_reflections_cache_finish(sldata, vedata->stl); + DRW_state_invert_facing(); + DRW_state_clip_planes_reset(); - EEVEE_lightprobes_updates(sldata, vedata->psl, vedata->stl); + DRW_stats_group_end(); - DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data); - DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data); + /* Restore */ + txl->planar_pool = tmp_planar_pool; + txl->planar_depth = tmp_planar_depth; } -static void downsample_planar(void *vedata, int level) +static void eevee_lightbake_render_scene_to_planars( + EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { - EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; - EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; - - const float *size = DRW_viewport_size_get(); - copy_v2_v2(stl->g_data->planar_texel_size, size); - for (int i = 0; i < level - 1; ++i) { - stl->g_data->planar_texel_size[0] /= 2.0f; - stl->g_data->planar_texel_size[1] /= 2.0f; - min_ff(floorf(stl->g_data->planar_texel_size[0]), 1.0f); - min_ff(floorf(stl->g_data->planar_texel_size[1]), 1.0f); - } - invert_v2(stl->g_data->planar_texel_size); + EEVEE_BakeRenderData brdata = { + .vedata = vedata, + .sldata = sldata, + }; - DRW_draw_pass(psl->probe_planar_downsample_ps); + render_reflections(lightbake_render_scene_reflected, &brdata, sldata->probes->planar_data, sldata->probes->num_planar); } +/** \} */ + +/* -------------------------------------------------------------------- */ -/* Glossy filter probe_rt to probe_pool at index probe_idx */ -static void glossy_filter_probe( - EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, EEVEE_PassList *psl, int probe_idx, float intensity) +/** \name Filtering + * \{ */ + +/* Glossy filter rt_color to light_cache->cube_tx.tex at index probe_idx */ +void EEVEE_lightbake_filter_glossy( + EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, + struct GPUTexture *rt_color, struct GPUFrameBuffer *fb, + int probe_idx, float intensity, int maxlevel) { + EEVEE_PassList *psl = vedata->psl; EEVEE_LightProbesInfo *pinfo = sldata->probes; + LightCache *light_cache = vedata->stl->g_data->light_cache; - pinfo->intensity_fac = intensity; + float target_size = (float)GPU_texture_width(rt_color); /* Max lod used from the render target probe */ - pinfo->lod_rt_max = floorf(log2f(pinfo->target_size)) - 2.0f; + pinfo->lod_rt_max = floorf(log2f(target_size)) - 2.0f; + pinfo->intensity_fac = intensity; /* Start fresh */ - GPU_framebuffer_ensure_config(&sldata->probe_filter_fb, { + GPU_framebuffer_ensure_config(&fb, { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_NONE }); /* 2 - Let gpu create Mipmaps for Filtered Importance Sampling. */ /* Bind next framebuffer to be able to gen. mips for probe_rt. */ - EEVEE_downsample_cube_buffer(vedata, sldata->probe_rt, (int)(pinfo->lod_rt_max)); + EEVEE_downsample_cube_buffer(vedata, rt_color, (int)(pinfo->lod_rt_max)); /* 3 - Render to probe array to the specified layer, do prefiltering. */ - float mipsize = pinfo->cubemap_res; - const int maxlevel = (int)floorf(log2f(pinfo->cubemap_res)); - const int min_lod_level = 3; - for (int i = 0; i < maxlevel - min_lod_level; i++) { + int mipsize = GPU_texture_width(light_cache->cube_tx.tex); + for (int i = 0; i < maxlevel + 1; i++) { float bias = (i == 0) ? -1.0f : 1.0f; - pinfo->texel_size = 1.0f / mipsize; - pinfo->padding_size = powf(2.0f, (float)(maxlevel - min_lod_level - 1 - i)); + pinfo->texel_size = 1.0f / (float)mipsize; + pinfo->padding_size = (float)(1 << (maxlevel - i - 1)); pinfo->padding_size *= pinfo->texel_size; pinfo->layer = probe_idx; - pinfo->roughness = (float)i / ((float)maxlevel - 4.0f); + pinfo->roughness = i / (float)maxlevel; pinfo->roughness *= pinfo->roughness; /* Disney Roughness */ pinfo->roughness *= pinfo->roughness; /* Distribute Roughness accros lod more evenly */ CLAMP(pinfo->roughness, 1e-8f, 0.99999f); /* Avoid artifacts */ @@ -1110,35 +1122,34 @@ static void glossy_filter_probe( #endif pinfo->samples_len_inv = 1.0f / pinfo->samples_len; - pinfo->lodfactor = bias + 0.5f * log((float)(pinfo->target_size * pinfo->target_size) * pinfo->samples_len_inv) / log(2); + pinfo->lodfactor = bias + 0.5f * log((float)(target_size * target_size) * pinfo->samples_len_inv) / log(2); - GPU_framebuffer_ensure_config(&sldata->probe_filter_fb, { + GPU_framebuffer_ensure_config(&fb, { GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE_MIP(sldata->probe_pool, i) + GPU_ATTACHMENT_TEXTURE_MIP(light_cache->cube_tx.tex, i) }); - GPU_framebuffer_bind(sldata->probe_filter_fb); - GPU_framebuffer_viewport_set(sldata->probe_filter_fb, 0, 0, mipsize, mipsize); + GPU_framebuffer_bind(fb); + GPU_framebuffer_viewport_set(fb, 0, 0, mipsize, mipsize); DRW_draw_pass(psl->probe_glossy_compute); mipsize /= 2; CLAMP_MIN(mipsize, 1); } - /* For shading, save max level of the octahedron map */ - sldata->common_data.prb_lod_cube_max = (float)(maxlevel - min_lod_level) - 1.0f; } -/* Diffuse filter probe_rt to irradiance_pool at index probe_idx */ -static void diffuse_filter_probe( - EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, EEVEE_PassList *psl, int offset, - float clipsta, float clipend, float vis_range, float vis_blur, float intensity) +/* Diffuse filter rt_color to light_cache->grid_tx.tex at index grid_offset */ +void EEVEE_lightbake_filter_diffuse( + EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, + struct GPUTexture *rt_color, struct GPUFrameBuffer *fb, + int grid_offset, float intensity) { - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; + EEVEE_PassList *psl = vedata->psl; EEVEE_LightProbesInfo *pinfo = sldata->probes; + LightCache *light_cache = vedata->stl->g_data->light_cache; - pinfo->intensity_fac = intensity; + float target_size = (float)GPU_texture_width(rt_color); - int pool_size[3]; - irradiance_pool_size_get(common_data->prb_irradiance_vis_size, pinfo->total_irradiance_samples, pool_size); + pinfo->intensity_fac = intensity; /* find cell position on the virtual 3D texture */ /* NOTE : Keep in sync with load_irradiance_cell() */ @@ -1152,377 +1163,114 @@ static void diffuse_filter_probe( pinfo->samples_len = 1024.0f; #endif - int cell_per_row = pool_size[0] / size[0]; - int x = size[0] * (offset % cell_per_row); - int y = size[1] * (offset / cell_per_row); + int cell_per_row = GPU_texture_width(light_cache->grid_tx.tex) / size[0]; + int x = size[0] * (grid_offset % cell_per_row); + int y = size[1] * (grid_offset / cell_per_row); #ifndef IRRADIANCE_SH_L2 /* Tweaking parameters to balance perf. vs precision */ const float bias = 0.0f; pinfo->samples_len_inv = 1.0f / pinfo->samples_len; - pinfo->lodfactor = bias + 0.5f * log((float)(pinfo->target_size * pinfo->target_size) * pinfo->samples_len_inv) / log(2); - pinfo->lod_rt_max = floorf(log2f(pinfo->target_size)) - 2.0f; + pinfo->lodfactor = bias + 0.5f * log((float)(target_size * target_size) * pinfo->samples_len_inv) / log(2); + pinfo->lod_rt_max = floorf(log2f(target_size)) - 2.0f; #else pinfo->shres = 32; /* Less texture fetches & reduce branches */ pinfo->lod_rt_max = 2.0f; /* Improve cache reuse */ #endif /* Start fresh */ - GPU_framebuffer_ensure_config(&sldata->probe_filter_fb, { + GPU_framebuffer_ensure_config(&fb, { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_NONE }); - /* 4 - Compute spherical harmonics */ - EEVEE_downsample_cube_buffer(vedata, sldata->probe_rt, (int)(pinfo->lod_rt_max)); + /* 4 - Compute diffuse irradiance */ + EEVEE_downsample_cube_buffer(vedata, rt_color, (int)(pinfo->lod_rt_max)); - GPU_framebuffer_ensure_config(&sldata->probe_filter_fb, { + GPU_framebuffer_ensure_config(&fb, { GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE_LAYER(sldata->irradiance_rt, 0) + GPU_ATTACHMENT_TEXTURE_LAYER(light_cache->grid_tx.tex, 0) }); - GPU_framebuffer_bind(sldata->probe_filter_fb); - GPU_framebuffer_viewport_set(sldata->probe_filter_fb, x, y, size[0], size[1]); + GPU_framebuffer_bind(fb); + GPU_framebuffer_viewport_set(fb, x, y, size[0], size[1]); DRW_draw_pass(psl->probe_diffuse_compute); - - /* World irradiance have no visibility */ - if (offset > 0) { - /* Compute visibility */ - pinfo->samples_len = 512.0f; /* TODO refine */ - pinfo->samples_len_inv = 1.0f / pinfo->samples_len; - pinfo->shres = common_data->prb_irradiance_vis_size; - pinfo->visibility_range = vis_range; - pinfo->visibility_blur = vis_blur; - pinfo->near_clip = -clipsta; - pinfo->far_clip = -clipend; - pinfo->texel_size = 1.0f / (float)common_data->prb_irradiance_vis_size; - - int cell_per_col = pool_size[1] / common_data->prb_irradiance_vis_size; - cell_per_row = pool_size[0] / common_data->prb_irradiance_vis_size; - x = common_data->prb_irradiance_vis_size * (offset % cell_per_row); - y = common_data->prb_irradiance_vis_size * ((offset / cell_per_row) % cell_per_col); - int layer = 1 + ((offset / cell_per_row) / cell_per_col); - const int vis_size = common_data->prb_irradiance_vis_size; - - GPU_framebuffer_ensure_config(&sldata->probe_filter_fb, { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE_LAYER(sldata->irradiance_rt, layer) - }); - GPU_framebuffer_bind(sldata->probe_filter_fb); - GPU_framebuffer_viewport_set(sldata->probe_filter_fb, x, y, vis_size, vis_size); - DRW_draw_pass(psl->probe_visibility_compute); - } } -/* Render the scene to the probe_rt texture. */ -static void render_scene_to_probe( +/* Filter rt_depth to light_cache->grid_tx.tex at index grid_offset */ +void EEVEE_lightbake_filter_visibility( EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, - const float pos[3], float clipsta, float clipend) + struct GPUTexture *UNUSED(rt_depth), struct GPUFrameBuffer *fb, + int grid_offset, float clipsta, float clipend, + float vis_range, float vis_blur, int vis_size) { - EEVEE_TextureList *txl = vedata->txl; EEVEE_PassList *psl = vedata->psl; EEVEE_LightProbesInfo *pinfo = sldata->probes; + LightCache *light_cache = vedata->stl->g_data->light_cache; - DRWMatrixState matstate; - float (*viewmat)[4] = matstate.mat[DRW_MAT_VIEW]; - float (*viewinv)[4] = matstate.mat[DRW_MAT_VIEWINV]; - float (*persmat)[4] = matstate.mat[DRW_MAT_PERS]; - float (*persinv)[4] = matstate.mat[DRW_MAT_PERSINV]; - float (*winmat)[4] = matstate.mat[DRW_MAT_WIN]; - float (*wininv)[4] = matstate.mat[DRW_MAT_WININV]; - - float posmat[4][4]; - unit_m4(posmat); - - /* Move to capture position */ - negate_v3_v3(posmat[3], pos); - - /* 1 - Render to each cube-face individually. - * We do this instead of using geometry shader because a) it's faster, - * b) it's easier than fixing the node-tree shaders (for view dependent effects). */ - pinfo->layer = 0; - perspective_m4(winmat, -clipsta, clipsta, -clipsta, clipsta, clipsta, clipend); - - /* Avoid using the texture attached to framebuffer when rendering. */ - /* XXX */ - GPUTexture *tmp_planar_pool = txl->planar_pool; - GPUTexture *tmp_maxz = txl->maxzbuffer; - txl->planar_pool = e_data.planar_pool_placeholder; - txl->maxzbuffer = e_data.depth_placeholder; - - DRW_stats_group_start("Cubemap Render"); - - /* Update common uniforms */ - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); - - for (int i = 0; i < 6; ++i) { - /* Recompute only on 1st drawloop. */ - pinfo->vis_data.cached = (i != 0); - - DRW_stats_group_start("Cubemap Face"); - - /* Setup custom matrices */ - mul_m4_m4m4(viewmat, cubefacemat[i], posmat); - mul_m4_m4m4(persmat, winmat, viewmat); - invert_m4_m4(persinv, persmat); - invert_m4_m4(viewinv, viewmat); - invert_m4_m4(wininv, winmat); - - DRW_viewport_matrix_override_set_all(&matstate); - - /* Be sure that cascaded shadow maps are updated. */ - EEVEE_draw_shadows(sldata, psl); - - GPU_framebuffer_bind(sldata->probe_face_fb[i]); - GPU_framebuffer_clear_depth(sldata->probe_face_fb[i], 1.0); - - /* Depth prepass */ - DRW_draw_pass(psl->depth_pass); - DRW_draw_pass(psl->depth_pass_cull); - - DRW_draw_pass(psl->probe_background); - - // EEVEE_create_minmax_buffer(vedata, sldata->probe_depth_rt); - - /* Rebind Target FB */ - GPU_framebuffer_bind(sldata->probe_face_fb[i]); - - /* Shading pass */ - EEVEE_draw_default_passes(psl); - DRW_draw_pass(psl->material_pass); - DRW_draw_pass(psl->sss_pass); /* Only output standard pass */ - - DRW_stats_group_end(); - } - - DRW_stats_group_end(); - - /* Make sure no aditionnal visibility check runs after this. */ - pinfo->vis_data.collection = NULL; - - /* Restore */ - txl->planar_pool = tmp_planar_pool; - txl->maxzbuffer = tmp_maxz; -} - -static void render_scene_to_planar( - EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, int layer, - EEVEE_LightProbeEngineData *ped) -{ - EEVEE_LightProbesInfo *pinfo = sldata->probes; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - EEVEE_PassList *psl = vedata->psl; - - float (*viewmat)[4] = ped->mats.mat[DRW_MAT_VIEW]; - float (*viewinv)[4] = ped->mats.mat[DRW_MAT_VIEWINV]; - float (*persmat)[4] = ped->mats.mat[DRW_MAT_PERS]; - float (*persinv)[4] = ped->mats.mat[DRW_MAT_PERSINV]; - float (*winmat)[4] = ped->mats.mat[DRW_MAT_WIN]; - float (*wininv)[4] = ped->mats.mat[DRW_MAT_WININV]; - - invert_m4_m4(viewinv, viewmat); - invert_m4_m4(persinv, persmat); - invert_m4_m4(wininv, winmat); - - DRW_stats_group_start("Planar Reflection"); - - DRW_viewport_matrix_override_set_all(&ped->mats); - - /* Don't reuse previous visibility. */ - pinfo->vis_data.cached = false; - - /* Be sure that cascaded shadow maps are updated. */ - EEVEE_draw_shadows(sldata, psl); - - /* Since we are rendering with an inverted view matrix, we need - * to invert the facing for back-face culling to be the same. */ - DRW_state_invert_facing(); - /* Set clipping plan */ - copy_v4_v4(sldata->clip_data.clip_planes[0], ped->planer_eq_offset); - DRW_uniformbuffer_update(sldata->clip_ubo, &sldata->clip_data); - DRW_state_clip_planes_count_set(1); - - GPU_framebuffer_ensure_config(&fbl->planarref_fb, { - GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_depth, layer), - GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_pool, layer) + pinfo->samples_len = 512.0f; /* TODO refine */ + pinfo->samples_len_inv = 1.0f / pinfo->samples_len; + pinfo->shres = vis_size; + pinfo->visibility_range = vis_range; + pinfo->visibility_blur = vis_blur; + pinfo->near_clip = -clipsta; + pinfo->far_clip = -clipend; + pinfo->texel_size = 1.0f / (float)vis_size; + + int cell_per_col = GPU_texture_height(light_cache->grid_tx.tex) / vis_size; + int cell_per_row = GPU_texture_width(light_cache->grid_tx.tex) / vis_size; + int x = vis_size * (grid_offset % cell_per_row); + int y = vis_size * ((grid_offset / cell_per_row) % cell_per_col); + int layer = 1 + ((grid_offset / cell_per_row) / cell_per_col); + + GPU_framebuffer_ensure_config(&fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE_LAYER(light_cache->grid_tx.tex, layer) }); - - GPU_framebuffer_bind(fbl->planarref_fb); - GPU_framebuffer_clear_depth(fbl->planarref_fb, 1.0); - - /* Avoid using the texture attached to framebuffer when rendering. */ - /* XXX */ - GPUTexture *tmp_planar_pool = txl->planar_pool; - GPUTexture *tmp_planar_depth = txl->planar_depth; - txl->planar_pool = e_data.planar_pool_placeholder; - txl->planar_depth = e_data.depth_array_placeholder; - - /* Slight modification: we handle refraction as normal - * shading and don't do SSRefraction. */ - - /* Depth prepass */ - DRW_draw_pass(psl->depth_pass_clip); - DRW_draw_pass(psl->depth_pass_clip_cull); - DRW_draw_pass(psl->refract_depth_pass); - DRW_draw_pass(psl->refract_depth_pass_cull); - - /* Background */ - DRW_draw_pass(psl->probe_background); - - EEVEE_create_minmax_buffer(vedata, tmp_planar_depth, layer); - - /* Compute GTAO Horizons */ - EEVEE_occlusion_compute(sldata, vedata, tmp_planar_depth, layer); - - /* Rebind Planar FB */ - GPU_framebuffer_bind(fbl->planarref_fb); - - /* Shading pass */ - EEVEE_draw_default_passes(psl); - DRW_draw_pass(psl->material_pass); - DRW_draw_pass(psl->sss_pass); /* Only output standard pass */ - DRW_draw_pass(psl->refract_pass); - - /* Transparent */ - if (DRW_state_is_image_render()) { - /* Do the reordering only for offline because it can be costly. */ - DRW_pass_sort_shgroup_z(psl->transparent_pass); - } - DRW_draw_pass(psl->transparent_pass); - - DRW_state_invert_facing(); - DRW_state_clip_planes_reset(); - - DRW_stats_group_end(); - - /* Make sure no aditionnal visibility check runs after this. */ - pinfo->vis_data.collection = NULL; - - /* Restore */ - txl->planar_pool = tmp_planar_pool; - txl->planar_depth = tmp_planar_depth; + GPU_framebuffer_bind(fb); + GPU_framebuffer_viewport_set(fb, x, y, vis_size, vis_size); + DRW_draw_pass(psl->probe_visibility_compute); } -static void render_world_to_probe(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl) +/* Actually a simple downsampling */ +static void downsample_planar(void *vedata, int level) { - EEVEE_LightProbesInfo *pinfo = sldata->probes; - DRWMatrixState matstate; - float (*viewmat)[4] = matstate.mat[DRW_MAT_VIEW]; - float (*viewinv)[4] = matstate.mat[DRW_MAT_VIEWINV]; - float (*persmat)[4] = matstate.mat[DRW_MAT_PERS]; - float (*persinv)[4] = matstate.mat[DRW_MAT_PERSINV]; - float (*winmat)[4] = matstate.mat[DRW_MAT_WIN]; - float (*wininv)[4] = matstate.mat[DRW_MAT_WININV]; - - /* For world probe, we don't need to clear since we render the background directly. */ - pinfo->layer = 0; - - perspective_m4(winmat, -0.1f, 0.1f, -0.1f, 0.1f, 0.1f, 1.0f); - invert_m4_m4(wininv, winmat); - - for (int i = 0; i < 6; ++i) { - /* Setup custom matrices */ - copy_m4_m4(viewmat, cubefacemat[i]); - mul_m4_m4m4(persmat, winmat, viewmat); - invert_m4_m4(persinv, persmat); - invert_m4_m4(viewinv, viewmat); - DRW_viewport_matrix_override_set_all(&matstate); + EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; + EEVEE_StorageList *stl = ((EEVEE_Data *)vedata)->stl; - GPU_framebuffer_bind(sldata->probe_face_fb[i]); - GPU_framebuffer_clear_depth(sldata->probe_face_fb[i], 1.0f); - DRW_draw_pass(psl->probe_background); + const float *size = DRW_viewport_size_get(); + copy_v2_v2(stl->g_data->planar_texel_size, size); + for (int i = 0; i < level - 1; ++i) { + stl->g_data->planar_texel_size[0] /= 2.0f; + stl->g_data->planar_texel_size[1] /= 2.0f; + min_ff(floorf(stl->g_data->planar_texel_size[0]), 1.0f); + min_ff(floorf(stl->g_data->planar_texel_size[1]), 1.0f); } -} - -static void lightprobe_cell_grid_location_get(EEVEE_LightGrid *egrid, int cell_idx, float r_local_cell[3]) -{ - /* Keep in sync with lightprobe_grid_display_vert */ - r_local_cell[2] = (float)(cell_idx % egrid->resolution[2]); - r_local_cell[1] = (float)((cell_idx / egrid->resolution[2]) % egrid->resolution[1]); - r_local_cell[0] = (float)(cell_idx / (egrid->resolution[2] * egrid->resolution[1])); -} + invert_v2(stl->g_data->planar_texel_size); -static void lightprobe_cell_world_location_get(EEVEE_LightGrid *egrid, float local_cell[3], float r_pos[3]) -{ - float tmp[3]; - - copy_v3_v3(r_pos, egrid->corner); - mul_v3_v3fl(tmp, egrid->increment_x, local_cell[0]); - add_v3_v3(r_pos, tmp); - mul_v3_v3fl(tmp, egrid->increment_y, local_cell[1]); - add_v3_v3(r_pos, tmp); - mul_v3_v3fl(tmp, egrid->increment_z, local_cell[2]); - add_v3_v3(r_pos, tmp); + DRW_draw_pass(psl->probe_planar_downsample_ps); } -static void lightprobes_refresh_world(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) +static void EEVEE_lightbake_filter_planar(EEVEE_Data *vedata) { - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - DRWMatrixState saved_mats; - - /* We need to save the Matrices before overidding them */ - DRW_viewport_matrix_get_all(&saved_mats); - - render_world_to_probe(sldata, psl); - if (pinfo->update_world & PROBE_UPDATE_CUBE) { - glossy_filter_probe(sldata, vedata, psl, 0, 1.0); - common_data->prb_num_render_cube = 1; - } - if (pinfo->update_world & PROBE_UPDATE_GRID) { - diffuse_filter_probe(sldata, vedata, psl, 0, 0.0, 0.0, 0.0, 0.0, 1.0); - - SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt); + EEVEE_TextureList *txl = vedata->txl; + EEVEE_FramebufferList *fbl = vedata->fbl; - GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0); - GPU_framebuffer_bind(sldata->probe_filter_fb); - DRW_draw_pass(psl->probe_grid_fill); + DRW_stats_group_start("Planar Probe Downsample"); - common_data->prb_num_render_grid = 1; - /* Reset volume history. */ - stl->effects->volume_current_sample = -1; - common_data->vol_history_alpha = 0.0f; - } - pinfo->update_world = 0; - DRW_viewport_request_redraw(); - /* Do not let this frame accumulate. */ - stl->effects->taa_current_sample = 1; + GPU_framebuffer_ensure_config(&fbl->planar_downsample_fb, { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(txl->planar_pool) + }); - DRW_viewport_matrix_override_set_all(&saved_mats); + GPU_framebuffer_recursive_downsample(fbl->planar_downsample_fb, MAX_PLANAR_LOD_LEVEL, &downsample_planar, vedata); + DRW_stats_group_end(); } -static void lightprobes_refresh_initialize_grid(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_LightProbesInfo *pinfo = sldata->probes; - EEVEE_PassList *psl = vedata->psl; - if (pinfo->grid_initialized) { - /* Grid is already initialized, nothing to do. */ - return; - } - /* Flood fill with world irradiance. */ - GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0); - GPU_framebuffer_bind(sldata->probe_filter_fb); - DRW_draw_pass(psl->probe_grid_fill); - - SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt); - - GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0); - GPU_framebuffer_bind(sldata->probe_filter_fb); - DRW_draw_pass(psl->probe_grid_fill); - - SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt); - - pinfo->grid_initialized = true; -} +/** \} */ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_FramebufferList *fbl = vedata->fbl; - EEVEE_TextureList *txl = vedata->txl; - Object *ob; EEVEE_LightProbesInfo *pinfo = sldata->probes; DRWMatrixState saved_mats; @@ -1533,9 +1281,6 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v return; } - EEVEE_planar_reflections_updates(sldata); - DRW_uniformbuffer_update(sldata->planar_ubo, &sldata->probes->planar_data); - /* We need to save the Matrices before overidding them */ DRW_viewport_matrix_get_all(&saved_mats); @@ -1547,13 +1292,13 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); - for (int i = 0; (ob = pinfo->probes_planar_ref[i]) && (i < MAX_PLANAR); i++) { - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); - LightProbe *prb = (LightProbe *)ob->data; - pinfo->vis_data.collection = prb->visibility_grp; - pinfo->vis_data.invert = prb->flag & LIGHTPROBE_FLAG_INVERT_GROUP; - render_scene_to_planar(sldata, vedata, i, ped); - } + /* Rendering happens here! */ + eevee_lightbake_render_scene_to_planars(sldata, vedata); + + /* Make sure no aditionnal visibility check runs after this. */ + pinfo->vis_data.collection = NULL; + + DRW_uniformbuffer_update(sldata->planar_ubo, &sldata->probes->planar_data); /* Restore */ common_data->prb_num_planar = pinfo->num_planar; @@ -1562,17 +1307,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v /* Prefilter for SSR */ if ((vedata->stl->effects->enabled_effects & EFFECT_SSR) != 0) { - const int max_lod = 9; - DRW_stats_group_start("Planar Probe Downsample"); - - GPU_framebuffer_ensure_config(&fbl->planar_downsample_fb, { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(txl->planar_pool) - }); - GPU_framebuffer_recursive_downsample(fbl->planar_downsample_fb, max_lod, &downsample_planar, vedata); - /* For shading, save max level of the planar map */ - common_data->prb_lod_planar_max = (float)(max_lod); - DRW_stats_group_end(); + EEVEE_lightbake_filter_planar(vedata); } DRW_viewport_matrix_override_set_all(&saved_mats); @@ -1586,239 +1321,18 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v common_data->ssr_toggle = vedata->stl->g_data->valid_double_buffer; } -static void lightprobes_refresh_cube(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - Object *ob; - for (int i = 1; (ob = pinfo->probes_cube_ref[i]) && (i < MAX_PROBE); i++) { - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); - if (!ped->need_update) { - continue; - } - LightProbe *prb = (LightProbe *)ob->data; - pinfo->vis_data.collection = prb->visibility_grp; - pinfo->vis_data.invert = prb->flag & LIGHTPROBE_FLAG_INVERT_GROUP; - render_scene_to_probe(sldata, vedata, ob->obmat[3], prb->clipsta, prb->clipend); - glossy_filter_probe(sldata, vedata, psl, i, prb->intensity); - ped->need_update = false; - ped->probe_id = i; - if (!ped->ready_to_shade) { - common_data->prb_num_render_cube++; - ped->ready_to_shade = true; - } -#if 0 - printf("Update Cubemap %d\n", i); -#endif - DRW_viewport_request_redraw(); - /* Do not let this frame accumulate. */ - stl->effects->taa_current_sample = 1; - /* Only do one probe per frame */ - return; - } - - pinfo->do_cube_update = false; -} - -static void lightprobes_refresh_all_no_world(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) -{ - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_PassList *psl = vedata->psl; - EEVEE_StorageList *stl = vedata->stl; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - Object *ob; - const DRWContextState *draw_ctx = DRW_context_state_get(); - RegionView3D *rv3d = draw_ctx->rv3d; - - if (draw_ctx->evil_C != NULL) { - /* Only compute probes if not navigating or in playback */ - struct wmWindowManager *wm = CTX_wm_manager(draw_ctx->evil_C); - if (((rv3d->rflag & RV3D_NAVIGATING) != 0) || ED_screen_animation_no_scrub(wm) != NULL) { - return; - } - } - /* We need to save the Matrices before overidding them */ - DRWMatrixState saved_mats; - DRW_viewport_matrix_get_all(&saved_mats); - /* Make sure grid is initialized. */ - lightprobes_refresh_initialize_grid(sldata, vedata); - /* Reflection probes depend on diffuse lighting thus on irradiance grid, - * so update them first. */ - while (pinfo->updated_bounce < pinfo->num_bounce) { - common_data->prb_num_render_grid = pinfo->num_grid; - /* TODO(sergey): This logic can be split into smaller functions. */ - for (int i = 1; (ob = pinfo->probes_grid_ref[i]) && (i < MAX_GRID); i++) { - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); - if (!ped->need_update) { - continue; - } - EEVEE_LightGrid *egrid = &pinfo->grid_data[i]; - LightProbe *prb = (LightProbe *)ob->data; - /* Find the next cell corresponding to the current level. */ - bool valid_cell = false; - int cell_id = ped->updated_cells; - float pos[3], grid_loc[3]; - /* Other levels */ - int current_stride = 1 << max_ii(0, ped->max_lvl - ped->updated_lvl); - int prev_stride = current_stride << 1; - bool do_rendering = true; - while (!valid_cell) { - cell_id = ped->updated_cells; - lightprobe_cell_grid_location_get(egrid, cell_id, grid_loc); - if (ped->updated_lvl == 0 && cell_id == 0) { - valid_cell = true; - ped->updated_cells = ped->num_cell; - continue; - } - else if (((((int)grid_loc[0] % current_stride) == 0) && - (((int)grid_loc[1] % current_stride) == 0) && - (((int)grid_loc[2] % current_stride) == 0)) && - !((((int)grid_loc[0] % prev_stride) == 0) && - (((int)grid_loc[1] % prev_stride) == 0) && - (((int)grid_loc[2] % prev_stride) == 0))) - { - valid_cell = true; - } - ped->updated_cells++; - if (ped->updated_cells > ped->num_cell) { - do_rendering = false; - break; - } - } - if (do_rendering) { - lightprobe_cell_world_location_get(egrid, grid_loc, pos); - SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt); - /* Temporary Remove all probes. */ - int tmp_num_render_grid = common_data->prb_num_render_grid; - int tmp_num_render_cube = common_data->prb_num_render_cube; - int tmp_num_planar = common_data->prb_num_planar; - float tmp_level_bias = egrid->level_bias; - common_data->prb_num_render_cube = 0; - common_data->prb_num_planar = 0; - /* Use light from previous bounce when capturing radiance. */ - if (pinfo->updated_bounce == 0) { - /* But not on first bounce. */ - common_data->prb_num_render_grid = 0; - } - else { - /* Remove bias */ - egrid->level_bias = (float)(1 << 0); - DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data); - } - pinfo->vis_data.collection = prb->visibility_grp; - pinfo->vis_data.invert = prb->flag & LIGHTPROBE_FLAG_INVERT_GROUP; - render_scene_to_probe(sldata, vedata, pos, prb->clipsta, prb->clipend); - diffuse_filter_probe(sldata, vedata, psl, egrid->offset + cell_id, - prb->clipsta, prb->clipend, egrid->visibility_range, prb->vis_blur, - prb->intensity); - /* To see what is going on. */ - SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt); - /* Restore */ - common_data->prb_num_render_cube = tmp_num_render_cube; - pinfo->num_planar = tmp_num_planar; - if (pinfo->updated_bounce == 0) { - common_data->prb_num_render_grid = tmp_num_render_grid; - } - else { - egrid->level_bias = tmp_level_bias; - DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data); - } -#if 0 - printf("Updated Grid %d : cell %d / %d, bounce %d / %d\n", - i, cell_id + 1, ped->num_cell, pinfo->updated_bounce + 1, pinfo->num_bounce); -#endif - } - if (ped->updated_cells >= ped->num_cell) { - ped->updated_lvl++; - ped->updated_cells = 0; - if (ped->updated_lvl > ped->max_lvl) { - ped->need_update = false; - } - egrid->level_bias = (float)(1 << max_ii(0, ped->max_lvl - ped->updated_lvl + 1)); - DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data); - } - /* Only do one probe per frame */ - DRW_viewport_request_redraw(); - /* Do not let this frame accumulate. */ - stl->effects->taa_current_sample = 1; - /* Reset volume history. */ - stl->effects->volume_current_sample = -1; - common_data->vol_history_alpha = 0.0f; - /* Restore matrices */ - DRW_viewport_matrix_override_set_all(&saved_mats); - return; - } - - pinfo->updated_bounce++; - common_data->prb_num_render_grid = pinfo->num_grid; - - if (pinfo->updated_bounce < pinfo->num_bounce) { - /* Retag all grids to update for next bounce */ - for (int i = 1; (ob = pinfo->probes_grid_ref[i]) && (i < MAX_GRID); i++) { - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); - ped->need_update = true; - ped->updated_cells = 0; - ped->updated_lvl = 0; - } - /* Reset the next buffer so we can see the progress. */ - /* irradiance_rt is already the next rt because of the previous SWAP */ - GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0); - GPU_framebuffer_bind(sldata->probe_filter_fb); - DRW_draw_pass(psl->probe_grid_fill); - - GPU_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0); - /* Swap AFTER */ - SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt); - } - } - /* Refresh cube probe when needed. */ - lightprobes_refresh_cube(sldata, vedata); - /* Restore matrices */ - DRW_viewport_matrix_override_set_all(&saved_mats); -} - -bool EEVEE_lightprobes_all_probes_ready(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(vedata)) -{ - EEVEE_LightProbesInfo *pinfo = sldata->probes; - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - - return ((pinfo->do_cube_update == false) && - (pinfo->updated_bounce == pinfo->num_bounce) && - (common_data->prb_num_render_cube == pinfo->num_cube)); -} - void EEVEE_lightprobes_refresh(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { - EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; - EEVEE_LightProbesInfo *pinfo = sldata->probes; - - /* Disable specular lighting when rendering probes to avoid feedback loops (looks bad). */ - common_data->spec_toggle = false; - common_data->ssr_toggle = false; - common_data->sss_toggle = false; - - /* Disable AO until we find a way to hide really bad discontinuities between cubefaces. */ - float tmp_ao_dist = common_data->ao_dist; - float tmp_ao_settings = common_data->ao_settings; - common_data->ao_settings = 0.0f; - common_data->ao_dist = 0.0f; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); + LightCache *light_cache = vedata->stl->g_data->light_cache; - /* Render world in priority */ - if (pinfo->update_world) { - lightprobes_refresh_world(sldata, vedata); - } - else if (EEVEE_lightprobes_all_probes_ready(sldata, vedata) == false && pinfo->all_materials_updated) { - lightprobes_refresh_all_no_world(sldata, vedata); + if (light_cache->flag & LIGHTCACHE_UPDATE_WORLD) { + DRWMatrixState saved_mats; + DRW_viewport_matrix_get_all(&saved_mats); + EEVEE_lightbake_update_world_quick(sldata, vedata, scene_eval); + DRW_viewport_matrix_override_set_all(&saved_mats); } - - /* Restore */ - common_data->spec_toggle = true; - common_data->ssr_toggle = true; - common_data->sss_toggle = true; - common_data->ao_dist = tmp_ao_dist; - common_data->ao_settings = tmp_ao_settings; } void EEVEE_lightprobes_free(void) diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c index 9214544fb17..0f0d9d281c9 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ b/source/blender/draw/engines/eevee/eevee_lights.c @@ -148,7 +148,7 @@ void EEVEE_lights_init(EEVEE_ViewLayerData *sldata) DRW_TEXTURE_FREE_SAFE(sldata->shadow_cube_blur); /* Compute adequate size for the octahedral map. */ - linfo->shadow_cube_store_size = (int)ceil(sqrt((sh_cube_size * sh_cube_size) * 6.0f)); + linfo->shadow_cube_store_size = OCTAHEDRAL_SIZE_FROM_CUBESIZE(sh_cube_size); CLAMP(linfo->shadow_cube_store_size, 1, 4096); CLAMP(sh_cube_size, 1, 4096); diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index 01599177fa4..2e568d97c07 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -30,15 +30,31 @@ #include "DNA_screen_types.h" #include "DNA_world_types.h" +#include "DEG_depsgraph_query.h" + #include "ED_screen.h" #include "eevee_private.h" +#include "eevee_lightcache.h" + +static void eevee_lookdev_lightcache_delete(EEVEE_Data *vedata) +{ + EEVEE_StorageList *stl = vedata->stl; + EEVEE_TextureList *txl = vedata->txl; + + MEM_SAFE_FREE(stl->lookdev_lightcache); + MEM_SAFE_FREE(stl->lookdev_grid_data); + MEM_SAFE_FREE(stl->lookdev_cube_data); + DRW_TEXTURE_FREE_SAFE(txl->lookdev_grid_tx); + DRW_TEXTURE_FREE_SAFE(txl->lookdev_cube_tx); +} void EEVEE_lookdev_cache_init( EEVEE_Data *vedata, DRWShadingGroup **grp, GPUShader *shader, DRWPass *pass, World *world, EEVEE_LightProbesInfo *pinfo) { EEVEE_StorageList *stl = vedata->stl; + EEVEE_TextureList *txl = vedata->txl; const DRWContextState *draw_ctx = DRW_context_state_get(); View3D *v3d = draw_ctx->v3d; if (LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d)) { @@ -47,6 +63,43 @@ void EEVEE_lookdev_cache_init( struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get(); GPUTexture *tex = NULL; + /* If one of the component is missing we start from scratch. */ + if ((stl->lookdev_grid_data == NULL) || + (stl->lookdev_cube_data == NULL) || + (txl->lookdev_grid_tx == NULL) || + (txl->lookdev_cube_tx == NULL)) + { + eevee_lookdev_lightcache_delete(vedata); + } + + if (stl->lookdev_lightcache == NULL) { + const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); +#if defined(IRRADIANCE_SH_L2) + int grid_res = 4; +#elif defined(IRRADIANCE_CUBEMAP) + int grid_res = 8; +#elif defined(IRRADIANCE_HL2) + int grid_res = 4; +#endif + int cube_res = OCTAHEDRAL_SIZE_FROM_CUBESIZE(scene_eval->eevee.gi_cubemap_resolution); + int vis_res = scene_eval->eevee.gi_visibility_resolution; + + stl->lookdev_lightcache = EEVEE_lightcache_create(1, 1, cube_res, vis_res, (int[3]){grid_res, grid_res, 1}); + + /* We do this to use a special light cache for lookdev. + * This lightcache needs to be per viewport. But we need to + * have correct freeing when the viewport is closed. So we + * need to reference all textures to the txl and the memblocks + * to the stl. */ + stl->lookdev_grid_data = stl->lookdev_lightcache->grid_data; + stl->lookdev_cube_data = stl->lookdev_lightcache->cube_data; + stl->lookdev_cube_mips = stl->lookdev_lightcache->cube_mips; + txl->lookdev_grid_tx = stl->lookdev_lightcache->grid_tx.tex; + txl->lookdev_cube_tx = stl->lookdev_lightcache->cube_tx.tex; + } + + stl->g_data->light_cache = stl->lookdev_lightcache; + *grp = DRW_shgroup_create(shader, pass); axis_angle_to_mat3_single(stl->g_data->studiolight_matrix, 'Z', v3d->shading.studiolight_rot_z); DRW_shgroup_uniform_mat3(*grp, "StudioLightMatrix", stl->g_data->studiolight_matrix); @@ -77,11 +130,9 @@ void EEVEE_lookdev_cache_init( ((pinfo->studiolight_index != sl->index) || (pinfo->studiolight_rot_z != v3d->shading.studiolight_rot_z))) { - pinfo->update_world |= PROBE_UPDATE_ALL; + stl->lookdev_lightcache->flag |= LIGHTCACHE_UPDATE_WORLD; pinfo->studiolight_index = sl->index; pinfo->studiolight_rot_z = v3d->shading.studiolight_rot_z; - pinfo->prev_wo_sh_compiled = false; - pinfo->prev_world = NULL; } } } diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 4a7e97756a9..20d755d2245 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -362,6 +362,8 @@ static void add_standard_uniforms( DRWShadingGroup *shgrp, EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, int *ssr_id, float *refract_depth, bool use_ssrefraction, bool use_alpha_blend) { + LightCache *lcache = vedata->stl->g_data->light_cache; + if (ssr_id == NULL) { static int no_ssr = -1.0f; ssr_id = &no_ssr; @@ -393,12 +395,12 @@ static void add_standard_uniforms( /* TODO if diffuse bsdf */ if (true) { - DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &sldata->irradiance_pool); + DRW_shgroup_uniform_texture_ref(shgrp, "irradianceGrid", &lcache->grid_tx.tex); } /* TODO if glossy bsdf */ if (true) { - DRW_shgroup_uniform_texture_ref(shgrp, "probeCubes", &sldata->probe_pool); + DRW_shgroup_uniform_texture_ref(shgrp, "probeCubes", &lcache->cube_tx.tex); DRW_shgroup_uniform_texture_ref(shgrp, "probePlanars", &vedata->txl->planar_pool); DRW_shgroup_uniform_int(shgrp, "outputSsrId", ssr_id, 1); } @@ -972,7 +974,6 @@ void EEVEE_materials_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_call_add(grp, geom, NULL); break; case GPU_MAT_QUEUED: - sldata->probes->all_materials_updated = false; /* TODO Bypass probe compilation. */ col = compile_col; break; @@ -1229,7 +1230,6 @@ static void material_opaque( } case GPU_MAT_QUEUED: { - sldata->probes->all_materials_updated = false; /* TODO Bypass probe compilation. */ color_p = compile_col; metal_p = spec_p = rough_p = ½ @@ -1316,7 +1316,6 @@ static void material_transparent( } case GPU_MAT_QUEUED: { - sldata->probes->all_materials_updated = false; /* TODO Bypass probe compilation. */ color_p = compile_col; metal_p = spec_p = rough_p = ½ @@ -1646,7 +1645,6 @@ void EEVEE_hair_cache_populate(EEVEE_Data *vedata, EEVEE_ViewLayerData *sldata, } case GPU_MAT_QUEUED: { - sldata->probes->all_materials_updated = false; color_p = compile_col; metal_p = spec_p = rough_p = ½ break; diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index fe837baf20e..e2a875dca1f 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -26,11 +26,14 @@ #ifndef __EEVEE_PRIVATE_H__ #define __EEVEE_PRIVATE_H__ +#include "DNA_lightprobe_types.h" + struct Object; struct EEVEE_BoundSphere; struct EEVEE_ShadowCasterBuffer; struct RenderLayer; struct RenderResult; +struct GPUFrameBuffer; extern struct DrawEngineType draw_engine_eevee_type; @@ -98,6 +101,10 @@ extern struct DrawEngineType draw_engine_eevee_type; #define USE_SCENE_LIGHT(v3d) ((!v3d) || (!LOOK_DEV_MODE_ENABLED(v3d)) || ((LOOK_DEV_MODE_ENABLED(v3d) && (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)))) #define LOOK_DEV_STUDIO_LIGHT_ENABLED(v3d) (LOOK_DEV_MODE_ENABLED(v3d) && !(v3d->shading.flag & V3D_SHADING_SCENE_WORLD)) +#define OCTAHEDRAL_SIZE_FROM_CUBESIZE(cube_size) ((int)ceilf(sqrtf((cube_size * cube_size) * 6.0f))) +#define MIN_CUBE_LOD_LEVEL 3 +#define MAX_PLANAR_LOD_LEVEL 9 + /* World shader variations */ enum { VAR_WORLD_BACKGROUND = 0, @@ -132,6 +139,27 @@ enum { VAR_MAT_SSSALBED = (1 << 17), }; +/* ************ PROBE UBO ************* */ + +/* They are the same struct as their Cache siblings. + * typedef'ing just to keep the naming consistent with + * other eevee types. */ +typedef LightProbeCache EEVEE_LightProbe; +typedef LightGridCache EEVEE_LightGrid; + +typedef struct EEVEE_PlanarReflection { + float plane_equation[4]; + float clip_vec_x[3], attenuation_scale; + float clip_vec_y[3], attenuation_bias; + float clip_edge_x_pos, clip_edge_x_neg; + float clip_edge_y_pos, clip_edge_y_neg; + float facing_scale, facing_bias, clipsta, pad; + float reflectionmat[4][4]; /* Used for sampling the texture. */ + float mtx[4][4]; /* Not used in shader. TODO move elsewhere. */ +} EEVEE_PlanarReflection; + +/* --------------------------------------- */ + typedef struct EEVEE_BoundSphere { float center[3], radius; } EEVEE_BoundSphere; @@ -272,6 +300,9 @@ typedef struct EEVEE_TextureList { struct GPUTexture *volume_scatter_history; struct GPUTexture *volume_transmittance_history; + struct GPUTexture *lookdev_grid_tx; + struct GPUTexture *lookdev_cube_tx; + struct GPUTexture *planar_pool; struct GPUTexture *planar_depth; @@ -288,6 +319,10 @@ typedef struct EEVEE_StorageList { struct EEVEE_PrivateData *g_data; + struct LightCache *lookdev_lightcache; + EEVEE_LightProbe *lookdev_cube_data; + EEVEE_LightGrid *lookdev_grid_data; + LightCacheTexture *lookdev_cube_mips; } EEVEE_StorageList; /* ************ LIGHT UBO ************* */ @@ -392,42 +427,11 @@ enum { LIGHT_UPDATE_SHADOW_CUBE = (1 << 0), }; -/* ************ PROBE UBO ************* */ -typedef struct EEVEE_LightProbe { - float position[3], parallax_type; - float attenuation_fac; - float attenuation_type; - float pad3[2]; - float attenuationmat[4][4]; - float parallaxmat[4][4]; -} EEVEE_LightProbe; - -typedef struct EEVEE_LightGrid { - float mat[4][4]; - int resolution[3], offset; - float corner[3], attenuation_scale; - float increment_x[3], attenuation_bias; /* world space vector between 2 opposite cells */ - float increment_y[3], level_bias; - float increment_z[3], pad4; - float visibility_bias, visibility_bleed, visibility_range, pad5; -} EEVEE_LightGrid; - -typedef struct EEVEE_PlanarReflection { - float plane_equation[4]; - float clip_vec_x[3], attenuation_scale; - float clip_vec_y[3], attenuation_bias; - float clip_edge_x_pos, clip_edge_x_neg; - float clip_edge_y_pos, clip_edge_y_neg; - float facing_scale, facing_bias, pad[2]; - float reflectionmat[4][4]; -} EEVEE_PlanarReflection; - /* ************ PROBE DATA ************* */ - typedef struct EEVEE_LightProbeVisTest { + struct Collection *collection; /* Skip test if NULL */ bool invert; bool cached; /* Reuse last test results */ - struct Collection *collection; /* Skip test if NULL */ } EEVEE_LightProbeVisTest; typedef struct EEVEE_LightProbesInfo { @@ -440,13 +444,9 @@ typedef struct EEVEE_LightProbesInfo { int updated_bounce; int num_bounce; int cubemap_res; - int target_size; - int grid_initialized; - struct World *prev_world; - int update_world; - bool prev_wo_sh_compiled; + /* Update */ bool do_cube_update; - bool all_materials_updated; + bool do_grid_update; /* For rendering probes */ float probemat[6][4][4]; int layer; @@ -465,15 +465,11 @@ typedef struct EEVEE_LightProbesInfo { int shres; int studiolight_index; float studiolight_rot_z; - /* List of probes in the scene. */ - /* XXX This is fragile, can get out of sync quickly. */ - struct Object *probes_cube_ref[MAX_PROBE]; - struct Object *probes_grid_ref[MAX_GRID]; - struct Object *probes_planar_ref[MAX_PLANAR]; + EEVEE_LightProbeVisTest planar_vis_tests[MAX_PLANAR]; /* UBO Storage : data used by UBO */ - struct EEVEE_LightProbe probe_data[MAX_PROBE]; - struct EEVEE_LightGrid grid_data[MAX_GRID]; - struct EEVEE_PlanarReflection planar_data[MAX_PLANAR]; + EEVEE_LightProbe probe_data[MAX_PROBE]; + EEVEE_LightGrid grid_data[MAX_GRID]; + EEVEE_PlanarReflection planar_data[MAX_PLANAR]; /* Probe Visibility Collection */ EEVEE_LightProbeVisTest vis_data; } EEVEE_LightProbesInfo; @@ -673,24 +669,18 @@ typedef struct EEVEE_ViewLayerData { struct GPUUniformBuffer *grid_ubo; struct GPUUniformBuffer *planar_ubo; - struct GPUFrameBuffer *probe_filter_fb; - struct GPUFrameBuffer *probe_face_fb[6]; - - struct GPUTexture *probe_rt; - struct GPUTexture *probe_depth_rt; - struct GPUTexture *probe_pool; - struct GPUTexture *irradiance_pool; - struct GPUTexture *irradiance_rt; - /* Common Uniform Buffer */ struct EEVEE_CommonUniformBuffer common_data; struct GPUUniformBuffer *common_ubo; struct EEVEE_ClipPlanesUniformBuffer clip_data; struct GPUUniformBuffer *clip_ubo; + + struct LightCache *fallback_lightcache; } EEVEE_ViewLayerData; /* ************ OBJECT DATA ************ */ + typedef struct EEVEE_LightData { short light_id, shadow_id; } EEVEE_LightData; @@ -726,29 +716,7 @@ typedef struct EEVEE_LampEngineData { typedef struct EEVEE_LightProbeEngineData { DrawData dd; - /* NOTE: need_full_update is set by dependency graph when the probe or it's - * object is updated. This triggers full probe update, including it's - * "progressive" GI refresh. - * - * need_update is always set to truth when need_full_update is tagged, but - * might also be forced to be kept truth during GI refresh stages. - * - * TODO(sergey): Is there a way to avoid two flags here, or at least make - * it more clear what's going on here? - */ - bool need_full_update; bool need_update; - - bool ready_to_shade; - int updated_cells; - int updated_lvl; - int num_cell; - int max_lvl; - int probe_id; /* Only used for display data */ - float probe_size; /* Only used for display data */ - DRWMatrixState mats; /* For planar probes */ - float planer_eq_offset[4]; - struct ListBase captured_object_list; } EEVEE_LightProbeEngineData; typedef struct EEVEE_ObjectEngineData { @@ -790,6 +758,8 @@ typedef struct EEVEE_PrivateData { struct DRWShadingGroup *planar_display_shgrp; struct GHash *material_hash; float background_alpha; /* TODO find a better place for this. */ + /* Chosen lightcache: can come from Lookdev or the viewlayer. */ + struct LightCache *light_cache; /* For planar probes */ float planar_texel_size[2]; /* For double buffering */ @@ -807,6 +777,7 @@ typedef struct EEVEE_PrivateData { /* eevee_data.c */ EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void); +EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer); EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure(void); EEVEE_ObjectEngineData *EEVEE_object_data_get(Object *ob); EEVEE_ObjectEngineData *EEVEE_object_data_ensure(Object *ob); @@ -855,17 +826,37 @@ void EEVEE_lights_update(EEVEE_ViewLayerData *sldata); void EEVEE_draw_shadows(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl); void EEVEE_lights_free(void); + /* eevee_lightprobes.c */ bool EEVEE_lightprobes_obj_visibility_cb(bool vis_in, void *user_data); -bool EEVEE_lightprobes_all_probes_ready(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_lightprobes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); -void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, Object *ob); +void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *ob); void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_lightprobes_refresh(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); void EEVEE_lightprobes_free(void); +void EEVEE_lightbake_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, GPUTexture *rt_color, GPUTexture *rt_depth); +void EEVEE_lightbake_render_world( + EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUFrameBuffer *face_fb[6]); +void EEVEE_lightbake_render_scene( + EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUFrameBuffer *face_fb[6], + const float pos[3], float near_clip, float far_clip); +void EEVEE_lightbake_filter_glossy( + EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUTexture *rt_color, struct GPUFrameBuffer *fb, + int probe_idx, float intensity, int maxlevel); +void EEVEE_lightbake_filter_diffuse( + EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUTexture *rt_color, struct GPUFrameBuffer *fb, + int grid_offset, float intensity); +void EEVEE_lightbake_filter_visibility( + EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, struct GPUTexture *rt_depth, struct GPUFrameBuffer *fb, + int grid_offset, float clipsta, float clipend, float vis_range, float vis_blur, int vis_size); + +void EEVEE_lightprobes_grid_data_from_object(Object *ob, EEVEE_LightGrid *prb_data, int *offset); +void EEVEE_lightprobes_cube_data_from_object(Object *ob, EEVEE_LightProbe *prb_data); +void EEVEE_lightprobes_planar_data_from_object(Object *ob, EEVEE_PlanarReflection *eplanar, EEVEE_LightProbeVisTest *vis_test); + /* eevee_depth_of_field.c */ int EEVEE_depth_of_field_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, Object *camera); void EEVEE_depth_of_field_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata); @@ -954,6 +945,9 @@ void EEVEE_render_update_passes(struct RenderEngine *engine, struct Scene *scene void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, DRWShadingGroup **grp, GPUShader *shader, DRWPass *pass, struct World *world, EEVEE_LightProbesInfo *pinfo); void EEVEE_lookdev_draw_background(EEVEE_Data *vedata); +/** eevee_engine.c */ +void EEVEE_cache_populate(void *vedata, Object *ob); + /* Shadow Matrix */ static const float texcomat[4][4] = { /* From NDC to TexCo */ {0.5f, 0.0f, 0.0f, 0.0f}, diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index c650a6945ac..bc1bd97bdde 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -134,17 +134,31 @@ void EEVEE_render_init(EEVEE_Data *ved, RenderEngine *engine, struct Depsgraph * EEVEE_volumes_cache_init(sldata, vedata); } +/* Used by light cache. in this case engine is NULL. */ void EEVEE_render_cache( void *vedata, struct Object *ob, struct RenderEngine *engine, struct Depsgraph *UNUSED(depsgraph)) { EEVEE_ViewLayerData *sldata = EEVEE_view_layer_data_ensure(); - - char info[42]; - BLI_snprintf(info, sizeof(info), "Syncing %s", ob->id.name + 2); - RE_engine_update_stats(engine, NULL, info); + EEVEE_LightProbesInfo *pinfo = sldata->probes; bool cast_shadow = false; + if (pinfo->vis_data.collection) { + /* Used for rendering probe with visibility groups. */ + bool ob_vis = BKE_collection_has_object_recursive(pinfo->vis_data.collection, ob); + ob_vis = (pinfo->vis_data.invert) ? !ob_vis : ob_vis; + + if (!ob_vis) { + return; + } + } + + if (engine) { + char info[42]; + BLI_snprintf(info, sizeof(info), "Syncing %s", ob->id.name + 2); + RE_engine_update_stats(engine, NULL, info); + } + if (ob->base_flag & BASE_VISIBLE) { EEVEE_hair_cache_populate(vedata, sldata, ob, &cast_shadow); } @@ -154,7 +168,7 @@ void EEVEE_render_cache( EEVEE_materials_cache_populate(vedata, sldata, ob, &cast_shadow); } else if (ob->type == OB_LIGHTPROBE) { - EEVEE_lightprobes_cache_add(sldata, ob); + EEVEE_lightprobes_cache_add(sldata, vedata, ob); } else if (ob->type == OB_LAMP) { EEVEE_lights_cache_add(sldata, ob); @@ -477,14 +491,8 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl DRW_viewport_matrix_override_set(g_data->viewinv, DRW_MAT_VIEWINV); /* Refresh Probes */ - while (EEVEE_lightprobes_all_probes_ready(sldata, vedata) == false) { - RE_engine_update_stats(engine, NULL, "Updating Probes"); - EEVEE_lightprobes_refresh(sldata, vedata); - /* Refreshing probes can take some times, allow exit. */ - if (RE_engine_test_break(engine)) { - return; - } - } + RE_engine_update_stats(engine, NULL, "Updating Probes"); + EEVEE_lightprobes_refresh(sldata, vedata); EEVEE_lightprobes_refresh_planar(sldata, vedata); DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); diff --git a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c index 74760b9c828..ef949c32eed 100644 --- a/source/blender/draw/engines/eevee/eevee_screen_raytrace.c +++ b/source/blender/draw/engines/eevee/eevee_screen_raytrace.c @@ -187,6 +187,7 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v EEVEE_StorageList *stl = vedata->stl; EEVEE_TextureList *txl = vedata->txl; EEVEE_EffectsInfo *effects = stl->effects; + LightCache *lcache = stl->g_data->light_cache; struct Gwn_Batch *quad = DRW_cache_fullscreen_quad_get(); @@ -230,7 +231,7 @@ void EEVEE_screen_raytrace_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *v DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.depth_src); DRW_shgroup_uniform_texture_ref(grp, "normalBuffer", &effects->ssr_normal_input); DRW_shgroup_uniform_texture_ref(grp, "specroughBuffer", &effects->ssr_specrough_input); - DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &sldata->probe_pool); + DRW_shgroup_uniform_texture_ref(grp, "probeCubes", &lcache->cube_tx.tex); DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &vedata->txl->planar_pool); DRW_shgroup_uniform_texture_ref(grp, "planarDepth", &vedata->txl->planar_depth); DRW_shgroup_uniform_texture_ref(grp, "hitBuffer", &effects->ssr_hit_output); diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index 560f898b275..d0cea65d05e 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -349,6 +349,7 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) EEVEE_StorageList *stl = vedata->stl; EEVEE_TextureList *txl = vedata->txl; EEVEE_EffectsInfo *effects = stl->effects; + LightCache *lcache = stl->g_data->light_cache; EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; if ((effects->enabled_effects & EFFECT_VOLUMETRIC) != 0) { @@ -417,7 +418,7 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) psl->volumetric_scatter_ps = DRW_pass_create("Volumetric Scattering", DRW_STATE_WRITE_COLOR); grp = DRW_shgroup_empty_tri_batch_create(scatter_sh, psl->volumetric_scatter_ps, common_data->vol_tex_size[2]); - DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &sldata->irradiance_pool); + DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &lcache->grid_tx.tex); DRW_shgroup_uniform_texture_ref(grp, "shadowCubeTexture", &sldata->shadow_cube_pool); DRW_shgroup_uniform_texture_ref(grp, "shadowCascadeTexture", &sldata->shadow_cascade_pool); DRW_shgroup_uniform_texture_ref(grp, "volumeScattering", &txl->volume_prop_scattering); diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl index d10f4bc0d42..5a72244cfbe 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_frag.glsl @@ -1,15 +1,18 @@ flat in int pid; -in vec3 worldNormal; -in vec3 worldPosition; +in vec2 quadCoord; out vec4 FragColor; void main() { - vec3 V = (ProjectionMatrix[3][3] == 0.0) /* if perspective */ - ? normalize(cameraPos - worldPosition) - : cameraForward; - vec3 N = normalize(worldNormal); - FragColor = vec4(textureLod_octahedron(probeCubes, vec4(reflect(-V, N), pid), 0.0, prbLodCubeMax).rgb, 1.0); + float dist_sqr = dot(quadCoord, quadCoord); + + /* Discard outside the circle. */ + if (dist_sqr > 1.0) + discard; + + vec3 view_nor = vec3(quadCoord, sqrt(max(0.0, 1.0 - dist_sqr))); + vec3 world_ref = mat3(ViewMatrixInverse) * reflect(vec3(0.0, 0.0, -1.0), view_nor); + FragColor = vec4(textureLod_octahedron(probeCubes, vec4(world_ref, pid), 0.0, prbLodCubeMax).rgb, 1.0); } diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl index b0a6cbe1707..b327878b63d 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_cube_display_vert.glsl @@ -1,26 +1,43 @@ -in vec3 pos; +/* XXX TODO fix code duplication */ +struct CubeData { + vec4 position_type; + vec4 attenuation_fac_type; + mat4 influencemat; + mat4 parallaxmat; +}; -/* Instance attrib */ -in int probe_id; -in vec3 probe_location; -in float sphere_size; +layout(std140) uniform probe_block { + CubeData probes_data[MAX_PROBE]; +}; + +uniform float sphere_size; +uniform vec3 screen_vecs[2]; flat out int pid; -out vec3 worldNormal; -out vec3 worldPosition; +out vec2 quadCoord; + +const vec2 pos[6] = vec2[6]( + vec2(-1.0, -1.0), + vec2( 1.0, -1.0), + vec2(-1.0, 1.0), + + vec2( 1.0, -1.0), + vec2( 1.0, 1.0), + vec2(-1.0, 1.0) +); void main() { - pid = probe_id; + pid = 1 + (gl_VertexID / 6); /* +1 for the world */ + int vert_id = gl_VertexID % 6; + + quadCoord = pos[vert_id]; - /* While this is not performant, we do this to - * match the object mode engine instancing shader. */ - mat4 offsetmat = mat4(1.0); /* Identity */ - offsetmat[3].xyz = probe_location; + vec3 ws_location = probes_data[pid].position_type.xyz; + vec3 screen_pos = screen_vecs[0] * quadCoord.x + screen_vecs[1] * quadCoord.y; + ws_location += screen_pos * sphere_size; - vec4 wpos = offsetmat * vec4(pos * sphere_size, 1.0); - worldPosition = wpos.xyz; - gl_Position = ViewProjectionMatrix * wpos; - worldNormal = normalize(pos); + gl_Position = ViewProjectionMatrix * vec4(ws_location, 1.0); + gl_Position.z += 0.0001; /* Small bias to let the icon draw without zfighting */ } diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl index d333ad34bb0..fd8eb157aa5 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_frag.glsl @@ -1,11 +1,19 @@ flat in int cellOffset; -in vec3 worldNormal; +in vec2 quadCoord; out vec4 FragColor; void main() { - IrradianceData ir_data = load_irradiance_cell(cellOffset, worldNormal); - FragColor = vec4(compute_irradiance(worldNormal, ir_data), 1.0); + float dist_sqr = dot(quadCoord, quadCoord); + + /* Discard outside the circle. */ + if (dist_sqr > 1.0) + discard; + + vec3 view_nor = vec3(quadCoord, sqrt(max(0.0, 1.0 - dist_sqr))); + vec3 world_nor = mat3(ViewMatrixInverse) * view_nor; + IrradianceData ir_data = load_irradiance_cell(cellOffset, world_nor); + FragColor = vec4(compute_irradiance(world_nor, ir_data), 1.0); } diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl index a017a791e41..7a92b55e530 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_grid_display_vert.glsl @@ -1,6 +1,4 @@ -in vec3 pos; - uniform float sphere_size; uniform int offset; uniform ivec3 grid_resolution; @@ -8,25 +6,44 @@ uniform vec3 corner; uniform vec3 increment_x; uniform vec3 increment_y; uniform vec3 increment_z; +uniform vec3 screen_vecs[2]; flat out int cellOffset; -out vec3 worldNormal; +out vec2 quadCoord; + +const vec2 pos[6] = vec2[6]( + vec2(-1.0, -1.0), + vec2( 1.0, -1.0), + vec2(-1.0, 1.0), + + vec2( 1.0, -1.0), + vec2( 1.0, 1.0), + vec2(-1.0, 1.0) +); void main() { + int cell_id = gl_VertexID / 6; + int vert_id = gl_VertexID % 6; + vec3 ls_cell_location; /* Keep in sync with update_irradiance_probe */ - ls_cell_location.z = float(gl_InstanceID % grid_resolution.z); - ls_cell_location.y = float((gl_InstanceID / grid_resolution.z) % grid_resolution.y); - ls_cell_location.x = float(gl_InstanceID / (grid_resolution.z * grid_resolution.y)); + ls_cell_location.z = float(cell_id % grid_resolution.z); + ls_cell_location.y = float((cell_id / grid_resolution.z) % grid_resolution.y); + ls_cell_location.x = float(cell_id / (grid_resolution.z * grid_resolution.y)); - cellOffset = offset + gl_InstanceID; + cellOffset = offset + cell_id; vec3 ws_cell_location = corner + (increment_x * ls_cell_location.x + increment_y * ls_cell_location.y + increment_z * ls_cell_location.z); - gl_Position = ViewProjectionMatrix * vec4(pos * 0.02 * sphere_size + ws_cell_location, 1.0); - worldNormal = normalize(pos); + + quadCoord = pos[vert_id]; + vec3 screen_pos = screen_vecs[0] * quadCoord.x + screen_vecs[1] * quadCoord.y; + ws_cell_location += screen_pos * sphere_size; + + gl_Position = ViewProjectionMatrix * vec4(ws_cell_location , 1.0); + gl_Position.z += 0.0001; /* Small bias to let the icon draw without zfighting */ } diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl index 0ffc0cc4b49..6ae13e0102d 100644 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_lib.glsl @@ -27,6 +27,7 @@ struct PlanarData { vec4 clip_edges; vec4 facing_scale_bias; mat4 reflectionmat; /* transform world space into reflection texture space */ + mat4 unused; }; #define pl_plane_eq plane_equation diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl index 644b449c03e..22194c22f39 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl @@ -256,7 +256,7 @@ void CLOSURE_NAME( PlanarData pd = planars_data[i]; /* Fade on geometric normal. */ - float fade = probe_attenuation_planar(pd, worldPosition, worldNormal, roughness); + float fade = probe_attenuation_planar(pd, worldPosition, (gl_FrontFacing) ? worldNormal : -worldNormal, roughness); if (fade > 0.0) { if (!(ssrToggle && ssr_id == outputSsrId)) { @@ -404,7 +404,7 @@ void CLOSURE_NAME( spec_occlu = 1.0; } - out_spec += spec_accum.rgb * ssr_spec * spec_occlu * float(specToggle); + out_spec += spec_accum.rgb * ssr_spec * spec_occlu; #endif #ifdef CLOSURE_REFRACTION @@ -419,7 +419,12 @@ void CLOSURE_NAME( vec2 C_brdf_lut = texture(utilTex, vec3(C_uv, 1.0)).rg; vec3 C_fresnel = F_ibl(vec3(0.04), brdf_lut) * specular_occlusion(NV, final_ao, C_roughness); - out_spec += C_spec_accum.rgb * C_fresnel * float(specToggle) * C_intensity; + out_spec += C_spec_accum.rgb * C_fresnel * C_intensity; +#endif + +#ifdef CLOSURE_GLOSSY + /* Global toggle for lightprobe baking. */ + out_spec *= float(specToggle); #endif /* ---------------------------------------------------------------- */ diff --git a/source/blender/editors/render/render_intern.h b/source/blender/editors/render/render_intern.h index 77bf16f5e22..585a7999290 100644 --- a/source/blender/editors/render/render_intern.h +++ b/source/blender/editors/render/render_intern.h @@ -56,6 +56,9 @@ void MATERIAL_OT_paste(struct wmOperatorType *ot); void SCENE_OT_view_layer_add(struct wmOperatorType *ot); void SCENE_OT_view_layer_remove(struct wmOperatorType *ot); +void SCENE_OT_light_cache_bake(struct wmOperatorType *ot); +void SCENE_OT_light_cache_free(struct wmOperatorType *ot); + void SCENE_OT_render_view_add(struct wmOperatorType *ot); void SCENE_OT_render_view_remove(struct wmOperatorType *ot); diff --git a/source/blender/editors/render/render_ops.c b/source/blender/editors/render/render_ops.c index fd534dd9129..7961ea27fdc 100644 --- a/source/blender/editors/render/render_ops.c +++ b/source/blender/editors/render/render_ops.c @@ -62,6 +62,9 @@ void ED_operatortypes_render(void) WM_operatortype_append(SCENE_OT_render_view_add); WM_operatortype_append(SCENE_OT_render_view_remove); + WM_operatortype_append(SCENE_OT_light_cache_bake); + WM_operatortype_append(SCENE_OT_light_cache_free); + #ifdef WITH_FREESTYLE WM_operatortype_append(SCENE_OT_freestyle_module_add); WM_operatortype_append(SCENE_OT_freestyle_module_remove); diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 1673e0d7ca0..8077079a9b5 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -33,6 +33,7 @@ #include "DNA_curve_types.h" #include "DNA_lamp_types.h" +#include "DNA_lightprobe_types.h" #include "DNA_material_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" @@ -94,6 +95,8 @@ #include "RE_pipeline.h" +#include "engines/eevee/eevee_lightcache.h" + #include "render_intern.h" // own include /********************** material slot operators *********************/ @@ -673,6 +676,184 @@ void SCENE_OT_view_layer_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/********************** light cache operators *********************/ +enum { + LIGHTCACHE_SUBSET_ALL = 0, + LIGHTCACHE_SUBSET_DIRTY, + LIGHTCACHE_SUBSET_CUBE, +}; + +static void light_cache_bake_tag_cache(Scene *scene, wmOperator *op) +{ + if (scene->eevee.light_cache != NULL) { + int subset = RNA_enum_get(op->ptr, "subset"); + switch (subset) { + case LIGHTCACHE_SUBSET_ALL: + scene->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_GRID | LIGHTCACHE_UPDATE_CUBE; + break; + case LIGHTCACHE_SUBSET_CUBE: + scene->eevee.light_cache->flag |= LIGHTCACHE_UPDATE_CUBE; + break; + case LIGHTCACHE_SUBSET_DIRTY: + /* Leave tag untouched. */ + break; + } + } +} + +/* catch esc */ +static int light_cache_bake_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Scene *scene = (Scene *) op->customdata; + + /* no running blender, remove handler and pass through */ + if (0 == WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_RENDER)) { + return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH; + } + + /* running render */ + switch (event->type) { + case ESCKEY: + return OPERATOR_RUNNING_MODAL; + } + return OPERATOR_PASS_THROUGH; +} + +static void light_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_RENDER); +} + +/* executes blocking render */ +static int light_cache_bake_exec(bContext *C, wmOperator *op) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + + G.is_break = false; + + /* TODO abort if selected engine is not eevee. */ + void *rj = EEVEE_lightbake_job_data_alloc(bmain, view_layer, scene, false); + + light_cache_bake_tag_cache(scene, op); + + short stop = 0, do_update; float progress; /* Not actually used. */ + EEVEE_lightbake_job(rj, &stop, &do_update, &progress); + EEVEE_lightbake_job_data_free(rj); + + // no redraw needed, we leave state as we entered it + ED_update_for_newframe(bmain, CTX_data_depsgraph(C)); + + WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene); + + return OPERATOR_FINISHED; +} + +static int light_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + int delay = RNA_int_get(op->ptr, "delay"); + + wmJob *wm_job = EEVEE_lightbake_job_create(wm, win, bmain, view_layer, scene, delay); + + if (!wm_job) { + return OPERATOR_CANCELLED; + } + + /* add modal handler for ESC */ + WM_event_add_modal_handler(C, op); + + light_cache_bake_tag_cache(scene, 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_jobs_start(wm, wm_job); + + WM_cursor_wait(0); + + return OPERATOR_RUNNING_MODAL; +} + +void SCENE_OT_light_cache_bake(wmOperatorType *ot) +{ + static const EnumPropertyItem light_cache_subset_items[] = { + {LIGHTCACHE_SUBSET_ALL, "ALL", 0, "All LightProbes", "Bake both irradiance grids and reflection cubemaps"}, + {LIGHTCACHE_SUBSET_DIRTY, "DIRTY", 0, "Dirty Only", "Only bake lightprobes that are marked as dirty"}, + {LIGHTCACHE_SUBSET_CUBE, "CUBEMAPS", 0, "Cubemaps Only", "Try to only bake reflection cubemaps if irradiance " + "grids are up to date"}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Bake Light Cache"; + ot->idname = "SCENE_OT_light_cache_bake"; + ot->description = "Bake the active view layer lighting"; + + /* api callbacks */ + ot->invoke = light_cache_bake_invoke; + ot->modal = light_cache_bake_modal; + ot->cancel = light_cache_bake_cancel; + ot->exec = light_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); +} + +static bool light_cache_free_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + + return scene->eevee.light_cache; +} + +static int light_cache_free_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + + if (!scene->eevee.light_cache) { + return OPERATOR_CANCELLED; + } + + EEVEE_lightcache_free(scene->eevee.light_cache); + scene->eevee.light_cache = NULL; + + EEVEE_lightcache_info_update(&scene->eevee); + + DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + + return OPERATOR_FINISHED; +} + +void SCENE_OT_light_cache_free(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Free Light Cache"; + ot->idname = "SCENE_OT_light_cache_free"; + ot->description = "Free cached indirect lighting"; + + /* api callbacks */ + ot->exec = light_cache_free_exec; + ot->poll = light_cache_free_poll; +} + /********************** render view operators *********************/ static bool render_view_remove_poll(bContext *C) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 83189159086..9aaced3e583 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -683,6 +683,18 @@ static void view3d_id_path_drop_copy(wmDrag *drag, wmDropBox *drop) } } +static void view3d_lightcache_update(bContext *C) +{ + PointerRNA op_ptr; + + WM_operator_properties_create(&op_ptr, "SCENE_OT_light_cache_bake"); + RNA_int_set(&op_ptr, "delay", 200); + RNA_enum_set_identifier(C, &op_ptr, "subset", "DIRTY"); + + WM_operator_name_call(C, "SCENE_OT_light_cache_bake", WM_OP_INVOKE_DEFAULT, &op_ptr); + + WM_operator_properties_free(&op_ptr); +} /* region dropbox definition */ static void view3d_dropboxes(void) @@ -980,6 +992,9 @@ static void view3d_main_region_listener( break; } break; + case NC_LIGHTPROBE: + ED_area_tag_refresh(sa); + break; case NC_IMAGE: /* this could be more fine grained checks if we had * more context than just the region */ @@ -1409,6 +1424,13 @@ static void space_view3d_listener( } } +static void space_view3d_refresh(const bContext *C, ScrArea *UNUSED(sa)) +{ + /* This is only used by the auto lightprobe refresh for the moment. + * So we don't need to check anything to know what to do. */ + view3d_lightcache_update((bContext *)C); +} + const char *view3d_context_dir[] = { "active_base", "active_object", NULL }; @@ -1509,6 +1531,7 @@ void ED_spacetype_view3d(void) st->free = view3d_free; st->init = view3d_init; st->listener = space_view3d_listener; + st->refresh = space_view3d_refresh; st->duplicate = view3d_duplicate; st->operatortypes = view3d_operatortypes; st->keymap = view3d_keymap; diff --git a/source/blender/makesdna/DNA_lightprobe_types.h b/source/blender/makesdna/DNA_lightprobe_types.h index 86d4645ecf5..c06d75a4021 100644 --- a/source/blender/makesdna/DNA_lightprobe_types.h +++ b/source/blender/makesdna/DNA_lightprobe_types.h @@ -106,6 +106,78 @@ enum { LIGHTPROBE_SHAPE_BOX = 1, }; +/* ------- Eevee LightProbes ------- */ +/* Needs to be there because written to file + * with the lightcache. */ + +typedef struct LightProbeCache { + float position[3], parallax_type; + float attenuation_fac; + float attenuation_type; + float pad3[2]; + float attenuationmat[4][4]; + float parallaxmat[4][4]; +} LightProbeCache; + +typedef struct LightGridCache { + float mat[4][4]; + int resolution[3], offset; /* offset to the first irradiance sample in the pool. */ + float corner[3], attenuation_scale; + float increment_x[3], attenuation_bias; /* world space vector between 2 opposite cells */ + float increment_y[3], level_bias; + float increment_z[3], pad4; + float visibility_bias, visibility_bleed, visibility_range, pad5; +} LightGridCache; + +/* ------ Eevee Lightcache ------- */ + +typedef struct LightCacheTexture { + struct GPUTexture *tex; + /* Copy of GPU datas to create GPUTextures on file read. */ + char *data; + int tex_size[3]; + char data_type; + char components; + char pad[2]; +} LightCacheTexture; + +typedef struct LightCache { + int flag; + /* only a single cache for now */ + int cube_len, grid_len; /* Number of probes to use for rendering. */ + int mips_len; /* Number of mipmap level to use. */ + int vis_res, ref_res; /* Size of a visibility/reflection sample. */ + int pad[2]; + /* In the future, we could create a bigger texture containing + * multiple caches (for animation) and interpolate between the + * caches overtime to another texture. */ + LightCacheTexture grid_tx; + LightCacheTexture cube_tx; /* Contains data for mipmap level 0. */ + LightCacheTexture *cube_mips; /* Does not contains valid GPUTexture, only data. */ + /* All lightprobes data contained in the cache. */ + LightProbeCache *cube_data; + LightGridCache *grid_data; +} LightCache; + +/* LightCache->flag */ +enum { + LIGHTCACHE_BAKED = (1 << 0), + LIGHTCACHE_BAKING = (1 << 1), + LIGHTCACHE_CUBE_READY = (1 << 2), + LIGHTCACHE_GRID_READY = (1 << 3), + /* Update tagging */ + LIGHTCACHE_UPDATE_CUBE = (1 << 4), + LIGHTCACHE_UPDATE_GRID = (1 << 5), + LIGHTCACHE_UPDATE_WORLD = (1 << 6), +}; + +/* EEVEE_LightCacheTexture->data_type */ +enum { + LIGHTCACHETEX_BYTE = (1 << 0), + LIGHTCACHETEX_FLOAT = (1 << 1), + LIGHTCACHETEX_UINT = (1 << 2), +}; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index f8d7e657b76..e6ad7835b24 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1389,6 +1389,9 @@ typedef struct SceneEEVEE { int gi_cubemap_resolution; int gi_visibility_resolution; + float gi_cubemap_draw_size; + float gi_irradiance_draw_size; + int taa_samples; int taa_render_samples; int sss_samples; @@ -1428,6 +1431,9 @@ typedef struct SceneEEVEE { int shadow_method; int shadow_cube_size; int shadow_cascade_size; + + struct LightCache *light_cache; + char light_cache_info[64]; } SceneEEVEE; /* *************************************************************** */ @@ -2101,6 +2107,9 @@ enum { SCE_EEVEE_SSR_ENABLED = (1 << 14), SCE_EEVEE_SSR_REFRACTION = (1 << 15), SCE_EEVEE_SSR_HALF_RESOLUTION = (1 << 16), + SCE_EEVEE_SHOW_IRRADIANCE = (1 << 17), + SCE_EEVEE_SHOW_CUBEMAPS = (1 << 18), + SCE_EEVEE_GI_AUTOBAKE = (1 << 19), }; /* SceneEEVEE->shadow_method */ diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 6b312634b50..68b1f5a74aa 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -5815,6 +5815,38 @@ static void rna_def_scene_eevee(BlenderRNA *brna) "Size of the shadow map applied to each irradiance sample"); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); + prop = RNA_def_property(srna, "gi_show_irradiance", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_SHOW_IRRADIANCE); + RNA_def_property_boolean_default(prop, 0); + RNA_def_property_ui_text(prop, "Show Irradiance Cache", "Display irradiance samples in the viewport"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); + + prop = RNA_def_property(srna, "gi_show_cubemaps", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_SHOW_CUBEMAPS); + RNA_def_property_boolean_default(prop, 0); + RNA_def_property_ui_text(prop, "Show Cubemap Cache", "Display captured cubemaps in the viewport"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC); + + prop = RNA_def_property(srna, "gi_irradiance_draw_size", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.05f, 10.0f); + RNA_def_property_float_default(prop, 0.1f); + RNA_def_property_ui_text(prop, "Irradiance Draw Size", "Size of the irradiance sample spheres to debug captured light"); + + prop = RNA_def_property(srna, "gi_cubemap_draw_size", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.05f, 10.0f); + RNA_def_property_float_default(prop, 0.3f); + RNA_def_property_ui_text(prop, "Cubemap Draw Size", "Size of the cubemap spheres to debug captured light"); + + prop = RNA_def_property(srna, "gi_auto_bake", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SCE_EEVEE_GI_AUTOBAKE); + RNA_def_property_boolean_default(prop, 0); + RNA_def_property_ui_text(prop, "Auto Bake", "Auto bake indirect lighting when editing probes"); + + prop = RNA_def_property(srna, "gi_cache_info", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "light_cache_info"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Light Cache Info", "Info on current cache status"); + /* Temporal Anti-Aliasing (super sampling) */ prop = RNA_def_property(srna, "taa_samples", PROP_INT, PROP_NONE); RNA_def_property_int_default(prop, 16); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 6d577afdbf9..ccbed11e6e8 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -524,6 +524,7 @@ enum { WM_JOB_TYPE_ALEMBIC, WM_JOB_TYPE_SHADER_COMPILATION, WM_JOB_TYPE_STUDIOLIGHT, + WM_JOB_TYPE_LIGHT_BAKE, /* add as needed, screencast, seq proxy build * if having hard coded values is a problem */ }; diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 8e05822dd55..8b7633b635b 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -257,6 +257,7 @@ typedef struct wmNotifier { #define NC_GPENCIL (22<<24) #define NC_LINESTYLE (23<<24) #define NC_CAMERA (24<<24) +#define NC_LIGHTPROBE (25<<24) /* data type, 256 entries is enough, it can overlap */ #define NOTE_DATA 0x00FF0000 |