/* * 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. * * Copyright 2016, Blender Foundation. */ /** \file * \ingroup draw_engine */ #include "DRW_render.h" #include "BLI_rand.h" #include "BLI_string_utils.h" #include "BLI_utildefines.h" #include "DNA_image_types.h" #include "DNA_lightprobe_types.h" #include "DNA_texture_types.h" #include "DNA_view3d_types.h" #include "DNA_world_types.h" #include "BKE_collection.h" #include "BKE_object.h" #include "MEM_guardedalloc.h" #include "GPU_capabilities.h" #include "GPU_material.h" #include "GPU_texture.h" #include "GPU_uniform_buffer.h" #include "DEG_depsgraph_query.h" #include "eevee_lightcache.h" #include "eevee_private.h" #include "WM_api.h" #include "WM_types.h" static struct { struct GPUTexture *planar_pool_placeholder; struct GPUTexture *depth_placeholder; struct GPUTexture *depth_array_placeholder; struct GPUVertFormat *format_probe_display_planar; } e_data = {NULL}; /* Engine data */ /* *********** FUNCTIONS *********** */ /* 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) { EEVEE_ObjectEngineData *oed = (EEVEE_ObjectEngineData *)user_data; /* test disabled if group is NULL */ if (oed == NULL || 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; } 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; } return vis_in && oed->ob_vis; } static void planar_pool_ensure_alloc(EEVEE_Data *vedata, int num_planar_ref) { EEVEE_TextureList *txl = vedata->txl; EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *fx = stl->effects; /* XXX TODO: OPTIMIZATION: 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. */ /* TODO: get screen percentage from layer setting. */ // const DRWContextState *draw_ctx = DRW_context_state_get(); // ViewLayer *view_layer = draw_ctx->view_layer; int screen_divider = 1; int width = max_ii(1, fx->hiz_size[0] / screen_divider); int height = max_ii(1, fx->hiz_size[1] / screen_divider); /* Fix case were the pool was allocated width the dummy size (1,1,1). */ if (txl->planar_pool && (num_planar_ref > 0) && (GPU_texture_width(txl->planar_pool) != width || GPU_texture_height(txl->planar_pool) != height)) { DRW_TEXTURE_FREE_SAFE(txl->planar_pool); DRW_TEXTURE_FREE_SAFE(txl->planar_depth); } /* We need an Array texture so allocate it ourself */ if (!txl->planar_pool) { if (num_planar_ref > 0) { txl->planar_pool = DRW_texture_create_2d_array(width, height, num_planar_ref, GPU_R11F_G11F_B10F, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); txl->planar_depth = DRW_texture_create_2d_array( width, height, num_planar_ref, GPU_DEPTH_COMPONENT24, 0, NULL); } else if (num_planar_ref == 0) { /* Makes Opengl Happy : Create a placeholder texture that will never be sampled but still * bound to shader. */ txl->planar_pool = DRW_texture_create_2d_array( 1, 1, 1, GPU_RGBA8, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); txl->planar_depth = DRW_texture_create_2d_array(1, 1, 1, GPU_DEPTH_COMPONENT24, 0, NULL); } } } void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; EEVEE_StorageList *stl = vedata->stl; const DRWContextState *draw_ctx = DRW_context_state_get(); const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); vedata->info[0] = '\0'; EEVEE_shaders_material_shaders_init(); memset(stl->g_data->bake_views, 0, sizeof(stl->g_data->bake_views)); memset(stl->g_data->cube_views, 0, sizeof(stl->g_data->cube_views)); memset(stl->g_data->world_views, 0, sizeof(stl->g_data->world_views)); memset(stl->g_data->planar_views, 0, sizeof(stl->g_data->planar_views)); if (EEVEE_lightcache_load(scene_eval->eevee.light_cache_data)) { stl->g_data->light_cache = scene_eval->eevee.light_cache_data; } else { if (scene_eval->eevee.light_cache_data && (scene_eval->eevee.light_cache_data->flag & LIGHTCACHE_NOT_USABLE)) { /* Error message info. */ BLI_snprintf( vedata->info, sizeof(vedata->info), "Error: LightCache cannot be loaded on this GPU"); } if (!sldata->fallback_lightcache) { #if defined(IRRADIANCE_SH_L2) int grid_res = 4; #elif defined(IRRADIANCE_HL2) int grid_res = 4; #endif sldata->fallback_lightcache = EEVEE_lightcache_create( 1, 1, scene_eval->eevee.gi_cubemap_resolution, scene_eval->eevee.gi_visibility_resolution, (int[3]){grid_res, grid_res, 1}); } stl->g_data->light_cache = sldata->fallback_lightcache; } if (!sldata->probes) { sldata->probes = MEM_callocN(sizeof(EEVEE_LightProbesInfo), "EEVEE_LightProbesInfo"); sldata->probe_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightProbe) * MAX_PROBE); sldata->grid_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightGrid) * MAX_GRID); sldata->planar_ubo = GPU_uniformbuf_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR); } 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->ssrefract_toggle = true; common_data->sss_toggle = true; /* 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); } } /* Only init the passes useful for rendering the light cache. */ void EEVEE_lightbake_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, GPUTexture *rt_color, GPUTexture *rt_depth) { EEVEE_PassList *psl = vedata->psl; LightCache *light_cache = vedata->stl->g_data->light_cache; EEVEE_LightProbesInfo *pinfo = sldata->probes; { DRW_PASS_CREATE(psl->probe_glossy_compute, DRW_STATE_WRITE_COLOR); DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_glossy_sh_get(), psl->probe_glossy_compute); DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1); DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1); DRW_shgroup_uniform_float(grp, "roughness", &pinfo->roughness, 1); DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1); DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1); DRW_shgroup_uniform_float(grp, "texelSize", &pinfo->texel_size, 1); DRW_shgroup_uniform_float(grp, "paddingSize", &pinfo->padding_size, 1); DRW_shgroup_uniform_float(grp, "fireflyFactor", &pinfo->firefly_fac, 1); DRW_shgroup_uniform_int(grp, "Layer", &pinfo->layer, 1); // DRW_shgroup_uniform_texture(grp, "texJitter", e_data.jitter); DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); DRW_shgroup_call_instances(grp, NULL, geom, 6); } { DRW_PASS_CREATE(psl->probe_diffuse_compute, DRW_STATE_WRITE_COLOR); DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_diffuse_sh_get(), psl->probe_diffuse_compute); #ifdef IRRADIANCE_SH_L2 DRW_shgroup_uniform_int(grp, "probeSize", &pinfo->shres, 1); #else DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1); DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1); DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1); #endif DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1); DRW_shgroup_uniform_texture(grp, "probeHdr", rt_color); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); DRW_shgroup_call(grp, geom, NULL); } { DRW_PASS_CREATE(psl->probe_visibility_compute, DRW_STATE_WRITE_COLOR); DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_filter_visibility_sh_get(), psl->probe_visibility_compute); DRW_shgroup_uniform_int(grp, "outputSize", &pinfo->shres, 1); DRW_shgroup_uniform_float(grp, "visibilityRange", &pinfo->visibility_range, 1); DRW_shgroup_uniform_float(grp, "visibilityBlur", &pinfo->visibility_blur, 1); DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_len, 1); DRW_shgroup_uniform_float(grp, "storedTexelSize", &pinfo->texel_size, 1); 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, "probeDepth", rt_depth); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); DRW_shgroup_call(grp, geom, NULL); } { DRW_PASS_CREATE(psl->probe_grid_fill, DRW_STATE_WRITE_COLOR); DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_grid_fill_sh_get(), psl->probe_grid_fill); DRW_shgroup_uniform_texture_ref(grp, "irradianceGrid", &light_cache->grid_tx.tex); struct GPUBatch *geom = DRW_cache_fullscreen_quad_get(); DRW_shgroup_call(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; { DRW_PASS_CREATE(psl->probe_background, DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL); DRWShadingGroup *grp = NULL; EEVEE_lookdev_cache_init(vedata, sldata, psl->probe_background, pinfo, &grp); if (grp == NULL) { Scene *scene = draw_ctx->scene; World *world = (scene->world) ? scene->world : EEVEE_world_default_get(); const int options = VAR_WORLD_BACKGROUND | VAR_WORLD_PROBE; struct GPUMaterial *gpumat = EEVEE_material_get(vedata, scene, NULL, world, options); grp = DRW_shgroup_material_create(gpumat, psl->probe_background); DRW_shgroup_uniform_float_copy(grp, "backgroundAlpha", 1.0f); } DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); DRW_shgroup_uniform_block(grp, "shadow_block", sldata->shadow_ubo); DRW_shgroup_uniform_block_ref(grp, "renderpass_block", &stl->g_data->renderpass_ubo); DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), 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; DRW_PASS_CREATE(psl->probe_display, state); if (!LOOK_DEV_STUDIO_LIGHT_ENABLED(draw_ctx->v3d)) { /* 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_create(EEVEE_shaders_probe_cube_display_sh_get(), psl->probe_display); 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", DRW_viewport_screenvecs_get(), 2); DRW_shgroup_uniform_float_copy( grp, "sphere_size", scene_eval->eevee.gi_cubemap_draw_size * 0.5f); /* TODO(fclem): get rid of those UBO. */ DRW_shgroup_uniform_block(grp, "planar_block", sldata->planar_ubo); DRW_shgroup_uniform_block(grp, "grid_block", sldata->grid_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_call_procedural_triangles(grp, NULL, cube_len * 2); } /* 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(EEVEE_shaders_probe_grid_display_sh_get(), 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", 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); /* TODO(fclem): get rid of those UBO. */ DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo); DRW_shgroup_uniform_block(shgrp, "planar_block", sldata->planar_ubo); DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo); DRW_shgroup_uniform_block(shgrp, "common_block", sldata->common_ubo); DRW_shgroup_uniform_block(shgrp, "renderpass_block", sldata->renderpass_ubo.combined); int tri_count = egrid->resolution[0] * egrid->resolution[1] * egrid->resolution[2] * 2; DRW_shgroup_call_procedural_triangles(shgrp, NULL, tri_count); } } } /* Planar Display */ { DRW_shgroup_instance_format(e_data.format_probe_display_planar, { {"probe_id", DRW_ATTR_INT, 1}, {"probe_mat", DRW_ATTR_FLOAT, 16}, }); DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_planar_display_sh_get(), psl->probe_display); DRW_shgroup_uniform_texture_ref(grp, "probePlanars", &txl->planar_pool); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); stl->g_data->planar_display_shgrp = DRW_shgroup_call_buffer_instance( grp, e_data.format_probe_display_planar, DRW_cache_quad_get()); } } else { stl->g_data->planar_display_shgrp = NULL; } } static bool eevee_lightprobes_culling_test(Object *ob) { LightProbe *probe = (LightProbe *)ob->data; 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]); } const DRWView *default_view = DRW_view_default_get(); return DRW_culling_box_test(default_view, &bbox); } case LIGHTPROBE_TYPE_CUBE: return true; /* TODO */ case LIGHTPROBE_TYPE_GRID: return true; /* TODO */ } BLI_assert(0); return true; } 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 >= EEVEE_PROBE_MAX) || (probe->type == LIGHTPROBE_TYPE_GRID && pinfo->num_grid >= EEVEE_PROBE_MAX) || (probe->type == LIGHTPROBE_TYPE_PLANAR && pinfo->num_planar >= MAX_PLANAR)) { printf("Too many probes in the view !!!\n"); return; } if (probe->type == LIGHTPROBE_TYPE_PLANAR) { /* TODO(fclem): Culling should be done after cache generation. * This is needed for future draw cache persistence. */ 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 */ DRWCallBuffer *grp = vedata->stl->g_data->planar_display_shgrp; if (grp && (probe->flag & LIGHTPROBE_FLAG_SHOW_DATA)) { DRW_buffer_add_entry(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; } } } void EEVEE_lightprobes_grid_data_from_object(Object *ob, EEVEE_LightGrid *egrid, int *offset) { LightProbe *probe = (LightProbe *)ob->data; 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))); } void EEVEE_lightprobes_cube_data_from_object(Object *ob, EEVEE_LightProbe *eprobe) { LightProbe *probe = (LightProbe *)ob->data; /* Update transforms */ copy_v3_v3(eprobe->position, ob->obmat[3]); /* Attenuation */ eprobe->attenuation_type = probe->attenuation_type; eprobe->attenuation_fac = 1.0f / max_ff(1e-8f, probe->falloff); unit_m4(eprobe->attenuationmat); scale_m4_fl(eprobe->attenuationmat, probe->distinf); mul_m4_m4m4(eprobe->attenuationmat, ob->obmat, eprobe->attenuationmat); invert_m4(eprobe->attenuationmat); /* Parallax */ unit_m4(eprobe->parallaxmat); 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, 1.0f - 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; } static void lightbake_planar_ensure_view(EEVEE_PlanarReflection *eplanar, const DRWView *main_view, DRWView **r_planar_view) { float winmat[4][4], viewmat[4][4], persmat[4][4]; DRW_view_viewmat_get(main_view, viewmat, false); /* Temporal sampling jitter should be already applied to the DRW_MAT_WIN. */ DRW_view_winmat_get(main_view, winmat, false); DRW_view_persmat_get(main_view, persmat, false); /* Invert X to avoid flipping the triangle facing direction. */ winmat[0][0] = -winmat[0][0]; winmat[1][0] = -winmat[1][0]; winmat[2][0] = -winmat[2][0]; winmat[3][0] = -winmat[3][0]; /* Reflect Camera Matrix. */ mul_m4_m4m4(viewmat, viewmat, eplanar->mtx); if (*r_planar_view == NULL) { *r_planar_view = DRW_view_create( viewmat, winmat, NULL, NULL, EEVEE_lightprobes_obj_visibility_cb); /* Compute offset plane equation (fix missing texels near reflection plane). */ float clip_plane[4]; copy_v4_v4(clip_plane, eplanar->plane_equation); clip_plane[3] += eplanar->clipsta; /* Set clipping plane */ DRW_view_clip_planes_set(*r_planar_view, &clip_plane, 1); } else { DRW_view_update(*r_planar_view, viewmat, winmat, NULL, NULL); } } static void eevee_lightprobes_extract_from_cache(EEVEE_LightProbesInfo *pinfo, LightCache *lcache) { /* copy the entire cache for now (up to MAX_PROBE) */ /* TODO: frustum 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; const DRWContextState *draw_ctx = DRW_context_state_get(); const Scene *scene_eval = DEG_get_evaluated_scene(draw_ctx->depsgraph); eevee_lightprobes_extract_from_cache(sldata->probes, light_cache); GPU_uniformbuf_update(sldata->probe_ubo, &sldata->probes->probe_data); GPU_uniformbuf_update(sldata->grid_ubo, &sldata->probes->grid_data); /* For shading, save max level of the octahedron map */ sldata->common_data.prb_lod_cube_max = (float)light_cache->mips_len; sldata->common_data.prb_irradiance_vis_size = light_cache->vis_res; sldata->common_data.prb_irradiance_smooth = square_f(scene_eval->eevee.gi_irradiance_smoothing); 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 light-cache 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)) { 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_data != NULL) { if (pinfo->do_grid_update) { scene_orig->eevee.light_cache_data->flag |= LIGHTCACHE_UPDATE_GRID; } /* If we update grid we need to update the cube-maps too. * So always refresh cube-maps. */ scene_orig->eevee.light_cache_data->flag |= LIGHTCACHE_UPDATE_CUBE; /* Tag the lightcache to auto update. */ scene_orig->eevee.light_cache_data->flag |= LIGHTCACHE_UPDATE_AUTO; /* Use a notifier to trigger the operator after drawing. */ WM_event_add_notifier(draw_ctx->evil_C, NC_LIGHTPROBE, scene_orig); } } } if (pinfo->num_planar > 0) { EEVEE_PassList *psl = vedata->psl; EEVEE_TextureList *txl = vedata->txl; DRW_PASS_CREATE(psl->probe_planar_downsample_ps, DRW_STATE_WRITE_COLOR); DRWShadingGroup *grp = DRW_shgroup_create(EEVEE_shaders_probe_planar_downsample_sh_get(), psl->probe_planar_downsample_ps); DRW_shgroup_uniform_texture_ref(grp, "source", &txl->planar_pool); DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1); DRW_shgroup_call_procedural_triangles(grp, NULL, pinfo->num_planar); } } /* -------------------------------------------------------------------- */ /** \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 near, float far, bool do_culling) { EEVEE_StorageList *stl = user_data->vedata->stl; DRWView **views = do_culling ? stl->g_data->bake_views : stl->g_data->world_views; float winmat[4][4], viewmat[4][4]; perspective_m4(winmat, -near, near, -near, near, near, far); /* Prepare views at the same time for faster culling. */ for (int i = 0; i < 6; i++) { unit_m4(viewmat); negate_v3_v3(viewmat[3], pos); mul_m4_m4m4(viewmat, cubefacemat[i], viewmat); if (do_culling) { if (views[i] == NULL) { views[i] = DRW_view_create(viewmat, winmat, NULL, NULL, NULL); } else { DRW_view_update(views[i], viewmat, winmat, NULL, NULL); } } else { if (views[i] == NULL) { const DRWView *default_view = DRW_view_default_get(); views[i] = DRW_view_create_sub(default_view, viewmat, winmat); } else { DRW_view_update_sub(views[i], viewmat, winmat); } } } for (int i = 0; i < 6; i++) { DRW_view_set_active(views[i]); callback(i, user_data); } } static void render_reflections(void (*callback)(int face, EEVEE_BakeRenderData *user_data), EEVEE_BakeRenderData *user_data, EEVEE_PlanarReflection *planar_data, int ref_count) { EEVEE_StorageList *stl = user_data->vedata->stl; DRWView *main_view = stl->effects->taa_view; DRWView **views = stl->g_data->planar_views; /* Prepare views at the same time for faster culling. */ for (int i = 0; i < ref_count; i++) { lightbake_planar_ensure_view(&planar_data[i], main_view, &views[i]); } for (int i = 0; i < ref_count; i++) { DRW_view_set_active(views[i]); callback(i, user_data); } } 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; /* 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); } 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, }; render_cubemap(lightbake_render_world_face, &brdata, (float[3]){0.0f}, 1.0f, 10.0f, false); } 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; EEVEE_PrivateData *g_data = user_data->vedata->stl->g_data; DRWView **views = g_data->bake_views; struct GPUFrameBuffer **face_fb = user_data->face_fb; /* Be sure that cascaded shadow maps are updated. */ EEVEE_shadows_draw(sldata, user_data->vedata, views[face]); GPU_framebuffer_bind(face_fb[face]); GPU_framebuffer_clear_depth(face_fb[face], 1.0f); DRW_draw_pass(psl->depth_ps); DRW_draw_pass(psl->probe_background); DRW_draw_pass(psl->material_ps); DRW_draw_pass(psl->material_sss_ps); /* Only output standard pass */ DRW_draw_pass(psl->transparent_pass); } /* 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_BakeRenderData brdata = { .vedata = vedata, .sldata = sldata, .face_fb = face_fb, }; render_cubemap(lightbake_render_scene_face, &brdata, pos, near_clip, far_clip, true); } 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_StorageList *stl = vedata->stl; EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_LightProbesInfo *pinfo = sldata->probes; GPU_framebuffer_ensure_config(&fbl->planarref_fb, {GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_depth, layer), GPU_ATTACHMENT_TEXTURE_LAYER(txl->planar_pool, layer)}); /* Use visibility info for this planar reflection. */ pinfo->vis_data = pinfo->planar_vis_tests[layer]; /* 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; DRW_stats_group_start("Planar Reflection"); /* Be sure that cascaded shadow maps are updated. */ EEVEE_shadows_draw(sldata, vedata, stl->g_data->planar_views[layer]); GPU_framebuffer_bind(fbl->planarref_fb); GPU_framebuffer_clear_depth(fbl->planarref_fb, 1.0); float prev_background_alpha = vedata->stl->g_data->background_alpha; vedata->stl->g_data->background_alpha = 1.0f; /* Slight modification: we handle refraction as normal * shading and don't do SSRefraction. */ DRW_draw_pass(psl->depth_clip_ps); DRW_draw_pass(psl->depth_refract_clip_ps); DRW_draw_pass(psl->probe_background); EEVEE_create_minmax_buffer(vedata, tmp_planar_depth, layer); EEVEE_occlusion_compute(sldata, vedata); GPU_framebuffer_bind(fbl->planarref_fb); /* Shading pass */ DRW_draw_pass(psl->material_ps); DRW_draw_pass(psl->material_sss_ps); /* Only output standard pass */ DRW_draw_pass(psl->material_refract_ps); /* 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_stats_group_end(); /* Restore */ txl->planar_pool = tmp_planar_pool; txl->planar_depth = tmp_planar_depth; vedata->stl->g_data->background_alpha = prev_background_alpha; } static void eevee_lightbake_render_scene_to_planars(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { EEVEE_BakeRenderData brdata = { .vedata = vedata, .sldata = sldata, }; render_reflections(lightbake_render_scene_reflected, &brdata, sldata->probes->planar_data, sldata->probes->num_planar); } /** \} */ /* -------------------------------------------------------------------- */ /** \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, float filter_quality, float firefly_fac) { EEVEE_PassList *psl = vedata->psl; EEVEE_LightProbesInfo *pinfo = sldata->probes; LightCache *light_cache = vedata->stl->g_data->light_cache; float target_size = (float)GPU_texture_width(rt_color); /* Max lod used from the render target probe */ pinfo->lod_rt_max = log2_floor_u(target_size) - 2.0f; pinfo->intensity_fac = intensity; /* Start fresh */ 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, rt_color, (int)(pinfo->lod_rt_max)); /* 3 - Render to probe array to the specified layer, do prefiltering. */ int mipsize = GPU_texture_width(light_cache->cube_tx.tex); for (int i = 0; i < maxlevel + 1; i++) { float bias = 0.0f; pinfo->texel_size = 1.0f / (float)mipsize; pinfo->padding_size = (i == maxlevel) ? 0 : (float)(1 << (maxlevel - i - 1)); pinfo->padding_size *= pinfo->texel_size; pinfo->layer = probe_idx * 6; pinfo->roughness = i / (float)maxlevel; /* Disney Roughness */ pinfo->roughness = square_f(pinfo->roughness); /* Distribute Roughness across lod more evenly */ pinfo->roughness = square_f(pinfo->roughness); CLAMP(pinfo->roughness, 1e-4f, 0.9999f); /* Avoid artifacts */ #if 1 /* Variable Sample count and bias (fast) */ switch (i) { case 0: pinfo->samples_len = 1.0f; bias = -1.0f; break; case 1: pinfo->samples_len = 32.0f; bias = 1.0f; break; case 2: pinfo->samples_len = 40.0f; bias = 2.0f; break; case 3: pinfo->samples_len = 64.0f; bias = 2.0f; break; default: pinfo->samples_len = 128.0f; bias = 2.0f; break; } #else /* Constant Sample count (slow) */ pinfo->samples_len = 1024.0f; #endif /* Cannot go higher than HAMMERSLEY_SIZE */ CLAMP(filter_quality, 1.0f, 8.0f); pinfo->samples_len *= filter_quality; pinfo->lodfactor = bias + 0.5f * log(square_f(target_size) / pinfo->samples_len) / log(2); pinfo->firefly_fac = (firefly_fac > 0.0) ? firefly_fac : 1e16; GPU_framebuffer_ensure_config(&fb, { GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_MIP(light_cache->cube_tx.tex, i), }); GPU_framebuffer_bind(fb); DRW_draw_pass(psl->probe_glossy_compute); mipsize /= 2; CLAMP_MIN(mipsize, 1); } } /* 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_PassList *psl = vedata->psl; EEVEE_LightProbesInfo *pinfo = sldata->probes; LightCache *light_cache = vedata->stl->g_data->light_cache; float target_size = (float)GPU_texture_width(rt_color); pinfo->intensity_fac = intensity; /* find cell position on the virtual 3D texture */ /* NOTE : Keep in sync with load_irradiance_cell() */ #if defined(IRRADIANCE_SH_L2) int size[2] = {3, 3}; #elif defined(IRRADIANCE_HL2) const int size[2] = {3, 2}; pinfo->samples_len = 1024.0f; #endif 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->lodfactor = bias + 0.5f * log(square_f(target_size) / pinfo->samples_len) / log(2); pinfo->lod_rt_max = log2_floor_u(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(&fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_NONE}); /* 4 - Compute diffuse irradiance */ EEVEE_downsample_cube_buffer(vedata, rt_color, (int)(pinfo->lod_rt_max)); GPU_framebuffer_ensure_config( &fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE_LAYER(light_cache->grid_tx.tex, 0)}); GPU_framebuffer_bind(fb); GPU_framebuffer_viewport_set(fb, x, y, size[0], size[1]); DRW_draw_pass(psl->probe_diffuse_compute); GPU_framebuffer_viewport_reset(fb); } /* Filter rt_depth to light_cache->grid_tx.tex at index grid_offset */ void EEVEE_lightbake_filter_visibility(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, 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_PassList *psl = vedata->psl; EEVEE_LightProbesInfo *pinfo = sldata->probes; LightCache *light_cache = vedata->stl->g_data->light_cache; pinfo->samples_len = 512.0f; /* TODO: refine. */ 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(fb); GPU_framebuffer_viewport_set(fb, x, y, vis_size, vis_size); DRW_draw_pass(psl->probe_visibility_compute); GPU_framebuffer_viewport_reset(fb); } /* Actually a simple down-sampling. */ static void downsample_planar(void *vedata, int level) { 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); DRW_draw_pass(psl->probe_planar_downsample_ps); } static void EEVEE_lightbake_filter_planar(EEVEE_Data *vedata) { EEVEE_TextureList *txl = vedata->txl; EEVEE_FramebufferList *fbl = vedata->fbl; 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_SCREEN_BUFFERS_LOD_LEVEL, &downsample_planar, vedata); DRW_stats_group_end(); } /** \} */ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; EEVEE_LightProbesInfo *pinfo = sldata->probes; if (pinfo->num_planar == 0) { /* Disable SSR if we cannot read previous frame */ common_data->ssr_toggle = vedata->stl->g_data->valid_double_buffer; common_data->prb_num_planar = 0; return; } /* Temporary Remove all planar reflections (avoid lag effect). */ common_data->prb_num_planar = 0; /* Turn off ssr to avoid black specular */ common_data->ssr_toggle = false; common_data->ssrefract_toggle = false; common_data->sss_toggle = false; common_data->ray_type = EEVEE_RAY_GLOSSY; common_data->ray_depth = 1.0f; GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); /* Rendering happens here! */ eevee_lightbake_render_scene_to_planars(sldata, vedata); /* Make sure no additional visibility check runs after this. */ pinfo->vis_data.collection = NULL; GPU_uniformbuf_update(sldata->planar_ubo, &sldata->probes->planar_data); /* Restore */ common_data->prb_num_planar = pinfo->num_planar; common_data->ssr_toggle = true; common_data->ssrefract_toggle = true; common_data->sss_toggle = true; /* Prefilter for SSR */ if ((vedata->stl->effects->enabled_effects & EFFECT_SSR) != 0) { EEVEE_lightbake_filter_planar(vedata); } if (DRW_state_is_image_render()) { /* Sort transparents because planar reflections could have re-sorted them. */ DRW_pass_sort_shgroup_z(vedata->psl->transparent_pass); } /* Disable SSR if we cannot read previous frame */ common_data->ssr_toggle = vedata->stl->g_data->valid_double_buffer; } void EEVEE_lightprobes_refresh(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { 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; if ((light_cache->flag & LIGHTCACHE_UPDATE_WORLD) && (light_cache->flag & LIGHTCACHE_BAKED) == 0) { EEVEE_lightbake_update_world_quick(sldata, vedata, scene_eval); } } void EEVEE_lightprobes_free(void) { MEM_SAFE_FREE(e_data.format_probe_display_planar); DRW_TEXTURE_FREE_SAFE(e_data.planar_pool_placeholder); DRW_TEXTURE_FREE_SAFE(e_data.depth_placeholder); DRW_TEXTURE_FREE_SAFE(e_data.depth_array_placeholder); }