diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2018-07-10 16:02:25 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2018-07-10 16:31:34 +0300 |
commit | 1a43e081873415754950766edaddad220adf67bc (patch) | |
tree | 6a4e61b2337606daf442973d9f82e1d56b906a98 /source/blender/draw/engines/eevee | |
parent | 97f90d48a02eef89949532b166f57ea178ee5a87 (diff) |
Eevee: LightCache: Initial Implementation
This separate probe rendering from viewport rendering, making possible to
run the baking in another thread (non blocking and faster).
The baked lighting is saved in the blend file. Nothing needs to be
recomputed on load.
There is a few missing bits / bugs:
- Cache cannot be saved to disk as a separate file, it is saved in the DNA
for now making file larger and memory usage higher.
- Auto update only cubemaps does update the grids (bug).
- Probes cannot be updated individually (considered as dynamic).
- Light Cache cannot be (re)generated during render.
Diffstat (limited to 'source/blender/draw/engines/eevee')
18 files changed, 2173 insertions, 1340 deletions
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 /* ---------------------------------------------------------------- */ |