diff options
Diffstat (limited to 'source/blender/draw/engines/eevee/eevee_lightprobes.c')
-rw-r--r-- | source/blender/draw/engines/eevee/eevee_lightprobes.c | 1709 |
1 files changed, 1709 insertions, 0 deletions
diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c new file mode 100644 index 00000000000..ab0b2c7fd29 --- /dev/null +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -0,0 +1,1709 @@ +/* + * 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_lightprobes.c + * \ingroup DNA + */ + +#include "DRW_render.h" + +#include "BLI_utildefines.h" +#include "BLI_string_utils.h" +#include "BLI_rand.h" + +#include "DNA_world_types.h" +#include "DNA_texture_types.h" +#include "DNA_image_types.h" +#include "DNA_lightprobe_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_object.h" + +#include "GPU_material.h" +#include "GPU_texture.h" +#include "GPU_glew.h" + +#include "eevee_engine.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 + +#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 { + struct GPUShader *probe_default_sh; + struct GPUShader *probe_filter_glossy_sh; + struct GPUShader *probe_filter_diffuse_sh; + struct GPUShader *probe_filter_visibility_sh; + struct GPUShader *probe_grid_fill_sh; + struct GPUShader *probe_grid_display_sh; + struct GPUShader *probe_planar_display_sh; + struct GPUShader *probe_planar_downsample_sh; + struct GPUShader *probe_cube_display_sh; + + struct GPUTexture *hammersley; + struct GPUTexture *planar_pool_placeholder; + struct GPUTexture *depth_placeholder; + struct GPUTexture *depth_array_placeholder; + struct GPUTexture *cube_face_minmaxz; + + int update_world; +} e_data = {NULL}; /* Engine data */ + +extern char datatoc_background_vert_glsl[]; +extern char datatoc_default_world_frag_glsl[]; +extern char datatoc_lightprobe_filter_glossy_frag_glsl[]; +extern char datatoc_lightprobe_filter_diffuse_frag_glsl[]; +extern char datatoc_lightprobe_filter_visibility_frag_glsl[]; +extern char datatoc_lightprobe_geom_glsl[]; +extern char datatoc_lightprobe_vert_glsl[]; +extern char datatoc_lightprobe_planar_display_frag_glsl[]; +extern char datatoc_lightprobe_planar_display_vert_glsl[]; +extern char datatoc_lightprobe_planar_downsample_frag_glsl[]; +extern char datatoc_lightprobe_planar_downsample_geom_glsl[]; +extern char datatoc_lightprobe_planar_downsample_vert_glsl[]; +extern char datatoc_lightprobe_cube_display_frag_glsl[]; +extern char datatoc_lightprobe_cube_display_vert_glsl[]; +extern char datatoc_lightprobe_grid_display_frag_glsl[]; +extern char datatoc_lightprobe_grid_display_vert_glsl[]; +extern char datatoc_lightprobe_grid_fill_frag_glsl[]; +extern char datatoc_irradiance_lib_glsl[]; +extern char datatoc_lightprobe_lib_glsl[]; +extern char datatoc_octahedron_lib_glsl[]; +extern char datatoc_bsdf_common_lib_glsl[]; +extern char datatoc_common_uniforms_lib_glsl[]; +extern char datatoc_bsdf_sampling_lib_glsl[]; + +extern GlobalsUboStorage ts; + +/* *********** FUNCTIONS *********** */ + +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 struct GPUTexture *create_hammersley_sample_texture(int samples) +{ + struct GPUTexture *tex; + float (*texels)[2] = MEM_mallocN(sizeof(float[2]) * samples, "hammersley_tex"); + int i; + + for (i = 0; i < samples; i++) { + double dphi; + BLI_hammersley_1D(i, &dphi); + float phi = (float)dphi * 2.0f * M_PI; + texels[i][0] = cosf(phi); + texels[i][1] = sinf(phi); + } + + tex = DRW_texture_create_1D(samples, DRW_TEX_RG_16, DRW_TEX_WRAP, (float *)texels); + MEM_freeN(texels); + return tex; +} + +static void planar_pool_ensure_alloc(EEVEE_Data *vedata, int num_planar_ref) +{ + /* 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_FramebufferList *fbl = vedata->fbl; + EEVEE_TextureList *txl = vedata->txl; + + const float *viewport_size = DRW_viewport_size_get(); + + /* TODO get screen percentage from layer setting */ + // const DRWContextState *draw_ctx = DRW_context_state_get(); + // ViewLayer *view_layer = draw_ctx->view_layer; + float screen_percentage = 1.0f; + + int width = (int)(viewport_size[0] * screen_percentage); + int height = (int)(viewport_size[1] * screen_percentage); + + /* 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, max_ff(1, num_planar_ref), + DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); + txl->planar_depth = DRW_texture_create_2D_array(width, height, max_ff(1, num_planar_ref), + DRW_TEX_DEPTH_24, 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, DRW_TEX_RGBA_8, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); + txl->planar_depth = DRW_texture_create_2D_array(1, 1, 1, DRW_TEX_DEPTH_24, 0, NULL); + } + } + + if (num_planar_ref > 0) { + /* NOTE : Depth buffer is 2D but the planar_pool tex is 2D array. + * DRW_framebuffer_init binds the whole texture making the framebuffer invalid. + * To overcome this, we bind the planar pool ourselves later */ + + /* XXX Do this one first so it gets it's mipmap done. */ + DRW_framebuffer_init(&fbl->planarref_fb, &draw_engine_eevee_type, 1, 1, NULL, 0); + } +} + +static void lightprobe_shaders_init(void) +{ + const char *filter_defines = "#define HAMMERSLEY_SIZE " STRINGIFY(HAMMERSLEY_SIZE) "\n" +#if defined(IRRADIANCE_SH_L2) + "#define IRRADIANCE_SH_L2\n" +#elif defined(IRRADIANCE_CUBEMAP) + "#define IRRADIANCE_CUBEMAP\n" +#elif defined(IRRADIANCE_HL2) + "#define IRRADIANCE_HL2\n" +#endif + "#define NOISE_SIZE 64\n"; + + char *shader_str = NULL; + + shader_str = BLI_string_joinN( + datatoc_common_uniforms_lib_glsl, + datatoc_bsdf_common_lib_glsl, + datatoc_bsdf_sampling_lib_glsl, + datatoc_lightprobe_filter_glossy_frag_glsl); + + e_data.probe_filter_glossy_sh = DRW_shader_create( + datatoc_lightprobe_vert_glsl, datatoc_lightprobe_geom_glsl, shader_str, filter_defines); + + e_data.probe_default_sh = DRW_shader_create( + datatoc_background_vert_glsl, NULL, datatoc_default_world_frag_glsl, NULL); + + MEM_freeN(shader_str); + + shader_str = BLI_string_joinN( + datatoc_common_uniforms_lib_glsl, + datatoc_bsdf_common_lib_glsl, + datatoc_bsdf_sampling_lib_glsl, + datatoc_lightprobe_filter_diffuse_frag_glsl); + + e_data.probe_filter_diffuse_sh = DRW_shader_create_fullscreen(shader_str, filter_defines); + + MEM_freeN(shader_str); + + shader_str = BLI_string_joinN( + datatoc_common_uniforms_lib_glsl, + datatoc_bsdf_common_lib_glsl, + datatoc_bsdf_sampling_lib_glsl, + datatoc_lightprobe_filter_visibility_frag_glsl); + + e_data.probe_filter_visibility_sh = DRW_shader_create_fullscreen(shader_str, filter_defines); + + MEM_freeN(shader_str); + + shader_str = BLI_string_joinN( + datatoc_octahedron_lib_glsl, + datatoc_common_uniforms_lib_glsl, + datatoc_bsdf_common_lib_glsl, + datatoc_irradiance_lib_glsl, + datatoc_lightprobe_lib_glsl, + datatoc_lightprobe_grid_display_frag_glsl); + + e_data.probe_grid_display_sh = DRW_shader_create( + datatoc_lightprobe_grid_display_vert_glsl, NULL, shader_str, filter_defines); + + MEM_freeN(shader_str); + + e_data.probe_grid_fill_sh = DRW_shader_create_fullscreen( + datatoc_lightprobe_grid_fill_frag_glsl, filter_defines); + + shader_str = BLI_string_joinN( + datatoc_octahedron_lib_glsl, + datatoc_common_uniforms_lib_glsl, + datatoc_bsdf_common_lib_glsl, + datatoc_lightprobe_lib_glsl, + datatoc_lightprobe_cube_display_frag_glsl); + + e_data.probe_cube_display_sh = DRW_shader_create( + datatoc_lightprobe_cube_display_vert_glsl, NULL, shader_str, NULL); + + MEM_freeN(shader_str); + + e_data.probe_planar_display_sh = DRW_shader_create( + datatoc_lightprobe_planar_display_vert_glsl, NULL, + datatoc_lightprobe_planar_display_frag_glsl, NULL); + + e_data.probe_planar_downsample_sh = DRW_shader_create( + datatoc_lightprobe_planar_downsample_vert_glsl, + datatoc_lightprobe_planar_downsample_geom_glsl, + datatoc_lightprobe_planar_downsample_frag_glsl, + NULL); + + e_data.hammersley = create_hammersley_sample_texture(HAMMERSLEY_SIZE); +} + +void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(vedata)) +{ + EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; + bool update_all = false; + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + IDProperty *props = BKE_view_layer_engine_evaluated_get(view_layer, COLLECTION_MODE_NONE, RE_engine_id_BLENDER_EEVEE); + + /* Shaders */ + if (!e_data.probe_filter_glossy_sh) { + lightprobe_shaders_init(); + } + + 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); + } + + common_data->spec_toggle = true; + common_data->ssr_toggle = true; + common_data->sss_toggle = true; + + int prop_bounce_num = BKE_collection_engine_property_value_get_int(props, "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 = BKE_collection_engine_property_value_get_int(props, "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_rt); + DRW_TEXTURE_FREE_SAFE(sldata->probe_pool); + } + + int visibility_res = BKE_collection_engine_property_value_get_int(props, "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) { + e_data.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, DRW_TEX_DEPTH_24, 0, NULL); + sldata->probe_rt = DRW_texture_create_cube(sldata->probes->target_size, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); + } + + DRWFboTexture tex_probe[2] = {{&sldata->probe_depth_rt, DRW_TEX_DEPTH_24, 0}, + {&sldata->probe_rt, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP}}; + DRW_framebuffer_init(&sldata->probe_fb, &draw_engine_eevee_type, sldata->probes->target_size, sldata->probes->target_size, tex_probe, 2); + + /* Minmaxz Pyramid */ + // DRWFboTexture tex_minmaxz = {&e_data.cube_face_minmaxz, DRW_TEX_RG_32, DRW_TEX_MIPMAP | DRW_TEX_TEMP}; + // DRW_framebuffer_init(&vedata->fbl->downsample_fb, &draw_engine_eevee_type, PROBE_RT_SIZE / 2, PROBE_RT_SIZE / 2, &tex_minmaxz, 1); + + /* 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, DRW_TEX_RGBA_8, DRW_TEX_FILTER, NULL); + } + + if (!e_data.depth_placeholder) { + e_data.depth_placeholder = DRW_texture_create_2D(1, 1, DRW_TEX_DEPTH_24, 0, NULL); + } + if (!e_data.depth_array_placeholder) { + e_data.depth_array_placeholder = DRW_texture_create_2D_array(1, 1, 1, DRW_TEX_DEPTH_24, 0, 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; + + 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; + if (wo) { + col = &wo->horr; + if (wo->update_flag != 0 || pinfo->prev_world != wo) { + e_data.update_world |= PROBE_UPDATE_ALL; + pinfo->updated_bounce = 0; + pinfo->grid_initialized = false; + } + wo->update_flag = 0; + + if (wo->use_nodes && wo->nodetree) { + struct GPUMaterial *gpumat = EEVEE_material_world_lightprobe_get(scene, wo); + + grp = DRW_shgroup_material_create(gpumat, psl->probe_background); + + if (grp) { + DRW_shgroup_uniform_float(grp, "backgroundAlpha", &stl->g_data->background_alpha, 1); + DRW_shgroup_call_add(grp, geom, NULL); + } + else { + /* Shader failed : pink background */ + static float pink[3] = {1.0f, 0.0f, 1.0f}; + col = pink; + } + } + + pinfo->prev_world = wo; + } + else if (pinfo->prev_world) { + pinfo->prev_world = NULL; + e_data.update_world |= PROBE_UPDATE_ALL; + pinfo->updated_bounce = 0; + pinfo->grid_initialized = false; + } + + /* 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); + + struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get(); + + DRWShadingGroup *grp = DRW_shgroup_instance_create(e_data.probe_filter_glossy_sh, psl->probe_glossy_compute, geom); + DRW_shgroup_uniform_float(grp, "intensityFac", &pinfo->intensity_fac, 1); + DRW_shgroup_uniform_float(grp, "sampleCount", &pinfo->samples_ct, 1); + DRW_shgroup_uniform_float(grp, "invSampleCount", &pinfo->invsamples_ct, 1); + DRW_shgroup_uniform_float(grp, "roughnessSquared", &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_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_set_instance_count(grp, 1); + } + + { + psl->probe_diffuse_compute = DRW_pass_create("LightProbe Diffuse Compute", DRW_STATE_WRITE_COLOR); + + DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_filter_diffuse_sh, 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_ct, 1); + DRW_shgroup_uniform_float(grp, "invSampleCount", &pinfo->invsamples_ct, 1); + DRW_shgroup_uniform_float(grp, "lodFactor", &pinfo->lodfactor, 1); + DRW_shgroup_uniform_float(grp, "lodMax", &pinfo->lod_rt_max, 1); + 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); + + struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get(); + DRW_shgroup_call_add(grp, geom, NULL); + } + + { + psl->probe_visibility_compute = DRW_pass_create("LightProbe Visibility Compute", DRW_STATE_WRITE_COLOR); + + DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_filter_visibility_sh, 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_ct, 1); + DRW_shgroup_uniform_float(grp, "invSampleCount", &pinfo->invsamples_ct, 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, "texHammersley", e_data.hammersley); + DRW_shgroup_uniform_texture(grp, "probeDepth", sldata->probe_depth_rt); + + struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get(); + DRW_shgroup_call_add(grp, geom, NULL); + } + + { + 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_buffer(grp, "irradianceGrid", &sldata->irradiance_pool); + + struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get(); + DRW_shgroup_call_add(grp, geom, NULL); + } + + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS | DRW_STATE_CULL_BACK; + psl->probe_display = DRW_pass_create("LightProbe Display", state); + + struct Gwn_Batch *geom = DRW_cache_sphere_get(); + DRWShadingGroup *grp = stl->g_data->cube_display_shgrp = DRW_shgroup_instance_create(e_data.probe_cube_display_sh, psl->probe_display, geom); + DRW_shgroup_attrib_float(grp, "probe_id", 1); /* XXX this works because we are still uploading 4bytes and using the right stride */ + DRW_shgroup_attrib_float(grp, "probe_location", 3); + DRW_shgroup_attrib_float(grp, "sphere_size", 1); + DRW_shgroup_uniform_buffer(grp, "probeCubes", &sldata->probe_pool); + DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + + geom = DRW_cache_quad_get(); + grp = stl->g_data->planar_display_shgrp = DRW_shgroup_instance_create(e_data.probe_planar_display_sh, psl->probe_display, geom); + DRW_shgroup_attrib_float(grp, "probe_id", 1); /* XXX this works because we are still uploading 4bytes and using the right stride */ + DRW_shgroup_attrib_float(grp, "probe_mat", 16); + DRW_shgroup_uniform_buffer(grp, "probePlanars", &txl->planar_pool); + } + + { + psl->probe_planar_downsample_ps = DRW_pass_create("LightProbe Planar Downsample", DRW_STATE_WRITE_COLOR); + + struct Gwn_Batch *geom = DRW_cache_fullscreen_quad_get(); + DRWShadingGroup *grp = stl->g_data->planar_downsample = DRW_shgroup_instance_create(e_data.probe_planar_downsample_sh, psl->probe_planar_downsample_ps, geom); + DRW_shgroup_uniform_buffer(grp, "source", &txl->planar_pool); + DRW_shgroup_uniform_float(grp, "fireflyFactor", &sldata->common_data.ssr_firefly_fac, 1); + } +} + +void EEVEE_lightprobes_cache_add(EEVEE_ViewLayerData *sldata, Object *ob) +{ + EEVEE_LightProbesInfo *pinfo = sldata->probes; + LightProbe *probe = (LightProbe *)ob->data; + + /* Step 1 find all lamps in the scene and setup them */ + if ((probe->type == LIGHTPROBE_TYPE_CUBE && pinfo->num_cube >= MAX_PROBE) || + (probe->type == LIGHTPROBE_TYPE_GRID && pinfo->num_grid >= MAX_PROBE)) + { + printf("Too much probes in the scene !!!\n"); + return; + } + + EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_ensure(ob); + + ped->num_cell = probe->grid_resolution_x * probe->grid_resolution_y * probe->grid_resolution_z; + + if ((probe->type == LIGHTPROBE_TYPE_GRID) && + ((pinfo->total_irradiance_samples + ped->num_cell) >= MAX_IRRADIANCE_SAMPLES)) + { + printf("Too much grid samples !!!\n"); + return; + } + + if (ped->need_full_update) { + ped->need_full_update = false; + + 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; + } + } + + if (e_data.update_world) { + ped->need_update = true; + ped->updated_cells = 0; + ped->updated_lvl = 0; + ped->probe_id = 0; + } + + pinfo->do_cube_update |= ped->need_update; + + if (probe->type == LIGHTPROBE_TYPE_CUBE) { + pinfo->probes_cube_ref[pinfo->num_cube] = ob; + pinfo->num_cube++; + } + else if (probe->type == LIGHTPROBE_TYPE_PLANAR) { + pinfo->probes_planar_ref[pinfo->num_planar] = ob; + pinfo->num_planar++; + } + else { /* GRID */ + pinfo->probes_grid_ref[pinfo->num_grid] = ob; + pinfo->num_grid++; + pinfo->total_irradiance_samples += ped->num_cell; + } +} + +/* TODO find a nice name to push it to math_matrix.c */ +static void scale_m4_v3(float R[4][4], float v[3]) +{ + for (int i = 0; i < 4; ++i) + mul_v3_v3(R[i], v); +} + +static void EEVEE_planar_reflections_updates(EEVEE_ViewLayerData *sldata, EEVEE_StorageList *stl) +{ + EEVEE_LightProbesInfo *pinfo = sldata->probes; + Object *ob; + float mtx[4][4], normat[4][4], imat[4][4], rangemat[4][4]; + + float viewmat[4][4], winmat[4][4]; + DRW_viewport_matrix_get(viewmat, DRW_MAT_VIEW); + DRW_viewport_matrix_get(winmat, DRW_MAT_WIN); + + 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; + + /* 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->viewmat, viewmat, mtx); + + /* TODO FOV margin */ + float winmat_fov[4][4]; + copy_m4_m4(winmat_fov, winmat); + + /* Apply Perspective Matrix. */ + mul_m4_m4m4(ped->persmat, winmat_fov, ped->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, winmat_fov, viewmat); /* TODO FOV margin */ + /* Convert from [-1, 1] to [0, 1] (NDC to Texture coord). */ + mul_m4_m4m4(eplanar->reflectionmat, rangemat, eplanar->reflectionmat); + + /* TODO frustum check. */ + ped->need_update = true; + + /* 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; + + /* 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); + } + } +} + +static void EEVEE_lightprobes_updates(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl, EEVEE_StorageList *stl) +{ + EEVEE_LightProbesInfo *pinfo = sldata->probes; + Object *ob; + + /* 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); + + /* 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 */ + 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; + } + + unit_m4(eprobe->parallaxmat); + scale_m4_fl(eprobe->parallaxmat, dist); + mul_m4_m4m4(eprobe->parallaxmat, ob->obmat, eprobe->parallaxmat); + invert_m4(eprobe->parallaxmat); + + /* 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); + } + } + + /* 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); + + /* 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; + } + + /* 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 = max_ff(max_ff(len_v3(egrid->increment_x), + len_v3(egrid->increment_y)), + len_v3(egrid->increment_z)) + 1.0f; + + /* Debug Display */ + if (DRW_state_draw_support() && + (probe->flag & LIGHTPROBE_FLAG_SHOW_DATA)) + { + struct Gwn_Batch *geom = DRW_cache_sphere_get(); + DRWShadingGroup *grp = DRW_shgroup_instance_create(e_data.probe_grid_display_sh, psl->probe_display, geom); + DRW_shgroup_set_instance_count(grp, ped->num_cell); + 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_buffer(grp, "irradianceGrid", &sldata->irradiance_pool); + DRW_shgroup_uniform_float(grp, "sphere_size", &probe->data_draw_size, 1); + } + } +} + +void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) +{ + EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; + EEVEE_StorageList *stl = vedata->stl; + EEVEE_LightProbesInfo *pinfo = sldata->probes; + Object *ob; + + /* Setup enough layers. */ + /* Free textures if number mismatch. */ + if (pinfo->num_cube != pinfo->cache_num_cube) { + DRW_TEXTURE_FREE_SAFE(sldata->probe_pool); + } + + 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; + } + + int irr_size[3]; + irradiance_pool_size_get(common_data->prb_irradiance_vis_size, pinfo->total_irradiance_samples, irr_size); + + 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); + } + + /* XXX this should be run each frame as it ensure planar_depth is set */ + planar_pool_ensure_alloc(vedata, pinfo->num_planar); + + /* Setup planar filtering pass */ + DRW_shgroup_set_instance_count(stl->g_data->planar_downsample, pinfo->num_planar); + + if (!sldata->probe_pool) { + sldata->probe_pool = DRW_texture_create_2D_array(pinfo->cubemap_res, pinfo->cubemap_res, max_ff(1, pinfo->num_cube), + DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); + if (sldata->probe_filter_fb) { + DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0); + } + + /* Tag probes to refresh */ + e_data.update_world |= PROBE_UPDATE_CUBE; + common_data->prb_num_render_cube = 0; + pinfo->cache_num_cube = pinfo->num_cube; + + 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; + } + } + + DRWFboTexture tex_filter = {&sldata->probe_pool, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP}; + + DRW_framebuffer_init(&sldata->probe_filter_fb, &draw_engine_eevee_type, pinfo->cubemap_res, pinfo->cubemap_res, &tex_filter, 1); + + +#ifdef IRRADIANCE_SH_L2 + /* we need a signed format for Spherical Harmonics */ + int irradiance_format = DRW_TEX_RGBA_16; +#else + int irradiance_format = DRW_TEX_RGBA_8; +#endif + + 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); + } + common_data->prb_num_render_grid = 0; + pinfo->updated_bounce = 0; + pinfo->grid_initialized = false; + e_data.update_world |= PROBE_UPDATE_GRID; + + 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; + } + } + + 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; + } + + EEVEE_lightprobes_updates(sldata, vedata->psl, vedata->stl); + EEVEE_planar_reflections_updates(sldata, vedata->stl); + + DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data); + DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data); + DRW_uniformbuffer_update(sldata->planar_ubo, &sldata->probes->planar_data); +} + +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); +} + +/* 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) +{ + EEVEE_LightProbesInfo *pinfo = sldata->probes; + + pinfo->intensity_fac = intensity; + + /* Max lod used from the render target probe */ + pinfo->lod_rt_max = floorf(log2f(pinfo->target_size)) - 2.0f; + + /* 2 - Let gpu create Mipmaps for Filtered Importance Sampling. */ + /* Bind next framebuffer to be able to gen. mips for probe_rt. */ + DRW_framebuffer_bind(sldata->probe_filter_fb); + EEVEE_downsample_cube_buffer(vedata, sldata->probe_filter_fb, sldata->probe_rt, (int)(pinfo->lod_rt_max)); + + /* 3 - Render to probe array to the specified layer, do prefiltering. */ + /* Detach to rebind the right mipmap. */ + DRW_framebuffer_texture_detach(sldata->probe_pool); + 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++) { + 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)); + /* XXX : WHY THE HECK DO WE NEED THIS ??? */ + /* padding is incorrect without this! float precision issue? */ + if (pinfo->padding_size > 32) { + pinfo->padding_size += 5; + } + if (pinfo->padding_size > 16) { + pinfo->padding_size += 4; + } + else if (pinfo->padding_size > 8) { + pinfo->padding_size += 2; + } + else if (pinfo->padding_size > 4) { + pinfo->padding_size += 1; + } + pinfo->layer = probe_idx; + pinfo->roughness = (float)i / ((float)maxlevel - 4.0f); + 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 */ + +#if 1 /* Variable Sample count (fast) */ + switch (i) { + case 0: pinfo->samples_ct = 1.0f; break; + case 1: pinfo->samples_ct = 16.0f; break; + case 2: pinfo->samples_ct = 32.0f; break; + case 3: pinfo->samples_ct = 64.0f; break; + default: pinfo->samples_ct = 128.0f; break; + } +#else /* Constant Sample count (slow) */ + pinfo->samples_ct = 1024.0f; +#endif + + pinfo->invsamples_ct = 1.0f / pinfo->samples_ct; + pinfo->lodfactor = bias + 0.5f * log((float)(pinfo->target_size * pinfo->target_size) * pinfo->invsamples_ct) / log(2); + + DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, i); + DRW_framebuffer_viewport_size(sldata->probe_filter_fb, 0, 0, mipsize, mipsize); + DRW_draw_pass(psl->probe_glossy_compute); + DRW_framebuffer_texture_detach(sldata->probe_pool); + + 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; + + /* reattach to have a valid framebuffer. */ + DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0); +} + +/* 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) +{ + EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; + EEVEE_LightProbesInfo *pinfo = sldata->probes; + + pinfo->intensity_fac = intensity; + + int pool_size[3]; + irradiance_pool_size_get(common_data->prb_irradiance_vis_size, pinfo->total_irradiance_samples, pool_size); + + /* 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_CUBEMAP) + int size[2] = {8, 8}; + pinfo->samples_ct = 1024.0f; +#elif defined(IRRADIANCE_HL2) + int size[2] = {3, 2}; + pinfo->samples_ct = 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); + +#ifndef IRRADIANCE_SH_L2 + /* Tweaking parameters to balance perf. vs precision */ + const float bias = 0.0f; + pinfo->invsamples_ct = 1.0f / pinfo->samples_ct; + pinfo->lodfactor = bias + 0.5f * log((float)(pinfo->target_size * pinfo->target_size) * pinfo->invsamples_ct) / log(2); + pinfo->lod_rt_max = floorf(log2f(pinfo->target_size)) - 2.0f; +#else + pinfo->shres = 32; /* Less texture fetches & reduce branches */ + pinfo->lod_rt_max = 2.0f; /* Improve cache reuse */ +#endif + + /* 4 - Compute spherical harmonics */ + DRW_framebuffer_bind(sldata->probe_filter_fb); + EEVEE_downsample_cube_buffer(vedata, sldata->probe_filter_fb, sldata->probe_rt, (int)(pinfo->lod_rt_max)); + + DRW_framebuffer_texture_detach(sldata->probe_pool); + DRW_framebuffer_texture_layer_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0, 0); + + DRW_framebuffer_viewport_size(sldata->probe_filter_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_ct = 512.0f; /* TODO refine */ + pinfo->invsamples_ct = 1.0f / pinfo->samples_ct; + 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); + + DRW_framebuffer_texture_detach(sldata->irradiance_rt); + DRW_framebuffer_texture_layer_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, layer, 0); + + DRW_framebuffer_viewport_size(sldata->probe_filter_fb, x, y, common_data->prb_irradiance_vis_size, + common_data->prb_irradiance_vis_size); + DRW_draw_pass(psl->probe_visibility_compute); + } + + /* reattach to have a valid framebuffer. */ + DRW_framebuffer_texture_detach(sldata->irradiance_rt); + DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0); +} + +/* Render the scene to the probe_rt texture. */ +static void render_scene_to_probe( + EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, + const float pos[3], float clipsta, float clipend) +{ + EEVEE_TextureList *txl = vedata->txl; + EEVEE_PassList *psl = vedata->psl; + EEVEE_StorageList *stl = vedata->stl; + EEVEE_LightProbesInfo *pinfo = sldata->probes; + + float winmat[4][4], wininv[4][4], posmat[4][4]; + + unit_m4(posmat); + + /* Move to capture position */ + negate_v3_v3(posmat[3], pos); + + /* 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). */ + 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_minz = stl->g_data->minzbuffer; + GPUTexture *tmp_maxz = txl->maxzbuffer; + txl->planar_pool = e_data.planar_pool_placeholder; + stl->g_data->minzbuffer = e_data.depth_placeholder; + txl->maxzbuffer = e_data.depth_placeholder; + + /* Update common uniforms */ + DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + + /* Detach to rebind the right cubeface. */ + DRW_framebuffer_bind(sldata->probe_fb); + DRW_framebuffer_texture_detach(sldata->probe_rt); + DRW_framebuffer_texture_detach(sldata->probe_depth_rt); + for (int i = 0; i < 6; ++i) { + float viewmat[4][4], persmat[4][4]; + float viewinv[4][4], persinv[4][4]; + + /* 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(persmat, DRW_MAT_PERS); + DRW_viewport_matrix_override_set(persinv, DRW_MAT_PERSINV); + DRW_viewport_matrix_override_set(viewmat, DRW_MAT_VIEW); + DRW_viewport_matrix_override_set(viewinv, DRW_MAT_VIEWINV); + DRW_viewport_matrix_override_set(winmat, DRW_MAT_WIN); + DRW_viewport_matrix_override_set(wininv, DRW_MAT_WININV); + + /* Be sure that cascaded shadow maps are updated. */ + EEVEE_draw_shadows(sldata, psl); + + DRW_framebuffer_cubeface_attach(sldata->probe_fb, sldata->probe_rt, 0, i, 0); + DRW_framebuffer_cubeface_attach(sldata->probe_fb, sldata->probe_depth_rt, 0, i, 0); + DRW_framebuffer_viewport_size(sldata->probe_fb, 0, 0, pinfo->target_size, pinfo->target_size); + + DRW_framebuffer_clear(false, true, false, NULL, 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 Planar FB */ + DRW_framebuffer_bind(sldata->probe_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_framebuffer_texture_detach(sldata->probe_rt); + DRW_framebuffer_texture_detach(sldata->probe_depth_rt); + } + DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_rt, 0, 0); + DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_depth_rt, 0, 0); + + DRW_viewport_matrix_override_unset(DRW_MAT_PERS); + DRW_viewport_matrix_override_unset(DRW_MAT_PERSINV); + DRW_viewport_matrix_override_unset(DRW_MAT_VIEW); + DRW_viewport_matrix_override_unset(DRW_MAT_VIEWINV); + DRW_viewport_matrix_override_unset(DRW_MAT_WIN); + DRW_viewport_matrix_override_unset(DRW_MAT_WININV); + + /* Restore */ + txl->planar_pool = tmp_planar_pool; + stl->g_data->minzbuffer = tmp_minz; + txl->maxzbuffer = tmp_maxz; +} + +static void render_scene_to_planar( + EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, int layer, + float (*viewmat)[4], float (*persmat)[4], + float clip_plane[4]) +{ + EEVEE_FramebufferList *fbl = vedata->fbl; + EEVEE_TextureList *txl = vedata->txl; + EEVEE_PassList *psl = vedata->psl; + + float viewinv[4][4]; + float persinv[4][4]; + + invert_m4_m4(viewinv, viewmat); + invert_m4_m4(persinv, persmat); + + DRW_viewport_matrix_override_set(persmat, DRW_MAT_PERS); + DRW_viewport_matrix_override_set(persinv, DRW_MAT_PERSINV); + DRW_viewport_matrix_override_set(viewmat, DRW_MAT_VIEW); + DRW_viewport_matrix_override_set(viewinv, DRW_MAT_VIEWINV); + + /* 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(); + + /* Be sure that cascaded shadow maps are updated. */ + EEVEE_draw_shadows(sldata, psl); + + DRW_state_clip_planes_add(clip_plane); + + /* Attach depth here since it's a DRW_TEX_TEMP */ + DRW_framebuffer_texture_layer_attach(fbl->planarref_fb, txl->planar_depth, 0, layer, 0); + DRW_framebuffer_texture_layer_attach(fbl->planarref_fb, txl->planar_pool, 0, layer, 0); + DRW_framebuffer_bind(fbl->planarref_fb); + + DRW_framebuffer_clear(false, true, false, NULL, 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; + + /* Depth prepass */ + DRW_draw_pass(psl->depth_pass_clip); + DRW_draw_pass(psl->depth_pass_clip_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 */ + DRW_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_state_invert_facing(); + DRW_state_clip_planes_reset(); + + /* Restore */ + txl->planar_pool = tmp_planar_pool; + txl->planar_depth = tmp_planar_depth; + DRW_viewport_matrix_override_unset(DRW_MAT_PERS); + DRW_viewport_matrix_override_unset(DRW_MAT_PERSINV); + DRW_viewport_matrix_override_unset(DRW_MAT_VIEW); + DRW_viewport_matrix_override_unset(DRW_MAT_VIEWINV); + + DRW_framebuffer_texture_detach(txl->planar_pool); + DRW_framebuffer_texture_detach(txl->planar_depth); +} + +static void render_world_to_probe(EEVEE_ViewLayerData *sldata, EEVEE_PassList *psl) +{ + EEVEE_LightProbesInfo *pinfo = sldata->probes; + float winmat[4][4], wininv[4][4]; + + /* 1 - Render to cubemap target using geometry shader. */ + /* 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); + + /* Detach to rebind the right cubeface. */ + DRW_framebuffer_bind(sldata->probe_fb); + DRW_framebuffer_texture_detach(sldata->probe_rt); + DRW_framebuffer_texture_detach(sldata->probe_depth_rt); + for (int i = 0; i < 6; ++i) { + float viewmat[4][4], persmat[4][4]; + float viewinv[4][4], persinv[4][4]; + + DRW_framebuffer_cubeface_attach(sldata->probe_fb, sldata->probe_rt, 0, i, 0); + DRW_framebuffer_viewport_size(sldata->probe_fb, 0, 0, pinfo->target_size, pinfo->target_size); + + /* 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(persmat, DRW_MAT_PERS); + DRW_viewport_matrix_override_set(persinv, DRW_MAT_PERSINV); + DRW_viewport_matrix_override_set(viewmat, DRW_MAT_VIEW); + DRW_viewport_matrix_override_set(viewinv, DRW_MAT_VIEWINV); + DRW_viewport_matrix_override_set(winmat, DRW_MAT_WIN); + DRW_viewport_matrix_override_set(wininv, DRW_MAT_WININV); + + DRW_draw_pass(psl->probe_background); + + DRW_framebuffer_texture_detach(sldata->probe_rt); + } + DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_rt, 0, 0); + DRW_framebuffer_texture_attach(sldata->probe_fb, sldata->probe_depth_rt, 0, 0); + + DRW_viewport_matrix_override_unset(DRW_MAT_PERS); + DRW_viewport_matrix_override_unset(DRW_MAT_PERSINV); + DRW_viewport_matrix_override_unset(DRW_MAT_VIEW); + DRW_viewport_matrix_override_unset(DRW_MAT_VIEWINV); + DRW_viewport_matrix_override_unset(DRW_MAT_WIN); + DRW_viewport_matrix_override_unset(DRW_MAT_WININV); +} + +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])); +} + +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); +} + +static void lightprobes_refresh_world(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) +{ + EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; + EEVEE_PassList *psl = vedata->psl; + + render_world_to_probe(sldata, psl); + if (e_data.update_world & PROBE_UPDATE_CUBE) { + glossy_filter_probe(sldata, vedata, psl, 0, 1.0); + common_data->prb_num_render_cube = 1; + } + if (e_data.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); + DRW_framebuffer_texture_detach(sldata->probe_pool); + DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0); + DRW_draw_pass(psl->probe_grid_fill); + DRW_framebuffer_texture_detach(sldata->irradiance_rt); + DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0); + common_data->prb_num_render_grid = 1; + } + e_data.update_world = 0; + DRW_viewport_request_redraw(); +} + +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; + } + DRW_framebuffer_texture_detach(sldata->probe_pool); + /* Flood fill with world irradiance. */ + DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0); + DRW_framebuffer_bind(sldata->probe_filter_fb); + DRW_draw_pass(psl->probe_grid_fill); + DRW_framebuffer_texture_detach(sldata->irradiance_rt); + + SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt); + DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0); + DRW_draw_pass(psl->probe_grid_fill); + + DRW_framebuffer_texture_detach(sldata->irradiance_rt); + SWAP(GPUTexture *, sldata->irradiance_pool, sldata->irradiance_rt); + /* Reattach to have a valid framebuffer. */ + DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0); + pinfo->grid_initialized = true; +} + +void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) +{ + EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; + EEVEE_TextureList *txl = vedata->txl; + Object *ob; + EEVEE_LightProbesInfo *pinfo = sldata->probes; + + if (pinfo->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 */ + /* TODO : Enable SSR in planar reflections? (Would be very heavy) */ + common_data->ssr_toggle = false; + common_data->sss_toggle = false; + + 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); + if (!ped->need_update) { + continue; + } + render_scene_to_planar(sldata, vedata, i, ped->viewmat, ped->persmat, ped->planer_eq_offset); + ped->need_update = false; + ped->probe_id = i; + } + + /* Restore */ + common_data->prb_num_planar = pinfo->num_planar; + common_data->ssr_toggle = true; + common_data->sss_toggle = true; + + /* If there is at least one planar probe */ + if (pinfo->num_planar > 0 && (vedata->stl->effects->enabled_effects & EFFECT_SSR) != 0) { + const int max_lod = 9; + DRW_stats_group_start("Planar Probe Downsample"); + DRW_framebuffer_recursive_downsample(vedata->fbl->downsample_fb, txl->planar_pool, 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(); + } + + /* Disable SSR if we cannot read previous frame */ + 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; + 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; + } + } + /* 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); + } + 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; + 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 */ + DRW_framebuffer_texture_detach(sldata->probe_pool); + DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_rt, 0, 0); + DRW_framebuffer_bind(sldata->probe_filter_fb); + DRW_draw_pass(psl->probe_grid_fill); + DRW_framebuffer_texture_detach(sldata->irradiance_rt); + DRW_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); +} + +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; + + /* 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; + + /* Render world in priority */ + if (e_data.update_world) { + lightprobes_refresh_world(sldata, vedata); + } + else if (EEVEE_lightprobes_all_probes_ready(sldata, vedata) == false) { + lightprobes_refresh_all_no_world(sldata, vedata); + } + + /* 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) +{ + DRW_SHADER_FREE_SAFE(e_data.probe_default_sh); + DRW_SHADER_FREE_SAFE(e_data.probe_filter_glossy_sh); + DRW_SHADER_FREE_SAFE(e_data.probe_filter_diffuse_sh); + DRW_SHADER_FREE_SAFE(e_data.probe_filter_visibility_sh); + DRW_SHADER_FREE_SAFE(e_data.probe_grid_fill_sh); + DRW_SHADER_FREE_SAFE(e_data.probe_grid_display_sh); + DRW_SHADER_FREE_SAFE(e_data.probe_planar_display_sh); + DRW_SHADER_FREE_SAFE(e_data.probe_planar_downsample_sh); + DRW_SHADER_FREE_SAFE(e_data.probe_cube_display_sh); + DRW_TEXTURE_FREE_SAFE(e_data.hammersley); + 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); +} |