From dccf46f18ffe741ae5f0193b75d4a5688096cf9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Foucault?= Date: Tue, 13 Jun 2017 17:39:39 +0200 Subject: Eevee: Add Irradiance Grid support Early implementation. Slow and still has quality 3 ways of storing irradiance: - Spherical Harmonics: Have problem with directionnal lighting. - HL2 diffuse cube: Very low resolution but smooth transitions. - Diffuse cube: High storage requirement. Also include some name change. --- source/blender/draw/CMakeLists.txt | 4 +- source/blender/draw/engines/eevee/eevee_data.c | 4 +- .../blender/draw/engines/eevee/eevee_lightprobes.c | 377 ++++++++++++++++----- .../blender/draw/engines/eevee/eevee_materials.c | 41 ++- source/blender/draw/engines/eevee/eevee_private.h | 32 +- .../engines/eevee/shaders/bsdf_common_lib.glsl | 85 ++++- .../engines/eevee/shaders/bsdf_sampling_lib.glsl | 19 ++ .../shaders/lightprobe_filter_diffuse_frag.glsl | 197 +++++++++++ .../eevee/shaders/lightprobe_filter_frag.glsl | 86 ----- .../shaders/lightprobe_filter_glossy_frag.glsl | 86 +++++ .../engines/eevee/shaders/lightprobe_sh_frag.glsl | 107 ------ .../engines/eevee/shaders/lit_surface_frag.glsl | 150 +++++++- source/blender/draw/intern/DRW_render.h | 2 +- source/blender/draw/intern/draw_manager.c | 4 +- 14 files changed, 872 insertions(+), 322 deletions(-) create mode 100644 source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl delete mode 100644 source/blender/draw/engines/eevee/shaders/lightprobe_filter_frag.glsl create mode 100644 source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl delete mode 100644 source/blender/draw/engines/eevee/shaders/lightprobe_sh_frag.glsl (limited to 'source') diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 35df504fa58..48d502eb342 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -118,8 +118,8 @@ data_to_c_simple(engines/clay/shaders/ssao_groundtruth.glsl SRC) data_to_c_simple(engines/eevee/shaders/default_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/default_world_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/background_vert.glsl SRC) -data_to_c_simple(engines/eevee/shaders/lightprobe_filter_frag.glsl SRC) -data_to_c_simple(engines/eevee/shaders/lightprobe_sh_frag.glsl SRC) +data_to_c_simple(engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl SRC) +data_to_c_simple(engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_geom.glsl SRC) data_to_c_simple(engines/eevee/shaders/lightprobe_vert.glsl SRC) data_to_c_simple(engines/eevee/shaders/lit_surface_frag.glsl SRC) diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index 52dc35b4977..5639250a076 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -53,13 +53,13 @@ static void eevee_scene_layer_data_free(void *storage) /* Probes */ MEM_SAFE_FREE(sldata->probes); DRW_UBO_FREE_SAFE(sldata->probe_ubo); + DRW_UBO_FREE_SAFE(sldata->grid_ubo); DRW_FRAMEBUFFER_FREE_SAFE(sldata->probe_fb); DRW_FRAMEBUFFER_FREE_SAFE(sldata->probe_filter_fb); - DRW_FRAMEBUFFER_FREE_SAFE(sldata->probe_sh_fb); DRW_TEXTURE_FREE_SAFE(sldata->probe_rt); DRW_TEXTURE_FREE_SAFE(sldata->probe_depth_rt); DRW_TEXTURE_FREE_SAFE(sldata->probe_pool); - DRW_TEXTURE_FREE_SAFE(sldata->probe_sh); + DRW_TEXTURE_FREE_SAFE(sldata->irradiance_pool); } static void eevee_lamp_data_free(void *storage) diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index 1acdd50635e..53cd49abf78 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -47,13 +47,14 @@ #include "eevee_private.h" /* TODO Option */ -#define LIGHTPROBE_TYPE_CUBE_SIZE 512 -#define PROBE_SIZE 1024 +#define PROBE_RT_SIZE 512 /* Cube render target */ +#define PROBE_OCTAHEDRON_SIZE 1024 +#define IRRADIANCE_POOL_SIZE 1024 static struct { struct GPUShader *probe_default_sh; - struct GPUShader *probe_filter_sh; - struct GPUShader *probe_spherical_harmonic_sh; + struct GPUShader *probe_filter_glossy_sh; + struct GPUShader *probe_filter_diffuse_sh; struct GPUTexture *hammersley; @@ -62,8 +63,9 @@ static struct { } e_data = {NULL}; /* Engine data */ extern char datatoc_default_world_frag_glsl[]; -extern char datatoc_lightprobe_filter_frag_glsl[]; -extern char datatoc_lightprobe_sh_frag_glsl[]; +extern char datatoc_fullscreen_vert_glsl[]; +extern char datatoc_lightprobe_filter_glossy_frag_glsl[]; +extern char datatoc_lightprobe_filter_diffuse_frag_glsl[]; extern char datatoc_lightprobe_geom_glsl[]; extern char datatoc_lightprobe_vert_glsl[]; extern char datatoc_bsdf_common_lib_glsl[]; @@ -104,17 +106,18 @@ static struct GPUTexture *create_hammersley_sample_texture(int samples) void EEVEE_lightprobes_init(EEVEE_SceneLayerData *sldata) { - if (!e_data.probe_filter_sh) { + /* Shaders */ + if (!e_data.probe_filter_glossy_sh) { char *shader_str = NULL; DynStr *ds_frag = BLI_dynstr_new(); BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl); BLI_dynstr_append(ds_frag, datatoc_bsdf_sampling_lib_glsl); - BLI_dynstr_append(ds_frag, datatoc_lightprobe_filter_frag_glsl); + BLI_dynstr_append(ds_frag, datatoc_lightprobe_filter_glossy_frag_glsl); shader_str = BLI_dynstr_get_cstring(ds_frag); BLI_dynstr_free(ds_frag); - e_data.probe_filter_sh = DRW_shader_create( + e_data.probe_filter_glossy_sh = DRW_shader_create( datatoc_lightprobe_vert_glsl, datatoc_lightprobe_geom_glsl, shader_str, "#define HAMMERSLEY_SIZE 1024\n" "#define NOISE_SIZE 64\n"); @@ -123,34 +126,47 @@ void EEVEE_lightprobes_init(EEVEE_SceneLayerData *sldata) datatoc_lightprobe_vert_glsl, datatoc_lightprobe_geom_glsl, datatoc_default_world_frag_glsl, NULL); MEM_freeN(shader_str); - } - /* Shaders */ - if (!e_data.hammersley) { + ds_frag = BLI_dynstr_new(); + BLI_dynstr_append(ds_frag, datatoc_bsdf_common_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_bsdf_sampling_lib_glsl); + BLI_dynstr_append(ds_frag, datatoc_lightprobe_filter_diffuse_frag_glsl); + shader_str = BLI_dynstr_get_cstring(ds_frag); + BLI_dynstr_free(ds_frag); + + e_data.probe_filter_diffuse_sh = DRW_shader_create_fullscreen( + shader_str, +#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 HAMMERSLEY_SIZE 1024\n" + "#define NOISE_SIZE 64\n"); + + MEM_freeN(shader_str); + e_data.hammersley = create_hammersley_sample_texture(1024); - e_data.probe_spherical_harmonic_sh = DRW_shader_create_fullscreen(datatoc_lightprobe_sh_frag_glsl, NULL); } if (!sldata->probes) { sldata->probes = MEM_callocN(sizeof(EEVEE_LightProbesInfo), "EEVEE_LightProbesInfo"); sldata->probe_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightProbe) * MAX_PROBE, NULL); + sldata->grid_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightGrid) * MAX_GRID, NULL); } /* Setup Render Target Cubemap */ if (!sldata->probe_rt) { - sldata->probe_rt = DRW_texture_create_cube(LIGHTPROBE_TYPE_CUBE_SIZE, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); - sldata->probe_depth_rt = DRW_texture_create_cube(LIGHTPROBE_TYPE_CUBE_SIZE, DRW_TEX_DEPTH_24, DRW_TEX_FILTER, NULL); + sldata->probe_rt = DRW_texture_create_cube(PROBE_RT_SIZE, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP, NULL); + sldata->probe_depth_rt = DRW_texture_create_cube(PROBE_RT_SIZE, DRW_TEX_DEPTH_24, DRW_TEX_FILTER, NULL); } DRWFboTexture tex_probe[2] = {{&sldata->probe_depth_rt, DRW_TEX_DEPTH_24, DRW_TEX_FILTER}, {&sldata->probe_rt, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP}}; - DRW_framebuffer_init(&sldata->probe_fb, &draw_engine_eevee_type, LIGHTPROBE_TYPE_CUBE_SIZE, LIGHTPROBE_TYPE_CUBE_SIZE, tex_probe, 2); - - /* Spherical Harmonic Buffer */ - DRWFboTexture tex_sh = {&sldata->probe_sh, DRW_TEX_RGBA_16, DRW_TEX_FILTER | DRW_TEX_MIPMAP}; - - DRW_framebuffer_init(&sldata->probe_sh_fb, &draw_engine_eevee_type, 9, 1, &tex_sh, 1); + DRW_framebuffer_init(&sldata->probe_fb, &draw_engine_eevee_type, PROBE_RT_SIZE, PROBE_RT_SIZE, tex_probe, 2); } void EEVEE_lightprobes_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl) @@ -158,7 +174,9 @@ void EEVEE_lightprobes_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_PassList * EEVEE_LightProbesInfo *pinfo = sldata->probes; pinfo->num_cube = 1; /* at least one for the world */ - memset(pinfo->probes_ref, 0, sizeof(pinfo->probes_ref)); + pinfo->num_grid = 0; + memset(pinfo->probes_cube_ref, 0, sizeof(pinfo->probes_cube_ref)); + memset(pinfo->probes_grid_ref, 0, sizeof(pinfo->probes_grid_ref)); { psl->probe_background = DRW_pass_create("World Probe Pass", DRW_STATE_WRITE_COLOR); @@ -212,11 +230,11 @@ void EEVEE_lightprobes_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_PassList * } { - psl->probe_prefilter = DRW_pass_create("LightProbe Filtering", DRW_STATE_WRITE_COLOR); + psl->probe_glossy_compute = DRW_pass_create("LightProbe Glossy Compute", DRW_STATE_WRITE_COLOR); struct Batch *geom = DRW_cache_fullscreen_quad_get(); - DRWShadingGroup *grp = DRW_shgroup_instance_create(e_data.probe_filter_sh, psl->probe_prefilter, geom); + DRWShadingGroup *grp = DRW_shgroup_instance_create(e_data.probe_filter_glossy_sh, psl->probe_glossy_compute, geom); DRW_shgroup_uniform_float(grp, "sampleCount", &sldata->probes->samples_ct, 1); DRW_shgroup_uniform_float(grp, "invSampleCount", &sldata->probes->invsamples_ct, 1); DRW_shgroup_uniform_float(grp, "roughnessSquared", &sldata->probes->roughness, 1); @@ -233,11 +251,17 @@ void EEVEE_lightprobes_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_PassList * } { - psl->probe_sh_compute = DRW_pass_create("LightProbe SH Compute", DRW_STATE_WRITE_COLOR); + psl->probe_diffuse_compute = DRW_pass_create("LightProbe Diffuse Compute", DRW_STATE_WRITE_COLOR); - DRWShadingGroup *grp = DRW_shgroup_create(e_data.probe_spherical_harmonic_sh, psl->probe_sh_compute); + 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", &sldata->probes->shres, 1); - DRW_shgroup_uniform_float(grp, "lodBias", &sldata->probes->lodfactor, 1); +#else + DRW_shgroup_uniform_float(grp, "sampleCount", &sldata->probes->samples_ct, 1); + DRW_shgroup_uniform_float(grp, "invSampleCount", &sldata->probes->invsamples_ct, 1); + DRW_shgroup_uniform_float(grp, "lodFactor", &sldata->probes->lodfactor, 1); + DRW_shgroup_uniform_float(grp, "lodMax", &sldata->probes->lodmax, 1); +#endif DRW_shgroup_uniform_texture(grp, "probeHdr", sldata->probe_rt); struct Batch *geom = DRW_cache_fullscreen_quad_get(); @@ -248,26 +272,34 @@ void EEVEE_lightprobes_cache_init(EEVEE_SceneLayerData *sldata, EEVEE_PassList * void EEVEE_lightprobes_cache_add(EEVEE_SceneLayerData *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 (pinfo->num_cube > MAX_PROBE) { + 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"); - pinfo->num_cube = MAX_PROBE; + return; } - else { - EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(ob); - if ((ob->deg_update_flag & DEG_RUNTIME_DATA_UPDATE) != 0) { - ped->need_update = true; - } + EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(ob); - if (e_data.update_world) { - ped->need_update = true; - } + if ((ob->deg_update_flag & DEG_RUNTIME_DATA_UPDATE) != 0) { + ped->need_update = true; + } - pinfo->probes_ref[pinfo->num_cube] = ob; + if (e_data.update_world) { + ped->need_update = true; + } + + if (probe->type == LIGHTPROBE_TYPE_CUBE) { + pinfo->probes_cube_ref[pinfo->num_cube] = ob; pinfo->num_cube++; } + else { /* GRID */ + pinfo->probes_grid_ref[pinfo->num_grid] = ob; + pinfo->num_grid++; + } } static void EEVEE_lightprobes_updates(EEVEE_SceneLayerData *sldata) @@ -275,10 +307,13 @@ static void EEVEE_lightprobes_updates(EEVEE_SceneLayerData *sldata) EEVEE_LightProbesInfo *pinfo = sldata->probes; Object *ob; - for (int i = 1; (ob = pinfo->probes_ref[i]) && (i < MAX_PROBE); i++) { + 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]; + /* 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); @@ -304,6 +339,67 @@ static void EEVEE_lightprobes_updates(EEVEE_SceneLayerData *sldata) mul_m4_m4m4(eprobe->parallaxmat, ob->obmat, eprobe->parallaxmat); invert_m4(eprobe->parallaxmat); } + + int offset = 1; /* to account for the world probe */ + for (int i = 0; (ob = pinfo->probes_grid_ref[i]) && (i < MAX_GRID); i++) { + LightProbe *probe = (LightProbe *)ob->data; + EEVEE_LightGrid *egrid = &pinfo->grid_data[i]; + + egrid->offset = offset; + + /* Set offset for the next grid */ + offset += probe->grid_resolution_x * probe->grid_resolution_y * probe->grid_resolution_z; + + /* Update transforms */ + float tmp[4][4] = { + {2.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 2.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 2.0f, 0.0f}, + {-1.0f, -1.0f, -1.0f, 1.0f} + }; + float tmp_grid_mat[4][4] = { + {1.0f / (float)(probe->grid_resolution_x + 1), 0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f / (float)(probe->grid_resolution_y + 1), 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f / (float)(probe->grid_resolution_z + 1), 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f} + }; + mul_m4_m4m4(tmp, tmp, tmp_grid_mat); + mul_m4_m4m4(egrid->mat, ob->obmat, tmp); + invert_m4(egrid->mat); + + float one_div_res[3]; + one_div_res[0] = 2.0f / (float)(probe->grid_resolution_x + 1); + one_div_res[1] = 2.0f / (float)(probe->grid_resolution_y + 1); + one_div_res[2] = 2.0f / (float)(probe->grid_resolution_z + 1); + + /* First cell. */ + copy_v3_v3(egrid->corner, one_div_res); + add_v3_fl(egrid->corner, -1.0f); + mul_m4_v3(ob->obmat, egrid->corner); + + /* Opposite neighbor cell. */ + copy_v3_fl3(egrid->increment_x, one_div_res[0], 0.0f, 0.0f); + add_v3_v3(egrid->increment_x, one_div_res); + 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, one_div_res[1], 0.0f); + add_v3_v3(egrid->increment_y, one_div_res); + 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, one_div_res[2]); + add_v3_v3(egrid->increment_z, one_div_res); + 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); + } + + pinfo->num_render_grid = pinfo->num_grid; } void EEVEE_lightprobes_cache_finish(EEVEE_SceneLayerData *sldata) @@ -318,7 +414,7 @@ void EEVEE_lightprobes_cache_finish(EEVEE_SceneLayerData *sldata) } if (!sldata->probe_pool) { - sldata->probe_pool = DRW_texture_create_2D_array(PROBE_SIZE, PROBE_SIZE, max_ff(1, pinfo->num_cube), + sldata->probe_pool = DRW_texture_create_2D_array(PROBE_OCTAHEDRON_SIZE, PROBE_OCTAHEDRON_SIZE, 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); @@ -327,27 +423,35 @@ void EEVEE_lightprobes_cache_finish(EEVEE_SceneLayerData *sldata) /* Tag probes to refresh */ e_data.update_world = true; e_data.world_ready_to_shade = false; - pinfo->num_render_probe = 0; + pinfo->num_render_cube = 0; pinfo->update_flag |= PROBE_UPDATE_CUBE; pinfo->cache_num_cube = pinfo->num_cube; - for (int i = 1; (ob = pinfo->probes_ref[i]) && (i < MAX_PROBE); i++) { + for (int i = 1; (ob = pinfo->probes_cube_ref[i]) && (i < MAX_PROBE); i++) { EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(ob); ped->need_update = true; ped->ready_to_shade = false; } } - DRWFboTexture tex_filter = {&sldata->probe_pool, DRW_TEX_RGB_11_11_10, DRW_TEX_FILTER | DRW_TEX_MIPMAP}; + 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, PROBE_SIZE, PROBE_SIZE, &tex_filter, 1); + DRW_framebuffer_init(&sldata->probe_filter_fb, &draw_engine_eevee_type, PROBE_OCTAHEDRON_SIZE, PROBE_OCTAHEDRON_SIZE, &tex_filter, 1); + + /* TODO Allocate bigger storage if needed. */ + if (!sldata->irradiance_pool) { + sldata->irradiance_pool = DRW_texture_create_2D(IRRADIANCE_POOL_SIZE, IRRADIANCE_POOL_SIZE, DRW_TEX_RGBA_16, DRW_TEX_FILTER, NULL); + pinfo->num_render_grid = 0; + } EEVEE_lightprobes_updates(sldata); DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data); + DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data); } -static void filter_probe(EEVEE_LightProbe *eprobe, EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl, int probe_idx) +/* Glossy filter probe_rt to probe_pool at index probe_idx */ +static void glossy_filter_probe(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl, int probe_idx) { EEVEE_LightProbesInfo *pinfo = sldata->probes; @@ -359,8 +463,8 @@ static void filter_probe(EEVEE_LightProbe *eprobe, EEVEE_SceneLayerData *sldata, /* 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 = PROBE_SIZE; - const int maxlevel = (int)floorf(log2f(PROBE_SIZE)); + float mipsize = PROBE_OCTAHEDRON_SIZE; + const int maxlevel = (int)floorf(log2f(PROBE_OCTAHEDRON_SIZE)); const int min_lod_level = 3; for (int i = 0; i < maxlevel - min_lod_level; i++) { float bias = (i == 0) ? 0.0f : 1.0f; @@ -399,12 +503,12 @@ static void filter_probe(EEVEE_LightProbe *eprobe, EEVEE_SceneLayerData *sldata, #endif pinfo->invsamples_ct = 1.0f / pinfo->samples_ct; - pinfo->lodfactor = bias + 0.5f * log((float)(LIGHTPROBE_TYPE_CUBE_SIZE * LIGHTPROBE_TYPE_CUBE_SIZE) * pinfo->invsamples_ct) / log(2); - pinfo->lodmax = floorf(log2f(LIGHTPROBE_TYPE_CUBE_SIZE)) - 2.0f; + pinfo->lodfactor = bias + 0.5f * log((float)(PROBE_RT_SIZE * PROBE_RT_SIZE) * pinfo->invsamples_ct) / log(2); + pinfo->lodmax = floorf(log2f(PROBE_RT_SIZE)) - 2.0f; DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, i); - DRW_framebuffer_viewport_size(sldata->probe_filter_fb, mipsize, mipsize); - DRW_draw_pass(psl->probe_prefilter); + 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; @@ -413,42 +517,83 @@ static void filter_probe(EEVEE_LightProbe *eprobe, EEVEE_SceneLayerData *sldata, /* For shading, save max level of the octahedron map */ pinfo->lodmax = (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_SceneLayerData *sldata, EEVEE_PassList *psl, int cell_idx) +{ + EEVEE_LightProbesInfo *pinfo = sldata->probes; + + /* TODO do things properly */ + float lodmax = pinfo->lodmax; + /* 4 - Compute spherical harmonics */ /* Tweaking parameters to balance perf. vs precision */ - pinfo->shres = 16; /* Less texture fetches & reduce branches */ - pinfo->lodfactor = 4.0f; /* Improve cache reuse */ - DRW_framebuffer_bind(sldata->probe_sh_fb); - DRW_draw_pass(psl->probe_sh_compute); - DRW_framebuffer_read_data(0, 0, 9, 1, 3, 0, (float *)eprobe->shcoefs); + DRW_framebuffer_bind(sldata->probe_filter_fb); + DRW_texture_generate_mipmaps(sldata->probe_rt); + + /* Bind the right texture layer (one layer per irradiance grid) */ + DRW_framebuffer_texture_detach(sldata->probe_pool); + DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->irradiance_pool, 0, 0); + + /* 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 = IRRADIANCE_POOL_SIZE / size[0]; + int x = size[0] * (cell_idx % cell_per_row); + int y = size[1] * (cell_idx / cell_per_row); + +#ifndef IRRADIANCE_SH_L2 + const float bias = 0.0f; + pinfo->invsamples_ct = 1.0f / pinfo->samples_ct; + pinfo->lodfactor = bias + 0.5f * log((float)(PROBE_RT_SIZE * PROBE_RT_SIZE) * pinfo->invsamples_ct) / log(2); + pinfo->lodmax = floorf(log2f(PROBE_RT_SIZE)) - 2.0f; +#else + pinfo->shres = 32; /* Less texture fetches & reduce branches */ + pinfo->lodmax = 2.0f; /* Improve cache reuse */ +#endif + + DRW_framebuffer_viewport_size(sldata->probe_filter_fb, x, y, size[0], size[1]); + DRW_draw_pass(psl->probe_diffuse_compute); /* reattach to have a valid framebuffer. */ + DRW_framebuffer_texture_detach(sldata->irradiance_pool); DRW_framebuffer_texture_attach(sldata->probe_filter_fb, sldata->probe_pool, 0, 0); + + /* restore */ + pinfo->lodmax = lodmax; } -/* Renders the probe with index probe_idx. - * Renders the world probe if probe_idx = -1. */ -static void render_one_probe(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl, int probe_idx) +/* Render the scene to the probe_rt texture. */ +static void render_scene_to_probe( + EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl, + const float pos[3], float clipsta, float clipend) { EEVEE_LightProbesInfo *pinfo = sldata->probes; - EEVEE_LightProbe *eprobe = &pinfo->probe_data[probe_idx]; - Object *ob = pinfo->probes_ref[probe_idx]; - LightProbe *prb = (LightProbe *)ob->data; float winmat[4][4], posmat[4][4]; unit_m4(posmat); - /* Update transforms */ - copy_v3_v3(eprobe->position, ob->obmat[3]); - /* Move to capture position */ - negate_v3_v3(posmat[3], ob->obmat[3]); + 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, -prb->clipsta, prb->clipsta, -prb->clipsta, prb->clipsta, prb->clipsta, prb->clipend); + perspective_m4(winmat, -clipsta, clipsta, -clipsta, clipsta, clipsta, clipend); /* Detach to rebind the right cubeface. */ DRW_framebuffer_bind(sldata->probe_fb); @@ -460,9 +605,10 @@ static void render_one_probe(EEVEE_SceneLayerData *sldata, EEVEE_PassList *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, LIGHTPROBE_TYPE_CUBE_SIZE, LIGHTPROBE_TYPE_CUBE_SIZE); + DRW_framebuffer_viewport_size(sldata->probe_fb, 0, 0, PROBE_RT_SIZE, PROBE_RT_SIZE); - DRW_framebuffer_clear(false, true, false, NULL, 1.0); + float clear[4] = {1.0f, 0.0f, 0.0f, 1.0f}; + DRW_framebuffer_clear(true, true, false, clear, 1.0); /* Setup custom matrices */ mul_m4_m4m4(viewmat, cubefacemat[i], posmat); @@ -498,14 +644,11 @@ static void render_one_probe(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl, DRW_viewport_matrix_override_unset(DRW_MAT_VIEW); DRW_viewport_matrix_override_unset(DRW_MAT_VIEWINV); DRW_viewport_matrix_override_unset(DRW_MAT_WIN); - - filter_probe(eprobe, sldata, psl, probe_idx); } -static void render_world_lightprobe(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl) +static void render_world_to_probe(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl) { EEVEE_LightProbesInfo *pinfo = sldata->probes; - EEVEE_LightProbe *eprobe = &pinfo->probe_data[0]; /* 1 - Render to cubemap target using geometry shader. */ /* For world probe, we don't need to clear since we render the background directly. */ @@ -513,8 +656,51 @@ static void render_world_lightprobe(EEVEE_SceneLayerData *sldata, EEVEE_PassList DRW_framebuffer_bind(sldata->probe_fb); DRW_draw_pass(psl->probe_background); +} + +static void update_irradiance_probe(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl, int probe_idx) +{ + EEVEE_LightProbesInfo *pinfo = sldata->probes; + EEVEE_LightGrid *egrid = &pinfo->grid_data[probe_idx]; + Object *ob = pinfo->probes_grid_ref[probe_idx]; + LightProbe *prb = (LightProbe *)ob->data; + + /* Temporary Remove all grids */ + int tmp_num_render_grid = pinfo->num_render_grid; + int tmp_num_render_cube = pinfo->num_render_cube; + pinfo->num_render_grid = 0; + pinfo->num_render_cube = 0; + + /* Render a cubemap and compute irradiance for every point inside the irradiance grid */ + for (int i = 0; i < egrid->resolution[0]; ++i) { + for (int j = 0; j < egrid->resolution[1]; ++j) { + for (int k = 0; k < egrid->resolution[2]; ++k) { + float pos[3], tmp[3]; + int cell = egrid->offset + k + j * egrid->resolution[2] + i * egrid->resolution[2] * egrid->resolution[1]; + + /* Compute world position of the sample */ + copy_v3_v3(pos, egrid->corner); + mul_v3_v3fl(tmp, egrid->increment_x, (float)i); + add_v3_v3(pos, tmp); + mul_v3_v3fl(tmp, egrid->increment_y, (float)j); + add_v3_v3(pos, tmp); + mul_v3_v3fl(tmp, egrid->increment_z, (float)k); + add_v3_v3(pos, tmp); + + /* TODO Remove specular */ + render_scene_to_probe(sldata, psl, pos, prb->clipsta, prb->clipend); + /* TODO Do not update texture while rendering but write to CPU memory (or another buffer). + * This will allow "multiple bounces" computation. */ + diffuse_filter_probe(sldata, psl, cell); + } + } + } + + /* Restore */ + pinfo->num_render_grid = tmp_num_render_grid; + pinfo->num_render_cube = tmp_num_render_cube; - filter_probe(eprobe, sldata, psl, 0); + /* TODO save in DNA / blendfile */ } void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl) @@ -526,16 +712,17 @@ void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl /* Render world in priority */ if (e_data.update_world) { - render_world_lightprobe(sldata, psl); + render_world_to_probe(sldata, psl); + glossy_filter_probe(sldata, psl, 0); + diffuse_filter_probe(sldata, psl, 0); + e_data.update_world = false; if (!e_data.world_ready_to_shade) { e_data.world_ready_to_shade = true; - pinfo->num_render_probe = 1; + pinfo->num_render_cube = 1; } - DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data); - DRW_viewport_request_redraw(); } else if (true) { /* TODO if at least one probe needs refresh */ @@ -548,19 +735,41 @@ void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl } } - for (int i = 1; (ob = pinfo->probes_ref[i]) && (i < MAX_PROBE); i++) { + for (int i = 1; (ob = pinfo->probes_cube_ref[i]) && (i < MAX_PROBE); i++) { EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(ob); if (ped->need_update) { - render_one_probe(sldata, psl, i); + LightProbe *prb = (LightProbe *)ob->data; + + render_scene_to_probe(sldata, psl, ob->obmat[3], prb->clipsta, prb->clipend); + glossy_filter_probe(sldata, psl, i); + ped->need_update = false; if (!ped->ready_to_shade) { - pinfo->num_render_probe++; + pinfo->num_render_cube++; ped->ready_to_shade = true; } - DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data); + DRW_viewport_request_redraw(); + + /* Only do one probe per frame */ + break; + } + } + + for (int i = 0; (ob = pinfo->probes_grid_ref[i]) && (i < MAX_GRID); i++) { + EEVEE_LightProbeEngineData *ped = EEVEE_lightprobe_data_get(ob); + + if (ped->need_update) { + update_irradiance_probe(sldata, psl, i); + + ped->need_update = false; + + if (!ped->ready_to_shade) { + pinfo->num_render_grid++; + ped->ready_to_shade = true; + } DRW_viewport_request_redraw(); @@ -574,7 +783,7 @@ void EEVEE_lightprobes_refresh(EEVEE_SceneLayerData *sldata, EEVEE_PassList *psl void EEVEE_lightprobes_free(void) { DRW_SHADER_FREE_SAFE(e_data.probe_default_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_filter_sh); - DRW_SHADER_FREE_SAFE(e_data.probe_spherical_harmonic_sh); + DRW_SHADER_FREE_SAFE(e_data.probe_filter_glossy_sh); + DRW_SHADER_FREE_SAFE(e_data.probe_filter_diffuse_sh); DRW_TEXTURE_FREE_SAFE(e_data.hammersley); } diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index b9ad507c6ce..40e6a89d209 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -41,6 +41,7 @@ #define SHADER_DEFINES \ "#define EEVEE_ENGINE\n" \ "#define MAX_PROBE " STRINGIFY(MAX_PROBE) "\n" \ + "#define MAX_GRID " STRINGIFY(MAX_GRID) "\n" \ "#define MAX_LIGHT " STRINGIFY(MAX_LIGHT) "\n" \ "#define MAX_SHADOW_CUBE " STRINGIFY(MAX_SHADOW_CUBE) "\n" \ "#define MAX_SHADOW_MAP " STRINGIFY(MAX_SHADOW_MAP) "\n" \ @@ -87,8 +88,6 @@ extern char datatoc_lit_surface_vert_glsl[]; extern char datatoc_shadow_frag_glsl[]; extern char datatoc_shadow_geom_glsl[]; extern char datatoc_shadow_vert_glsl[]; -extern char datatoc_lightprobe_filter_frag_glsl[]; -extern char datatoc_lightprobe_sh_frag_glsl[]; extern char datatoc_lightprobe_geom_glsl[]; extern char datatoc_lightprobe_vert_glsl[]; extern char datatoc_background_vert_glsl[]; @@ -186,11 +185,25 @@ void EEVEE_materials_init(void) e_data.default_lit = DRW_shader_create( datatoc_lit_surface_vert_glsl, NULL, frag_str, SHADER_DEFINES +#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 MESH_SHADER\n"); e_data.default_lit_flat = DRW_shader_create( datatoc_lit_surface_vert_glsl, NULL, frag_str, SHADER_DEFINES +#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 MESH_SHADER\n" "#define USE_FLAT_NORMAL\n"); @@ -250,22 +263,21 @@ struct GPUMaterial *EEVEE_material_world_background_get(struct Scene *scene, Wor SHADER_DEFINES "#define WORLD_BACKGROUND\n"); } -struct GPUMaterial *EEVEE_material_mesh_lightprobe_get(struct Scene *scene, Material *ma) -{ - return GPU_material_from_nodetree( - scene, ma->nodetree, &ma->gpumaterial, &DRW_engine_viewport_eevee_type, - VAR_MAT_MESH | VAR_MAT_PROBE, - datatoc_lightprobe_vert_glsl, NULL, e_data.frag_shader_lib, - SHADER_DEFINES "#define MESH_SHADER\n" "#define PROBE_CAPTURE\n"); -} - struct GPUMaterial *EEVEE_material_mesh_get(struct Scene *scene, Material *ma) { return GPU_material_from_nodetree( scene, ma->nodetree, &ma->gpumaterial, &DRW_engine_viewport_eevee_type, VAR_MAT_MESH, datatoc_lit_surface_vert_glsl, NULL, e_data.frag_shader_lib, - SHADER_DEFINES "#define MESH_SHADER\n"); + SHADER_DEFINES +#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 MESH_SHADER\n"); } struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma) @@ -280,13 +292,16 @@ struct GPUMaterial *EEVEE_material_hair_get(struct Scene *scene, Material *ma) static void add_standard_uniforms(DRWShadingGroup *shgrp, EEVEE_SceneLayerData *sldata) { DRW_shgroup_uniform_block(shgrp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(shgrp, "grid_block", sldata->grid_ubo); DRW_shgroup_uniform_block(shgrp, "light_block", sldata->light_ubo); DRW_shgroup_uniform_block(shgrp, "shadow_block", sldata->shadow_ubo); DRW_shgroup_uniform_int(shgrp, "light_count", &sldata->lamps->num_light, 1); - DRW_shgroup_uniform_int(shgrp, "probe_count", &sldata->probes->num_render_probe, 1); + DRW_shgroup_uniform_int(shgrp, "probe_count", &sldata->probes->num_render_cube, 1); + DRW_shgroup_uniform_int(shgrp, "grid_count", &sldata->probes->num_render_grid, 1); DRW_shgroup_uniform_float(shgrp, "lodMax", &sldata->probes->lodmax, 1); DRW_shgroup_uniform_texture(shgrp, "utilTex", e_data.util_tex); DRW_shgroup_uniform_buffer(shgrp, "probeCubes", &sldata->probe_pool); + DRW_shgroup_uniform_buffer(shgrp, "irradianceGrid", &sldata->irradiance_pool); DRW_shgroup_uniform_buffer(shgrp, "shadowCubes", &sldata->shadow_depth_cube_pool); DRW_shgroup_uniform_buffer(shgrp, "shadowCascades", &sldata->shadow_depth_cascade_pool); } diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index f3c8a63a5d7..77aa8d04278 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -32,6 +32,7 @@ extern struct DrawEngineType draw_engine_eevee_type; /* Minimum UBO is 16384 bytes */ #define MAX_PROBE 128 /* TODO : find size by dividing UBO max size by probe data size */ +#define MAX_GRID 64 /* TODO : find size by dividing UBO max size by grid data size */ #define MAX_LIGHT 128 /* TODO : find size by dividing UBO max size by light data size */ #define MAX_SHADOW_CUBE 42 /* TODO : Make this depends on GL_MAX_ARRAY_TEXTURE_LAYERS */ #define MAX_SHADOW_MAP 64 @@ -39,6 +40,11 @@ extern struct DrawEngineType draw_engine_eevee_type; #define MAX_CASCADE_NUM 4 #define MAX_BLOOM_STEP 16 +/* Only define one of these. */ +// #define IRRADIANCE_SH_L2 +// #define IRRADIANCE_CUBEMAP +#define IRRADIANCE_HL2 + typedef struct EEVEE_PassList { /* Shadows */ struct DRWPass *shadow_pass; @@ -49,8 +55,8 @@ typedef struct EEVEE_PassList { /* Probes */ struct DRWPass *probe_background; struct DRWPass *probe_meshes; - struct DRWPass *probe_prefilter; - struct DRWPass *probe_sh_compute; + struct DRWPass *probe_glossy_compute; + struct DRWPass *probe_diffuse_compute; /* Effects */ struct DRWPass *motion_blur; @@ -169,7 +175,6 @@ enum { /* ************ PROBE UBO ************* */ typedef struct EEVEE_LightProbe { float position[3], parallax_type; - float shcoefs[9][3], pad2; float attenuation_fac; float attenuation_type; float pad3[2]; @@ -177,12 +182,23 @@ typedef struct EEVEE_LightProbe { float parallaxmat[4][4]; } EEVEE_LightProbe; +typedef struct EEVEE_LightGrid { + float mat[4][4]; + int resolution[3], offset; + float corner[3], pad1; + float increment_x[3], pad2; /* world space vector between 2 opposite cells */ + float increment_y[3], pad3; + float increment_z[3], pad4; +} EEVEE_LightGrid; + /* ************ PROBE DATA ************* */ typedef struct EEVEE_LightProbesInfo { int num_cube, cache_num_cube; + int num_grid, cache_num_grid; int update_flag; /* Actual number of probes that have datas. */ - int num_render_probe; + int num_render_cube; + int num_render_grid; /* For rendering probes */ float probemat[6][4][4]; int layer; @@ -198,9 +214,11 @@ typedef struct EEVEE_LightProbesInfo { struct GPUTexture *backgroundtex; /* List of probes in the scene. */ /* XXX This is fragile, can get out of sync quickly. */ - struct Object *probes_ref[MAX_PROBE]; + struct Object *probes_cube_ref[MAX_PROBE]; + struct Object *probes_grid_ref[MAX_GRID]; /* UBO Storage : data used by UBO */ struct EEVEE_LightProbe probe_data[MAX_PROBE]; + struct EEVEE_LightGrid grid_data[MAX_GRID]; } EEVEE_LightProbesInfo; /* EEVEE_LightProbesInfo->update_flag */ @@ -274,15 +292,15 @@ typedef struct EEVEE_SceneLayerData { struct EEVEE_LightProbesInfo *probes; struct GPUUniformBuffer *probe_ubo; + struct GPUUniformBuffer *grid_ubo; struct GPUFrameBuffer *probe_fb; struct GPUFrameBuffer *probe_filter_fb; - struct GPUFrameBuffer *probe_sh_fb; struct GPUTexture *probe_rt; struct GPUTexture *probe_depth_rt; struct GPUTexture *probe_pool; - struct GPUTexture *probe_sh; + struct GPUTexture *irradiance_pool; struct ListBase probe_queue; /* List of probes to update */ } EEVEE_SceneLayerData; diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl index 4aa97780637..77ff614b2c1 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_common_lib.glsl @@ -11,7 +11,6 @@ struct ProbeData { vec4 position_type; - vec4 shcoefs[7]; vec4 attenuation_fac_type; mat4 influencemat; mat4 parallaxmat; @@ -25,6 +24,36 @@ struct ProbeData { #define p_atten_fac attenuation_fac_type.x #define p_atten_type attenuation_fac_type.y +struct GridData { + mat4 localmat; + ivec4 resolution_offset; + vec4 ws_corner; /* world space position */ + vec4 ws_increment_x; /* world space vector between 2 opposite cells */ + vec4 ws_increment_y; + vec4 ws_increment_z; +}; + +#define g_corner ws_corner.xyz +#define g_increment_x ws_increment_x.xyz +#define g_increment_y ws_increment_y.xyz +#define g_increment_z ws_increment_z.xyz +#define g_resolution resolution_offset.xyz +#define g_offset resolution_offset.w + +#ifdef IRRADIANCE_CUBEMAP +struct IrradianceData { + vec3 color; +}; +#elif defined(IRRADIANCE_SH_L2) +struct IrradianceData { + vec3 shcoefs[9]; +}; +#else /* defined(IRRADIANCE_HL2) */ +struct IrradianceData { + vec3 cubesides[3]; +}; +#endif + /* TODO remove sh once we have irradiance grid */ #define shcoef0 shcoefs[0].rgb #define shcoef1 vec3(shcoefs[0].a, shcoefs[1].rg) @@ -226,38 +255,62 @@ float buffer_depth(bool is_persp, float z, float zf, float zn) #define spherical_harmonics spherical_harmonics_L2 /* http://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/ */ -vec3 spherical_harmonics_L1(vec3 N, vec4 shcoefs[3]) +vec3 spherical_harmonics_L1(vec3 N, vec3 shcoefs[4]) { vec3 sh = vec3(0.0); - sh += 0.282095 * shcoef0; + sh += 0.282095 * shcoefs[0]; - sh += -0.488603 * N.z * shcoef1; - sh += 0.488603 * N.y * shcoef2; - sh += -0.488603 * N.x * shcoef3; + sh += -0.488603 * N.z * shcoefs[1]; + sh += 0.488603 * N.y * shcoefs[2]; + sh += -0.488603 * N.x * shcoefs[3]; return sh; } -vec3 spherical_harmonics_L2(vec3 N, vec4 shcoefs[7]) +vec3 spherical_harmonics_L2(vec3 N, vec3 shcoefs[9]) { vec3 sh = vec3(0.0); - sh += 0.282095 * shcoef0; + sh += 0.282095 * shcoefs[0]; - sh += -0.488603 * N.z * shcoef1; - sh += 0.488603 * N.y * shcoef2; - sh += -0.488603 * N.x * shcoef3; + sh += -0.488603 * N.z * shcoefs[1]; + sh += 0.488603 * N.y * shcoefs[2]; + sh += -0.488603 * N.x * shcoefs[3]; - sh += 1.092548 * N.x * N.z * shcoef4; - sh += -1.092548 * N.z * N.y * shcoef5; - sh += 0.315392 * (3.0 * N.y * N.y - 1.0) * shcoef6; - sh += -1.092548 * N.x * N.y * shcoef7; - sh += 0.546274 * (N.x * N.x - N.z * N.z) * shcoef8; + sh += 1.092548 * N.x * N.z * shcoefs[4]; + sh += -1.092548 * N.z * N.y * shcoefs[5]; + sh += 0.315392 * (3.0 * N.y * N.y - 1.0) * shcoefs[6]; + sh += -1.092548 * N.x * N.y * shcoefs[7]; + sh += 0.546274 * (N.x * N.x - N.z * N.z) * shcoefs[8]; return sh; } +vec3 hl2_basis(vec3 N, vec3 cubesides[3]) +{ + vec3 irradiance = vec3(0.0); + + vec3 n_squared = N * N; + + irradiance += n_squared.x * cubesides[0]; + irradiance += n_squared.y * cubesides[1]; + irradiance += n_squared.z * cubesides[2]; + + return irradiance; +} + +vec3 compute_irradiance(vec3 N, IrradianceData ird) +{ +#if defined(IRRADIANCE_CUBEMAP) + return ird.color; +#elif defined(IRRADIANCE_SH_L2) + return spherical_harmonics_L2(N, ird.shcoefs); +#else /* defined(IRRADIANCE_HL2) */ + return hl2_basis(N, ird.cubesides); +#endif +} + vec3 get_specular_dominant_dir(vec3 N, vec3 R, float roughness) { float smoothness = 1.0 - roughness; diff --git a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl b/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl index 898c7bfc578..3997de7a22d 100644 --- a/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/bsdf_sampling_lib.glsl @@ -37,6 +37,11 @@ float pdf_ggx_reflect(float NH, float a2) return NH * a2 / D_ggx_opti(NH, a2); } +float pdf_hemisphere() +{ + return 0.5 * M_1_PI; +} + vec3 sample_ggx(float nsample, float a2, vec3 N, vec3 T, vec3 B) { vec3 Xi = hammersley_3d(nsample); @@ -52,3 +57,17 @@ vec3 sample_ggx(float nsample, float a2, vec3 N, vec3 T, vec3 B) return tangent_to_world(Ht, N, T, B); } + +vec3 sample_hemisphere(float nsample, vec3 N, vec3 T, vec3 B) +{ + vec3 Xi = hammersley_3d(nsample); + + float z = Xi.x; /* cos theta */ + float r = sqrt( 1.0f - z*z ); /* sin theta */ + float x = r * Xi.y; + float y = r * Xi.z; + + vec3 Ht = vec3(x, y, z); + + return tangent_to_world(Ht, N, T, B); +} \ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl new file mode 100644 index 00000000000..c08135aa979 --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_diffuse_frag.glsl @@ -0,0 +1,197 @@ + +uniform samplerCube probeHdr; +uniform int probeSize; +uniform float lodFactor; +uniform float lodMax; + +in vec3 worldPosition; + +out vec4 FragColor; + +#define M_4PI 12.5663706143591729 + +const mat3 CUBE_ROTATIONS[6] = mat3[]( + mat3(vec3( 0.0, 0.0, -1.0), + vec3( 0.0, -1.0, 0.0), + vec3(-1.0, 0.0, 0.0)), + mat3(vec3( 0.0, 0.0, 1.0), + vec3( 0.0, -1.0, 0.0), + vec3( 1.0, 0.0, 0.0)), + mat3(vec3( 1.0, 0.0, 0.0), + vec3( 0.0, 0.0, 1.0), + vec3( 0.0, -1.0, 0.0)), + mat3(vec3( 1.0, 0.0, 0.0), + vec3( 0.0, 0.0, -1.0), + vec3( 0.0, 1.0, 0.0)), + mat3(vec3( 1.0, 0.0, 0.0), + vec3( 0.0, -1.0, 0.0), + vec3( 0.0, 0.0, -1.0)), + mat3(vec3(-1.0, 0.0, 0.0), + vec3( 0.0, -1.0, 0.0), + vec3( 0.0, 0.0, 1.0))); + +vec3 get_cubemap_vector(vec2 co, int face) +{ + return normalize(CUBE_ROTATIONS[face] * vec3(co * 2.0 - 1.0, 1.0)); +} + +float area_element(float x, float y) +{ + return atan(x * y, sqrt(x * x + y * y + 1)); +} + +float texel_solid_angle(vec2 co, float halfpix) +{ + vec2 v1 = (co - vec2(halfpix)) * 2.0 - 1.0; + vec2 v2 = (co + vec2(halfpix)) * 2.0 - 1.0; + + return area_element(v1.x, v1.y) - area_element(v1.x, v2.y) - area_element(v2.x, v1.y) + area_element(v2.x, v2.y); +} + +vec3 octahedral_to_cubemap_proj(vec2 co) +{ + co = co * 2.0 - 1.0; + + vec2 abs_co = abs(co); + vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y)); + + if ( abs_co.x + abs_co.y > 1.0 ) { + v.xy = (abs(co.yx) - 1.0) * -sign(co.xy); + } + + return v; +} + +void main() +{ +#if defined(IRRADIANCE_SH_L2) + float pixstep = 1.0 / probeSize; + float halfpix = pixstep / 2.0; + + /* Downside: leaks negative values, very bandwidth consuming */ + int comp = int(gl_FragCoord.x) % 3 + (int(gl_FragCoord.y) % 3) * 3; + + float weight_accum = 0.0; + vec3 sh = vec3(0.0); + + for (int face = 0; face < 6; ++face) { + for (float x = halfpix; x < 1.0; x += pixstep) { + for (float y = halfpix; y < 1.0; y += pixstep) { + float weight, coef; + vec2 facecoord = vec2(x,y); + vec3 cubevec = get_cubemap_vector(facecoord, face); + + if (comp == 0) { + coef = 0.282095; + } + else if (comp == 1) { + coef = -0.488603 * cubevec.z * 2.0 / 3.0; + } + else if (comp == 2) { + coef = 0.488603 * cubevec.y * 2.0 / 3.0; + } + else if (comp == 3) { + coef = -0.488603 * cubevec.x * 2.0 / 3.0; + } + else if (comp == 4) { + coef = 1.092548 * cubevec.x * cubevec.z * 1.0 / 4.0; + } + else if (comp == 5) { + coef = -1.092548 * cubevec.z * cubevec.y * 1.0 / 4.0; + } + else if (comp == 6) { + coef = 0.315392 * (3.0 * cubevec.y * cubevec.y - 1.0) * 1.0 / 4.0; + } + else if (comp == 7) { + coef = 1.092548 * cubevec.x * cubevec.y * 1.0 / 4.0; + } + else { /* (comp == 8) */ + coef = 0.546274 * (cubevec.x * cubevec.x - cubevec.z * cubevec.z) * 1.0 / 4.0; + } + + weight = texel_solid_angle(facecoord, halfpix); + + vec4 sample = textureLod(probeHdr, cubevec, lodMax); + sh += sample.rgb * coef * weight; + weight_accum += weight; + } + } + } + sh *= M_4PI / weight_accum; + + FragColor = vec4(sh, 1.0); +#else +#if defined(IRRADIANCE_CUBEMAP) + /* Downside: Need lots of memory for storage, distortion due to octahedral mapping */ + const vec2 map_size = vec2(16.0); + const vec2 texelSize = 1.0 / map_size; + vec2 uvs = mod(gl_FragCoord.xy, map_size) * texelSize; + const float paddingSize = 1.0; + + /* Add a N pixel border to ensure filtering is correct + * for N mipmap levels. */ + uvs += uvs * texelSize * paddingSize * 2.0; + uvs -= texelSize * paddingSize; + + /* edge mirroring : only mirror if directly adjacent + * (not diagonally adjacent) */ + vec2 m = abs(uvs - 0.5) + 0.5; + vec2 f = floor(m); + if (f.x - f.y != 0.0) { + uvs = 1.0 - uvs; + } + + /* clamp to [0-1] */ + uvs = fract(uvs); + + /* get cubemap vector */ + vec3 cubevec = octahedral_to_cubemap_proj(uvs); + +#elif defined(IRRADIANCE_HL2) + /* Downside: very very low resolution (6 texels), bleed lighting because of interpolation */ + int x = int(gl_FragCoord.x) % 3; + int y = int(gl_FragCoord.y) % 2; + + vec3 cubevec = vec3(1.0, 0.0, 0.0); + + if (x == 1) { + cubevec = cubevec.yxy; + } + else if (x == 2) { + cubevec = cubevec.yyx; + } + + if (y == 1) { + cubevec = -cubevec; + } +#endif + + vec3 N, T, B, V; + + N = normalize(cubevec); + + make_orthonormal_basis(N, T, B); /* Generate tangent space */ + + /* Integrating Envmap */ + float weight = 0.0; + vec3 out_radiance = vec3(0.0); + for (float i = 0; i < sampleCount; i++) { + vec3 L = sample_hemisphere(i, N, T, B); /* Microfacet normal */ + float NL = dot(N, L); + + if (NL > 0.0) { + /* Coarse Approximation of the mapping distortion + * Unit Sphere -> Cubemap Face */ + const float dist = 4.0 * M_PI / 6.0; + float pdf = pdf_hemisphere(); + /* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */ + float lod = clamp(lodFactor - 0.5 * log2(pdf * dist), 0.0, lodMax) ; + + out_radiance += textureLod(probeHdr, L, lod).rgb * NL / pdf; + } + weight += 1.0; + } + + FragColor = vec4(out_radiance / weight, 1.0); +#endif +} \ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_frag.glsl deleted file mode 100644 index 33714c5293c..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_frag.glsl +++ /dev/null @@ -1,86 +0,0 @@ - -uniform samplerCube probeHdr; -uniform float roughnessSquared; -uniform float texelSize; -uniform float lodFactor; -uniform float lodMax; -uniform float paddingSize; - -in vec3 worldPosition; - -out vec4 FragColor; - -vec3 octahedral_to_cubemap_proj(vec2 co) -{ - co = co * 2.0 - 1.0; - - vec2 abs_co = abs(co); - vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y)); - - if ( abs_co.x + abs_co.y > 1.0 ) { - v.xy = (abs(co.yx) - 1.0) * -sign(co.xy); - } - - return v; -} - -void main() { - vec2 uvs = gl_FragCoord.xy * texelSize; - - /* Add a N pixel border to ensure filtering is correct - * for N mipmap levels. */ - uvs += uvs * texelSize * paddingSize * 2.0; - uvs -= texelSize * paddingSize; - - /* edge mirroring : only mirror if directly adjacent - * (not diagonally adjacent) */ - vec2 m = abs(uvs - 0.5) + 0.5; - vec2 f = floor(m); - if (f.x - f.y != 0.0) { - uvs = 1.0 - uvs; - } - - /* clamp to [0-1] */ - uvs = fract(uvs); - - /* get cubemap vector */ - vec3 cubevec = octahedral_to_cubemap_proj(uvs); - - vec3 N, T, B, V; - - vec3 R = normalize(cubevec); - - /* Isotropic assumption */ - N = V = R; - - make_orthonormal_basis(N, T, B); /* Generate tangent space */ - - /* Noise to dither the samples */ - /* Note : ghosting is better looking than noise. */ - // setup_noise(); - - /* Integrating Envmap */ - float weight = 0.0; - vec3 out_radiance = vec3(0.0); - for (float i = 0; i < sampleCount; i++) { - vec3 H = sample_ggx(i, roughnessSquared, N, T, B); /* Microfacet normal */ - vec3 L = -reflect(V, H); - float NL = dot(N, L); - - if (NL > 0.0) { - float NH = max(1e-8, dot(N, H)); /* cosTheta */ - - /* Coarse Approximation of the mapping distortion - * Unit Sphere -> Cubemap Face */ - const float dist = 4.0 * M_PI / 6.0; - float pdf = pdf_ggx_reflect(NH, roughnessSquared); - /* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */ - float lod = clamp(lodFactor - 0.5 * log2(pdf * dist), 0.0, lodMax) ; - - out_radiance += textureLod(probeHdr, L, lod).rgb * NL; - weight += NL; - } - } - - FragColor = vec4(out_radiance / weight, 1.0); -} \ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl new file mode 100644 index 00000000000..33714c5293c --- /dev/null +++ b/source/blender/draw/engines/eevee/shaders/lightprobe_filter_glossy_frag.glsl @@ -0,0 +1,86 @@ + +uniform samplerCube probeHdr; +uniform float roughnessSquared; +uniform float texelSize; +uniform float lodFactor; +uniform float lodMax; +uniform float paddingSize; + +in vec3 worldPosition; + +out vec4 FragColor; + +vec3 octahedral_to_cubemap_proj(vec2 co) +{ + co = co * 2.0 - 1.0; + + vec2 abs_co = abs(co); + vec3 v = vec3(co, 1.0 - (abs_co.x + abs_co.y)); + + if ( abs_co.x + abs_co.y > 1.0 ) { + v.xy = (abs(co.yx) - 1.0) * -sign(co.xy); + } + + return v; +} + +void main() { + vec2 uvs = gl_FragCoord.xy * texelSize; + + /* Add a N pixel border to ensure filtering is correct + * for N mipmap levels. */ + uvs += uvs * texelSize * paddingSize * 2.0; + uvs -= texelSize * paddingSize; + + /* edge mirroring : only mirror if directly adjacent + * (not diagonally adjacent) */ + vec2 m = abs(uvs - 0.5) + 0.5; + vec2 f = floor(m); + if (f.x - f.y != 0.0) { + uvs = 1.0 - uvs; + } + + /* clamp to [0-1] */ + uvs = fract(uvs); + + /* get cubemap vector */ + vec3 cubevec = octahedral_to_cubemap_proj(uvs); + + vec3 N, T, B, V; + + vec3 R = normalize(cubevec); + + /* Isotropic assumption */ + N = V = R; + + make_orthonormal_basis(N, T, B); /* Generate tangent space */ + + /* Noise to dither the samples */ + /* Note : ghosting is better looking than noise. */ + // setup_noise(); + + /* Integrating Envmap */ + float weight = 0.0; + vec3 out_radiance = vec3(0.0); + for (float i = 0; i < sampleCount; i++) { + vec3 H = sample_ggx(i, roughnessSquared, N, T, B); /* Microfacet normal */ + vec3 L = -reflect(V, H); + float NL = dot(N, L); + + if (NL > 0.0) { + float NH = max(1e-8, dot(N, H)); /* cosTheta */ + + /* Coarse Approximation of the mapping distortion + * Unit Sphere -> Cubemap Face */ + const float dist = 4.0 * M_PI / 6.0; + float pdf = pdf_ggx_reflect(NH, roughnessSquared); + /* http://http.developer.nvidia.com/GPUGems3/gpugems3_ch20.html : Equation 13 */ + float lod = clamp(lodFactor - 0.5 * log2(pdf * dist), 0.0, lodMax) ; + + out_radiance += textureLod(probeHdr, L, lod).rgb * NL; + weight += NL; + } + } + + FragColor = vec4(out_radiance / weight, 1.0); +} \ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/lightprobe_sh_frag.glsl b/source/blender/draw/engines/eevee/shaders/lightprobe_sh_frag.glsl deleted file mode 100644 index 1bb14b76f42..00000000000 --- a/source/blender/draw/engines/eevee/shaders/lightprobe_sh_frag.glsl +++ /dev/null @@ -1,107 +0,0 @@ - -uniform samplerCube probeHdr; -uniform int probeSize; -uniform float lodBias; - -in vec3 worldPosition; - -out vec4 FragColor; - -#define M_4PI 12.5663706143591729 - -const mat3 CUBE_ROTATIONS[6] = mat3[]( - mat3(vec3( 0.0, 0.0, -1.0), - vec3( 0.0, -1.0, 0.0), - vec3(-1.0, 0.0, 0.0)), - mat3(vec3( 0.0, 0.0, 1.0), - vec3( 0.0, -1.0, 0.0), - vec3( 1.0, 0.0, 0.0)), - mat3(vec3( 1.0, 0.0, 0.0), - vec3( 0.0, 0.0, 1.0), - vec3( 0.0, -1.0, 0.0)), - mat3(vec3( 1.0, 0.0, 0.0), - vec3( 0.0, 0.0, -1.0), - vec3( 0.0, 1.0, 0.0)), - mat3(vec3( 1.0, 0.0, 0.0), - vec3( 0.0, -1.0, 0.0), - vec3( 0.0, 0.0, -1.0)), - mat3(vec3(-1.0, 0.0, 0.0), - vec3( 0.0, -1.0, 0.0), - vec3( 0.0, 0.0, 1.0))); - -vec3 get_cubemap_vector(vec2 co, int face) -{ - return normalize(CUBE_ROTATIONS[face] * vec3(co * 2.0 - 1.0, 1.0)); -} - -float area_element(float x, float y) -{ - return atan(x * y, sqrt(x * x + y * y + 1)); -} - -float texel_solid_angle(vec2 co, float halfpix) -{ - vec2 v1 = (co - vec2(halfpix)) * 2.0 - 1.0; - vec2 v2 = (co + vec2(halfpix)) * 2.0 - 1.0; - - return area_element(v1.x, v1.y) - area_element(v1.x, v2.y) - area_element(v2.x, v1.y) + area_element(v2.x, v2.y); -} - -void main() -{ - float pixstep = 1.0 / probeSize; - float halfpix = pixstep / 2.0; - - float weight_accum = 0.0; - vec3 sh = vec3(0.0); - - int shnbr = int(floor(gl_FragCoord.x)); - - for (int face = 0; face < 6; ++face) { - for (float x = halfpix; x < 1.0; x += pixstep) { - for (float y = halfpix; y < 1.0; y += pixstep) { - float shcoef; - - vec2 facecoord = vec2(x,y); - vec3 cubevec = get_cubemap_vector(facecoord, face); - float weight = texel_solid_angle(facecoord, halfpix); - - if (shnbr == 0) { - shcoef = 0.282095; - } - else if (shnbr == 1) { - shcoef = -0.488603 * cubevec.z * 2.0 / 3.0; - } - else if (shnbr == 2) { - shcoef = 0.488603 * cubevec.y * 2.0 / 3.0; - } - else if (shnbr == 3) { - shcoef = -0.488603 * cubevec.x * 2.0 / 3.0; - } - else if (shnbr == 4) { - shcoef = 1.092548 * cubevec.x * cubevec.z * 1.0 / 4.0; - } - else if (shnbr == 5) { - shcoef = -1.092548 * cubevec.z * cubevec.y * 1.0 / 4.0; - } - else if (shnbr == 6) { - shcoef = 0.315392 * (3.0 * cubevec.y * cubevec.y - 1.0) * 1.0 / 4.0; - } - else if (shnbr == 7) { - shcoef = 1.092548 * cubevec.x * cubevec.y * 1.0 / 4.0; - } - else { /* (shnbr == 8) */ - shcoef = 0.546274 * (cubevec.x * cubevec.x - cubevec.z * cubevec.z) * 1.0 / 4.0; - } - - vec4 sample = textureLod(probeHdr, cubevec, lodBias); - sh += sample.rgb * shcoef * weight; - weight_accum += weight; - } - } - } - - sh *= M_4PI / weight_accum; - - FragColor = vec4(sh, 1.0); -} \ No newline at end of file diff --git a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl index 8f11835a1cc..6dd9bbcdf6c 100644 --- a/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl @@ -1,12 +1,13 @@ uniform int light_count; uniform int probe_count; +uniform int grid_count; uniform mat4 ProjectionMatrix; uniform mat4 ViewMatrixInverse; uniform sampler2DArray probeCubes; +uniform sampler2D irradianceGrid; uniform float lodMax; -uniform vec3 shCoefs[9]; #ifndef UTIL_TEX #define UTIL_TEX @@ -20,6 +21,10 @@ layout(std140) uniform probe_block { ProbeData probes_data[MAX_PROBE]; }; +layout(std140) uniform grid_block { + GridData grids_data[MAX_GRID]; +}; + layout(std140) uniform light_block { LightData lights_data[MAX_LIGHT]; }; @@ -305,6 +310,88 @@ float probe_attenuation(vec3 W, ProbeData pd) return fac; } +IrradianceData load_irradiance_cell(int cell, vec3 N) +{ + /* Keep in sync with diffuse_filter_probe() */ + +#if defined(IRRADIANCE_CUBEMAP) + + #define AMBIANT_CUBESIZE 8 + ivec2 cell_co = ivec2(AMBIANT_CUBESIZE); + int cell_per_row = textureSize(irradianceGrid, 0).x / cell_co.x; + cell_co.x *= cell % cell_per_row; + cell_co.y *= cell / cell_per_row; + + vec2 texelSize = 1.0 / vec2(AMBIANT_CUBESIZE); + + vec2 uvs = mapping_octahedron(N, texelSize); + uvs *= vec2(AMBIANT_CUBESIZE) / vec2(textureSize(irradianceGrid, 0)); + uvs += vec2(cell_co) / vec2(textureSize(irradianceGrid, 0)); + + IrradianceData ir; + ir.color = texture(irradianceGrid, uvs).rgb; + +#elif defined(IRRADIANCE_SH_L2) + + ivec2 cell_co = ivec2(3, 3); + int cell_per_row = textureSize(irradianceGrid, 0).x / cell_co.x; + cell_co.x *= cell % cell_per_row; + cell_co.y *= cell / cell_per_row; + + ivec3 ofs = ivec3(0, 1, 2); + + IrradianceData ir; + ir.shcoefs[0] = texelFetch(irradianceGrid, cell_co + ofs.xx, 0).rgb; + ir.shcoefs[1] = texelFetch(irradianceGrid, cell_co + ofs.yx, 0).rgb; + ir.shcoefs[2] = texelFetch(irradianceGrid, cell_co + ofs.zx, 0).rgb; + ir.shcoefs[3] = texelFetch(irradianceGrid, cell_co + ofs.xy, 0).rgb; + ir.shcoefs[4] = texelFetch(irradianceGrid, cell_co + ofs.yy, 0).rgb; + ir.shcoefs[5] = texelFetch(irradianceGrid, cell_co + ofs.zy, 0).rgb; + ir.shcoefs[6] = texelFetch(irradianceGrid, cell_co + ofs.xz, 0).rgb; + ir.shcoefs[7] = texelFetch(irradianceGrid, cell_co + ofs.yz, 0).rgb; + ir.shcoefs[8] = texelFetch(irradianceGrid, cell_co + ofs.zz, 0).rgb; + +#else /* defined(IRRADIANCE_HL2) */ + + ivec2 cell_co = ivec2(3, 2); + int cell_per_row = textureSize(irradianceGrid, 0).x / cell_co.x; + cell_co.x *= cell % cell_per_row; + cell_co.y *= cell / cell_per_row; + + ivec3 is_negative = ivec3(step(0.0, -N)); + + IrradianceData ir; + ir.cubesides[0] = texelFetch(irradianceGrid, cell_co + ivec2(0, is_negative.x), 0).rgb; + ir.cubesides[1] = texelFetch(irradianceGrid, cell_co + ivec2(1, is_negative.y), 0).rgb; + ir.cubesides[2] = texelFetch(irradianceGrid, cell_co + ivec2(2, is_negative.z), 0).rgb; + +#endif + + return ir; +} + +vec3 get_cell_color(ivec3 localpos, ivec3 gridres, int offset, vec3 ir_dir) +{ + /* Keep in sync with update_irradiance_probe */ + + int cell = offset + localpos.z + localpos.y * gridres.z + localpos.x * gridres.z * gridres.y; + IrradianceData ir_data = load_irradiance_cell(cell, ir_dir); + return compute_irradiance(ir_dir, ir_data); +} + +vec3 trilinear_filtering(vec3 weights, + vec3 cell0_col, vec3 cell_x_col, vec3 cell_y_col, vec3 cell_z_col, vec3 cell_xy_col, vec3 cell_xz_col, vec3 cell_yz_col, vec3 cell_xyz_col) +{ + vec3 x_mix_0 = mix(cell0_col, cell_x_col, weights.x); + vec3 x_mix_y = mix(cell_y_col, cell_xy_col, weights.x); + vec3 x_mix_z = mix(cell_z_col, cell_xz_col, weights.x); + vec3 x_mix_yz = mix(cell_yz_col, cell_xyz_col, weights.x); + vec3 y_mix_0 = mix(x_mix_0, x_mix_y, weights.y); + vec3 y_mix_z = mix(x_mix_z, x_mix_yz, weights.y); + vec3 z_mix1 = mix(y_mix_0, y_mix_z, weights.z); + return z_mix1; +} + vec3 eevee_surface_lit(vec3 world_normal, vec3 albedo, vec3 f0, float roughness, float ao) { roughness = clamp(roughness, 1e-8, 0.9999); @@ -375,12 +462,71 @@ vec3 eevee_surface_lit(vec3 world_normal, vec3 albedo, vec3 f0, float roughness, } } + for (int i = 0; i < MAX_GRID && i < grid_count; ++i) { + GridData gd = grids_data[i]; + + vec3 localpos = (gd.localmat * vec4(sd.W, 1.0)).xyz; + + vec3 localpos_max = vec3(gd.g_resolution + ivec3(1)) - localpos; + float fade = min(1.0, min_v3(min(localpos_max, localpos))); + + if (fade > 0.0) { + localpos -= 1.0; + vec3 localpos_floored = floor(localpos); + vec3 trilinear_weight = fract(localpos); /* fract(-localpos) */ + + float weight_accum = 0.0; + vec3 irradiance_accum = vec3(0.0); + + /* For each neighboor cells */ + for (int i = 0; i < 8; ++i) { + ivec3 offset = ivec3(i, i >> 1, i >> 2) & ivec3(1); + vec3 cell_cos = clamp(localpos_floored + vec3(offset), vec3(0.0), vec3(gd.g_resolution) - 1.0); + + /* We need this because we render probes in world space (so we need light vector in WS). + * And rendering them in local probe space is too much problem. */ + vec3 ws_cell_location = gd.g_corner + + (gd.g_increment_x * cell_cos.x + + gd.g_increment_y * cell_cos.y + + gd.g_increment_z * cell_cos.z); + vec3 ws_point_to_cell = ws_cell_location - sd.W; + vec3 ws_light = normalize(ws_point_to_cell); + + vec3 trilinear = mix(1 - trilinear_weight, trilinear_weight, offset); + float weight = trilinear.x * trilinear.y * trilinear.z; + + /* Smooth backface test */ + // weight *= max(0.005, dot(ws_light, sd.N)); + + /* Avoid zero weight */ + weight = max(0.00001, weight); + + vec3 color = get_cell_color(ivec3(cell_cos), gd.g_resolution, gd.g_offset, sd.N); + + weight_accum += weight; + irradiance_accum += color * weight; + } + + vec3 indirect_diffuse = irradiance_accum / weight_accum; + + // float influ_diff = min(fade, (1.0 - spec_accum.a)); + float influ_diff = min(1.0, (1.0 - spec_accum.a)); + + diff_accum.rgb += indirect_diffuse * influ_diff; + diff_accum.a += influ_diff; + + // return texture(irradianceGrid, sd.W.xy).rgb; + } + } + /* World probe */ if (spec_accum.a < 1.0 || diff_accum.a < 1.0) { ProbeData pd = probes_data[0]; + IrradianceData ir_data = load_irradiance_cell(0, sd.N); + vec3 spec = textureLod_octahedron(probeCubes, vec4(spec_dir, 0), roughness * lodMax).rgb; - vec3 diff = spherical_harmonics(sd.N, pd.shcoefs); + vec3 diff = compute_irradiance(sd.N, ir_data); diff_accum.rgb += diff * (1.0 - diff_accum.a); spec_accum.rgb += spec * (1.0 - spec_accum.a); diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 37b67cd1237..71b0396dc49 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -201,7 +201,7 @@ void DRW_framebuffer_texture_attach(struct GPUFrameBuffer *fb, struct GPUTexture void DRW_framebuffer_cubeface_attach(struct GPUFrameBuffer *fb, struct GPUTexture *tex, int slot, int face, int mip); void DRW_framebuffer_texture_detach(struct GPUTexture *tex); void DRW_framebuffer_blit(struct GPUFrameBuffer *fb_read, struct GPUFrameBuffer *fb_write, bool depth); -void DRW_framebuffer_viewport_size(struct GPUFrameBuffer *UNUSED(fb_read), int w, int h); +void DRW_framebuffer_viewport_size(struct GPUFrameBuffer *fb_read, int x, int y, int w, int h); void DRW_framebuffer_free(struct GPUFrameBuffer *fb); #define DRW_FRAMEBUFFER_FREE_SAFE(fb) do { \ if (fb != NULL) { \ diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 77206a256f2..62a95bcc22b 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -2103,9 +2103,9 @@ void DRW_framebuffer_blit(struct GPUFrameBuffer *fb_read, struct GPUFrameBuffer GPU_framebuffer_blit(fb_read, 0, fb_write, 0, depth); } -void DRW_framebuffer_viewport_size(struct GPUFrameBuffer *UNUSED(fb_read), int w, int h) +void DRW_framebuffer_viewport_size(struct GPUFrameBuffer *UNUSED(fb_read), int x, int y, int w, int h) { - glViewport(0, 0, w, h); + glViewport(x, y, w, h); } /* Use color management profile to draw texture to framebuffer */ -- cgit v1.2.3